调用get_envaddress后复合字面量varplus内容为何被篡改?
复合字面量栈内存被覆盖的原因解析
问题场景
问题出在insert_envar函数中,相关代码如下:
char **get_envaddress(char **envp, char *find) { int i; int length; length = ft_strlen(find); i = 0; while (envp[i]) { if (!ft_strncmp(find, envp[i], length)) return (&envp[i]); i++; } return (NULL); } char **variable_and_end(char *string) { return ((char *[]){envvar, &string[index]}); } void insert_envar(char **string, char *varplus, char **envp) { char *build; *varplus = 0; varplus = (char *)variable_and_end(varplus + 1); envp = get_envaddress(envp, ((char **)varplus)[0]); *envp = ft_strchr(*envp, '=') + 1; free(((char **)varplus)[0]); varplus = ((char **)varplus)[1]; build = ft_strjoin(*string, *envp); ft_stradd(&build, varplus); free(*string); *string = build; }
当varplus接收variable_and_end返回的复合字面量后,其[0]和[1]的内容符合预期,但调用get_envaddress函数后,varplus的内容甚至[0]、[1]的内存地址都发生了变化——而get_envaddress并未直接操作varplus的内存或值。推测是栈内存变动影响了复合字面量。
验证过程
为验证推测,在获取varplus后将两个字符串保存到全局的test指针数组中,程序恢复正常。修改后的代码及输出如下:
void insert_envar(char **string, char *varplus, char **envp) { char *build; *varplus = 0; varplus = (char *)variable_and_end(varplus + 1); test[0] = ((char **)varplus)[0]; test[1] = ((char **)varplus)[1]; printf("varplus: [0] = %s\nvarplus: [1] = %s\n", ((char **)varplus)[0], ((char **)varplus)[1]); envp = get_envaddress(envp, ((char **)varplus)[0]); printf("varplus: [0] = %s\nvarplus: [1] = %s\n", ((char **)varplus)[0], ((char **)varplus)[1]); printf("test: [0] = %s\ntest: [1] = %s\n", test[0], test[1]); *envp = ft_strchr(*envp, '=') + 1; free(((char **)varplus)[0]); varplus = ((char **)varplus)[1]; build = ft_strjoin(*string, *envp); ft_stradd(&build, varplus); free(*string); *string = build; }
输出结果:
varplus: [0] = PATH varplus: [1] = "asd varplus: [0] = PATH varplus: [1] = ��� test: [0] = PATH test: [1] = "asd
调用get_envaddress前varplus内容正常,调用后[1]损坏,但test数组始终保留正确内容。
原因分析
问题的核心是复合字面量的存储位置:
- 在C语言中,复合字面量(比如
(char *[]){envvar, &string[index]})的生命周期是所在的块作用域。variable_and_end函数返回的复合字面量是在该函数的栈帧上创建的,当variable_and_end执行完毕返回后,这个栈帧会被标记为可复用。 - 此时
varplus保存的是指向这个已失效栈帧内存的指针,属于悬空指针。 - 当调用
get_envaddress时,新的函数栈帧会覆盖之前variable_and_end栈帧的内存区域,导致原本存储复合字面量数组的内存被改写,所以varplus指向的内容会出现损坏。 - 而全局的
test数组存储的是指针的副本,指向的是原本的字符串数据(envvar和&string[index]本身的内存不在栈帧上),所以不会被栈帧覆盖影响,始终保持正确。
内容的提问来源于stack exchange,提问作者Rubens Freire
相关产品推荐
相关产品推荐

