3_Semantic Pitfalls 语义错误


                                第三章: 语义陷阱


  3.1 Pointers and arrays: (指针和数组陷阱)


      在C语言中,数组和指针的意义是相互联系的:


            1)、C 语言只有一维数组,而且数组的元素个数必须在编译之前用一个定值给出( 静态数组 );

                       

                        但是数组中可以包括数组( 即二维数组 );


            2)、事实上,数组只能做两件事情: 声明数组中元素个数 和 得到一个数组的头指针;


                         其他的操作,实际上是由指针来完成的;( 尽管有一些看起来是数组实现的! )


例:

               p = a;

               a[ i ] = 84;          等价于     *( p + i ) = 84;


          1. 数组的声明:    int a[ 3 ];


          2. 二维数组的声明 : int calendar[ 12 ][ 31 ];

注:    

               此处表示为 : calendar 含有十二个数组,而每一个数组中又单独包括31一个元素;


          3. 数组名:


               除了在 sizeof( ) 中使用之外,数组名都表示指向它所代表的数组的首个元素的地址的指针


               并且当一个指针指向了一个数组之中的元素时:


                    当指针加 1 时,并不是地址数加 1; 而是指向数组中的下一个元素;


注:

               当使用数组名赋值给一个指针时 :  p = a; 是允许的;


                                   但是 : p = &a; 是错误的!   


           因为 a 已经表示了地址, 而 &a 表示指向一个数组的指针,但是对于 p 来说,表示一个指向 int类型的指针


          4. 数组名在 sizeof 中的使用:

         

                   对于一般情况下,数组名既代表了首个元素的地址的指针;


          但是对于 sizeof(a) 其表示的是:整个数组元素的大小,而非一个指针的大小!


          5. 二维数组与指针:


               对于二维数组 : int  calendar[ 12 ][ 31 ];   那么  calendar[ 4 ]的取值为如何呢?

        

            事实上,calendar[ 4 ] 是一个包括了 31 个 int 元素的一位数组;

所以我们可以有:

               int *p;     p  = calendar[ 4 ];

或者:

               int i;        i = calendar[4][7];   equals to   i = *( *(calendar + 4) + 7 );


注:

           对于 p = calendar;    这个语句是错误的,因为 calendar 是一个指向 数组的指针,而 p 是一个指向 int 的;

      


例: 清除一个由二位数组表示的日历:


int month;

for(month = 0; month < 12; month++)

{

    int day;

    for( day = 0; day < 31; day++  )

    {

         calendar[month][day] = 0;

    }

}


当利用指针来表示的时候,我们可以有:

int (*monthp) [31];

for( monthp = calendar ; monthp < &calendar[12] ; monthp++ )

{

    int *dayp;

    for( dayp = *monthp; dayp < &monthp[31]; dayp++ )

    {

        *dayp = 0;

    }

}


  3.2 Pointers are not arrays : (指针不完全等于数组)


          在C语言中, 对于一个字符串,在最末尾会自动加上一个 ‘\0’;这可能出现很多问题:


          1. 我们有两个字符串 : s 和 t ,并且我们期望能把它们合并成一个字符串 r;

1):考虑方法:

     char *r;

     strcpy(r,s);

     strcat(r,t);

则会产生错误: 因为在使用 r 时,r 没有指向任何被分配的空间区域!


2):修改:

     char r[1000];

     strcpy(r,s);

     strcat(r,t);


即: 将 r 分配一段连续的空间,并指向首元素;但此时 空间仍然有容量限制!


3):再次修改:( 使用malloc 函数 )

     char *r,*malloc();

     r = malloc( strlen(s) + strlen(t) );

     strcpy( r,s );

     strcat( r,t );


这个程序仍旧错误: 首先:malloc 可能无法分配足够的空间而返回一个 NULL指针;

                                 其次:在使用结束后,要使用 free 来释放空间!

                                 第三:这个malloc语句没有提供足够的空间!!


          对于函数 strlen ,在计算字符串长度的时候,是不计算 '\0' 的空间的;

       

所以:  当 strlen(s) 取值为 n 时, 实际需要的存储空间为 n + 1;


4):最终方案:


char *r, *malloc( ) ;

r = malloc( strlen(s) + strlen(t) + 1 );    // 对应错误3

if( !r )          // 对应错误1;

{

    complain();

    exit(1);

}

strcpy(r,s);

strcat(r,t);

...............


free(r);       // 对应错误2;



  3.3 Array declarations as parameters : (数组与参量陷阱)


     在C语言中,我们无法直接在函数中引用整个数组,但是我们可以通过数组名的方式引入指针:

例:

          char hello[ ] = "hello";

          printf( "%s\n ", hello );     equals to   printf( "%s\n" , &hello[0] );


对于形参,我们有:


     int strlen( char s[ ] )   equals to    int strlen( char *s );

 

注: 对于:


     extern char *hello;            defferent from   extern char hello[ ];


  3.4 Eschew synecdoche : (避免代换)


       对于两个指向同一个存储空间的指针相互赋值,其指向的存储空间并不会被复制!


  3.5 Null pointers are not null strings : (空指针不是空字符串!)


        当给一个指针直接赋值常量时,通常是与实现功能无关的,但是要注意给指针赋值 0;


通常我们有: #define NULL 0


        即将某个指针变为空指针;!


此时,在使用空指针的时候,一定要注意:


                Not ask what is in the memory it addresses!


例如:

        对于:         if( p == ( char *)  0 )        是允许的!

但是对于:         if( strcmp( p , ( char *) 0 )  == 0 )         是非法的,因为函数 strcmp 要寻找 p 的指向区域!






你可能感兴趣的:(C语言复习)