c语言指针详解_C语言之精华——指针详解(下)

2023-10-30

点击上方“C语言中文社区”,选择“设为星标★

技术干货第一时间送达!

4c287bfe2c03697118e045c08217370f.png

https://www.cnblogs.com/lulipro/p/7460206.html

C语言之精华——指针详解(上)

目录

  • 数组和指针
  • 函数和指针
  • const 和 指针
  • 深拷贝和浅拷贝
  • 附加知识

数组和指针

1、数组名作为右值的时候,就是第一个元素的地址。

int main(void){
    int arr[3] = {1,2,3};

    int*p_first = arr;
    printf("%d\n",*p_first);  //1
    return 0;
}

2、指向数组元素的指针 支持 递增 递减 运算。(实质上所有指针都支持递增递减 运算 ,但只有在数组中使用才是有意义的)

int main(void){
    int arr[3] = {1,2,3};

    int*p = arr;
    for(;p!=arr+3;p++){
        printf("%d\n",*p);
    }
    return 0;
}

3、p= p+1 意思是,让 p 指向原来指向的内存块的下一个相邻的相同类型的内存块。同一个数组中,元素的指针之间可以做减法运算,此时,指针之差等于下标之差。

4、p[n] == *(p+n)

p[n][m] == *( *(p+n)+ m )

5、当对数组名使用sizeof时,返回的是整个数组占用的内存字节数。当把数组名赋值给一个指针后,再对指针使用sizeof运算符,返回的是指针的大小。

这就是为什么我么将一个数组传递给一个函数时,需要另外用一个参数传递数组元素个数的原因了。

int main(void){
    int arr[3] = {1,2,3};

    int*p = arr;
    printf("sizeof(arr)=%d\n",sizeof(arr));  //sizeof(arr)=12
    printf("sizeof(p)=%d\n",sizeof(p));   //sizeof(p)=4

    return 0;
}

函数和指针

「函数的参数和指针」

C 语言中,实参传递给形参,是按值传递的,也就是说,函数中的形参是实参的拷贝份,形参和实参只是在值上面一样,而不是同一个内存数据对象。这就意味着:这种数据传递是单向的,即从调用者传递给被调函数,而被调函数无法修改传递的参数达到回传的效果。

void change(int a){
    a++;      //在函数中改变的只是这个函数的局部变量a,而随着函数执行结束,a被销毁。age还是原来的age,纹丝不动。
}
int main(void){
    int age = 19;
    change(age);
    printf("age = %d\n",age);   // age = 19
    return 0;
}

有时候我们可以使用函数的返回值来回传数据,在简单的情况下是可以的,但是如果返回值有其它用途(例如返回函数的执行状态量),或者要回传的数据不止一个,返回值就解决不了了。

传递变量的指针可以轻松解决上述问题。

void change(int* pa){
    (*pa)++;   //因为传递的是age的地址,因此pa指向内存数据age。当在函数中对指针pa解地址时,
               //会直接去内存中找到age这个数据,然后把它增1。
}
int main(void){
    int age = 19;
    change(&age);
    printf("age = %d\n",age);   // age = 20
    return 0;
}

再来一个老生常谈的,用函数交换 2 个变量的值的例子:

#include
void swap_bad(int a,int b);
void swap_ok(int*pa,int*pb);

int main(){
    int a = 5;
    int b = 3;
    swap_bad(a,b);       //Can`t swap;
    swap_ok(&a,&b);      //OK
    return 0;
}

//错误的写法
void swap_bad(int a,int b){
    int t;
    t=a;
    a=b;
    b=t;
}

//正确的写法:通过指针
void swap_ok(int*pa,int*pb){
    int t;
    t=*pa;
    *pa=*pb;
    *pb=t;
}
f69338585870c71b1131a7571f840254.png
ca3b3982a73c64d1ccc6cb93e2299dbd.png

有的时候,我们通过指针传递数据给函数不是为了在函数中改变他指向的对象,相反,我们防止这个目标数据被改变。传递指针只是为了避免拷贝大型数据。考虑一个结构体类型 Student。我们通过 show 函数输出 Student 变量的数据。

typedef struct
{
    char name[31];
    int age;
    float score;
}Student;


