指针与函数传递


title: 指针与函数传递
date: 2024-09-14 21:33:51
description: 函数传递多个元素
tags:

  • c language

skaiuijing

理解指针

很多人经常使用指针,看到这个标题可能不屑一顾。但笔者想说,把指针当作理所当然而不去探索它的本质,这是不对的。

先问个问题:指针是地址还是变量?(int *)a +1 增加了多少?

如果说 int a代表一个int大小的a变量,那么 int *a代表什么呢?答案是指向一个int 变量的指针变量a。

既然a有大小,是int,那么int *a有没有大小呢?答案是:肯定有!可能有人会回答一个字的大小,其实多少有点不严谨,指针的大小取决于具体的计算机架构和编译器,例如在常见的MCU-stm32上,一个指针的大小是32位,对应一个字。

所以说,指针的本质也是变量,(int *)a + 1,其实是加了对应一个指针的大小。

为什么指针能够修改内存

笔者定义一个结构体:

#include<stdio.h>struct store{int a;int b;
};int main()
{long long address;struct store *pointself;//此时pointself的值是随机的、无意义的pointself = &address;pointself->a = 10;pointself->b = 5;printf("address:%d,address-a: %d,address-b: %d\n",pointself,&(pointself->a),&(pointself->b));printf("store: %lld, a:%d ,b = %d\n",address,(int)address,(int)(*(((int *)&address) + 1 )));}

程序的运行结果:

address:6422032,address-a: 6422032,address-b: 6422036
store: 21474836490, a:10 ,b = 5

x86-64为大端序,转为二进制就是:10100000000000000000000000000001010

假设架构为x86*64,那么内存布局如下:

变量a (4 bytes)b (4 bytes)
1010101
地址00000000 01100001 11111110 0001000000000000 01100001 11111110 00010100

让我们思考一下,poinself->a = 10时,到底发生了什么?

答案是:机器通过pointself这个指针,找到了这个具体地址对应的空间,然后存放了a的值。

反汇编如下:(程序已经被gcc优化得非常好了,可能不足以解释这个过程,但是可以作为参考)

main:push    rbpmov     rbp, rspsub     rsp, 32mov     QWORD PTR [rbp-8], 0   //把这块区域作为存储address的空间,初始化为0lea     rax, [rbp-8]mov     QWORD PTR [rbp-16], rax //存储address的地址mov     DWORD PTR [rbp-8], 10	//存储amov     DWORD PTR [rbp-4], 5	//存储bmov     rax, QWORD PTR [rbp-16]mov     rsi, raxlea     rdx, [rbp-8]lea     rcx, [rbp-4]mov     edi, OFFSET FLAT:.LC0mov     eax, 0call    printfmov     rax, QWORD PTR [rbp-16]mov     rax, QWORD PTR [rax]mov     esi, eaxmov     rax, QWORD PTR [rbp-16]mov     eax, DWORD PTR [rax+4]mov     edi, OFFSET FLAT:.LC1mov     edx, eaxmov     eax, 0call    printfmov     eax, 0leaveret
  1. 内存分配
    • sub rsp, 32 分配了 32 个字节的栈空间,用于存储 addresspointself
    • mov QWORD PTR [rbp-8], 0 初始化 address 为 0。
    • lea rax, [rbp-8] 计算 address 的地址并存储到寄存器 rax
    • mov QWORD PTR [rbp-16], raxaddress 的地址存储到 pointself
  2. 赋值操作
    • mov DWORD PTR [rbp-8], 10 将值 10 存储到 pointself->a 对应的内存位置。
    • mov DWORD PTR [rbp-4], 5 将值 5 存储到 pointself->b 对应的内存位置。
  3. 打印操作
    • mov rax, QWORD PTR [rbp-16]pointself 的值加载到寄存器 rax
    • mov rsi, raxrax 的值移动到 rsi,作为 printf 的参数。
    • lea rdx, [rbp-8] 计算 pointself->a 的地址并存储到 rdx
    • lea rcx, [rbp-4] 计算 pointself->b 的地址并存储到 rcx
    • mov edi, OFFSET FLAT:.LC0 将格式字符串的地址加载到 edi
    • call printf 调用 printf 函数。
    • mov rax, QWORD PTR [rbp-16]pointself 的值加载到寄存器 rax
    • mov rax, QWORD PTR [rax]address 的值加载到寄存器 rax
    • mov esi, eaxeax 的值移动到 esi,作为 printf 的参数。
    • mov rax, QWORD PTR [rbp-16]pointself 的值加载到寄存器 rax
    • mov eax, DWORD PTR [rax+4]pointself->b 的值加载到寄存器 eax
    • mov edi, OFFSET FLAT:.LC1 将格式字符串的地址加载到 edi
    • mov edx, eaxeax 的值移动到 edx,作为 printf 的参数。
    • call printf 调用 printf 函数。

现在你是否明白为什么我们在使用指针时,常常要malloc?其实就是在给指针变量赋一个有意义的值,不然当我们使用->时,计算机会发现这块空间存储了莫名其妙的东西,或者是这块空间压根不存在。

既然理解了指针,那么该使用它了。

在使用函数时,我们常常使用return传递某些变量,但是,有时候我们希望函数能够传递多个元素,这时有什么好办法呢?

现在,是时候看看指针的伟大之处了。

1.使用指针修改对应地址指向的值

#include <stdio.h>void getTwoValues(int *x, int *y) {*x = 10;*y = 20;
}int main() {int a, b;getTwoValues(&a, &b);printf("Returned values: a = %d, b = %d\n", a, b);return 0;
}

