内存管理

C语言中,栈内存(局部变量)自动分配/释放,静态区(全局、静态变量)编译时分配;堆内存需手动分配/释放,核心函数有3个:

 malloc函数

  • 原型:void* malloc(size_t size);
  • 功能:在堆上分配连续的size字节内存,不初始化内存内容。
  • 返回值:成功则返回指向内存首地址的void*指针;失败返回NULL(如内存不足)。
  • 用法示例:
    int *p = (int*)malloc(5 * sizeof(int)); // 分配能存5个int的内存(需强转) 
    if (p == NULL) { // 检查分配失败 printf("malloc failed\n"); return -1; 
    } 
    

 calloc函数

  • 原型:void* calloc(size_t num, size_t size);
  • 功能:在堆上分配**num个大小为size的连续内存块**,且自动初始化为0
  • 返回值:成功返回内存首地址void*;失败返回NULL
  • malloc的区别:calloc会初始化内存为0,且参数是“个数+单个大小”(malloc是总字节数)。
  • 用法示例:
    int *q = (int*)calloc(5, sizeof(int)); // 分配5个int,每个初始化为0 
    if (q == NULL) { /* 错误处理 */ } 
    

 realloc函数

  • 原型:void* realloc(void* ptr, size_t new_size);
  • 功能:调整已分配内存块的大小(基于ptr指向的原内存)。
  • 调整逻辑:
    • 若原内存后有足够空间,直接扩展,返回原地址;
    • 若空间不足,重新分配新内存块,复制原数据到新块,释放原内存,返回新地址;
    • ptrNULL,等价于malloc(new_size);若new_size为0,等价于free(ptr)(不同实现有差异)。
  • 返回值:成功返回新内存首地址;失败返回NULL(原ptr指向的内存不变)。
  • 用法示例:
    p = realloc(p, 10 * sizeof(int)); // 将p的内存从5个int扩容到10个int 
    if (p == NULL) { /* 错误处理(原p内存仍有效) */ } 
    

内存释放函数free

  • 原型:void free(void* ptr);
  • 功能:将ptr指向的动态分配的堆内存归还给系统,避免内存泄漏。
  • 注意点:
    • ptr必须是malloc/calloc/realloc返回的指针(否则行为未定义,如野指针、栈内存指针);
    • 释放后ptr变为悬空指针(指向无效内存),需手动置NULL避免误用;
    • 不可重复释放同一指针(未定义行为,可能崩溃)。
  • 用法示例:
    free(p); 
    p = NULL; // 释放后置空,避免悬空指针 
    

常见问题与注意事项

  1. 分配失败检查:调用malloc/calloc/realloc后,必须判断返回值是否为NULL,否则后续操作空指针会崩溃。
  2. 内存泄漏:动态分配的内存未用free释放,程序结束前不会自动回收,长期运行会耗尽内存。
  3. 悬空指针free后指针未置NULL,后续若误操作(如解引用、再次free)会触发未定义行为。
  4. 内存越界:访问分配内存范围外的地址(如p[5]但只分配了5个int,索引0~4有效),会破坏内存结构,导致程序崩溃或数据错乱。
  5. realloc的风险:若realloc失败返回NULL,原ptr仍有效,需单独处理(如备份原指针)。

C语言动态内存管理核心是malloc/calloc/realloc分配堆内存,free释放内存。需牢记分配必检查、释放要置空、避免越界/重复释放,才能安全管理内存。

指针

一、指针的基本概念

  1. 定义与声明

    指针是存储另一个变量的地址的变量。其声明形式如下:

    数据类型 *指针名;

    其中数据类型是指针指向的数据类型的说明符,而*表示这是一个指针变量。

    示例:

    int *p; // p 是一个指向整数的指针
  2. 取地址运算符(&)和解引用运算符(*)

    • &:取地址运算符,用于获取变量的地址。

      int a = 5;
      int *p = &a; // p 现在保存了 a 的地址
    • *:解引用运算符,访问指针所指向的值。

      int value = *p; // value 将会是 5,即 p 所指向位置的值

二、指针与数组

在C语言中,数组名实际上是一个指向数组第一个元素的常量指针。这意味着你可以使用指针来遍历数组。

