指针的概念:创建一个指针类型的数据,在64位系统中,一个指针类型的数据用8个字节表示。指针类型的数据存储某个数据的地址。编译器,可以根据指针地址处理该地址的数据。
问题:字符串指针是指向字符串的指针,初始化字符串指针时,不需要为该字符串指针分配内存;而初始化字符串二重指针时,却需要用malloc为二重指针分配内存?
一重字符串指针
首先考虑字符串的存储模型,如下图所示。字符串变量s其实是存储了字符串首字母的地址,指针p为字符串首字母的地址。
输出字符串时,printf(),读入的地址认为是字符串的首地址,遇到“\0”,认为时字符串的结束。程序执行代码如下图所示。
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *s = "abcdefgh";
printf("s:%s\n", s);
}
输出结果:
s:abcdefgh
字符串指针初始化时,指定的是字符串常量,且字符串常量存储在代码区中,以后没法更改。可能是因为字符串常量,所以不需要为指针s分配内存。但如果字符串指针指向的不是字符串常量呢?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
char *p = NULL;
//~ p = malloc(7 * sizeof(char));
strcpy(p, "123456");
printf("p:%s\n", p);
//~ free(p)
}
在上面的代码中,将字符串”123456“复制,并用指针p指向复制后的字符串,运行程序,结果出现段错误。
如果取消注释的那行,为p在堆中分配7个字节的内存,则程序可正常输出如下:
s:123456
所以呢,对于一重字符串指针来说,如果在初始化时指向字符串常量,就需要不在堆中分配空间,毕竟已经在代码块有个存储位置。但如果在初始化时,需要指向堆中的字符串,就必须在堆中先为字符串分配空间。
二重字符串指针
定义二重指针char **p,*p代表指向字符串的指针。
p指向的是指针的地址,而非字母的地址,可认为p指向了一个指针数组;这个指针数组并非存储在代码块的常量,所以需要在堆中为指针数组分配空间。假设需要m个字符串,就要为p分配m*sizeof(char *)的空间。
二重指针p的示意图如下所示。
p指向指针数组首地址,*p指向字符串地址,**p指向字符串首元素。
关于二重指针的代码如下所示:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char **p;
char s1[10] = {'1' , '2', '3', '4', '5'};
char * s2;
p = malloc(2);
s2 = "abcdefgh";
*p = s1;
p[1] = s2;
printf("s0:%s\n", p[0]);
printf("s1:%s\n", p[1]);
free(p);
}
p指向的指针数组分别指向字符串s1的首地址,和s2的首地址。
函数可执行,输出如下:
s:12345
s:abcdefgh
说明二重字符串数组需要在堆中分配空间。
总结:
是否为字符串指针分配空间,依据为字符串指针指向的数据是否为堆中的数据。
1,一重指针如果指向字符串常量,则不需要为字符串常量分配空间;
2,一重指针如果指向需在堆中分配的字符串,比如一重指针为strcpy的目标地址,则需要为指针在堆中分配空间;
3,而二重指针始终指向字符串指针数组,不可能为存储在代码块的常量,所以必须为二重字符串指针在堆中分配空间。