//打印Student变量信息
void show(const Student * ps){
    printf("name:%s , age:%d , score:%.2f\n",ps->name,ps->age,ps->score);
}

我们只是在 show 函数中取读 Student 变量的信息,而不会去修改它,为了防止意外修改,我们使用了常量指针去约束。另外我们为什么要使用指针而不是直接传递 Student 变量呢?从定义的结构看出,Student 变量的大小至少是 39 个字节,那么通过函数直接传递变量,实参赋值数据给形参需要拷贝至少 39 个字节的数据,极不高效。而传递变量的指针却快很多,因为在同一个平台下,无论什么类型的指针大小都是固定的:X86 指针 4 字节,X64 指针 8 字节,远远比一个 Student 结构体变量小。

「函数的指针」

每一个函数本身也是一种程序数据,一个函数包含了多条执行语句,它被编译后,实质上是多条机器指令的合集。在程序载入到内存后,函数的机器指令存放在一个特定的逻辑区域:代码区。既然是存放在内存中,那么函数也是有自己的指针的。C 语言中,函数名作为右值时,就是这个函数的指针。

void echo(const char *msg)
{
    printf("%s",msg);
}
int main(void)
{
    void(*p)(const char*) = echo;   //函数指针变量指向echo这个函数

    p("Hello ");      //通过函数的指针p调用函数,等价于echo("Hello ")
    echo("World\n");
    return 0;
}

const 和 指针

const 到底修饰谁?谁才是不变的?

下面是我总结的经验,分享一下。如果 const 后面是一个类型,则跳过最近的原子类型,修饰后面的数据。(原子类型是不可再分割的类型,如 int, short , char,以及 typedef 包装后的类型)

如果 const 后面就是一个数据,则直接修饰这个数据。

int main(){
    int a = 1;

    int const *p1 = &a;        //const后面是*p1,实质是数据a,则修饰*p1,通过p1不能修改a的值
    const int*p2 =  &a;        //const后面是int类型,则跳过int ,修饰*p2, 效果同上

    int* const p3 = NULL;      //const后面是数据p3。也就是指针p3本身是const .

    const int* const p4 = &a;  // 通过p4不能改变a 的值,同时p4本身也是 const
    int const* const p5 = &a;  //效果同上

    return 0;

}
typedef int* pint_t;  //将 int* 类型 包装为 pint_t,则pint_t 现在是一个完整的原子类型

int main(){

    int a  = 1;
    const pint_t p1 = &a;  //同样,const跳过类型pint_t,修饰p1,指针p1本身是const
    pint_t const p2 = &a;  //const 直接修饰p,同上

    return 0;

}

深拷贝和浅拷贝

如果 2 个程序单元(例如 2 个函数)是通过拷贝 他们所共享的数据的 指针来工作的,这就是浅拷贝,因为真正要访问的数据并没有被拷贝。如果被访问的数据被拷贝了,在每个单元中都有自己的一份,对目标数据的操作相互 不受影响,则叫做深拷贝。

17bda05e1cadb9446a7a36c8fbab6a39.png

附加知识

指针和引用这个 2 个名词的区别。他们本质上来说是同样的东西。指针常用在 C 语言中,而引用,则用于诸如 Java,C#等 在语言层面封装了对指针的直接操作的编程语言中。引用是编程语言提供给程序员的抽象机制,而指针是操作系统提供给软件开发模型的抽象机制。

大端模式和小端模式

  1. Little-Endian 就是数据的低字节排放在内存的低地址端,高字节排放在内存的高地址端。个人 PC 常用,Intel X86 处理器是小端模式。
  2. B i g-Endian 就是数据的高字节排放在内存的低地址端,低字节排放在内存的高地址端。

采用大端方式 进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。有些机器同时支持大端和小端模式。

假如 short 类型占用 2 个字节,且存储的地址为 0x30。short a = 1;

如下图:

7195f9ed1a0f3a56e82c494c85e68297.png
#include

//测试机器使用的是否为小端模式。是,则返回true,否则返回false
//这个方法判别的依据就是:C语言中一个对象的地址就是这个对象占用的字节中,地址值最小的那个字节的地址。
int isSmallIndain(void){
      unsigned short val = 0x0001;
      unsigned char* p = (unsigned char*)&val;  //C/C++:对于多字节数据,取地址是取的数据对象的第一个字节的地址,也就是数据的低地址

      return (*p == 0x01);
}

