c语言装逼的代码(抠一抠C语言的一些细节)
C语言是现在大部分流行语言的源起,也是编写系统软件的不二之选,C语言的一些细节值得深入探究,我来为大家讲解一下关于c语言装逼的代码?跟着小编一起来看一看吧!
c语言装逼的代码
C语言是现在大部分流行语言的源起,也是编写系统软件的不二之选,C语言的一些细节值得深入探究。
1 关于赋值运算符(普通变量和指针变量的赋值)先看C语言代码和对应的汇编代码
7: int a = 5;
00401048 mov dword ptr [ebp-4],5
8: int* b = &a;
0040104F lea eax,[ebp-4]
int a = 5; 汇编指令是move,表示将5的二进制序列传送到a所对应的内存空间。
可以理解为int a ← 5,将值5赋给a。
再看指针变量的赋值:
int* b = &a;
汇编指令是lea,是Load effective address的缩写——取有效地址,也就是取偏移地址。
可以理解为 b → a,b指向a。
用结构体做单链表的结点时,这样理解更容易理解链表指针的移动:
struct pNode* p,p2;
p = p->next;//p指向p的下一个节点空间
p2 = p2->next->next; //p2指向p2的下一个的下一个节点空间
二者的区别在于:为变量赋值是通过赋值表达式在运行期间动态赋值,而为变量赋初值则是在定义变量的同时在编译时静态赋值。如对a进行赋值,对b进行赋初值,形式如下。
int a,b=3;
a=2;
为变量赋值占用的是运行时间,而为变量赋初值占用的是编译时间。
从上面的分析得知,变量不是一定要初始化的,也可以先进行定义,再进行赋值,这和初始化的效果是一样的。但是如果想提高运行效率,就得对变量进行初始化。
3 const的一点细节编译器通常不为普通const只读变量分配存储空间,而是将它们保存在符号表中,这使它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也更高。
4 逗号作为运算符和分隔符逗号在C语言中有时可以作为运算符來连接表达式,有时还可以作为分隔符,起到分隔的作用。那么,该如何区分逗号是运算符还是分隔符呢?
“,”作为分隔符主要用于以下情况:
(1) 变量声明时,使用逗号分隔多个变量名。
(2) 函数有多个参数时,用逗号分隔参数。
逗号运算符是C语言提供的一种特殊的运算符,用它可以将两个表达式连接起来。例如,“1 2,4 5”,称为逗号表达式,也称为“顺序求值运算符”。它的表达式一般形式为“表达式1,表达式2”,执行顺序是先计算表达式1的值,再计算表达式2的值。表达式也可以扩展为“表达式1,表达式2,表达式3,…,表达式n”。
c=(a=3,2*a);
是将一个逗号表达式的值赋给变量c,第一个表达式a=3,第二个表达式2*a,计算得到6,因此变量c的值是6。
代码d=a=b=3,2*a;整个是一个逗号表达式,这里逗号表达式的值同样是2*a的值,结果是6。但是变量d的值是在第一个表达式中计算得到的,得到值为3。
在使用逗号运算符的时候,要注意它的优先级和结合顺序。逗号运算符优先级最低,丼且是自左至右结合的。
5 自增运算的不同写法在效率上的一点区别x = x 1,x = 1,x ,这三个表达式哪个执行效率最高?
第一个表达式x=x 1的执行过程是先读取等号右边的x的地址,计算x 1的值,然后读取等号左边的x的地址,最后将等号右边的值传给等号左边的值。
第二个表达式x =1的执行过程是先读取等号右边的x的地址,然后计算x 1的值,最后将得到的值传给左边的x,因为x的地址已在前面读出,故省去了传值过程;
第三个表达式的执行过程是先读取x的地址,然后x自增1;
因此,x 的效率最高。
编译器优化后汇编代码应该是一样的。
从汇编来看前自增与后自增的区别:
7: a=c ;
004016AF mov eax,dword ptr [ebp-0Ch]
004016B2 mov dword ptr [ebp-4],eax
004016B5 mov ecx,dword ptr [ebp-0Ch]
004016B8 add ecx,1
004016BB mov dword ptr [ebp-0Ch],ecx
8: b= c;
004016BE mov edx,dword ptr [ebp-0Ch]
004016C1 add edx,1
004016C4 mov dword ptr [ebp-0Ch],edx
004016C7 mov eax,dword ptr [ebp-0Ch]
004016CA mov dword ptr [ebp-8],eax
switch相对于if…else if,具有较高的运行效率。因为switch会在编译期建立一个跳转列表,运行时可以根据跳转列表进行直接跳转。
在switch语句中,当找到与swith表达式相等的case时,执行case下的语句。case下的所有语句都执行完成后,如果一直没有break,那么程序将会执行到下一个case,而不管它的值是否与switch表达式相等,即多个case之间不具有天然的互斥性。要想使程序执行完一个case后的语句,而不进入下一个case,必须使用break语句,使程序退出switch结构。这样,后面的case也就不执行了。
C语言一个分支的结束是依赖break完成的。case只决定—序到哪里执行,而不决定到哪里结束。结束位置由break来决定,没有break就一直执行到switch完整结构结束。
所以,case是swithc语句的入口(特殊情况下也可以是default),而由break提供出口(特殊情况下是最后一条case语句的结束块符号“}”或return。
7 联合体变量赋初值由于联合体变量具有所有成员共享一个内存地址的特点,因此为联合体变量赋初值时只能给该变量的第一个成员赋初值,其他成员不能赋初值。例如:
union number
{
int i;
char c;
float f;
}m={2};
数组名是一指针常量,所以不能被再次赋值。只能对其数据元素逐一操作。
数组名做函数参数时退化为指针,在函数体内不能得到其长度。
二维数组名并不能赋给一个二次指针,只能赋给一个数组指针,或通过强制类型转换,赋给一个一次指针:
int arr[5][5] = {1,2,3,4,5,6,7,8};
int (*pa)[5] = arr;
int *p = (int*)arr;
cout<<*pa[1] 2<<endl; // 8
cout<<*(p 7)<<endl; // 8
指针变量声明并指向目标地址时,由类型信息决定目标内存空间的字节长度,当其指向一个n维数组时,其长度信息为一个n-1维数组的字节长度,所以n维数组指针声明时需要显式指定除第一维以外的其它长度信息。
指针数组名在语法层面等价于一个二级指针。
9 内在管理函数及库中一些算法的参数和返回值的void类型malloc、sort等函数的参数和返回值都使用void类型。原因是C是一门强类型语言,又没有类型模板机制来做泛型表示。
void指针只能单纯表示地址信息,其附加的类型信息不存在,所以其为一个不完全类型,只能用于声明和传址,当需要解引用或做指针算术运算时,其类型信息附加的字节长度信息不可或缺,所以需有具体类型的强制转换。
-End-
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com