深度解析Linux-C——函数和内存管理

news2024/11/28 0:51:44

目录

函数指针:

 指针函数:

参数为指针的函数: 

参数为数组的函数:

C语言内存管理

 stdlib.h头文件常用函数介绍

1、局部变量

 2、全局变量

3、 堆空间变量

4、静态变量

5、常量


函数指针:

指向函数的指针,称为函数指针

void   f (int); // 函数 f 的类型是: void (int)
void (*p)(int); // 指针 p 专门用于指向类型为 void (int) 的函数

p = &f; // p 指向 f(取址符&可以省略)
p =  f; // p 指向 f

// 以下三个式子是等价的:
  f (666); // 直接调用函数 f
(*p)(666); // 通过索引指针 p 的目标,间接调用函数 f
 p (666); // 函数指针在索引其目标时,星号可以省略

 指针函数:

指针函数就是返回指针值的函数,本质是一个函数。

//一般定义方法有以下三种格式
int *fun(int x,int y);
int * fun(int x,int y);
int* fun(int x,int y);

一般在申请堆空间的时候函数需要返回一个指针,例如

int *get_memory(int size)
{
    int *p = malloc(sizeof(int) * size);
    printf("分配的堆空间地址 %p\n", p);

    return p;
}

参数为指针的函数: 

函数有形参和实参之分,如何通过形参改变实参呢?

函数的传递分为两种,一种是值传递,一种是地址传递

参数的值传递,形参与实参相互独立互不影响。

参数的地址传递,形参可以通过地址修改实参的数据。(通过函数的地址传递的解引用才能修改实参的数据)

有以下三种情况

#include <stdio.h>

void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
    printf("swap中a=%d, b=%d\n", a, b);
}

int main()
{
    int a = 100, b = 200;
    swap(a, b);
    printf("main函数中a=%d, b=%d\n", a, b);
    return 0;
}

这是一个简单的交换函数,输出如图

 可以看到在main函数中,函数的值实际上是没有交换的,这就是函数的值传递

第二种情况

#include <stdio.h>

void swap_addr(int *a, int *b)
{
    printf("swap函数中交换之前 a=%p,b=%p\n", a, b);
    printf("swap函数中交换之前 *a=%d,*b=%d\n\n", *a, *b);
    int *tmp = a;   // 只是交换了,两个指针所指向的地址而已,并不是交换地址上的内容(数据)!
    a = b;
    b = tmp;
    printf("swap函数中交换之后 *a=%d,*b=%d\n", *a, *b);
    printf("swap函数中交换之后 a=%p,b=%p\n\n", a, b);
}

int main()
{
    int a = 100, b = 200;
    printf("main函数中交换之前 &a=%p,&b=%p\n", &a, &b);
    printf("main函数中交换之前 a=%d, b=%d\n\n", a, b);
    swap_addr(&a, &b);
    printf("main函数中交换之后 &a=%p,&b=%p\n", &a, &b);
    printf("main函数中交换之后 a=%d, b=%d\n", a, b);
    return 0;
}

输出如图

这种情况虽然是函数的地址传递,但是main函数中的值仍然没有变化,这就是因为我们只对地址进行了交换,没有对地址中的值解引用交换

所以第三种情况时对swap_addr()函数进行修改

void swap_addr_new(int *a, int *b)
{
    int tmp = *a; 
    *a = *b;
    *b = tmp;
}

可以看到,main函数中的值被成功修改

参数为数组的函数:

在函数形参为数组时,编译器为了节省内存空间会自动将数组转换为指向数组首地址的指针

如:

int arry_sum(int arry[5]) 等价于 int arry_sum_x(int *arry)

正常由于arry是含有五个元素的int型数组,所以sizeof(arry)等于20,但由于函数中arry[5]是一个指针,所以在函数中sizeof(arry)等于8

二维数组同理

int two_arry_sum(int arr[3][3]) 等价于 int two_arry_sum_x(int (*arr)[3]) 等价于 int two_arry_sum_x(int (*)[3])

