函数分配的堆内存为何表现如栈内存?C指针传参问题排查
问题描述
我花了两天排查,查了不少帖子还是搞不懂堆内存的异常情况。我想写个程序,从命令行参数(char* argv[])获取密码,在堆上分配char* buffer存储输入,再和硬编码的char* password比对。
我在main()里声明了指针char* buffer,让AllocateBuffer()接收这个指针作为参数,用malloc给它分配内存。我没返回指针地址,觉得指针在main()里声明,分配的堆内存之后手动free就行。
在AllocateBuffer()里我把malloc分配的内存填成'A',验证下来一切正常,但调用StoreBuffer()尝试存储目标内容时,发现指针指向的内存块没了,就像栈内存一样!
char* buffer指针在main()里,分配的内存明明在堆上,这完全不符合预期。我是C语言新手,之前学的都说指针是传引用,到底哪里错了?我漏了什么?
附代码
#include<stdio.h> #include<string.h> #include<stdlib.h> int GetBufferLength(int args_num, char* argsv[]) { unsigned int blen = 0; for (int i = 1; i < args_num; i++) { blen += strlen(argsv[i]) + 1; } printf("BufferLength is :%u\n", blen); return blen; } int AllocateBuffer(char* bufferName, int bufferLen) { bufferName = malloc(sizeof(char) * bufferLen); if (bufferName != NULL) { printf("buffer Allocation Ok !!!\n"); for (int i = 0; i < bufferLen; i++) { if (i == bufferLen - 1) { *(bufferName + i) = '\0'; printf("End address that buffer points to in Heap : %p\n", (bufferName + i)); printf("*(bufferName+%d) : %c\n", i, *(bufferName + i)); break; } *(bufferName + i) = 'A'; printf("*(bufferName+%d) : %c\n", i, *(bufferName + i)); } return 1; } else { printf("Could not allocate buffer!!!\n"); return 0; } } // 这里只是做缓冲区检查,实际代码已省略以便理解 void StoreBuffer(char* bufferName, int buffer_length, const char* argsv[], int argsc) { printf("%s", bufferName); } // 以下函数仅展示程序流程,可跳过直接看main() void CompareBuffer(char* bufferName, char* string) { printf("CompareBuffer : %s", bufferName); if (strcmp(bufferName, string) == 0) { printf("Password correct.\n"); } else { printf("Password NOT correct.\n"); } } void DeallocBuffer(char* bufferName) { free(bufferName); bufferName = NULL; } // main() int main(int argc, char* argv[]) { char* password = "super secret"; int bufferlength = GetBufferLength(argc, argv); char* buffer = NULL; if (AllocateBuffer(buffer, bufferlength)) { // 这里是出错点:Storebuffer()中buffer为null,见下方命令行输出 StoreBuffer(buffer, bufferlength, argv, argc); //CompareBuffer(buffer, password); //DeallocBuffer(buffer); } return 0; }
命令行输入与运行结果
C:\Users\Strakizzz\Desktop\C\experiment\experiment\atrocity\bin>atrocity32.exe super secret BufferLength is :13 buffer Allocation Ok !!! *(bufferName+0) : A *(bufferName+1) : A *(bufferName+2) : A *(bufferName+3) : A *(bufferName+4) : A *(bufferName+5) : A *(bufferName+6) : A *(bufferName+7) : A *(bufferName+8) : A *(bufferName+9) : A *(bufferName+10) : A *(bufferName+11) : A End address that buffer points to in Heap : 00AF6C84 *(bufferName+12) : (null)
问题根源与解决方案
问题根源
C语言里所有参数传递都是传值,包括指针。你传给AllocateBuffer()的buffer是原指针的副本,函数里给这个副本赋值malloc返回的堆地址,完全不会改变main()里原buffer的值——原buffer始终是NULL,所以调用StoreBuffer()时传的是NULL,自然输出(null)。
所谓“指针传引用”是误解,指针本身也是一个值(内存地址),传递指针本质是把这个地址值复制了一份传给函数,函数修改的是副本,和原指针无关。
修复方法
有两种常见的修复方式:
方法一:让AllocateBuffer()返回分配好的指针
修改函数的签名和实现,直接返回malloc的结果:
char* AllocateBuffer(int bufferLen) { char* bufferName = malloc(sizeof(char) * bufferLen); if (bufferName != NULL) { printf("buffer Allocation Ok !!!\n"); for (int i = 0; i < bufferLen; i++) { if (i == bufferLen - 1) { *(bufferName + i) = '\0'; printf("End address that buffer points to in Heap : %p\n", (bufferName + i)); printf("*(bufferName+%d) : %c\n", i, *(bufferName + i)); break; } *(bufferName + i) = 'A'; printf("*(bufferName+%d) : %c\n", i, *(bufferName + i)); } } else { printf("Could not allocate buffer!!!\n"); } return bufferName; }
然后修改main()里的调用逻辑:
buffer = AllocateBuffer(bufferlength); if (buffer != NULL) { StoreBuffer(buffer, bufferlength, argv, argc); // CompareBuffer(buffer, password); // DeallocBuffer(buffer); }
方法二:传递指针的指针(char**)
通过传递指针的地址,让函数能直接修改原指针的值:
int AllocateBuffer(char** bufferName, int bufferLen) { *bufferName = malloc(sizeof(char) * bufferLen); if (*bufferName != NULL) { printf("buffer Allocation Ok !!!\n"); for (int i = 0; i < bufferLen; i++) { if (i == bufferLen - 1) { *(*bufferName + i) = '\0'; printf("End address that buffer points to in Heap : %p\n", (*bufferName + i)); printf("*(*bufferName+%d) : %c\n", i, *(*bufferName + i)); break; } *(*bufferName + i) = 'A'; printf("*(*bufferName+%d) : %c\n", i, *(*bufferName + i)); } return 1; } else { printf("Could not allocate buffer!!!\n"); return 0; } }
修改main()里的调用:
if (AllocateBuffer(&buffer, bufferlength)) { StoreBuffer(buffer, bufferlength, argv, argc); // CompareBuffer(buffer, password); // DeallocBuffer(buffer); }
额外提示
你的DeallocBuffer()也存在同样的问题:函数里的bufferName是原指针的副本,把它设为NULL不会改变main()里的原指针。如果想在函数内把原指针置空,同样需要传递指针的指针;或者在main()里调用free(buffer)后,手动把buffer = NULL。
内容的提问来源于stack exchange,提问作者Sotiris