示例:

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 等价于 int *p = &arr[0];
for (int i = 0; i < 5; ++i) {printf("%d ", *(p + i)); // 使用指针访问数组元素
}

三、指针与函数

  1. 传递指针作为参数

    通过传递指针给函数,可以在函数内部修改调用者提供的变量的值。

    示例:

    void increment(int *n) {(*n)++;
    }int main() {int number = 10;increment(&number);// number 现在是 11
    }
  2. 返回指针

    函数可以返回一个指针,但需要小心处理动态分配的内存以避免内存泄漏。

    示例:

    int* createArray(int size) {return malloc(size * sizeof(int));
    }

四、指针的算术运算

指针支持加法和减法操作,允许你移动指针指向不同的内存位置。

  • 指针加法:将指针向前移动若干个元素的位置。

    int arr[] = {1, 2, 3};
    int *p = arr;
    p++; // p 现在指向 arr[1]
  • 指针减法:将指针向后移动若干个元素的位置或计算两个指针之间的距离。

五、多级指针

指针本身也可以有地址,即指向指针的指针或多级指针。

示例:

int a = 10;
int *p = &a; // p 是一个指向 int 的指针
int **pp = &p; // pp 是一个指向 int* 类型指针的指针

六、void指针

void*是一种特殊类型的指针,它可以指向任何数据类型的变量,但它不能直接解引用,因为编译器不知道它实际指向的数据类型。

示例:

void* ptr;
int a = 10;
ptr = &a;
int *intptr = (int*)ptr; // 需要强制转换为具体类型才能解引用

七、指针的安全注意事项

  • 空指针检查:在使用指针前,应该检查它是否为NULL

    if (ptr != NULL) {// 安全使用指针
    }
  • 避免悬空指针:当指针指向的内存被释放后,该指针变成悬空指针。访问悬空指针会导致未定义行为。

  • 正确管理动态内存:使用malloc, calloc, realloc分配内存,并确保使用free释放不再使用的内存以避免内存泄漏。

函数指针

在C语言中,指针不仅可以指向变量,还可以指向函数。函数指针是一种特殊的指针类型,它存储的是函数的起始地址,可以通过这个指针来调用函数。

一、定义和声明函数指针

定义一个函数指针需要指定其指向的函数的返回类型以及参数列表。其基本语法如下:

返回类型 (*指针名)(参数类型列表);

例如,假设有一个返回类型为int,接受两个int类型参数的函数,那么对应的函数指针可以这样定义:

int add(int a, int b) {return a + b;
}int (*funcPtr)(int, int); // 定义一个函数指针

二、初始化函数指针

你可以将函数的名字赋值给函数指针,因为函数名本质上是指向函数入口点的指针。例如:

funcPtr = add; // 将add函数的地址赋给funcPtr

或者直接在声明时初始化:

int (*funcPtr)(int, int) = add;

三、通过函数指针调用函数

一旦函数指针被初始化,就可以像普通函数那样使用它来调用函数:

int result = funcPtr(3, 4); // 相当于调用add(3, 4)
printf("%d\n", result); // 输出7

四、函数指针作为参数传递

函数指针可以作为参数传递给其他函数,这在实现回调机制时非常有用。例如:

void executeOperation(int (*operation)(int, int), int a, int b) {printf("Result: %d\n", operation(a, b));
}int main() {executeOperation(add, 5, 3); // 传递add函数的指针return 0;
}

五、函数指针数组

你也可以创建一个函数指针数组,用于存储多个函数指针。这对于实现类似多态的行为很有帮助。

int subtract(int a, int b) {return a - b;
}int main() {int (*operations[2])(int, int) = {add, subtract}; // 函数指针数组int result1 = operations[0](10, 5); // 调用addint result2 = operations[1](10, 5); // 调用subtractprintf("Add: %d, Subtract: %d\n", result1, result2);return 0;
}

六、注意事项

  • 类型匹配:函数指针的类型必须与它指向的函数的签名完全匹配(包括返回类型和参数列表)。
  • 空指针检查:如同其他类型的指针一样,使用前应确保函数指针不是NULL,避免未定义行为。
  • 函数指针与函数本身的区别:虽然函数名可以直接赋值给函数指针,但它们并不完全相同。函数名是编译时常量,而函数指针是一个变量,可以在运行时改变其所指向的函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90717.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90717.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/90717.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用GPU训练模型