C语言内存管理

可以参考这篇文章,写的非常详细,这里只做总结和补充 

                                              C语言 内存管理 - 知乎 (zhihu.com)

 stdlib.h头文件常用函数介绍
 #include <stdlib.h>
    ---------分配----------

       void *malloc(size_t size); //分配大小为size 的堆空间 

       void *calloc(size_t nmemb, size_t size); //分配大写为 size * nmemb 的堆空间 

    ---------修改----------  

       void *realloc(void *ptr, size_t size); //修改ptr的堆空间大小为 size
 
       void *reallocarray(void *ptr, size_t nmemb, size_t size);//修改ptr的堆空间大小为 size * nmemb

    ---------释放----------  
    
       void free(void *ptr); //释放ptr 所指向的堆空间 
1、局部变量
局部变量:自动分配空间,自动释放空间 
 
存储区:栈空间  

定义:在函数内部的 花括号定义 { }

作用域:只在当前 花括号{ } 内有效 

生命周期: 只在当前 花括号{ } 内有效 
 2、全局变量
全局变量: 在整个程序中,都一直存在 

存储区:数据段  

定义:在所有的函数外面定义  
 
作用域:在整个程序中都有效  

生命周期:在整个程序中都有效 
#include <stdio.h>

int a = 10, b = 20;

void swap_all()
{
    printf("swap函数中全局变量地址 &a=%p,&b=%p\n", &a, &b);
    printf("swap函数中全局变量交换前 a=%d,b=%d\n", a, b);
    int tmp = a;
    a = b;
    b = tmp;
    printf("swap函数中全局变量交换后 a=%d,b=%d\n\n", a, b);
}

int main()
{
    swap_all(&a, &b);
    printf("main函数中全局变量 a=%d, b=%d\n", a, b);
    return 0;
}

  输出结果

但是如果在main函数中重新定义 int a,b;则main函数会输出其中的局部变量,因为局部变量在本函数中优先级大于全局变量。

3、 堆空间变量
堆空间变量: 由用户自己去管理空间   (最灵活的内存空间)

存储区:堆空间 

定义:调用 malloc,calloc 进行分配  

作用域:在空间未被释放之前都有效  
 
生命周期:利用 malloc,calloc 分配, 利用 free 释放

int *Heap()
{
    // int value = 100; //会释放,不能返回
    int *value = malloc(sizeof(int));
    // 8个局部变量的指针空间    4个堆空间变量   = 12 个空间
    *value = 100; // 往堆空间存储数据
    printf("value=%p,*value=%d\n", value, *value);
    return value; // 返回堆空间,没问题  , 指针会释放!  堆不会
    // return &value;
}

 //在堆内存中申请一个空间,依次存入元素

#include <stdio.h>
#include <stdlib.h>

void *size1(int size)
{
    int *p = (int *)malloc(sizeof(int) * size);
     return p;
}

int main()
{
    int size=5;
    int *q = size1(size);
    for(int i = 0; i<5; i++)
    {   
        q[i] = i;
        printf("%d\n", q[i]);
    }

}

 内存释放和内存泄露

int *Heap_tmp()
{
    int *tmp = malloc(sizeof(int));
    *tmp = 100;
    printf("tmp=%p,*tmp=%d\n", tmp, *tmp);
    // 内存泄漏!!堆空间的地址已经找不到了!
    // 注意:如果在一个函数中使用了堆空间, 记得释放 或者 把空间地址返回出去,否则会出现内存泄漏
}

需要添加free()函数 

4、静态变量
静态变量:在整个程序中,都一直存在
 
存储区:数据段    

定义:利用 static 关键字定义  

作用域:在整个程序中都有效 
   
生命周期:在整个程序中都有效   

静态局部变量

1.静态局部变量,只能被初始化1次 ,存储在数据段,不会释放。

#include <stdio.h>

void increment() {
    static int count = 0;  // 静态局部变量
    count++;
    printf("Count: %d\n", count);
}

