数组、指针和动态内存也是密切相关的。容易出现的错误仍然是边界和初始化问题。
18.3.1 非数组的指针
【例18.8】下面程序将数组t和s中的内容赋给指针变量p,但输出结果并没有包括s的全部内容。找出错误之处并改正之。
#include <stdio.h>#include <string.h>int main (){ int i=0,j=0; char t="abcdefghij",s="klmnopqrstuvwxyz",*p; p=t; i=strlen(t); while (( p[i+j] = s[j]) !='/0' ) j++; printf("%s/n",p); return 0;}
原因是用数组t初始化指针的想法是想利用超出t的存储空间来存储s,这是危险的做法。越界之后,并不能保证有连续的有效存储空间用以存储字符串s。
可以另外定义一个大于s和t总长度的字符数组。例如
char st[30];p=st;
然后使用如下两个循环完成赋值:
while (( p[i] = t[i]) !='/0' ) i++;while (( p[i+j] = s[j]) !='/0' ) j++;p[i+j]='/0';
一般采用申请动态内存的方法,即为指针变量申请足够的存储空间。
p=(char*)malloc ( strlen(t)+strlen(t)+1)
strlen函数计算的是实际字符串长度,所以要增加一个结束位。实际使用时,需要判别申请是否成功。这块内存虽然是非数组的指针,但却可以像数组那样使用下标。程序中演示了两种反序输出的方法,特别是演示下标为负值的使用方法,以便更好地理解动态内存的特点及指针的灵活使用方法。
//完整的程序#include <stdio.h>#include <string.h>#include <stdlib.h>int main (){ int i=0,j=0; char t="abcdefghij",s="klmnopqrstuvwxyz",*p; if ( (p=(char*)malloc ( strlen(t)+strlen(t)+8)) == NULL ) { printf ( "内存分配错误!/n" ); exit(1); } while (( p[i] = t[i]) !='/0' ) i++; while (( p[i+j] = s[j]) !='/0' ) j++; p[i+j]='/0'; printf("%s/n",p); for(i=25; i>-1; i--) printf("%c",p[i]); printf("/n"); p=p+25; for(i=0; i>-26; i--) printf("%c",p[i]); printf("/n"); p=p-25; free(p); return 0;}
程序输出结果如下:
abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcba
释放内存,必须保证指针指向申请的动态内存的开始位置,否则会出错。所以程序中执行“p=p-25;”。申请内存时多申请了6个,是为了保证free可靠执行。
数值数组的使用方法与此类似,不再赘述。
18.3.2 NULL指针
在语句
if ( (p=(char*)malloc ( strlen(t) + strlen(t) + 8)) == NULL )
中使用了空指针。空指针的表示为:
p=NULL;
有时在赋值或比较运算的情况下会使用NULL指针,但在其他情况不能使用NULL指针。因为NULL指针并不指向任何对象,而且空指针也不是空字符串,所以对空指针p而言,使用如下两个语句会得到什么结果呢?
printf("%s/n", p);printf(p);
为了代码的文档化,常采取如下定义:
#define NULL 0
由此可见,p的行为没有定义,这两条语句在不同的机器上可能有不同的效果。
在禁止读取内存0地址的机器上,语句
printf("%d/n", *p);
将会执行失败。在允许的机器上,则会以十进制方式输出内存位置0中存放的字符内容。
要注意的是,空指针并不是空字符串。无论使用0还是NULL,效果都是相同的。当将0赋值给一个指针变量时,绝对不能企图使用该指针所指向的内存中存储的内容。
有些C语言实现对内存位置0只允许读,不允许写。在这种情况下,NULL指针指向的也是垃圾信息,所以也不能错用NULL指针。
所以,对指针进行递增和递减操作必须预防越界。在达到最后一个边界时,要特别小心谨慎。释放不用的内存时,必须保证指针指向所申请内存的首地址,否则就会出错。在某些场合,为了保证释放,甚至需要多申请部分内存区域。