【C语言】动态内存经典笔试题(上卷)

前言

本系列将详细讲解4道有关动态内存的经典笔试题,以助于加深对动态内存的理解。这些题目都非常经典,你可能随时会遇到它们,所以非常重要

本文讲解其中的前两题。

第一题

这个程序运行的结果是什么?

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;
}
分析

代码的执行顺序从主函数开始,主函数中调用了Test(),所以我们去看Test()。创建了空指针str传给函数GetMemory(),开辟了100字节的地址,首地址存到p中。再回到Test(),往下执行。

要注意的是,实参传给形参时,形参是实参的一份临时拷贝。所以相当于我们在GetMemory里创建了一个指针变量p,里面存的是NULL,因为p得到的是str的值;在堆上申请了100个字节的空间,假设地址是0x0012ff40,然后这个地址就放入p中,p有能力能找到这块空间。

但此时并没有将这个地址给str,str依然是NULL,GetMemory函数结束后,str还是NULL,没有指向有效的空间,据我们对strcpy的了解,已经对空指针解引用操作了,程序会崩溃。这是这段代码的第一个问题。

第二个问题,我们没有free我们在GetMemory里开辟的这块空间,回到Test之后没人记得这块空间在哪,就形成了内存泄漏的问题。

补充这个printf(str);的语法是没有问题的。虽然我们一般会使用printf("%s\n",str);的方式,但其实printf("haha\n");我们也是直接把字符串给printf了,本质上我们给的是首字符的地址。就像char*p = "haha";我们也是把字符首地址赋给p而不是把字符赋给p。  也就是printf有首字符地址就可以打印。

怎么改正这个代码?

我们可以去思考这个代码的原意是什么,从函数名GetMemory可以看出,是想申请(get)一块内存(memory),将这块内存用于存放拷贝内容。所以我们可以从原意出发去修改。

改法1:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void GetMemory(char** p)//二级指针接收
{
    *p = (char*)malloc(100);//*p相当于str,str=(char*)malloc(100);开辟的内存首地址确实给了str
}

void Test(void)
{
    char* str = NULL;
    GetMemory(&str);//改为传str的地址
    strcpy(str, "hello world");
    printf(str);
    free(str);//避免内存泄漏
    str=NULL;
}

int main()
{
    Test();
    return 0;
}

为了理解这么改的含义,我们可以简化一下这个问题,如果我们写这样的代码:

 这就类似我们上面修改前的代码,p只是a的一份临时拷贝,里面放着0,当它改为10后,对a并没有实质影响。那么如果我们想通过改变p来改变a的值,就会把代码改成这样:

可以看到,此时我们的a打印出来,已经被改为10了。

同样的,str虽然是指针,但指针变量也是变量。我们现在是想将str里的内容(NULL)改为我们开辟的内存的起始地址,所以我们直接传str是不能达到效果的,应该传&str,就像&a,但是不同的在于str本身就是个指针,而指针的地址应该是个二级指针,所以我们要用二级指针来接收。

最后,再记得释放str和置为空指针,避免内存泄漏就行了。

改法2:

我们把p返回并用str接收就行了。但是这样写有点多此一举,改成这样更简单、合适:

第二题

下面这个程序运行结果是什么?

char *GetMemory(void)
{
     char p[] = "hello world";
     return p;
}

void Test(void)
{
     char *str = NULL;
     str = GetMemory();
     printf(str);
}

int main()
{
    Test();
    return 0;
}
分析:

打印结果是这个:

这题的突破口在于,p可是个数组,进入函数GetMemory创建,出了函数就会销毁。而在销毁前,我们先把p返回了。p是数组名,即起始地址。这个地址返回后我们放到str中,str能找到这块空间。内存里确实有这块空间,但是能否用这块空间取决于我们有没有使用权限。出了函数GetMemory,其实这块空间的使用权限就没有了,但str仍能找到它(str就是个野指针)。打印出来“烫烫烫…”说明这块空间使用权限不属于当前程序时,空间中内容可能被改掉了,我们非要打印就不再是hello world了。

注意,这个现象发生根本原因在于我们的p是个数组,如果改为melloc开辟的空间,就不会有这个问题。因为如果我们不手动释放,在程序彻底结束前,p就一直指向这块空间且有使用权限。数组(也相当于局部变量)开辟的空间是放在上的。所以这个问题是一个典型的返回栈空间(临时空间)地址的问题。

我们可以返回一个变量,但不能返回一个变量的地址。因为这个变量(比如数组)出了函数就销毁了,记住地址也没用。