2.返回指向数组的指针

#include <stdio.h>int* getTwoValues() {static int values[2];  // Static array so it persists after the function returnsvalues[0] = 5;values[1] = 15;return values;
}int main() {int *values = getTwoValues();printf("Returned values: values[0] = %d, values[1] = %d\n", values[0], values[1]);return 0;
}

3.返回指向结构体的指针

#include <stdio.h>
#include <stdlib.h>struct TwoValues {int val1;int val2;
};struct TwoValues* getTwoValues() {struct TwoValues *result = (struct TwoValues *)malloc(sizeof(struct TwoValues));result->val1 = 50;result->val2 = 60;return result;
}int main() {struct TwoValues *values = getTwoValues();printf("Returned values: val1 = %d, val2 = %d\n", values->val1, values->val2);free(values); return 0;
}

这里解释一下,定义变量,其实就是开辟一片内存空间。我们一般在函数中定义的变量,都是局部变量,被存储在栈中,而malloc开辟的空间,是在堆中。栈的空间在函数结束后会被回收,而堆不会,必须手动清理,否则它永远存在,并且会造成内存溢出等问题。所以,我们可以使用动态内存分配来存储变量的值。

当然,你要是对地址、空间、内存这些东西感到烦躁,也有别的方法。不一定只能使用指针。

直接使用结构体

#include <stdio.h>struct TwoValues {int val1;int val2;
};struct TwoValues getTwoValues() {struct TwoValues result;result.val1 = 100;result.val2 = 200;return result;
}int main() {struct TwoValues values = getTwoValues();printf("Returned values: val1 = %d, val2 = %d\n", values.val1, values.val2);return 0;
}

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

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

相关文章

Go语言错误处理详解

Go语言以其简洁、高效和并发能力著称。在实际开发中&#xff0c;错误处理是一个不可避免且至关重要的部分。本文将深入探讨Go语言中的错误处理机制&#xff0c;涵盖其原理、使用方法、最佳实践&#xff0c;并提供丰富的代码示例和中文注释。 一、错误处理的基本概念 在Go语言…

7. 探究模型参数与显存的关系以及不同精度造成的影响

这篇文章将探讨两个重点&#xff1a; 模型参数与显存&#xff08;GPU 内存&#xff09;之间的关系不同精度的导入方式&#xff0c;以及它们对显存和性能的影响 理解这些概念会让你在模型的选择上更加游刃有余。 文章目录 模型参数与显存的关系模型参数量与内存占用GPU 显存需求…

JMeter脚本开发

环境部署 Ubuntu系统 切换到root用户 sudo su 安装上传下载的命令 apt install lrzsz 切换文件目录 cd / 创建文件目录 mkdir java 切换到Java文件夹下 cd java 输入rz回车 选择jdk Linux文件上传 解压安装包 tar -zxvf jdktab键 新建数据库 运行sql文件 选择sql文件即…

基于51单片机的电饭锅控制系统proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1CGyg6uPhFI0MeaBWwe_HAg 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectro…

RedisTemplate操作ZSet的API

文章目录 ⛄概述⛄常见命令有⛄RedisTemplate API❄️❄️ 向集合中插入元素&#xff0c;并设置分数❄️❄️向集合中插入多个元素,并设置分数❄️❄️按照排名先后(从小到大)打印指定区间内的元素, -1为打印全部❄️❄️获得指定元素的分数❄️❄️返回集合内的成员个数❄️❄…

前端网络层性能优化

前言 在数字时代&#xff0c;速度已成为互联网体验的关键。用户对网页加载时间的容忍度越来越低&#xff0c;每一毫秒的延迟都可能导致用户的流失。根据谷歌的研究&#xff0c;页面加载时间超过3秒的网站&#xff0c;其跳出率会增加120%。在这个以用户为中心的网络世界里&…

Git换行符自动转换参数core.autocrlf的用法

core.autocrlf 是 Git 中用于控制换行符自动转换的配置选项。它有以下几个可能的值&#xff1a; 1. true 作用&#xff1a;在 checkin 时将 CRLF 转换为 LF&#xff0c;在 checkout 时将 LF 转换为 CRLF。适用场景&#xff1a;适用于 Windows 用户&#xff0c;希望在本地文件…

