运算符,一种特殊的标识符----小话c语言(8)

来源:互联网 发布:桌面日历 for mac 编辑:程序博客网 时间:2024/06/06 02:52

[Mac-10.7.1 Lion Intel-based gcc 4.2.1]


Q: 可以把运算符看成特殊的标识符么?

A: 是的。例如 >= 运算符两个字符之间不能含有空格,这和标识符是类似的。

#include <stdio.h>int main(){    1 > = 2;    return 0;}

编译:

可以看到,> 和 = 符号中间的空格导致了编译器不能理解。


Q: 为什么会产生运算符结合性这个概念?

A: 结合性是在优先级相同的情况下才会进行结合性的判断得到表达式运算的真正的顺序。


Q: 赋值运算符怎么证明确实是右结合性?

A: 如下代码:

#include <stdio.h>int main(){    int a, b, c;    a = b = c = 1;    return 0;}

gcc  -S  operator.c得到汇编代码(部分):

movl$1, -20(%rbp)movl-20(%rbp), %eaxmovl%eax, -16(%rbp)movl-16(%rbp), %eaxmovl%eax, -12(%rbp)

-20(%rbp)即是表示c,-16(%rbp)表示b, -12(%rbp)表示a, 可以看到依次给c, b, a赋值,也就是体现了如下运算过程:

(a = (b = (c = 1)));


Q: 如下代码为什么结果始终不对?

#include <stdio.h>int main(){    int a = 2;    if(a & 1  == 0)        printf("a & 1 == 0");    else        printf("a & 1 != 0");    return 0;}

为什么一直输出“a & 1 != 0”  ?

A: 这是因为==的优先级高于表示位与运算符&.所以a & 1 == 0的实际代码是a & (1 == 0),也就是a & 0, 当然结果不是预期了。

可以看下它的汇编(部分):

    leaq    L_.str(%rip), %rcx    movq    %rcx, %rdi    callq   _printf
L_.str:    .asciz   "a & 1 != 0"

可以看到编译器进行了优化,直接输出L_.str字符串,根本没有进行运行时再次运算if表达式的值。在这种情况下,需要注意是否忽略了运算符优先级导致编译器直接优化了。


Q: sizeof到底是个运算符还是关键字?

A: 它应该被看成运算符。下面是c标准内容:

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

可以看出,sizeof被看成运算符。不过从另一个角度来说,关键字是从词法的角度进行分析的,运算符是从语法的角度分析的;如果从语法的角度来说,sizeof可以看成运算符,而从词法的角度来说,它不能看成关键字。不过依然不能把它当成变量来申明:

#include <stdio.h>int main(){    int sizeof = 1;    return 0;}

编译:

可以看到编译出错了。


Q: 都说sizeof在编译期就可以计算出数值,怎么证明?

A: 

#include <stdio.h>int main(){    int i = 1;    printf("%d\n", sizeof(i));    return 0;}

它对应的汇编代码为(部分):

                movl$4, %eaxxorb%cl, %clleaqL_.str(%rip), %rdxmovq%rdx, %rdimovq%rax, %rsimovb%cl, %alcallq_printf

笔者所在机器为x64架构,在x64上的参数传递,如果参数不超过6个,一般是先入寄存器,参数从左到右分别放入:rdi,rsi,rdx,rcx,r8,r9.

可以看到数字4直接放入了%eax, 最后用%eax的扩展寄存器%rax将数据放入%rsi; sizeof(i)的值在编译期就已经计算得到了4.


Q: sizeof后面可以跟表达式,下面的代码,为什么i++无效呢?

#include <stdio.h>int main(){    int i = 1;    sizeof(i++);    printf("%d\n", i);    return 0;}

运行结果:

A: 正因为sizeof是编译期求值的,所以如果它跟着表达式,那么表达式是不被计算的,只是根本表达式的类型得到它占用的空间。看下它的汇编:

movl$1, -12(%rbp)movl-12(%rbp), %eaxxorb%cl, %clleaqL_.str(%rip), %rdxmovq%rdx, %rdimovl%eax, %esimovb%cl, %alcallq_printf

可以看到,调用printf函数之前直接将数据1当做参数输出,至于sizeof(i++)中的i++根本没有对应指令。


Q: 对于普通数组arr来说,sizeof(arr)可以确定arr的大小,那变长数组的sizeof如何计算呢?

A: 正因为是可变数组,所以sizeof计算它大小的过程将被推迟到运行时。

#include <stdio.h>#definePRINT_D(intValue)printf(#intValue" is %lu\n", (intValue));size_t  get_arr_len(int n){    char arr[n + 1];    return sizeof(arr);}int main(){    PRINT_D(get_arr_len(1))    return 0;}
编译运行:

这里先介绍下VLA(变长数组),它是c99引入的特性,表示一个数组的长度不一定在编译期就可以确定,可以推迟到运行时。

我们先看下上面代码的汇编(部分):

0x0000000100000dcc <get_arr_len+28>:add    $0x1,%edi0x0000000100000dcf <get_arr_len+31>:mov    %edi,%ecx0x0000000100000dd1 <get_arr_len+33>:mov    %rsp,%rdx0x0000000100000dd4 <get_arr_len+36>:mov    %rdx,-0x20(%rbp)0x0000000100000dd8 <get_arr_len+40>:mov    %rcx,-0x10(%rbp)0x0000000100000ddc <get_arr_len+44>:movl   $0x1,-0x24(%rbp)0x0000000100000de3 <get_arr_len+51>:mov    -0x20(%rbp),%rcx0x0000000100000de7 <get_arr_len+55>:mov    %rcx,%rsp0x0000000100000dea <get_arr_len+58>:mov    -0x10(%rbp),%rcx0x0000000100000dee <get_arr_len+62>:mov    (%rax),%rax0x0000000100000df1 <get_arr_len+65>:mov    -0x8(%rbp),%rdx0x0000000100000df5 <get_arr_len+69>:cmp    %rdx,%rax0x0000000100000df8 <get_arr_len+72>:mov    %rcx,-0x30(%rbp)0x0000000100000dfc <get_arr_len+76>:jne    0x100000e08 <get_arr_len+88>

可以看到,第一行执行了get_arr_len中n+1的操作,最后将得到的数据传递到%ecx中,最终在倒数第二行的地方将此数据传递出去;可以看出是在运行时计算数组大小的。


Q: 如下代码关于位运算符的操作为何最终结果和预期不符?

#include <stdio.h>int main(){    unsigned char c = 0xfc;    unsigned int i = ~c;    printf("%#x\n",i);    return 0;}

运行结果:

按照上面的代码,~c应该得到的是0x03, 那么结果应该是0x03, 怎么会是上面图片的结果呢?

A: 这是因为位运算是被提升到整形运算的。上面的变量c是无符号字符型,在进行~位运算时,是首先提升为整形,即为0x000000fc, 然后取反得到0xffffff03, 所以i得到的数值是这个。同理,如果c是char类型,提升为整形时为0xfffffffc,再取反得到的就是0x03.其实变量被提升有很多地方,比如short计算时也会提升为int再继续计算。


xichen

2012-5-15 11:04:55