本文代码详解参考&#xff1a; 模型训练基础流程-CSDN博客 目录 为什么要用GPU训练模型 什么是CUDA 利用GPU训练—方式一(.cuda()) 利用GPU训练—方式二 (.to()) Google Colaboratory 为什么要用GPU训练模型 用 GPU 训练模型的核心原因是GPU 的硬件架构和计算特性&#…

Matlab学习笔记:自定义函数

MATLAB 学习笔记&#xff1a;自定义函数自定义函数是MATLAB编程的基础&#xff0c;它允许你将重复代码封装成可重用的模块&#xff0c;提高代码的可读性和效率。本笔记将覆盖所有重点知识点&#xff0c;包括语法细节、输入输出处理、函数文件管理、错误处理等。我会用自然易懂的…

【数学建模 | Matlab】二维绘图 和 三维绘图

- 第 115 篇 - Date: 2025 - 07 - 23 Author: 郑龙浩&#xff08;仟墨&#xff09; 续写上一篇&#xff0c;如下文章&#xff1a; 【数学建模|Matlab】Matlab「基础知识」和「基础操作」 五 二维绘图 1 举例 % 二维平面绘图&#xff08;扩展至 -2π 到 2π&#xff09; x …

MCP (Model Context Protocol) 与 HTTP API:大模型时代的通信新范式

MCP (Model Context Protocol) 与 HTTP API&#xff1a;大模型时代的通信新范式在数字世界的两端&#xff0c;API 扮演着不可或缺的桥梁角色。我们熟知的 HTTP API 是 Web 互联互通的基石&#xff0c;驱动着无数应用程序的交互。然而&#xff0c;随着大型语言模型&#xff08;L…

CentOS 搭建 Docker 私有镜像仓库

CentOS 搭建 Docker 私有镜像仓库 搭建 Docker 私有镜像仓库能为团队提供高效、安全的镜像管理方案。下面将详细介绍每个步骤的操作细节&#xff0c;其中命令部分均用代码块展示。 一、环境准备 要搭建 Docker 私有镜像仓库&#xff0c;首先得确保服务器环境符合要求&#xff0…

Zookeeper的简单了解

Zookeeper的简单了解 Zookeeper是一个为分布式应用程序提供协调服务的中间件。 主要作用有三点&#xff1a;分布式锁、注册中心、配置管理、。 特点有读写速度快&#xff08;内存存储&#xff09;、有监听机制&#xff08;用于发布订阅&#xff09;、保证了顺序一致性&#xff…

Android Fragment 全解析

在 Android 开发中&#xff0c;Fragment 是构建灵活界面的核心组件 —— 它既能像 “迷你 Activity” 一样包含布局和逻辑&#xff0c;又能灵活地嵌入到不同 Activity 中复用。无论是平板的多面板布局&#xff0c;还是手机的单页切换&#xff0c;Fragment 都能让界面适配更高效…

0-1BFS(双端队列,洛谷P4667 [BalticOI 2011] Switch the Lamp On 电路维修 (Day1)题解)

对于权重为0或1的路径搜索中&#xff0c;使用双端队列可以对最短路问题进行时间复杂度的优化&#xff0c;由于优先队列的O(longn)级别的插入时间&#xff0c;对于双端队列O(1)插入可以将时间复杂度减少至O(M); https://www.luogu.com.cn/problem/P4667 #include<bits/stdc…

基于LNMP架构的分布式个人博客搭建

1.运行环境主机主机名系统服务192.168.75.154Server-WebLinuxWeb192.168.75.155Server-NFS-DNSLinuxNFS/DNS2.基础配置配置主机名&#xff0c;静态IP地址开启防火墙并配置部分开启SElinux并配置服务器之间使用同ntp.aliyun.com进行时间同步服务器之间使用用ntp.aliyun.com进行时…

基于开源AI智能名片链动2+1模式S2B2C商城小程序的人格品牌化实现路径研究

摘要&#xff1a;在数字化消费时代&#xff0c;人格品牌化已成为企业突破同质化竞争的核心策略。本文以开源AI智能名片、链动21模式与S2B2C商城小程序的融合为切入点&#xff0c;构建“技术赋能-关系重构-价值共生”的人格品牌化理论框架。通过分析用户触达、信任裂变与价值沉淀…

