C traps and Puzzles

    技术2022-05-11  13

    下面的例子都在Ubuntu8.04 GCC下编译的结果,有些没有给结果

    编程一定要自己动手试一试!

    1.定义与声明,定义要分配内存,声明只是声明在别处定义了

    int a;     //定义

    extern int a;  //声明

     

    char str[100]

    extern char str[]  //ok

     

    char * str[]

    extern char str[] //error

     

    char str[100]

    extern char * str //error

    数组和指针是两码事!只是在某些情况下为了简化编译都当做指针处理 2.register 不能用取址运算符,因为可能不存放在内存中. #include <stdio.h> int main(void) {     register int r=10;     int * ptr=&r;     printf("%d/n",*ptr);     return 0; }

     

    3.sizeof是关键字不是函数也不是宏 (define 不是关键字),sizeof在编译的时候就确定了,由编译器算出 int i=0; sizeof(i) //4 ok sizeof(int) //4 ok sizeof i   //4 ok sizeof int //error! int *p=NULL; sizeof(p)  //ok 4 sizeof(*p)  //ok 4! int a[100]; sizeof(a)   //100 sizeof(&a)  //4 sizeof(&a[0]) //4

     

    enum{A,B,C}e;

    sizeof(e)   //4

     

    4.void 指针

    ANSI规定以不能对void 指针进行算法操作

    void * vPtr;

    vPtr++;     //error

    vPtr+=1;   //error

    但在GNU中

    用GCC编译都是合法的!

    用G++编译cpp文件确是不合法的!

    VC没试过不知道。

     

     

    5.volatile关键字

    告诉编译器每次使用变量值的时候都是从内存中读出,而不进行任何优化,常用在多线程编程中。

     

     

    6.空结构体空类

    struct st{}st;

    class cl{}cl;

    g++编译结果sizeof(st)=1 sizeof(cl)=1;

    gcc sizeof(st)=0!

     

    7.fleible array

    C99中,结构体的最后一个元素允许是大小未知的数组,但这个数组前面必须有一个其他类型的成员

    struct F{int i; char a[0];}   sizeof(F)=4

    struct F1{int i;char a[];}    sizeof(F1)=4

    F1 * f1;

    f1=(F1 *)malloc(sizeof(struct F1)+100*sizeof(char));/*给f1分配4+100byte的空间,这样数组a就有100个元素了*/

    sizeof(f1)=4   //!还是4,说明sizeof是在编译的时候就确定了!

     

     

    8.编写程序测试big/small endian

    bool smallEndian(void)

    {

        union endian{int i;char ch;}c;

        c.i=1;

        return 1==c.ch?true:false;

    }

     

    9.你真的理解指针么?试试下面的程序

    #include <stdio.h> int main(void) {     int a[5]={1,2,3,4,5};     int *ptr1=(int *)(&a+1);     int *ptr2=(int *)((int)a+1);     printf("%x,%x/n",ptr1[-1],*ptr2);     return 0; }

    10.typdef与primitive types

    #include <stdio.h>

    typedef struct st{ }st,* stp;

    int main(void) {     const struct st s;     struct st * sp;     const stp s1;     stp const s2;     *s1=s;     s2=sp;     return 0; } 不要被stp的指针迷惑

    const stp s1;

    stp const s2;

    两者中的const都是修饰s1的,也就是s1指向的内容可变,但指针不可变!

    体会区别

    const int i1;

    int const i2;//两者意思相同

    const char * str;

    char * cont str;//两者意思不同

     

    还有下面的例子

    <script type="text/javascript"> </script> <script type="text/javascript"> </script> typedef char * mytype; void fun(const mytype p){} int main(void) {     const char * p="thunder";     fun(p);     return 0; }

     

    11.typedef 与define的区别

     

    12.移位操作

    C中规定移位的位数应该是0到type的最高比特位之间,如果超过了对应type的bit位,那么只能取余,如果是负数未定义

    int a=2;

    int b=-1;

    a>>=34;  //0,warning

    b>>= -1;  //-1,warning

    移位操作还有很多精妙的应用如下面这个

    unsigned int bit_reverse(unsigned int n) {       n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa);       n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc);       n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0);       n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00);       n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);       return n; } int main(void) {     unsigned int x=0x12345678;     printf("%x/n",bit_reverse(x)); }

     

    13  ++ -- 操作符

    int i=4;

    (++i)+(++i)+(++i)=?

    不同的编译器得到的结果可能不同,可能是21或者19

    gcc下先计算(++i)+(++i) 这样i自增两次为6,和为12

    然后加上++i  结果是19

    int r=(i++,++i,i+12)是多少

    gcc下逗号运算符从左到右计算,所以r=18,i=6

     

    14 几个有用的宏

    __LINE__

    __FILE__

    __DATE__

    __TIME__

    __STDC__

    ##          #define Link(x)   Link##x

    #            #define Print(x)  "Print #x"

     

    15. a与&a

    char a[]="Hello World";

    a是数组首元素的首地址

    &a是整个数组的首地址

    两者的值是相同的但类型不一样

    char (*ptr)[];

    ptr=a;          // warning: initialization from incompatible pointer type

    ptr=&a;       //ok

    #include "test.h" int main(void) { char str[][4]={​{1,2,3,4},{5,6,7,8}}; char *pc1=(char *)(&str+1); char *pc2=(char *)(&str[0]+1); char *pc3=(char *)(str+1); printf("%d,%d,%d/n",sizeof(str),sizeof(str[0]),sizeof(*str)); printf("%d,%d,%d/n",*(pc1-1),*(pc2-1),*(pc3-1)); return 0; }

     

    16.逗号表达式

    int a,b;

    b=(a=3*5,a*4);

    a=?

    b=?

    逗号运算符从左到右运算,并返回最右端计算结果

     

    17.转义字符

    #include <stdio.h> int main(void) {     int i;     char s[]="//123456/123456/t";     for(i=0;i<strlen(s);i++)         printf("%d:%c/n",i,s[i]);     return 0; } 转义字符 意义 ASCII码值(十进制) /a 响铃(BEL) 007 /b 退格(BS) 008 /f 换页(FF) 012 /n 换行(LF) 010 /r 回车(CR) 013 /t 水平制表(HT) 009 /v 垂直制表(VT) 011 // 反斜杠 092 /? 问号字符 063 /'' 单引号字符 039 /" 双引号字符 034 /0 空字符(NULL) 000 /ddd 任意字符 三位八进制 /xhh 任意字符 二位十六进制

     

    18.除法乘法

    #include <iostream> using namespace std; int main(void) {     int a,b;     if(a*b/a*b==1)         cout<<"ok 1"<<endl;     if(a/b*b/a==1)         cout<<"ok 2"<<endl;     if(a/b*b+a%b==a)         cout<<"ok 3"<<endl;     if(a/b*b==a)         cout<<"ok 4"<<endl;     return 0; }

     

    19.函数参数默认值

    #include <iostream> using namespace std; void f1(int x=0,int y=0); void f2(int x=0,int y=0){cout<<"f2"<<endl;} void f3(int x=0,int y);//error void f4(int x,int y=0);//ok void f5(int x=0,int y,int z=0);//error void f2(int x){cout<<"f22"<<endl;} int main(void) {     f2(1,1);     f2(1);//error: call of overloaded f2(int i) is ambigous     return 0; }

     

     

     

    未完待续

    参考书目<Expert C Programming><C traps and pits><Pointers on C><c深度剖析>还有一些公司笔试题

     


    最新回复(0)