int main(void){

    if(isSmallIndain())
    {
        puts("小端");
    } else{
        puts("大端");
    }

    return 0;

}

第二种方法,使用 union 类型

#include


typedef union {
    unsigned short us;
    unsigned char  uc;
}Test_t;

int main(void){
    Test_t val;
    val.us = 0x0001;

    if(val.uc==0x01)
    {
        puts("小端");
    } else{
        puts("大端");
    }

    return 0;

}
#include


//打印出一个unsigned short int 类型的原始字节流
//这个例子中很明显看到,取到a的首地址后,我们循环递增了p,而非递减p,也从来不会看到有从首地址递减输出数据的字节的写法。
//这也就佐证了:在C语言中,对于一个多字节数据,它的地址就是它占用的所有字节中的地址值最小的那个字节的虚拟空间地址
//这也又说明了一个事实:C语言中,一个多字节数据类型的实例,占用的虚拟内存空间是连续的。
int main(void){
    size_t i;
    unsigned short int a = 0xA1FF;
    unsigned char*p = (unsigned char*)&a;

    for( i=0;i<sizeof(a);++i)
    {
        printf("%#x ",*p);   //小端平台输出:0xFF 0xA1
        p++;                 //大端平台输出:0xA1 0xFF

    }
    printf("\n\n");

    return 0;
}
喜欢就点个在看再走吧 2c23fd524abd3aa77df3081622afcb2a.png
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c语言指针详解_C语言之精华——指针详解(下) 的相关文章

  • python 数据分析--数据处理工具Pandas(2)

    数据处理模块 Pandas 4 Pandas处理字符串和日期数据 5 Pandas 数据清洗 5 1 重复观测处理 5 2 缺失值处理 5 2 1 删除法 5 2 2 替换法 5 3 异常值处理 6 获取数据子集 7 透视表 合并与连接 分
  • Transformer中的position encoding(位置编码一)

    本文主要讲解Transformer 中的 position encoding 在当今CV的目标检测最前沿 都离不开position encoding 在DETR VIT MAE框架中应用广泛 下面谈谈我的理解 一般position enco
  • Activity 的启动分析 ( 9.0 )

    Activity 的启动系统已经做了很多的封装 使得我们在开发的时候不用去关注底层的东西 需要一句代码就可以搞定拉起一个Activity Intent intent new Intent this TestActivity class st
  • Excel如何制作动态模糊匹配的下拉菜单?

    之前给大家分享了如何使用函数制作模糊匹配的下拉菜单 但函数那家伙的特点是小巧灵 数据量稍大 效率就比较差了 众所周知 在Excel里 高效率解决复杂问题 还是得靠又傻又愣的VBA 今天就再给大家分享一下 如何使用VBA制作更好用的动态模糊匹
  • python的label显示图片,如何在标签中显示图片和文本(PyQt)

    我需要在标签中显示图片和文本 这是我的代码 import sys from PyQt5 QtCore import from PyQt5 QtGui import from PyQt5 QtWidgets import class MyLa
  • 机械臂编程_建立自己的机械臂-编程

    机械臂编程 现在 手臂已经组装好了 是时候将其提升到一个新的水平 现在是释放野兽并完全控制整个机器人手臂的时候了 在这篇文章的结尾 您应该对如何对该机械臂进行编程以完成您想要的事情有一个想法 要了解我如何到达这里 请访问我以前的文章 该文章
  • Kubernetes 工作负载控制器Deployment和Replicaset

    Deployment 介绍 管理Pod和ReplicaSet 具有上线部署 副本设定 滚动升级 回滚等功能 提供声明式更新 例如只更新一个新的Image 应用场景 网站 API 微服务 Deployment 使用流程 项目生命周期 Depl
  • SpringCache入门

    1 简单介绍 Spring Cache 是 Spring 提供的一整套的缓存解决方案 虽然它本身并没有提供缓存的实现 但是它提供了一整套的接口和代码规范 配置 注解等 这样它就可以整合各种缓存方案了 比如 Redis Ehcache 我们也
  • java 对象获取堆_【性能优化】面试官:Java中的对象和数组都是在堆上分配的吗?...

    写在前面 从开始学习Java的时候 我们就接触了这样一种观点 Java中的对象是在堆上创建的 对象的引用是放在栈里的 那这个观点就真的是正确的吗 如果是正确的 那么 面试官为啥会问 Java中的对象就一定是在堆上分配的吗 这个问题呢 看来
  • 单元测试方法总结

    1 引言 应用系统的实施代码构建完成之后 并不代表项目已经结束 至少还有系统测试 部署以及性能调优等工作需要完成 测试的目的是检验开发结果是否满足规定需求 测试时保证软件质量的重要手段 是软件开发不可缺少的组成部分 虽然测试是一件乏味的工作
  • Mysql查询字符串中某个字符串出现的次数

    目录 1 查单个字符出现的次数 2 查多个字符出现的次数 3 函数讲解 1 查单个字符出现的次数 比如我想查how do you do 字符串当中出现d的次数 第一眼看上去有点懵 首先mysql并没有直接计算出现字符次数的函数 所以才使用了
  • React/React Native之状态机原理

    在讲React React Native状态机原理之前 先让大家看一个春哥用React编写的小案例的网页效果图 当文本框中的内容发生变化时 会将文本框中的内容同步输出 按照我们之前Android和iOS的思维 当文本框中内容发生变化时 它会
  • LeetCode 426. 将二叉搜索树转化为排序的双向链表

    将一个二叉搜索树就地转化为一个已排序的双向循环链表 可以将左右孩子指针作为双向循环链表的前驱和后继指针 为了让您更好地理解问题 以下面的二叉搜索树为例 我们希望将这个二叉搜索树转化为双向循环链表 链表中的每个节点都有一个前驱和后继指针 对于
  • 1. 两数之和

    两数之和 给定一个整数数组 nums 和一个整数目标值 target 请你在该数组中找出 和为目标值 target 的那 两个 整数 并返回它们的数组下标 你可以假设每种输入只会对应一个答案 但是 数组中同一个元素在答案里不能重复出现 你可
  • 白嫖免费版gpt与wetab插件的使用

    目录 网址 如何使用 wetab 介绍wetab 怎么获得这个插件 使用效果 网址 1 wetab网站 chatgpt 免费 不需要账号 不需要翻墙 2 gpt镜像网站 https chatbot theb ai chat 1002 免费
  • [前端css-4] 实现粒子button按钮

    前端css 4 实现粒子button按钮 1 效果展示 效果预览 2 知识点 1 position 相对定位 绝对定位 2 伪类 active hover 3 animation 动画 3 源代码
  • Linux查看文件夹大小的相关命令

    linux的 du 命令 1 简介 du show disk useage 查看磁盘使用情况 2 语法 du abcDhHklmsSx L lt 符号连接 gt X lt 文件 gt block size exclude lt 目录或文件
  • Spring对象初始化赋值的三种方式

    第一种
  • centos7纯净化系统初始化安装配置基本功能

    配置网络信息 首先把你的虚拟机网络配置成net或者桥接模式 根据你的环境而定 通过ip ad 命令查看你使用的网卡名称 比如我的是ens33 查看网卡配置文件信息 vi etc sysconfig network scripts ifcfg

