02--C编程细节整理(一)
用C语言比较多,这篇是平时攒下的。有些内容在工作后可能会很常见,但是不用容易忘,所以就写篇博客吧。
1. printf的用法
%*可以用来跳过字符,可以用于未知缩进。像下面一样.
for(i = 1; i < 10; i++)
{
printf("%*c\r%*c\n", 9 - abs(i - 5), '*', abs(i - 5) + 1, '*');
}
%[]可以用来读取指定的内容,%[^]可以用来忽略指定内容(正则表达式?)
%m可以不带参数,输出产生的错误信息
2. 二.关于define的用法
#是将后面所跟随内容字符串化,##则是将前后内容连接起来
还有linux下各种多层宏定义。。。宏定义可谓博大精深啊。。
3. .关于setjmp和longjmp
目前在linux多线程的头文件pthreadtypes.h中包含了此函数的头文件。
这个主要作用是用来从异常恢复的.也可以做"软重启".
因为setjmp把环境变量的设置和栈的值都保存在参数env数组中,当longjmp时,根据其值来恢复setjmp时的位置.
这个env通常是一个全局变量.毕竟这是要跨越函数的.
4. #include
其实这个东西可以写在任何的地方,你可以试试将main里面代码写在某个文件中,然后在main中包含这个文件。代码照样运行。
5. .sizeof
当sizeof一个数组名的时候,如果数组和sizeof在同一个代码块中,那么返回数组长,否则返回指针大小。
(当数组名传递给函数的时候传递的仅仅是个指针)
6. .volatile
让每次读取数据都从内存中读取,而不是在其他位置。可以防止部分编译器优化。在嵌入式中和多线程中有所应用。
最近学linux发现还可以用用来描述自己定义的锁!从而避免了编译器直接把锁优化了。
7. main函数的奇妙用法.
其实main函数是可以递归的。
8. 关于||和&&
其实||的优先级比&&低。
9. 如何让返回值有多重类型?
用union定义一个结构体,那么就可以选择返回多种类型了.
10. 为何通常c中的指针大小为4个字节?
一种可能是CPU的地址线只有32根.另一种是由于最大程序限制在4G=2^32B来确定的.
11. 当函数返回double值的时候是80位的.
所以在返回值赋给一个double变量的时候会损失精度.
12. . __cdecl关键字
这个关键字是用来定义函数参数入栈顺序从右向左的.并且我在VC上测试的时候发现是先计算再入栈.这个估计用的很少吧..
刚学AT&T的32位汇编,发现其实是用pushl来将参数逐一入栈,然后弹出传给函数的.估计加了__cdecl的话就会使用pushl的方式来传值吧.
貌似这样就只能是从右到左传参数给函数啊..嗯..还需学习..
13. 内存对齐
结构体和联合体中,比如struct{int a;char b; double c};那么这个结构体大小为16(32位机),不是13.这一点我经常忘掉..但是发现这个错误的时候又很容易想起来..
14. 位段
这个是1位1位的来分配的..(原来还以为是按字节来的..)
这个东西刚开始觉得似乎蛮有用.但是似乎用途只有在网络协议需要这么斤斤计较..
百度百科上说只能是unsigned和int类型指定,但是我在dev c++上写c程序,也可以用char类型.
并且这个后面:加的值不能超过原来的类型的大小.
考虑到存储器的对齐和效率应该应用不多.
15. 关于union的用法
其实这个可以用来显示一些不能见的内容..比方说将一个指针和一个int定义在一起..如果指针是不能见的,那么可以通过以int的形式来访问..
union a{
point A;
};
如果A不能直接访问,那么就可以用B来访问。也算是编译器的漏洞吧。
我记得C++好像有一种类型指针是这样的,不允许见到其值.但是可以用int来知道其值.
原先刷题还遇到过一个问题.VC不能用long long类型的于是想用union{char a[8];int a}.这样来变成long long的..结果printf貌似只能识别到32位长度的大小..
所以还是老老实实去linux下编译了...
16. 关于typedef与const联合使用时一个值得注意的地方
typedef char* zifu;
const zifu a;
那么a是个什么样的指针呢?是不能改变所指向地址的值还是不能改变自身?
这个问题我一开是也想错了...想成用define的效果了...其实上面的定义等价于
char* const a;
也就是说不能改变指针自身,但是可以改变所指向的值..typedef与define相比会扩展一些内容.
17. .关于typedef的其他容易忘记的方法.
函数指针 typedef int (*)(char ) a;
那么 a c; c就是一个返回值为int,参数为char类型的函数指针.
避免弄成int (*)(*f)(int ,char)(char)这种一团麻乱的样子..很容易就看错的..
申请数组 typedef int int_shuzu[10];
那么int_shuzu a; a就是一个数组指针了..这种很容易看不懂的..
18. 关于参数传递
void fun(a,b,c)
int a,b,c;
{}
你很可能没见过这种传值方式,不过它确实是可以编译成功的。可以少写几个int~
19. assert
C的异常处理宏。最近才发现有这个东西。这个比try--catch简单多了。用于判断某个条件是否满足,不满足的话跳转到自己的函数上来显示信息。用来调试程序很方便的。建议学习~
20. 关于如何把递归用函数指针来代替的方式.
这个方法很奇葩..感觉还是能有点用的.
#include <stdio.h>
typedef int (*PFUN)(int);
PFUN ptr[2];
int end(int a){
return 0;
}
int sum(int a){
return a + ptr[!!a](a - 1);
}
int main(){
int T, m;
scanf("%d", &T);
while(T--){
ptr[0] = end;
ptr[1] = sum;
scanf("%d", &m);
printf("%d\n", sum(m));
}
return 0;
}
这是通过函数指针加位运算来模仿递归...目标是求1+2+...+n..
两次逻辑取反刚好把大于1的数转为1,原来为零仍然为零.
这个技巧就当作好玩吧~~
21. 字符串常量
sizeof( 'a' );你猜猜是几?结果是4!!!!这是C里面估计很少人注意到的差别..当然也没什么用就是了..(C++里面是1,但是C++还有'aa'这种单引号里面两个字符的情况..结果是4)
22. .强制转换
这是我看一个opencv程序中发现的。你可能永远都只将强制转换用于赋值符号的右边,但其实在左边也是可以的.
就向这样
((float*)(img->imageData + i* img->widthStep))[j]=mapp[i][j];
23. 重定向调试
freopen("文件名","r",stdin);写在所有从输入流读取信息的函数之前。
将标准输入重定向为文件中的内容。对于那些喜欢刷ACM的人来说,用了这个函数调试真是方便多了啊。
对于stdin这个是操作系统默认分给C程序的3个流指针之一,在linux下貌似文件描述符号0就是指stdin。当然还有1和2,分别是stdout,stderr。
三个指针定义在stdio.h中。
24. 关于指针与int类型
其实int类型可以赋值给指针类型!!
以前一直记得是必须加强制转换的,但是在一本书上看到居然可以直接这样!!
只是给个warming!!这对于底层程序员是多么方便的事情啊!!!
在dev c++和VC上测试通过。还可以换成short*,double*,long*~
int i;
char*p;
i=1354;
p = i;
25. 关于宏函数与逗号表达式
最近看操作系统内核源码发现的。。
其实逗号表达式和宏是一对好基友啊~不仅让宏函数的功能更多,还可以让宏函数带上返回值!
并且和内联函数一样高效率!!
好比说交换a和b两个值并且返回它们的和的宏函数,可以这样写:
#define swap(a,b,c) (c=a,a=b,b=c,a+b)
这样只用保证abc三个变量都是同一类型即可,就不用写那么多函数了~~
是不是有点类似C++的模版啊~~~还提高了不少开发效率呢~~~
由于是宏函数,所以就无法用于下面的回调函数中了,不过宏函数本身就这么方便和高效,这点缺点还是可以容忍的.
26. .回调函数
原来一直用qsort,但是不知道还有回调函数这一名字.
这就有点像C++中的泛型算法了.可以把判断的部分通过函数封装起来.
将判断方法通过参数的方式传递给函数,然后实现泛型~~
void qsort(void *base,int nelem,int width,int (*fcmp)(const void *,const void *));
注意最后一个参数就是排序的条件判断函数,也就是回调函数。
类似的还有多线程中指定的入口函数。
27. 关于_exit和exit的差别
_exit终止调用进程,但不关闭文件,不清除输出缓存,也不调用出口函数。
exit函数将终止调用进程。在退出程序之前,所有文件关闭,缓冲输出内容.
这实在其他地方直接抄来的.在linux下如果fork子进程来做点事的话可能会用到.
28. .关于int a[0];的用法。
原来学c的时候弄过这个,但只是当作实验。。没想到还真的有人会用这个东西。
主要用来当作内存地址的标签用。可以通过编译器来记住指针地址,但是不会消耗实际内存。
只有当分配空间之后才能使用。这是得多么节约空间才会这么写啊。。。。
注意,这貌似是C99标准才支持的一个用法,并且 gcc编译的时候需要带上-pedantic选项。
29. 关于unsigned整数做循环变量
unsigned 0-1答案的二进制码是全一啊!!!
原来写单片机程序就被坑过..编译器虽然会警告,但是通常都被我忽略了...
记住,只要是for中的循环变量比较,要么全是有符号的,要么全是无符号的!!
30. 关于为什么要使用二进制读文件和写文件
这是学python的时候晓得的。。当你的文件保存为二进制的话,就可以用你的方法来解析文件。
如果不用二进制,那么就得看操作系统了。所以,对于保存独有的数据,多用二进制保存吧。话说数据库的数据文件都是这种方式读写文件的吧。
而且二进制也不容被人了解。。好多游戏的文件都是明文的呢。。。
31. .sizeof(表达式)的问题
sizeof的表达式并不计算!!!只得出是什么类型的而已!!
int a=1;
printf("%d\n",sizeof(a=2));
printf("%d\n",a);
结果是4,1
测试环境dev c++
32. 三目运算符与if/else哪个效率高?
个人在gcc下测试,发现汇编没有差别。不知道其他平台是否也是这样。当然我测试的方式可能不对。下文还会再说三目运算符的。
在CSAPP中说gcc会优化三目运算符。所以估计在某些特定情况下效率比if高点。
33. .一种简单的宏交换
#define swap(a,b) (a ^= b^= a^= b)
多么简洁啊~虽然以前也写过这种异或交换,但是从来想过还能用连等啊~~
34. long double
这是C99标准中的新类型.在<深入理解计算机系统>里面说是扩展浮点数.有80个二进制位.
这80位刚好对应intel的CPU中的FPU.我的ATT汇编总结中讲过这个寄存器,原来以为只是浮点返回值和浮点运算中间过程用到.
现在看来还可以当作变量用.需要注意的是在gcc下一般是会按4字节对齐的,所以不是10个字节,而是12个字节.
35. 关于<符号
这是看CSAPP上的.说<符号对于分支跳转指令来说预测的准确率并不是特别的好.
所以刚好想到c++中循环的条件判断用!=的习惯.虽然C++ primer上说是因为迭代器可能在循环中改变的原因,但是从这个来看还是养成写!=的习惯比较好.
可能得到的效率不一定提高特别多,但是对于高并发高吞吐量的系统来说,我猜这点细微的差别还是有变化的.
还是养成写!=的习惯吧~
36. 关于三目运算符
一般情况下没什么问题,但是在某些编译器下,对于 xp?*xp:0这种xp是NULL指针的时候就不能用了,否则会造成内存错误!
这时因为编译器对三目运算符进行了优化。先算出后面两个表达式的结果,然后再来判断条件。
不过我用gcc测试了一下,发现gcc不是这样的,所以在gcc下这么用没什么问题。
以后见到这种情况别奇怪就是了~
37. .关于宏函数中使用 #和##
之前一直不晓得这有什么实际用处,但是在描述路径的时候很有用.
好比说你之前文件在inc/现在改成在incc/下.那么完全可以写一个函数用来拼接
#define f(PATH,FILE) # x##y
这种形式.那么更换文件目录的时候就很方便了.
38. .关于定义多个同名全局变量
在同一文件中是不能定义多个同名全局变量的,但是在多个文件中就可以。
CSAPP上写的.gcc的连接器在linking的时候额会把全局变量分为弱和强两种状态.
并且优先选择强状态,但可能类型会存在问题
好比在A文件如下定义了x和y
void other();
int x=112233;
int y=332211;
int main(){
prntf("x=0x%x y=0x%x \n",x,y);
other();
prntf("x=0x%x y=0x%x \n",x,y);
}
在另一个文件中也定义了x,但类型是double
double x;
void other(){
x=-0.0;
}
然后gcc 两个文件。有一个警告。暂时先不管。
你运行了之后会发现,y的值居然变了!
也就是说,在other中,仍然认为x是double类型的!
double由于是8字节而int是4字节,给x赋值会覆盖原来的y!
表示如果other函数中换成纯粹的0.那么y就是0!
39. .restrict关键字
这个常常忘记是什么意思...是C99新加的标准.让编译器翻译的时候,对其内存的操作只能通过其指针来完成.方便编译器优化.
不过C++里面好像还没有这个关键字.
40. .const与define定义一个常量的区别
const指定的是一个变量,而define相当于直接替换.
const是可以带指定类型的,define则是默认类型的.
同时const定义的变量还可以用取地址符&来得到其值.
41. 关于GCC内联汇编
用一般的asm volatile()形式的话,在默认情况下是没什么问题的。但是如果在编译条件中加上了-std=c99的话则不行。
具体我也不清楚怎么搞。。没查到什么有用的东西。自己试过在__asm__volatile_和__asm volatile这些形式都不行。
有高手会的话还请指教!
42. .关于GNU C的__attribute__
这个是GNU C的一个特色。可以设置函数,变量,以及类型的属性值。通常不用管这个东西,但是有一些软件有要求会需要用到这个。
具体我就不展开了,百度可以搜出很多内容。这里提一个__attribute__(( aligned(n))).在结构体后面加上后可以按照n字节对齐。默认按最大变量字节对齐。
43. .关于可变参数宏的实现(va_arg).
有一部分靠的是编译器支持"..."这种参数形式,然后保证不等长参数都可以完全入栈。实际上,根据C标准的入栈规则,参数是依次从右往左入栈的。只要顺着esp寄存器来找,那么就可以依次读出参数的其值。需要注意的是读出多少个字节,不然栈就乱了。所以必须在读出的时候加上参数类型。printf实现中也是先根据字符串中的类型再读的。我个人实测后确实如此~
测试结果如下,Linux下gcc编译。
如图所示,显然可以看到printf中实现的算法是先根据字符串对应的格式来从内存中读取的。也就是说如果类型选错了,内容显示一定乱了。
44. 关于返回结构体
最近看编译器实现方面的东西,貌似C++那种传递性的使用(如cout<< xxx<<<xx;这种)应该是不难实现的。然后我就上测试了下,我发现GCC下函数返回结构体是可以直接在后面加上.成员来访问成员的。。结果如下
45. 关于**指针和(*)[]指针
我发现很多人还是不清楚**指针和(*)[]指针的差别。第一个我就叫它双层指针,第二个叫行指针。有人喜欢把第一个叫二维指针吧,如果这么叫很容易晕。
实际上前者是一个指向指针的指针,而后者是一个带长度的指针。前者可以指向后者。后者与一般指针的差别就是你p++的时候是移动多个变量长度!其他指针只移动一个变量长度。这里的长度说的是存储变量地址的大小。前者指向一个指针变量的地址,而后者多用于指向一个二维数组的地址。所以传递二维数组参数的时候,用后者!
46. 关于%模运算
其实是可以模负数的!但和数学定义不同,对于负数的模仍然带有符号。
用C语言比较多,这篇是平时攒下的。有些内容在工作后可能会很常见,但是不用容易忘,所以就写篇博客吧。
1. printf的用法
%*可以用来跳过字符,可以用于未知缩进。像下面一样.
for(i = 1; i < 10; i++)
{
printf("%*c\r%*c\n", 9 - abs(i - 5), '*', abs(i - 5) + 1, '*');
}
%[]可以用来读取指定的内容,%[^]可以用来忽略指定内容(正则表达式?)
%m可以不带参数,输出产生的错误信息
2. 二.关于define的用法
#是将后面所跟随内容字符串化,##则是将前后内容连接起来
还有linux下各种多层宏定义。。。宏定义可谓博大精深啊。。
3. .关于setjmp和longjmp
目前在linux多线程的头文件pthreadtypes.h中包含了此函数的头文件。
这个主要作用是用来从异常恢复的.也可以做"软重启".
因为setjmp把环境变量的设置和栈的值都保存在参数env数组中,当longjmp时,根据其值来恢复setjmp时的位置.
这个env通常是一个全局变量.毕竟这是要跨越函数的.
4. #include
其实这个东西可以写在任何的地方,你可以试试将main里面代码写在某个文件中,然后在main中包含这个文件。代码照样运行。
5. .sizeof
当sizeof一个数组名的时候,如果数组和sizeof在同一个代码块中,那么返回数组长,否则返回指针大小。
(当数组名传递给函数的时候传递的仅仅是个指针)
6. .volatile
让每次读取数据都从内存中读取,而不是在其他位置。可以防止部分编译器优化。在嵌入式中和多线程中有所应用。
最近学linux发现还可以用用来描述自己定义的锁!从而避免了编译器直接把锁优化了。
7. .main函数的奇妙用法.
其实main函数是可以递归的。
8. .关于||和&&
其实||的优先级比&&低。
9. ,如何让返回值有多重类型?
用union定义一个结构体,那么就可以选择返回多种类型了.
10. .为何通常c中的指针大小为4个字节?
一种可能是CPU的地址线只有32根.另一种是由于最大程序限制在4G=2^32B来确定的.
11. .当函数返回double值的时候是80位的.
所以在返回值赋给一个double变量的时候会损失精度.
12. . __cdecl关键字
这个关键字是用来定义函数参数入栈顺序从右向左的.并且我在VC上测试的时候发现是先计算再入栈.这个估计用的很少吧..
刚学AT&T的32位汇编,发现其实是用pushl来将参数逐一入栈,然后弹出传给函数的.估计加了__cdecl的话就会使用pushl的方式来传值吧.
貌似这样就只能是从右到左传参数给函数啊..嗯..还需学习..
13. 内存对齐
结构体和联合体中,比如struct{int a;char b; double c};那么这个结构体大小为16(32位机),不是13.这一点我经常忘掉..但是发现这个错误的时候又很容易想起来..
14. .位段
这个是1位1位的来分配的..(原来还以为是按字节来的..)
这个东西刚开始觉得似乎蛮有用.但是似乎用途只有在网络协议需要这么斤斤计较..
百度百科上说只能是unsigned和int类型指定,但是我在dev c++上写c程序,也可以用char类型.
并且这个后面:加的值不能超过原来的类型的大小.
考虑到存储器的对齐和效率应该应用不多.
15. .关于union的用法
其实这个可以用来显示一些不能见的内容..比方说将一个指针和一个int定义在一起..如果指针是不能见的,那么可以通过以int的形式来访问..
union a{
point A;
};
如果A不能直接访问,那么就可以用B来访问。也算是编译器的漏洞吧。
我记得C++好像有一种类型指针是这样的,不允许见到其值.但是可以用int来知道其值.
原先刷题还遇到过一个问题.VC不能用long long类型的于是想用union{char a[8];int a}.这样来变成long long的..结果printf貌似只能识别到32位长度的大小..
所以还是老老实实去linux下编译了...
16. .关于typedef与const联合使用时一个值得注意的地方
typedef char* zifu;
const zifu a;
那么a是个什么样的指针呢?是不能改变所指向地址的值还是不能改变自身?
这个问题我一开是也想错了...想成用define的效果了...其实上面的定义等价于
char* const a;
也就是说不能改变指针自身,但是可以改变所指向的值..typedef与define相比会扩展一些内容.
17. .关于typedef的其他容易忘记的方法.
函数指针 typedef int (*)(char ) a;
那么 a c; c就是一个返回值为int,参数为char类型的函数指针.
避免弄成int (*)(*f)(int ,char)(char)这种一团麻乱的样子..很容易就看错的..
申请数组 typedef int int_shuzu[10];
那么int_shuzu a; a就是一个数组指针了..这种很容易看不懂的..
18. .关于参数传递
void fun(a,b,c)
int a,b,c;
{}
你很可能没见过这种传值方式,不过它确实是可以编译成功的。可以少写几个int~
19. .assert
C的异常处理宏。最近才发现有这个东西。这个比try--catch简单多了。用于判断某个条件是否满足,不满足的话跳转到自己的函数上来显示信息。用来调试程序很方便的。建议学习~
20. 关于如何把递归用函数指针来代替的方式.
这个方法很奇葩..感觉还是能有点用的.
#include <stdio.h>
typedef int (*PFUN)(int);
PFUN ptr[2];
int end(int a){
return 0;
}
int sum(int a){
return a + ptr[!!a](a - 1);
}
int main(){
int T, m;
scanf("%d", &T);
while(T--){
ptr[0] = end;
ptr[1] = sum;
scanf("%d", &m);
printf("%d\n", sum(m));
}
return 0;
}
这是通过函数指针加位运算来模仿递归...目标是求1+2+...+n..
两次逻辑取反刚好把大于1的数转为1,原来为零仍然为零.
这个技巧就当作好玩吧~~
21. 字符串常量
sizeof( 'a' );你猜猜是几?结果是4!!!!这是C里面估计很少人注意到的差别..当然也没什么用就是了..(C++里面是1,但是C++还有'aa'这种单引号里面两个字符的情况..结果是4)
22. .强制转换
这是我看一个opencv程序中发现的。你可能永远都只将强制转换用于赋值符号的右边,但其实在左边也是可以的.
就向这样
((float*)(img->imageData + i* img->widthStep))[j]=mapp[i][j];
23. 重定向调试
freopen("文件名","r",stdin);写在所有从输入流读取信息的函数之前。
将标准输入重定向为文件中的内容。对于那些喜欢刷ACM的人来说,用了这个函数调试真是方便多了啊。
对于stdin这个是操作系统默认分给C程序的3个流指针之一,在linux下貌似文件描述符号0就是指stdin。当然还有1和2,分别是stdout,stderr。
三个指针定义在stdio.h中。
24. 关于指针与int类型
其实int类型可以赋值给指针类型!!
以前一直记得是必须加强制转换的,但是在一本书上看到居然可以直接这样!!
只是给个warming!!这对于底层程序员是多么方便的事情啊!!!
在dev c++和VC上测试通过。还可以换成short*,double*,long*~
int i;
char*p;
i=1354;
p = i;
25. 关于宏函数与逗号表达式
最近看操作系统内核源码发现的。。
其实逗号表达式和宏是一对好基友啊~不仅让宏函数的功能更多,还可以让宏函数带上返回值!
并且和内联函数一样高效率!!
好比说交换a和b两个值并且返回它们的和的宏函数,可以这样写:
#define swap(a,b,c) (c=a,a=b,b=c,a+b)
这样只用保证abc三个变量都是同一类型即可,就不用写那么多函数了~~
是不是有点类似C++的模版啊~~~还提高了不少开发效率呢~~~
由于是宏函数,所以就无法用于下面的回调函数中了,不过宏函数本身就这么方便和高效,这点缺点还是可以容忍的.
26. .回调函数
原来一直用qsort,但是不知道还有回调函数这一名字.
这就有点像C++中的泛型算法了.可以把判断的部分通过函数封装起来.
将判断方法通过参数的方式传递给函数,然后实现泛型~~
void qsort(void *base,int nelem,int width,int (*fcmp)(const void *,const void *));
注意最后一个参数就是排序的条件判断函数,也就是回调函数。
类似的还有多线程中指定的入口函数。
27. 关于_exit和exit的差别
_exit终止调用进程,但不关闭文件,不清除输出缓存,也不调用出口函数。
exit函数将终止调用进程。在退出程序之前,所有文件关闭,缓冲输出内容.
这实在其他地方直接抄来的.在linux下如果fork子进程来做点事的话可能会用到.
28. .关于int a[0];的用法。
原来学c的时候弄过这个,但只是当作实验。。没想到还真的有人会用这个东西。
主要用来当作内存地址的标签用。可以通过编译器来记住指针地址,但是不会消耗实际内存。
只有当分配空间之后才能使用。这是得多么节约空间才会这么写啊。。。。
注意,这貌似是C99标准才支持的一个用法,并且 gcc编译的时候需要带上-pedantic选项。
29. 关于unsigned整数做循环变量
unsigned 0-1答案的二进制码是全一啊!!!
原来写单片机程序就被坑过..编译器虽然会警告,但是通常都被我忽略了...
记住,只要是for中的循环变量比较,要么全是有符号的,要么全是无符号的!!
30. 关于为什么要使用二进制读文件和写文件
这是学python的时候晓得的。。当你的文件保存为二进制的话,就可以用你的方法来解析文件。
如果不用二进制,那么就得看操作系统了。所以,对于保存独有的数据,多用二进制保存吧。话说数据库的数据文件都是这种方式读写文件的吧。
而且二进制也不容被人了解。。好多游戏的文件都是明文的呢。。。
31. .sizeof(表达式)的问题
sizeof的表达式并不计算!!!只得出是什么类型的而已!!
int a=1;
printf("%d\n",sizeof(a=2));
printf("%d\n",a);
结果是4,1
测试环境dev c++
32. 三目运算符与if/else哪个效率高?
个人在gcc下测试,发现汇编没有差别。不知道其他平台是否也是这样。当然我测试的方式可能不对。下文还会再说三目运算符的。
在CSAPP中说gcc会优化三目运算符。所以估计在某些特定情况下效率比if高点。
33. .一种简单的宏交换
#define swap(a,b) (a ^= b^= a^= b)
多么简洁啊~虽然以前也写过这种异或交换,但是从来想过还能用连等啊~~
34. long double
这是C99标准中的新类型.在<深入理解计算机系统>里面说是扩展浮点数.有80个二进制位.
这80位刚好对应intel的CPU中的FPU.我的ATT汇编总结中讲过这个寄存器,原来以为只是浮点返回值和浮点运算中间过程用到.
现在看来还可以当作变量用.需要注意的是在gcc下一般是会按4字节对齐的,所以不是10个字节,而是12个字节.
35. .关于<符号
这是看CSAPP上的.说<符号对于分支跳转指令来说预测的准确率并不是特别的好.
所以刚好想到c++中循环的条件判断用!=的习惯.虽然C++ primer上说是因为迭代器可能在循环中改变的原因,但是从这个来看还是养成写!=的习惯比较好.
可能得到的效率不一定提高特别多,但是对于高并发高吞吐量的系统来说,我猜这点细微的差别还是有变化的.
还是养成写!=的习惯吧~
36. .关于三目运算符
一般情况下没什么问题,但是在某些编译器下,对于 xp?*xp:0这种xp是NULL指针的时候就不能用了,否则会造成内存错误!
这时因为编译器对三目运算符进行了优化。先算出后面两个表达式的结果,然后再来判断条件。
不过我用gcc测试了一下,发现gcc不是这样的,所以在gcc下这么用没什么问题。
以后见到这种情况别奇怪就是了~
37. .关于宏函数中使用 #和##
之前一直不晓得这有什么实际用处,但是在描述路径的时候很有用.
好比说你之前文件在inc/现在改成在incc/下.那么完全可以写一个函数用来拼接
#define f(PATH,FILE) # x##y
这种形式.那么更换文件目录的时候就很方便了.
38. .关于定义多个同名全局变量
在同一文件中是不能定义多个同名全局变量的,但是在多个文件中就可以。
CSAPP上写的.gcc的连接器在linking的时候额会把全局变量分为弱和强两种状态.
并且优先选择强状态,但可能类型会存在问题
好比在A文件如下定义了x和y
void other();
int x=112233;
int y=332211;
int main(){
prntf("x=0x%x y=0x%x \n",x,y);
other();
prntf("x=0x%x y=0x%x \n",x,y);
}
在另一个文件中也定义了x,但类型是double
double x;
void other(){
x=-0.0;
}
然后gcc 两个文件。有一个警告。暂时先不管。
你运行了之后会发现,y的值居然变了!
也就是说,在other中,仍然认为x是double类型的!
double由于是8字节而int是4字节,给x赋值会覆盖原来的y!
表示如果other函数中换成纯粹的0.那么y就是0!
39. .restrict关键字
这个常常忘记是什么意思...是C99新加的标准.让编译器翻译的时候,对其内存的操作只能通过其指针来完成.方便编译器优化.
不过C++里面好像还没有这个关键字.
40. .const与define定义一个常量的区别
const指定的是一个变量,而define相当于直接替换.
const是可以带指定类型的,define则是默认类型的.
同时const定义的变量还可以用取地址符&来得到其值.
41. 关于GCC内联汇编
用一般的asm volatile()形式的话,在默认情况下是没什么问题的。但是如果在编译条件中加上了-std=c99的话则不行。
具体我也不清楚怎么搞。。没查到什么有用的东西。自己试过在__asm__volatile_和__asm volatile这些形式都不行。
有高手会的话还请指教!
42. .关于GNU C的__attribute__
这个是GNU C的一个特色。可以设置函数,变量,以及类型的属性值。通常不用管这个东西,但是有一些软件有要求会需要用到这个。
具体我就不展开了,百度可以搜出很多内容。这里提一个__attribute__(( aligned(n))).在结构体后面加上后可以按照n字节对齐。默认按最大变量字节对齐。
43. .关于可变参数宏的实现(va_arg).
有一部分靠的是编译器支持"..."这种参数形式,然后保证不等长参数都可以完全入栈。实际上,根据C标准的入栈规则,参数是依次从右往左入栈的。只要顺着esp寄存器来找,那么就可以依次读出参数的其值。需要注意的是读出多少个字节,不然栈就乱了。所以必须在读出的时候加上参数类型。printf实现中也是先根据字符串中的类型再读的。我个人实测后确实如此~
测试结果如下,Linux下gcc编译。
如图所示,显然可以看到printf中实现的算法是先根据字符串对应的格式来从内存中读取的。也就是说如果类型选错了,内容显示一定乱了。
44. 关于返回结构体
最近看编译器实现方面的东西,貌似C++那种传递性的使用(如cout<< xxx<<<xx;这种)应该是不难实现的。然后我就上测试了下,我发现GCC下函数返回结构体是可以直接在后面加上.成员来访问成员的。。结果如下
45. 关于**指针和(*)[]指针
我发现很多人还是不清楚**指针和(*)[]指针的差别。第一个我就叫它双层指针,第二个叫行指针。有人喜欢把第一个叫二维指针吧,如果这么叫很容易晕。
实际上前者是一个指向指针的指针,而后者是一个带长度的指针。前者可以指向后者。后者与一般指针的差别就是你p++的时候是移动多个变量长度!其他指针只移动一个变量长度。这里的长度说的是存储变量地址的大小。前者指向一个指针变量的地址,而后者多用于指向一个二维数组的地址。所以传递二维数组参数的时候,用后者!
46. 关于%模运算
其实是可以模负数的!但和数学定义不同,对于负数的模仍然带有符号。
02--C编程细节整理(一)的更多相关文章
- 长文梳理muduo网络库核心代码、剖析优秀编程细节
前言 muduo库是陈硕个人开发的tcp网络编程库,支持Reactor模型,推荐大家阅读陈硕写的<Linux多线程服务端编程:使用muduo C++网络库>.本人前段时间出于个人学习.找工 ...
- C# 异步编程Task整理(二)异常捕捉
一.在任务并行库中,如果对任务运行Wait.WaitAny.WaitAll等方法,或者求Result属性,都能捕获到AggregateException异常. 可以将AggregateExceptio ...
- ffmpeg细节整理记录
ffmpeg细节整理记录 1.-vcodec.-code:v.-c:v ffmpeg的官方文档 -vcodec 是 -code:v 别名. -vcodec codec (output) Set the ...
- 02网络编程( socket套接字+TCP粘包 )
目录 02 网络编程 一.socket套接字编程 二.简易代码模板 2.1 服务端 2.2 客户端 三.通信循环及代码优化 四.黏包现象 五.struct模块 六.简易版本报头 七.上传文件数据 * ...
- SOCKET网络编程细节问题(2)
SOCKET网络编程快速上手(二)——细节问题(2) 2.TCP数据包接收问题 对初学者来说,很多都会认为:客户端与服务器最终的打印数据接收或者发送条数都该是一致的,1000条发送打印,1000条接收 ...
- SOCKET网络编程细节问题1
SOCKET网络编程快速上手(二)——细节问题(1) 三.细节问题一个也不能少 Socket编程说简单也简单,程序很容易就能跑起来,说麻烦还真是麻烦,程序动不动就出问题.记得刚开始写网络代码的时候,那 ...
- JavaScript学习总结-技巧、有用函数、简洁方法、编程细节
整理JavaScript方面的一些技巧.比較有用的函数,常见功能实现方法,仅作參考 变量转换 //edit http://www.lai18.com var myVar = "3.14159 ...
- Java编程规范整理
分享一份网友整理的编程过程中的命名规范 包命名 包名按照域名的范围从大到小逐步列出,恰好和Internet上的域名命名规则相反. 由一组以"."连接的标识符构成,通常第一个标识符为 ...
- SOCKET网络编程细节问题(4)
SOCKET网络编程快速上手(二)——细节问题(4) 5.慢系统调用及EINTR 还记得前面readn和writen函数么?里面有个EINTR,现在就来谈谈这个,这个很重要. Linux世界有个叫信号 ...
随机推荐
- [luogu2461 SDOI2008] 递归数列 (矩阵乘法)
传送门 Description 一个由自然数组成的数列按下式定义: 对于i <= k:ai = bi 对于i > k: ai = c1ai-1 + c2ai-2 + ... + ckai- ...
- 继续聊WPF——Expander控件(1)
这个控件最实用的地方,就是做导航栏. <StackPanel Margin="20,20" Width="100" Height="460&qu ...
- MySQL主要命令(4)
显示数据, 给列区别名: select coL_name as 别名 from table_name;
- mongodb--group聚合运算
mongodb本质就是要做一个高性能,能简单则简单,不要把mongodb中的运算做的太复杂 count 最简单的一个聚合方法 distinct 选择结果中剔除重复的一个键值, 跟sql语句的效果是一样 ...
- _DataStructure_C_Impl:求图G中从顶点u到顶点v的一条简单路径
#pragma once #include<stdio.h> #include<stdlib.h> #define StackSize 100 typedef int Data ...
- 站点搭建从零開始(七) WordPress站点的完好
1.WordPress站点前后端经常使用语言简单介绍和执行过程 通常一个站点的整个构建过程中须要大量的技术支持,尤其是用到非常多种计算机语言.站点的构建主要分后端和前端两部分,后端代码在server上 ...
- 扩展欧几里德 poj1061 青蛙的约会
扩展欧几里德很经典.可是也有时候挺难用的.一些东西一下子想不明确.. 于是来了一个逆天模板..仅仅要能列出Ax+By=C.就能解出x>=bound的一组解了~ LL exgcd(LL a, LL ...
- Sublime Text 2 SFTP UnicodeDecodeError错误!
右键-->SFTP/FTP ->Sync Remote To Local {作者:半条虫(466814195)} 提示下面错误 An unexpected error occurred, ...
- Android应用开发进阶篇-场景文字识别
因为研究生毕业项目须要完毕一个基于移动终端的场景文字识别系统.尽管离毕业尚早,但出于兴趣的缘故,近一段抽时间完毕了这样一套系统. 主要的架构例如以下: client:Android应用实现拍摄场景图片 ...
- nyoj170 网络的可靠性(第三届河南省程序设计大赛)
题目170 题目信息 执行结果 pid=170" style="text-decoration:none; color:rgb(55,119,188)">本题排行 ...