比如,下面这个代码就是返回一个变量:

实际上,编译器会怎么处理这个代码呢?

a会销毁,编译器会把10放到一个寄存器中。寄存器是CPU里的一个存储空间,是不会销毁的。a销毁了没关系,会将寄存器里的10再给n。就像一个托管。

而如果返回地址,就不行:

 

此时p得到返回的地址,但是指向的空间已经归还操作系统,所以p是个野指针。虽然我们打印出来发现还是原来的值,这只是巧合。如果改为这样:

可以看到,在前面打印了一次其他内容后,就无法再打印出2077。 为什么呢?这说明我们在打印*p时,这个空间还给操作系统了,里面已经不再是2077了。我们在printf("See\n");的时候可能已经把这块空间申请走了,把原本内容覆盖了。

 

可以看到,在test函数结束后,函数栈帧销毁了,而printf("See\n");在申请空间时可能把原来test的空间覆盖了,所以可能会把p指向的空间的内容修改。

再次重申,这是返回栈空间地址的问题。

一旦有人接收了这个地方,就注定是个野指针。

附加题

我们再看看这两个题:

一、以下程序有什么问题?

int* f1(void) {
	int x = 10;
	return (&x);
}

分析:这个也是返回栈空间地址的问题。

二、以下程序有什么问题?

int* f2(void) {
	int* ptr;
	*ptr = 10;
	return ptr;
}

分析:ptr没有初始化就进行了解引用,ptr是野指针。

到此,上卷结束,祝阅读愉快^_^

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/690999.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

读书笔记:左耳听风

程序员如何用技术变现 我完全没有必要通过打工听人安排而活着&#xff0c;而是反过来通过在公司工作提高自己的技能&#xff0c;让自己可以更为独立和自由地生活。 因而&#xff0c;在工作当中&#xff0c;对于那些没什么技术含量的工作&#xff0c;我基本上就像是在学生时代那…

“中新美”三重身份,能帮SHEIN解决上市问题吗?

一家公司的海外上市之路能有多复杂&#xff1f;辗转多地的SHEIN&#xff0c;可能是当前最有话语权回答这个问题的公司。最近&#xff0c;它又有了新消息。 在上市信息多次更改后&#xff0c;伦敦正在成为SHEIN最有可能的“着陆”点。巴伦周刊援引英国天空新闻报道称&#xff0…

在windows下使用本地AI模型提供翻译、对话、文生图服务

文章目录 在windows下使用本地AI模型提供翻译、对话、文生图服务ollama简介下载安装配置环境变量模型安装目录服务监听地址跨域配置我的配置注意事项 开机自启 使用运行模型对话时的命令 查看本地已安装模型删除模型 查看ollama支持的模型 Docker Desktop简介下载安装配置开机自…

AI绘画中的图像格式技术

在数字艺术的广阔天地里&#xff0c;AI绘画作为一种新兴的艺术形式&#xff0c;正在逐渐占据一席之地。不同于传统绘画&#xff0c;AI绘画依赖于复杂的算法和机器学习模型来生成图像&#xff0c;而这一切的背后&#xff0c;图像格式技术发挥着至关重要的作用。图像格式不仅关系…

VisionPro的应用和入门教程

第1章 关于VisionPro 1.1 康耐视的核心技术 1. 先进的视觉系统 康耐视的视觉系统结合了高性能的图像传感器、复杂的算法和强大的计算能力&#xff0c;能够实时捕捉、分析和处理高分辨率图像。其视觉系统包括固定式和手持式两种&#xff0c;适用于各种工业环境。无论是精密电…

《C++避坑神器·二十七》VS中release打断点方法,#undef作用

1、release打断点方式 2、#undef作用 #undef指令用于”取消“已定义的#define指令 案例&#xff1a;

CorelDraw安装时界面显示不全的解决方案

问题原因&#xff1a;安装包权限 解决方案&#xff1a; 1、安装包解压后&#xff0c;找到Setup文件&#xff0c;复制粘贴到当前文件夹并重命名为Getup后&#xff0c;右击Getup文件&#xff0c;选择“以管理员身份运行” 说明&#xff1a;除了命名Gsetup。还可以命名为其他的…

vue ts 导入 @/assets/ 红色显示的问题解决

vue ts 导入 /assets/ 红色显示的问题解决 一、问题描述 在使用的时候这样导入会出现如上的错误。 在使用的时候&#xff0c;导入的类型也没有对应的代码提示&#xff0c;说明导入有问题。 二、解决 在 tsconfig.json 中添加如下内容&#xff1a; {"compilerOptions&…