int main() {
    increment();  // 第一次调用,count 初始化为 0,然后加 1,输出 1
    increment();  // 第二次调用,count 不再初始化,直接加 1,输出 2
    increment();  // 第三次调用,count 继续加 1,输出 3
    return 0;
}

2.静态全局变量,只能在当前文件使用,无法跨文件使用,防止全局变量名冲突。

5、常量
常量:在整个程序中,都一直存在
   
存储区:数据段  

定义:利用 const 关键字定义 
 
作用域:在整个程序中都有效 
 
生命周期:在整个程序中都有效  

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

针对datax-web 中Swagger UI接口未授权访问

application.yml 添加以下配置 实现访问doc.html 以及/v2/api-docs 接口时需要进行简单的校验 swagger:basic:enable: trueusername: adminpassword: 12345 配置重启后再进行相关访问则需要输入用户名和密码

Radon(拉当) 变换:超详细讲解(附MATLAB,Python 代码)

Radon 变换 Radon 变换是数学上用于函数或图像的一种积分变换&#xff0c;广泛应用于图像处理领域&#xff0c;尤其是在计算机断层成像 (CT) 中。本文档将详细介绍 Radon 变换的数学含义及其在图像处理中的应用。 数学定义 Radon 变换的数学定义是将二维函数 f ( x , y ) f…

Spark实时(二):StructuredStreaming编程模型

文章目录 StructuredStreaming编程模型 一、基础语义 二、事件时间和延迟数据 三、​​​​​​​容错语义 StructuredStreaming编程模型 一、基础语义 Structured Streaming处理实时数据思想是将实时数据看成一张没有边界的表,数据源源不断的追加到这张表中,这可以让我…

零基础STM32单片机编程入门(二十二) ESP8266 WIFI模块实战含源码

文章目录 一.概要二.ESP8266 WIFI模块主要性能参数三.ESP8266 WIFI模块芯片内部框图四.ESP8266 WIFI模块原理图五.ESP8266 WIFI模块与单片机通讯方法1.硬件连接2.ESP8266模块AT指令介绍 六.STM32单片机与ESP8266WIFI模块通讯实验1.硬件准备2.软件工程3.软件主要代码4.实验效果 …

Super 4PCS配准算法

Nicolas Mellado&#xff0c;CNRS&#xff08;Centre national de la recherche scientifique&#xff0c;法国国家科学研究中心&#xff09;的研究员&#xff0c;在IRIT&#xff08;Institut de Recherche en Informatique de Toulouse&#xff0c;图卢兹计算机科学研究所&…

LeetCode Hot100 搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。…

自动化测试 pytest 中 scope 限制 fixture使用范围!

导读 fixture 是 pytest 中一个非常重要的模块&#xff0c;可以让代码更加简洁。 fixture 的 autouse 为 True 可以自动化加载 fixture。 如果不想每条用例执行前都运行初始化方法(可能多个fixture)怎么办&#xff1f;可不可以只运行一次初始化方法&#xff1f; 答&#xf…

学习笔记:《用Python进行数据分析》之通用函数

通用函数&#xff08;即ufunc&#xff09;是一种对ndarray中的数据执行元素级运算的函数。你可以将其看做简单函数&#xff08;接受一个或多个标量值&#xff0c;并产生一个或多个标量值&#xff09;的矢量化包装器。 许多ufunc都是简单的元素级变体&#xff0c;如sqrt和exp&a…

HTML前端面试题之<iframe>标签

面试题&#xff1a;iframe 标签的作用是什么?有哪些优缺点 ? 讲真&#xff0c;刷这道面试题之前我根本没有接触过iframe&#xff0c;网课没讲过&#xff0c;项目实战没用过&#xff0c;但却在面试题里出现了&#xff01;好吧&#xff0c;我只能说&#xff1a;前端路漫漫&…

通过 C# 写入数据到Excel表格

