C语言动态内存管理(二)经典笔试题

简介: C语言动态内存管理(二)经典笔试题

四、笔试题

1.请问运行Test函数会有什么样的结果?

void GetMemory(char* p)
{
  p = (char*)malloc(100);
}
void Test(void)
{
  char* str = NULL;
  GetMemory(str);
  strcpy(str, "hello world");
  printf(str);
}
int main() {
  Test();
  return 0;
}

结果:

结果运行出错的原因:

(1)

调用GetMemory函数的时候,str的传参为值传递,p是str的临时拷贝,所以在GetMemory函数内部将动态开辟空间的地址存放在p中的时候,不会影响str,所以GetMemory函数返回之后,str中依然是NULL指针。strcpy函数就会调用失败,原因是对NULL指针的解引用操作,程序会崩溃

(2)

GetMemory函数内部malloc申请的空间没有机会释放,造成了内存泄漏

本题目注意点:

(1)注意这个程序想释放也释放不了,无论是在主函数中释放还是在test函数内部。因为一旦返回,就没有人记得动态开辟的空间在哪里,动态开辟空间的地址带不出来。

(2)而这道题的本意是想把100个字节的地址放到str里面,然后把hello world拷贝到str指向的空间里。

改正该题目的错误:

正确修改1:(利用传值)
char* GetMemory(char* p) {
  p = (char*)malloc(100);
  return p;
}
void Test(void) {
  char* str = NULL;
  str = GetMemory(str);
  strcpy(str, "hello world");
  printf(str);
  free(str);
  str = NULL;
  return 0;
}
int main() {
  Test();
  return 0;
}

解释:

(1)这里也是利用传值,但是GetMemory函数返回的是指针,p所指向的是malloc出来的空间,是在堆上,除了函数不销毁这块空间,通过p返回的地址仍然可以找到这块空间。

与原题传值对比:

原题目中的p是函数的形参变量,形参变量是个临时变量,出了函数就销毁了。p销毁了,p这块空间还给操作系统,没人记得malloc申请的空间在哪里。

(2)使用完动态开辟的这块空间用free回收掉

正确修改2:(利用传址)
void GetMemory(char** p) {
  *p = (char*)malloc(100);
}
void Test(void) {
  char* str = NULL;
  GetMemory(&str);
  strcpy(str, "hello world");
  printf(str);
  free(str);
  str = NULL;
  return 0;
}
int main() {
  Test();
  return 0;
}

解释:

将指针str的地址传给GetMemory函数的形参,str是char类型的是一级指针,它的地址用二级指针**p接收,在GetMemory函数中的p存的就是str,这样的话不通过返回值也能把地址带回来

本题涉及知识点

关于printf打印字符的两种方式:

比如这句代码 char* p=“hehe\n”;

意思不是把字符串hehe\n放到p里面,而是把这个表达式字符串首地址h的地址放到p中

2.请问运行Test函数会有什么样的结果?

char* GetMemory(void)
{
  char p[] = "hello world";
  return p;
}
void Test(void)
{
  char* str = NULL;
  str = GetMemory();
  printf(str);
}
int main() {
  Test();
  return 0;
}

结果

出错原因:

返回栈空间地址的问题:

GetMemory函数内部创建的数组是临时的,虽然返回了数组的起始地址给str,但是数组的内存出了GetMemory函数就被回收了,而str依然保存了数组的起始地址,这时使用str,str就是野指针

通俗理解本题:

张三开了一间房,告诉李四明天来如家酒店302房间可入住,但是在李四来之前,张三已经把房间退了。等到李四根据张三提供的地址来到302时却无法入住,这间房也不属于李四。

这里的把房间退了相当于将空间还给操作系统,即使你有这个空间的地址信息,而这个空间确不属于你,且不能访问这个空间

教训:局部变量的地址不要随便返回

举例说明:

根据上面的讲解,这里打印的应该是随机值,而这里刚好打印出正确的结果,是怎么回事?难道这样写也正确吗?

答:这样写是错误的。之所以刚好打印出结果10,是因为恰好空间没有被改掉的情况。


当多打印个别的语句时,就可以发现此时*p的值改变成5了。

这里为什么多打印个hehe下面就变了?

答:因为栈帧空间会被覆盖

重点内容:注意区分在函数里什么时候可以返回

char* GetMemory(char* p) {
  p = (char*)malloc(100);
  return p;
}

3.请问运行Test函数会有什么样的结果?

void GetMemory(char** p, int num)
{
  *p = (char*)malloc(num);
}
void Test(void)
{
  char* str = NULL;
  GetMemory(&str, 100);
  strcpy(str, "hello");
  printf(str);
}

错误点:

malloc动态开辟的空间在使用完没有释放掉,造成内存泄漏

改正:

释放指针并将指针置为空

重点

free完之后的指针如果不是空指针,一定要置为空指针,避免野指针问题

4.请问运行Test函数会有什么样的结果?

void Test(void)
{
  char* str = (char*)malloc(100);
  strcpy(str, "hello");
  free(str);
  if (str != NULL)
  {
    strcpy(str, "world");
    printf(str);
  }
}

错误点:

(1)free完动态开辟的空间应该及时将指针置空,这里没有置空

(2)str指向的空间已经回收了,因为str没有置为空指针,还能找到刚开辟的那块空间,但是这块空间已经回收给操作系统了,不属于str,不能用,拷贝时形成非法访问,此时的str是野指针

改正:

5.Nice校招笔试题

指出下面哪段程序有问题,并说明问题是什么。

(1)

该题是返回栈空间地址的问题。

如果有人接收了这个函数返回的指针,就会造成野指针的问题。

(2)

指针ptr没有初始化,没有任何指向,里面是随机值。

不知道ptr指向谁,直接对它解引用

对野指针解引用,还把10放进去,非法访问内存。

结语:

本篇内容就到这里啦,关于动态内存管理的内容未完待续,请见下篇博客。如果对大家有帮助的话,希望友友们可以点赞收藏博客,关注后续的学习内容哦!❤️💕💕

相关文章
|
13天前
|
C语言
【C语言】:总结动态内存的常见错误
【C语言】:总结动态内存的常见错误
11 0
|
13天前
|
C语言
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
19 0
|
13天前
|
编译器 测试技术 C语言
【C语言】:自定义类型:结构体的使用及其内存对齐
【C语言】:自定义类型:结构体的使用及其内存对齐
29 7
|
13天前
|
C语言
【C语言】:4大内存函数
【C语言】:4大内存函数
13 2
|
9天前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
14 0
|
9天前
|
程序员 C语言
C语言内存管理:malloc、calloc、realloc与free函数详解
C语言内存管理:malloc、calloc、realloc与free函数详解
8 0
|
10天前
|
存储 编译器 C语言
C语言的联合体:一种节省内存的数据结构
C语言的联合体:一种节省内存的数据结构
10 0
|
12天前
|
存储 C语言
【C语言进阶篇】整数在内存的存储——原码、反码、补码
【C语言进阶篇】整数在内存的存储——原码、反码、补码
|
12天前
|
程序员 编译器 C语言
【c语言】详解动态内存管理
【c语言】详解动态内存管理
10 0
|
13天前
|
程序员 C语言 C++
【C语言】:柔性数组和C/C++中程序内存区域划分
【C语言】:柔性数组和C/C++中程序内存区域划分
11 0