LineageOS刷机教程

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ LineageOS 是一个基于 Android 开源项目&#xff08;AOSP&#xff09;的开源操作系统&#xff0c;主要由社区开发者维护。它起源于 CyanogenMod 项目&#xff…

10年Python程序员教你多平台采集10万+电商数据【附实例】

10万级电商数据采集需要注意什么&#xff1f; 在进行10万级电商数据采集时&#xff0c;有许多关键因素需要注意&#xff1a; 1. 采集平台覆盖&#xff1a;确保可以覆盖主流的电商平台&#xff0c;如淘宝、天猫、京东、拼多多等。 2. 数据字段覆盖&#xff1a;检查是否可以对平…

go 笔记

数据结构与 方法&#xff08;增删改查&#xff09; 安装goland,注意版本是2024.1.1&#xff0c;不是2024.2.1&#xff0c;软件下载地址也在链接中提供了 ‘go’ 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 在 Windows 搜索栏中输入“环境变量”&#…

架构理论碰撞:对比TOGAF、Zachman、DODAF和FEAF等主流架构框架

信息架构框架对比分析&#xff1a;选择适合企业的最佳方案 在企业数字化转型过程中&#xff0c;信息架构的设计与实施至关重要。成功的信息架构能够有效地支持业务流程优化&#xff0c;提升数据管理效率&#xff0c;推动技术创新。然而&#xff0c;不同的信息架构框架各有其独…

linux gcc 静态库的简单介绍

在 Linux 上&#xff0c;使用 GCC 编译器来创建和调用静态库时&#xff0c;涉及的实现原理和调用机制可以分为以下几个步骤&#xff1a; 1. 静态库的创建 静态库&#xff08;通常以 .a 结尾&#xff09;是由多个目标文件&#xff08;.o 文件&#xff09;打包在一起的归档文件…

判断线是否相交、判断点是否在线上、求线相交交点

先定义个点、线结构 typedef struct tagStruVertex {double x;double y;double distanceTo(const tagStruVertex& point) const{return sqrt((x - point.x) * (x - point.x) (y - point.y) * (y - point.y));}bool equal(const tagStruVertex& point) const{if (poin…

COMTRADE binary数据文件解析

一、COMTRADE 二进制文件的解析需要用到cfg文件中的配置信息&#xff0c;以及dat文件中的数据。 二、cfg文件 1、cfg文件整体配置 2、cfg文件实例 厂站名&#xff0c;记录装置&#xff0c;COMTRADE标准版本年号 SMARTSTATION,IED123,2013 总通道数&#xff0c;模拟通道编号&…

记录word转xml文件踩坑

word文件另存为xml文件后&#xff0c;xml文件乱码 解决方法&#xff1a; 1.用word打开.docx文件 2.另存为xml文件 3.点击工具 -> Web选项 -> 编码&#xff0c;选择UTF-8 4.点击确定 5.使用notpad打开xml文件 6.使用xml tool进行xml格式化即可。

uniapp小程序,使用腾讯地图获取定位

本篇文章分享一下在实际开发小程序时遇到的需要获取用户当前位置的问题&#xff0c;在小程序开发过程中经常使用到获取定位功能。uniapp官方也提供了相应的API供我们使用。 官网地址&#xff1a;uni.getLocation(OBJECT)) 官网获取位置的详细介绍这里就不再讲述了&#xff0c;大…

安宝特方案 | 医疗AR眼镜,重新定义远程会诊体验

【AR眼镜&#xff1a;重新定义远程会诊体验】 在快速发展的医疗领域&#xff0c;安宝特医疗AR眼镜以其尖端技术和创新功能&#xff0c;引领远程会诊的未来&#xff0c;致力于为为医生和患者带来更高效、精准和无缝的医疗体验。 探索安宝特医疗AR眼镜如何在医疗行业中引领新风潮…

视频推拉流/直播点播EasyDSS平台安装失败并报错“install mediaserver error”是什么原因?

TSINGSEE青犀视频推拉流/直播点播EasyDSS平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务&#xff0c;在应用场景中可实现视频直播、点播、转码、管理、录像、检索、时移回看等。此外&#xff0c;平台还支持用户自行上传视频文件&#xff0c;也可…

Gitbook 本地安装教程

Gitbook 本地安装教程 安装 node [nodejs的v10.21.0版本&#xff0c;下载地址&#xff1a;https://nodejs.org/dist/v10.21.0/node-v10.21.0-x64.msi] 其他版本有问题 npmnpm install -g gitbook-cligitbook init [初始化目录结构]gitbook build [编译]gitbook serve [运行] …

MongoDB日志级别

日志 查看当前的日志级别 根据你提供的 MongoDB 命令结果&#xff0c;命令 db.adminCommand({ getParameter: "logComponentVerbosity" }) 返回了 "ok" : 0&#xff0c;这意味着命令执行失败&#xff0c;没有成功获取到日志级别的配置信息。错误信息 &quo…