C语言中为何遍历函数返回的输入字符串时报segmentation fault:11错误
这个报错是两个叠加的代码错误共同导致的,不存在“修改函数返回类型就莫名崩溃”的玄学问题:
第一处错误:返回值和声明的类型完全不匹配
函数开头声明的返回值类型是const char*(即指向字符的指针,也就是通常意义上的字符串类型),但函数体里实际写的返回语句是return *source;
按照C语言语法规则,*source等价于取source数组的第一个元素,是一个char类型的单字符值,根本不是指针。
这里的实际执行效果是:把这个字符对应的ASCII编码值,硬当作一个内存地址,传给了后续的strcpy函数——比如你输入的第一个字符是'A',它的ASCII值是65,相当于strcpy要从内存地址0x41的位置开始读字符串。这个地址根本不是当前程序有权限访问的内存区域,直接就触发段错误了,甚至还轮不到第二个错误生效就已经崩溃了。
第二处错误:局部栈数组的生命周期完全不支持返回值传递
哪怕你修正了返回值的写法,改成return source;(正确返回数组首地址),程序依然会出问题:source是在iter_string函数内部定义的局部自动变量,存储在函数的栈帧空间上。当iter_string执行完成、返回main函数时,这块栈空间会被系统立即回收,后续的strcpy、printf等函数调用会直接复用这块栈内存覆盖原有数据。
这时候你拿到的“字符串指针”指向的是已经失效的栈内存,读取这类内存属于标准定义的未定义行为——哪怕偶尔没立刻报段错误,读出来的也全是被覆盖后的乱码,完全没有意义。
顺带纠正代码里的注释误区:
source是函数内部定义的局部数组,不是作为函数参数传入的指针,因此在函数内部执行sizeof(source)得到的是整个数组的总大小(30字节),不是注释里写的“指针大小4字节”。只有当数组作为函数参数传入时,才会退化为指向首元素的指针,此时sizeof才会得到指针的大小。
从函数返回字符串的常见正确实现方式
- 在堆上通过
malloc申请内存存储字符串,这块空间不会因为函数返回被自动回收,使用完成后记得调用free释放 - 让调用方把自己定义的字符串数组作为参数传入函数,函数直接向传入的内存空间写入内容
- 把局部数组修饰为
static静态变量,这类变量的生命周期覆盖整个程序运行周期,不会因为函数返回被销毁(注意这种写法不是线程安全的,多次函数调用会复用同一块静态内存互相覆盖)
内容的提问来源于stack exchange,提问作者Random person

