第三章: 语义陷阱
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 的指向区域!