C语言中from[-1]为何返回'\0'?反转函数异常正常运行原因
from[-1]却总能得到'\0'? 我编写了一个C语言函数reverse用于反转复制数组,代码如下:
void reverse(char to[], char from[], int size) { int i; i = 0; // Copy all the characters before the null character into 'to'. i ends at size-1 for the null character while ((to[i] = from[(size-1)-i])) ++i; }
一段时间后我发现,该函数执行时会计算from[-1]——理论上此处应为垃圾值,但无论输入是什么,代码都能正常运行。通过调试发现,from[-1]的结果始终是'\0',反转后的字符串始终能正确以null终止。我知道可以手动添加to[i] = '\0'来确保终止,但当前写法从未出现问题,以下是完整的测试代码:
#include <stdio.h> #define MAXLINE 1000 int megetline(char s[],int lim); void reverse(char to[], char from[], int size); /* This program reverses it's input a line at a time */ int main() { int len; char line[MAXLINE], r_str[MAXLINE]; while ((len = megetline(line, MAXLINE)) > 0) { reverse(r_str, line, len); printf("%s", r_str); } return 0; } /* Best time: 0.000070 secs */ int megetline(char s[],int lim) { int c, i; for (i = 0; i < lim-1 && (c = getchar()) != EOF && c != '\n'; ++i) s[i] = c; if (c == '\n') { s[i] = c; ++i; } s[i] = '\0'; return i; } void reverse(char to[], char from[], int size) { int i; i = 0; // Copy all the characters before the null character into 'to'. i ends at size-1 for the null character while ((to[i] = from[(size-1)-i])) ++i; }
想请教各位,这种情况出现的原因是什么?
原因分析
1. 栈内存布局的巧合
你的line和r_str都是main函数中的局部数组,会被分配在栈内存中。栈的生长方向通常是从高地址向低地址,先声明的变量会占据更高的内存地址。在你的代码里,line先于r_str声明,所以line的内存地址范围比r_str更高——这意味着line[-1]对应的内存位置,刚好是r_str数组的最后一个元素(或者栈上相邻的其他内存区域)。
而在你的运行环境中,这块相邻的内存恰好被填充了'\0'——可能是编译器在Debug模式下自动初始化栈内存为0,或是之前的函数调用结束后留下了0值,也可能是操作系统对栈内存的默认清零策略。这种巧合让你每次访问from[-1]都能拿到'\0'。
2. 本质是未定义行为,完全不可靠
必须明确:访问from[-1]属于数组越界访问,是C标准定义的未定义行为。标准没有规定越界访问的结果,它可能返回0,也可能返回随机垃圾值,甚至直接导致程序崩溃。你当前的代码能正常运行纯粹是运气,一旦更换编译器、操作系统,或是调整局部变量的声明顺序、数组大小,这个代码很可能立刻出现问题——比如反转后的字符串夹杂乱码,或者程序直接崩溃。
3. 正确的写法:手动添加终止符
绝对不能依赖这种巧合来保证程序正确性。正确的做法是在反转完成后,手动给目标数组添加'\0'终止符,同时避免数组越界访问。比如修改reverse函数:
void reverse(char to[], char from[], int size) { int i; for (i = 0; i < size; ++i) { to[i] = from[(size-1)-i]; } to[i] = '\0'; // 手动添加字符串终止符 }
或者保留while循环逻辑,但调整循环条件避免越界:
void reverse(char to[], char from[], int size) { int i = 0; while (i < size) { to[i] = from[(size-1)-i]; ++i; } to[i] = '\0'; }
这样既能完成反转,又能确保字符串正确终止,同时不会触发未定义行为。
内容的提问来源于stack exchange,提问作者teencorn