随机推荐

  • Touch 电容式触摸按键 触摸按键PCB设计参考

    电容式触摸按键设计应用参考 电容式触摸按键美观时尚 与传统的机械按键相比 具有寿命长 功耗小 成本低 体积小 持久耐用等优点 只要轻轻触碰就可实现开关控制 量化调节甚至是方向控制 颠覆了传统意义上的机械按键 现在电容式触摸按键已经广泛应用于
  • import nvidia_smi标红解决办法

    pip install nvidia ml py3 i http pypi douban com simple trusted host pypi douban com
  • Leetcode-数据库-175

    第二高的薪水 我第一次尝试 SELECT salary AS SecondHighestSalary FROM Employee ORDER BY salary DESC LIMIT 1 1 根据salary降序 然后取第二个 但是这有一个
  • MAC book上安装source insight

    MAC上安装sourceinsight 1 下载破解的crossover 链接 https pan baidu com s 1i5fqE2p 密码 k65q 2 下载破解的SourceInsight 3 5 http pan baidu c
  • mac homebrew命令报错

    在使用Mac 的brew命令安装git时 出现以下报错 usr local Homebrew Library Homebrew version rb 368 in initialize Version 通过百度搜索 有以下两个解决方法 1
  • gcd函数和lcm函数(c/c++)

    gcd函数和lcm函数 c c gcd函数简介 最大公因数 英语 highest common factor hcf 也称最大公约数 英语 greatest common divisor gcd 是数学词汇 指能够整除多个整数的最大正整数
  • 前端常用代码整理(不断更新中)

    文章目录 一 js 1 随机函数代码 2 倒计时代码 3 精确显示时间代码 4 表单删除常用代码 5 排他思想核心代码 6 轮播图案例核心代码 一 js 1 随机函数代码 function getRandom min max return
  • python基础(上篇)【原创真实个人记录】

    下面是博主在学习python中的一些笔记记录 以这种文章的形式记录我所做的笔记 一方面是方便我日后的回溯学习 另一方面也是想要看看自己一点一滴的对于编程思想上的进步 对于一些编程小白也是非常友好的 也可以随时来进行查找知识点 本代码全在py
  • 为什么总是在电路里摆两个0.1uF和0.01uF的电容?

    一 旁路和去耦 旁路电容 Bypass Capacitor 和去耦电容 Decoupling Capacitor 这两个概念在电路中是常见的 但是真正理解起来并不容易 要理解这两个词汇 还得回到英文语境中去 Bypass在英语中有抄小路的意
  • electron 的c++ addon编译方法

    https electron atom io docs tutorial using native node modules
  • Spring AOP中何时使用JDK动态代理,何时使用CGLIB动态代理?为什么springboot 2.x默认是使用CGLIB动态代理?

    主要取决于 DefaultAopProxyFactory 类的 createAopProxy 方法 Override public AopProxy createAopProxy AdvisedSupport config throws A
  • JWT 实现登录认证 + Token 自动续期方案,这才是正确的使用姿势!

    因公众号更改推送规则 请点 在看 并加 星标 第一时间获取精彩技术分享 点击关注 互联网架构师公众号 领取架构师全套资料 都在这里 0 2T架构师学习资料干货分 上一篇 SpringBoot 还在用 if 校验参数 那你真的太low了 老司
  • IE和火狐在js使用上的注意点

    一 document formName item itemName 问题 问题说明 IE下 可以使用 document formName item itemName 或 document formName elements elementN
  • 2021-09-03

    yyyy jjj 标题 e r t List item hhhh jjjk rr df
  • 【考研】噫嘘唏,考研个人经验汇总

    纯干货 不上升到哲学 提个醒 实际安排可调整 安静 是好环境 我默认你心理素质强大 所以下面不讲心态的事情 目录 选学校 备考 复试 选学校 给多个意向学校的难度做个排列 首先搜索意向专业的全国高校排名 即搜XX专业大学排名 然后在专业排名
  • 指针与指针型的引用

    引用就是某一变量 目标 的一个别名 对引用的操作与对变量直接操作完全一样 引用的声明方法 类型标识符 引用名 目标变量名 例1 int a int ra a 定义引用ra 它是变量a的引用 即别名 例2 引用型的指针 链表创建时 有这样一段
  • 向上管理怎么做

    https zhidao baidu com question 924075435910031019 html 管理是一门人文科学 需要智商和情商双管齐下 同时也要懂得做好平衡两者冠希 对一个好的管理者这两点要求很高 管理一般都是向下驱动进
  • UE4之-落地快捷键

    End键 在Delete旁边 点击键盘上的End键可将一个物体从高处落地
  • win10 + VS2015 + libtorch 环境搭建

    pytorch地址 本地启动 PyTorch 本次实验使用的环境是win10 VS2015 C 版本是C 11 所以最新的libtorch是用不了的 除非VS使用最新版的VS比如VS2019 VS2022 至于为什么我在这篇帖子找到了答案
  • c语言指针详解_C语言之精华——指针详解(下)

    点击上方 C语言中文社区 选择 设为星标 技术干货第一时间送达 https www cnblogs com lulipro p 7460206 html C语言之精华 指针详解 上 目录 数组和指针 函数和指针 const 和 指针 深拷贝