Excel 是一款广泛应用于数据处理、分析和报告制作的电子表格软件。在商业、学术和日常生活中&#xff0c;Excel 的使用极为普遍。本文将详细介绍如何使用免费.NET库将数据写入到 Excel 中&#xff0c;包括文本、数值、数组、和DataTable数据的输入。 文章目录 C# 在Excel单元格…

uniapp map组件自定义markers标记点

需求是根据后端返回数据在地图上显示标记点&#xff0c;并且根据数据状态控制标记点颜色&#xff0c;标记点背景通过两张图片实现控制 <mapstyle"width: 100vw; height: 100vh;":markers"markers":longitude"locaInfo.longitude":latitude&…

Python 实现股票指标计算——DMA

DMA (Deviation Moving Average) - 平均差 1 公式 DMA 收盘价N1日简单平均 - 收盘价N2日简单平均 AMA M日DMA简单平均 2 数据准备 我们以科创50指数 000688 为例&#xff0c;指数开始日期为2019-12-31&#xff0c;数据格式如下&#xff1a; 3 计算过程 def calculate_d…

构造+有序集合,CF 1023D - Array Restoration

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1023D - Array Restoration 二、解题报告 1、思路分析 先考虑合法性检查&#xff1a; 对于数字x&#xff0c;其最左位置和最右位置 之间如果存在数字比x小&#xff0c;则非法 由于q次操作&#xff0c;第q…

Dav_笔记11:SQL Tuning Overview-sql调优 之 5

构建SQL测试用例 对于许多与SQL相关的问题&#xff0c;获得可重现的测试用例可以更轻松地解决问题。从11g第2版&#xff08;11.2&#xff09;开始&#xff0c;Oracle数据库包含SQL测试用例构建器&#xff0c;它可以自动完成收集和复制尽可能多的有关问题及其发生环境的信息的难…

Asp .Net Core 系列:详解授权以及实现角色、策略、自定义三种授权和自定义响应

什么是授权&#xff08;Authorization&#xff09;&#xff1f; 在 ASP.NET Core 中&#xff0c;授权&#xff08;Authorization&#xff09;是控制对应用资源的访问的过程。它决定了哪些用户或用户组可以访问特定的资源或执行特定的操作。授权通常与身份验证&#xff08;Auth…

vue3 vxe-table 点击行,不显示选中状态,加上设置isCurrent: true就可以设置选中行的状态。

1、上个图&#xff0c;要实现这样的&#xff1a; Vxe Table v4.6 官方文档 2、使用 row-config.isCurrent 显示高亮行&#xff0c;当前行是唯一的&#xff1b;用户操作点击选项时会触发事件 current-change <template><div><p><vxe-button click"sel…

大模型算法面试题(十二)

本系列收纳各种大模型面试题及答案。 1、领域模型Continue PreTrain数据如何选取 在领域模型的Continue PreTrain&#xff08;持续预训练&#xff09;过程中&#xff0c;数据选取是一个至关重要的步骤&#xff0c;它直接影响模型在特定领域上的性能和泛化能力。以下是一些关于…

【资料分享】2024钉钉杯大数据挑战赛A题思路解析+代码演示

2024第三届钉钉杯大学生大数据挑战赛今天已经开赛&#xff0c;【A题】思路解析代码&#xff0c;资料预览&#xff1a;

京东发行稳定币的背后

加密市场很热&#xff0c;京东也要来分一杯羹&#xff1f; 7月24日&#xff0c;据财联社报道&#xff0c;京东科技旗下的京东币链科技 ( 香港 ) 将在香港发行与港元 1:1锚定的加密货币稳定币&#xff0c;在市场上掀起广泛热议。 由于众所周知的监管原因&#xff0c;国内大厂在早…

Window下CLion实现本机通过socket通信-C++

1.引言-什么是socket socket即套接字&#xff0c;用于描述地址和端口&#xff0c;是一个通信链的句柄。应用程序通过socket向网络发出请求或者回应。 sockets(套接字)编程有三种&#xff0c;流式套接字&#xff08;SOCK_STREAM&#xff09;&#xff0c;数据报套接字&#xff…