设计模式十一:享元模式(Flyweight Pattern)

享元模式是一种结构型设计模式&#xff0c;它通过共享对象来最小化内存使用或计算开销。这种模式适用于大量相似对象的情况&#xff0c;通过共享这些对象的公共部分来减少资源消耗。基本概念享元模式的核心思想是将对象的内在状态&#xff08;不变的部分&#xff09;和外在状态…

Webpack/Vite 终极指南:前端开发的“涡轮增压引擎“

开篇:当你的项目变成"俄罗斯套娃" "我的index.js怎么引入了87个文件?!" —— 这是每个前端开发者第一次面对复杂项目依赖时的真实反应。别担心,今天我要带你认识两位"打包侠":老牌劲旅Webpack和新锐黑马Vite 一、构建工具:前端世界的&qu…

Windows 下配置 GPU 用于深度学习(PyTorch)的完整流程

1. 安装 NVIDIA 显卡驱动 前往 NVIDIA官网 下载并安装适合你显卡型号&#xff08;如 5070Ti&#xff09;的最新版驱动。下载 NVIDIA 官方驱动 | NVIDIA安装完成后建议重启电脑。 2. 安装 CUDA Toolkit 前往 CUDA Toolkit 下载页。 选择 Windows、x86_64、你的系统版本&#…

详解力扣高频SQL50题之180. 连续出现的数字【困难】

传送门&#xff1a;180. 连续出现的数字 题目 表&#xff1a;Logs -------------------- | Column Name | Type | -------------------- | id | int | | num | varchar | -------------------- 在 SQL 中&#xff0c;id 是该表的主键。 id 是一个自增列。 找出所有至少连续…

VSCode 报错 Error: listen EACCES: permission denied 0.0.0.0:2288

使用 npm run dev 启动项目时报错&#xff1a;error when starting dev server: Error: listen EACCES: permission denied 0.0.0.0:2288at Server.setupListenHandle [as _listen2] (node:net:1881:21)at listenInCluster (node:net:1946:12)at Server.listen (node:net:2044:…

[2025CVPR-图象超分辨方向]DORNet:面向退化的正则化网络,用于盲深度超分辨率

1. ​问题背景与挑战​ 盲深度超分辨率&#xff08;Blind Depth Super-Resolution, DSR&#xff09;的目标是从低分辨率&#xff08;LR&#xff09;深度图中恢复高分辨率&#xff08;HR&#xff09;深度图&#xff0c;但现有方法在真实场景下面临显著挑战&#xff1a; ​已知…

关系与逻辑运算 —— 寄存器操作的 “入门钥匙”

前言 哈喽大家好&#xff0c;这里是 Hello_Embed 的新一篇学习笔记。在前文中&#xff0c;我们学习了如何用结构体指针操作硬件寄存器&#xff0c;而寄存器的配置往往离不开位运算和条件判断 —— 比如通过逻辑运算精准修改某几位的值&#xff0c;通过关系运算判断硬件状态。这…

使用 Python 将 CSV 文件转换为带格式的 Excel 文件

在日常的数据处理和报表生成工作中&#xff0c;CSV 格式因其简洁性而被广泛采用。但在展示数据时&#xff0c;CSV 文件往往缺乏格式和结构化样式&#xff0c;不利于阅读与分析。相比之下&#xff0c;Excel 格式&#xff08;如 .xlsx&#xff09;不仅支持丰富的样式设置&#xf…

每天读本书-《如何度过每天的24小时》

全景式书籍探索框架 1. “这本书是关于什么的&#xff1f;”——核心定位 一句话核心思想&#xff1a;这本书的核心并非教你如何高效地工作&#xff0c;而是倡导你将工作之外的“自由时间”视为一个“内在的另一天”&#xff0c;并投入智力与热情去经营它&#xff0c;从而获得精…

前端开发 React 状态优化

为了更深入地理解 React 状态管理的性能问题及其解决方案&#xff0c;本文将详细分析 React Context 和 State 的性能问题&#xff0c;配以示例代码说明优化策略。之后&#xff0c;讨论 Redux 作为不可变库的性能问题&#xff0c;并引出 Immer 作为优化解决方案。1. React Stat…