4. 几个经典的笔试题
4.1 题目1:
void GetMemory(char *p) { p = (char *)malloc(100); } void Test(void) { char *str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); //注意,该行代码没有错误!解析会讲 }
输出:
解析:
void GetMemory(char *p) //用形参 p 接收 str { p = (char *)malloc(100); //将动态开辟的内存首地址传给指针变量 p ,并没有传给 str } void Test(void) { char *str = NULL; GetMemory(str); //函数调用结束后,str 还是空指针 strcpy(str, "hello world"); //无法将该字符串复制到空指针内 printf(str); }
为什么可以这样写:
printf(str);
我们先来看看 printf 函数的原型:
int printf ( const char * format, ... );
将格式所指向的 C 字符串写入标准输出(标准输出)。如果 format 包含格式说明符(以 % 开头的子序列),则格式后面的其他参数将被格式化并插入到结果字符串中,以替换它们各自的说明符。
还记得我们之前接触的 printf 的写法有:
printf("hello world");
之所以能打印出来,是因为我们将字符串的首元素 ‘h’ 的地址传入了 printf 函数中,那么我们直接传字符串的首元素地址 str 也是一样的
4.2 题目2:
char *GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char *str = NULL; str = GetMemory(); printf(str); }
输出:
解析:
char *GetMemory(void) { char p[] = "hello world"; return p; //指针p指向了字符‘h’, } void Test(void) { char *str = NULL; str = GetMemory(); //接收指针p的值,但当该函数调用结束后,局部变量 p 所指向的内容就被销毁了 printf(str); //所以打印出来就是乱码 }
4.3 题目3:
void GetMemory(char **p, int num) { *p = (char *)malloc(num); } void Test(void) { char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); }
输出:
咋一看,似乎没啥问题,虽然输出没问题,但是我们开辟的动态内存没有进行释放,导致了内存泄漏,这虽然不影响结果,但也是很严重对问题。
4.4 题目4:
void Test(void) { char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); if (str != NULL) { strcpy(str, "world"); printf(str); } }
输出:
输出没有问题,我们看下面的警告,因为本题没有判断 str 是否为空,所以在复制字符串的时候警告,当 free 完后,那块动态内存开辟的空间就不属于我了,str 就是野指针了,再进行访问就是非法访问。
我们可以对其进行修改一下:
void Test(void) { char* str = (char*)malloc(100); if(str == NULL) //判断是否为空指针 return 1; strcpy(str, "hello"); free(str); str = NULL; //养成习惯,free 后就置空,防止野指针 if (str != NULL) //这样就不会再出错了 { strcpy(str, "world"); printf(str); } }
最后
下期讲解 C/C++ 程序的内存开辟和柔性数组。