为何char**与char*[]的解引用行为存在差异?
为什么
char**初始化后解引用会触发段错误? 核心问题出在**char** sa = { s };的初始化方式完全错误**,和char* arr[] = { s }的内存结构天差地别,下面逐一拆解:
1. 两种定义的本质区别
char* arr[] = { s }:这是定义了一个char*类型的数组,数组第一个元素被赋值为s(即字符串"test"的指针)。数组在内存中是连续空间,arr作为数组名,会隐式转换为指向数组首元素的指针(类型为char**)。所以*arr或arr[0]都能正确取到s,用%s打印自然能输出字符串。char** sa = { s }:这是试图用初始化列表给单个指针变量赋值。C标准中,指针只能用单个表达式(或NULL)初始化,这里的大括号毫无意义,编译器会直接把s的值(字符串"test"的地址)赋值给sa本身,而不是让sa指向一个存放s的内存位置。也就是说,sa现在不是指向char*的指针,而是直接变成了字符串的指针。
2. 逐个分析测试用例的异常原因
Test 1
sa的值被设置为s的值(字符串"test"的地址),所以打印sa的地址和s完全一致,这是错误初始化带来的结果。
Test 2
*sa是解引用sa指向的地址——也就是字符串"test"的首地址,这里的内容是字符t(ASCII值0x74)。因为用%p打印,64位系统会从该地址读取8字节数据当作指针值,而字符串的字节序列是0x74 0x65 0x73 0x74 0x00 ...,小端字节序下会被拼接成0x65540a0074736574,这显然不是一个有效的内存地址。
Test 3
printf("sa:\t%s\n", sa);能输出test纯属巧合:因为sa的值刚好是字符串的首地址,%s会从该地址开始打印字符,直到遇到\0。但这完全不是char**的正确用法。
Test 5
printf("sa:\t%s\n\n", *sa);会触发段错误:*sa是前面提到的无效指针值,用%s去解引用这个不属于进程的内存地址,操作系统直接抛出段错误终止程序。
3. 正确的char**用法
如果要让char**指向一个char*变量,需要先有一个存放char*的内存空间,比如:
char* s = "test"; // 方式1:指向栈上的char*变量 char* temp = s; char** sa = &temp; // 方式2:用动态内存分配 char** sa = malloc(sizeof(char*)); *sa = s;
或者直接让char**指向char*数组的首元素:
char* arr[] = { s }; char** sa = arr;
此时*sa就能正确拿到s,用%s打印也不会有问题。
内容的提问来源于stack exchange,提问作者Ryan Pereira
相关产品推荐
相关产品推荐