Progressive Feature Fusion Framework Based on Graph Convolutional Network

以Resnet50作为主干网络&#xff0c;然后使用GCN逐层聚合多级特征&#xff0c;逐级聚合这种模型架构早已不新鲜&#xff0c;这篇文章使用GCN的方式对特征进行聚合&#xff0c;没有代码。这篇文章没有过多的介绍如何构造的节点特征和邻接矩阵&#xff0c;我觉得对于图卷积来说&a…

MyBatisPlus插件生成代码

文章目录 概要安装插件使用插件 概要 MyBatis-Plus 是 MyBatis 的增强工具&#xff0c;旨在简化 MyBatis 的开发。MyBatis-Plus 代码生成器插件可以自动生成项目中常见的代码&#xff0c;如实体类、Mapper 接口、Service 接口和实现类、Controller 等&#xff0c;从而减少手动…

【Vue】mutations

文章目录 一、定义mutations二、组件中提交 mutations三、带参数的 mutations 一、定义mutations mutations是vuex中的对象&#xff0c;这个对象可以定义在当前store的配置项中 const store new Vuex.Store({state: {count: 0},// 定义mutations// mutations是一个对象&#x…

Java面试八股之什么是反射,实现原理是什么

Java中什么是反射&#xff0c;实现原理是什么 Java中的反射&#xff08;Reflection&#xff09;是一种强大的特性&#xff0c;它允许程序在运行时检查和操作类、接口、字段和方法的信息。简而言之&#xff0c;反射机制使得程序能够在运行时动态地了解和使用自身或其他程序集中…

ubuntu 用户名及密码忘记操作

1、重启系统&#xff0c;长按Shift键&#xff0c;直到出现菜单&#xff0c;选则高级设置。选择recovery mode&#xff0c;即恢复模式 2、选择root 3、# 后面敲入 sudo passwd 用户名 4、# passwd "用户名" 之后再敲两次密码就可以了。(如果提示修改失败可先执行&a…

【Ardiuno】实验使用ESP32连接Wifi(图文)

ESP32最为精华和有特色的地方当然是wifi连接&#xff0c;这里我们就写程序实验一下适使用ESP32主板连接wifi&#xff0c;为了简化实验我们这里只做了连接部分&#xff0c;其他实验在后续再继续。 由于本实验只要在串口监视器中查看结果状态即可&#xff0c;因此电路板上无需连…

vue2 中如何使用 render 函数编写组件

vue2 中如何使用 render 函数编写组件 render 基础语法createElement返回值&#xff1a;VNode参数处理样式和类组件 propsHTML 特性和 DOM 属性处理事件插槽指令v-model 指令其他属性 使用 render 封装一个输入框其他问题参考 vue 提供了声明式编写 UI 的方式&#xff0c;即 vu…

tcp aimd 窗口的推导

旧事重提&#xff0c;今天用微分方程的数值解观测 tcp aimd 窗口值。 设系统 AI&#xff0c;MD 参数分别为 a 1&#xff0c;b 0.5&#xff0c;丢包率由 buffer 大小&#xff0c;red 配置以及线路误码率共同决定&#xff0c;设为 p&#xff0c;窗口为 W&#xff0c;则有&…

C++STL简介

一、STL介绍 STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 二、STL的版本 1、原始版本 Alexander Stepanov、Meng Lee 在惠普实验室完…

数字证书和CA

CA&#xff08;Certificate Authority&#xff09;证书颁发机构 验证数字证书是否可信需要使用CA的公钥 操作系统或者软件本身携带一些CA的公钥&#xff0c;同时也可以向提供商申请公钥 数字证书的内容 数字证书通常包含以下几个主要部分&#xff1a; 主体信息&#xff08…

搭建多平台比价系统需要了解的电商API接口?

搭建一个多平台比价系统涉及多个步骤&#xff0c;以下是一个大致的指南&#xff1a; 1. 确定需求和目标 平台选择&#xff1a;确定你想要比较价格的平台&#xff0c;例如电商网站、在线旅行社等。数据类型&#xff1a;明确你需要收集哪些数据&#xff0c;如产品价格、产品名称…

仪表板展示|DataEase看中国:2024年高考数据前瞻

背景介绍 2024年高考即将来临。根据教育部公布的数据&#xff0c;2024年全国高考报名人数为1342万人&#xff0c;相比2023年增加了51万人。高考报名人数的增加&#xff0c;既体现了我国基础教育的普及范围之广&#xff0c;也反映了社会对高等教育的重视和需求。 随着中央和各…