字符串数组C代码异常运行及指针解引用差异的技术问询
C语言指针与字符串数组的疑问
我正在学习指针与字符串数组,编写了如下C代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> void function (char **, char **); int main () { char *list[2], *list1[] = {"word0", "word1", "word2"}; function(list, list1); return 0; } void function (char **list, char **list1) { for(int i=0; i<2; i++) { printf("Insert string\n"); scanf("%s", (list+i)); //adds 2 strings in list } for(int i=0; i<2; i++) { printf("%s\n", (list+i)); //reads the strings that I just added but if I place * before (list+i) the code crashes } printf("%s\n", *(list1+1)); //reads the second string of list1 but if I don't place * before (list+1) it reads some weird chars }
我原本认为这段代码无法按注释中的方式运行,但实际却能正常工作,对此感到困惑。我知道可以使用calloc等其他方法解决相关问题,但核心疑问是:为何这段代码本应失效却能运行?以及为什么访问list1元素时需要在(list1+1)前添加*,而访问list元素时不需要添加*,添加*反而会导致程序崩溃?
一、为什么代码看似有问题却能运行?
这段代码本质是触发了未定义行为却侥幸没崩溃,问题出在scanf("%s", (list+i))这一行:
list是char**类型(指向char指针的指针),list+i等价于&list[i],也就是数组第i个char*变量的内存地址。scanf("%s")要求传入char*类型参数(字符串存储的起始地址),但这里传入的是char**类型,属于类型不匹配。编译器可能仅给出警告或直接忽略,执行时会把&list[i]当成char*使用,直接将输入的字符串写入list[i]这个指针变量的内存空间。- 只要输入的字符串长度不超过指针变量的字节数(32位系统为4字节,64位为8字节),就不会立刻触发内存溢出崩溃,所以看起来“正常运行”。但这完全是巧合,属于严重错误——你没有给指针分配内存存储字符串,反而直接覆盖了指针变量本身的内存值。
二、为什么list和list1的访问方式完全相反?
1. list1的访问逻辑
list1是字符串字面量数组,*list1[] = {"word0", "word1", "word2"}的定义意味着:
list1的每个元素都是char*类型,指向对应的字符串字面量(比如list1[1]指向"word1"的首地址)。list1+i是指向第i个char*元素的指针(类型为char**),而printf("%s")需要的是char*类型(字符串的首地址),所以必须加*,通过*(list1+i)拿到list1[i]这个char*,才能正确输出字符串。- 如果不加
*,直接传list1+i,printf会把这个char**类型的指针地址当成char*读取,自然会输出乱码(读取的是指针变量本身的内存内容,不是目标字符串)。
2. list的访问逻辑
经过前面的scanf操作后,list[i]这个指针变量的内存已经被输入的字符串覆盖:
- 比如输入"abc",
list[i]的4字节内存会被写成'a' 'b' 'c' '\0'(32位系统下)。 - 此时
list+i是&list[i],也就是存储了"abc"的内存地址,直接传给printf("%s"),会从这个地址开始读字符串,刚好是输入内容,所以能正常输出。 - 但如果加
*,*(list+i)就是list[i],而list[i]现在的值是0x63626100(小端模式下"abc"的ASCII值组合),这是一个无效的内存地址,printf访问该地址会触发崩溃。
内容的提问来源于stack exchange,提问作者Niconsky
相关产品推荐
相关产品推荐

