C

1、const并非真正的常量。引用http://blog.ednchina.com/fafen/29169/category.aspxhttp://blog.ednchina.com/fafen/185804/message.aspx的例子,switch语句中的case并不能使用const“常量”。实际上,如下代码在Eclipse C中通过:
    const char ch = 'A';
    
char * q = &ch;
    
*= 'C';
    printf(
"%c\n", ch);

结果输出C,只不过有类型兼容警告。声明为const只不过是希望程序不要(通过该名字)修改它,只读即可,但其值可能被(其它指向该变量的引用)修改,比如用来修饰硬件寄存器的值。

     char a[4]="abcd";
     
const char *= a;
     a[
0= 'b';
     //
*= 'a';//error

另外,如果const修饰初始化的字符串(如const char *s="string";),则该字符串实际存在于文本段,文本段一般是只读和可执行的,所以任何对它的修改都可能导致段错误。

     char a[]="abcd";
     
char *="abcd";

sizeof(a)=5,sizeof(s)=4,a[]包含了字符串的结束符'\0',除非制定它的大小。如果指定了大小比如a[4],但字符数超过了该大小,则编译器一般会自动截断并发出警告。说到const就不能不提volatile,参见http://superding.spaces.live.com/blog/cns!89E842A8485366C7!1019.entry

 

2、大小端。内存地址从低到高的方向,一个整数从高字节开始存放就是大端(高大低小)。

0x0001存放于地址0x0-0, 0x1-0, 0x2-0, 0x3-1就是大尾端。

#define BIG_ENDIAN ({int i=1;((char *)&i)[0]==0;})

堆栈溢出,http://www.stmcu.org/myspace/blog/show.php?id=167317

内存地址对齐,http://blog.csdn.net/reiskie/archive/2007/06/11/1647605.aspx

 

3、见过长这样的数组?int a[5], *p=a; 2[a]=1; printf("%d\n", 2[p]);反正编译后都等于*(a+2)或*(2+p),不在乎多么奇怪的写法,比如继续写int i=2; i=i[a];不过让人看到这种代码肯定会让人头疼死的。 a[0]=({p[1]=2;});*({p+1;})=7;*(p=a)=0; 这些都是正确的语法,但多此一举。更过分的是*({int tmp;p=&tmp;})=3;恐怕p会成为野指针了。见怪不怪,只是为了理解

#define container_of(ptr, type, member) ({            \
    
const typeof(((type *)0)->member) * __mptr = (ptr);    \
    (type *)((char *)__mptr - offsetof(type, member)); })

 

 4. i = i++ + j;问题,即是先计算本表达式还是先完成自增运算。这是一个无聊的问题,这有SB才会这样写代码,可是偏偏有很多公司会问这样的问题。

int i=0, j=1;
= i++ + j; 

 在Linux上gcc编译运行的结果是2。 

int i=0;
= i++;

 结果是0。没有任何解释,看汇编代码就知道了,-S选项,第一段汇编代码:

.LCFI1:
    movl    $
0, -4(%rbp)
    movl    $
1, -8(%rbp)
    movl    -
4(%rbp), %eax
    addl    -
8(%rbp), %eax /* 先完成了i+j的计算 */
    movl    %eax, -
4(%rbp) /* 结果写回给i */
    leaq    -
4(%rbp), %rax /* i的地址 */
    incl    (%rax) /* 计算i++,可见表达式计算完成之后才自增 */
    movl    $
0, %eax 
    
leave
    
ret

  第二段汇编代码: 

.LCFI1:
    movl    $0, -4(%rbp) /*相当于把立即数0赋给i*/
    movl    -4(%rbp), %eax /*取i的值放入寄存器eax中*/
    leaq    -4(%rbp), %rdx /*传i的地址给寄存器rdx*/
    incl    (%rdx) /*该地址的内容增1*/
    movl    %eax, -4(%rbp) /*将eax的值写回i,可见这里是完成了自增操作之后再赋值的*/
    movl    $0, %eax
    
leave
    
ret

 对于这样的计算顺序是否有规范不得而知,不然就看编译器实现吧。在其他语言中,对以上两例,PHP和Java得出的结果是1和0。 

posted @ 2009-08-05 16:44  千年  阅读(262)  评论(0编辑  收藏  举报