C语言:指针(1)

一. 内存和地址

比如,我们的内存就相当⼀栋宿舍楼,楼里有很多的房间,每个房间都有一个房间号,每个房间里都住着8个人。这时如果你的朋友想要来找你,我们只需要把房间号告诉他就能快速的找到我们。

然而,,在计算机中每个房间就相当一个内存单元,每个内存单元都有一个编号,每个编号对应着一个房间号,房间里的8个人对应着8个比特位。

  1. 1Byte = 8bit
  2. 1KB = 1024Byte
  3. 1MB = 1024KB
  4. 1GB = 1024MB
  5. 1TB = 1024GB
  6. 1PB = 1024TB 

然而,在生活中我们把房间号叫地址,在计算机中我们把内存单元的编号也称为地址。在C语言中我们给地址起了新的名字叫:指针

所以我们可以理解为: 内存单元的编号 == 地址 == 指针

二. 指针变量和地址

1.取地址操作符(&)

在C语言中,如果我们想要的到地址就需要用到取地址操作符(&)

#include<stdio.h>
int main()
{int a = 10;printf("%p\n", &a);return 0;
}

在x86环境下 

2. 指针变量和解引用操作符(*) 

这时,我们已经将a的地址取出来了,我们肯定要将地址存储起来,方便以后的使用,那么我们该如何存储地址呢?这就需要用到指针变量了。

1.指针变量

#include<stdio.h>
int main()
{int a = 10;int* pa = &a;return 0;
}

2.指针类型

  • int* pa = &a;
  • *是指pa为指针变量
  • int是指pa指向的对象为int类型

3.解引用操作符 (*)

既然,我们已经将地址存储起来了,那么我们该如何使用他呢?这时就要用到解引用操作符(*)了。

#include<stdio.h>
int main()
{int a = 10;int* pa = &a;printf("%d\n", *pa);return 0;
}

还是,这段代码如果我们想用指针变量打印a的值我们就可以用*pa来解决。 

#include<stdio.h>
int main()
{int a = 10;int* pa = &a;*pa = 20;printf("%d\n", a);return 0;
}

同时,如果我们想要改变a的值,我们只需要给*p重新赋值即可。

3.指针变量的大小

 我们可以通过代码来了解一下指针变量的大小。

#include<stdio.h>
int main()
{printf("%zd\n", sizeof(int*));printf("%zd\n", sizeof(char*));printf("%zd\n", sizeof(float*));printf("%zd\n", sizeof(double*));printf("%zd\n", sizeof(long*));printf("%zd\n", sizeof(short*));printf("%zd\n", sizeof(long long*));return 0;
}

在x86环境下

在x64环境下

我们可以看到在x86环境下所有类型指针变量的大小都是4个字节,x64下为8个字节。所以我们可以得出结论。

  • 32位平台下地址是32个bit位,指针变量大小是4个字节(x86)
  • 64位平台下地址是64个bit位,指针变量大小是8个字节   (x64)
  • 注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的

那么在相同平台下,大小都相同,那指针变量类型有什么意义呢?

 三.指针变量类型的意义

1.指针的解引用

#include<stdio.h>
int main()
{int a = 0x11223344;int* pa = &a;*pa = 0;return 0;
}

在int*类型下,将*pa赋值为0,会将四个值都赋为0

 

在char*类型下,将*pc赋值为0,只会将第一个赋值为0

结论:指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。

 下面这些时不同的类型,在不同环境下的字节大小

2.指针+-整数

#include <stdio.h>
int main()
{int n = 10;int* pi = &n;char* pc =&n;printf("&n   =%p\n", &n);printf("*pi  =%p\n", pi);printf("*pi+1=%p\n", pi + 1);printf("*pc  =%p\n", pc);printf("*pc+1=%p\n", pc + 1);return 0;
}

 我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节

指针+1,其实跳过1个指针指向的元素。 

3.void* 指针

在指针类型中有⼀种特殊的类型是 void * 类型的,可以理解为无具体类型的指针或者叫泛型指 针),这种类型的指针可以用来接受任意类型地址。但是也有局限性,void* 类型的指针不能直接进行指针的+-整数和解引用的运算。

 例如,如果我们用void* pa存放a的地址,我们发现如果我们要改变a的值,是无法改变的,因为我们并不知道要存放几个字节,同时我们也无法进行指针的运算,因为我们也不知道要跳过几个字节。

四.const 

1.const修饰变量

const的作用其实是使变量不能被修改。

我们可以看下面这段代码。

我们可以发现当我们想改变a的值时,我们发现a不许需被修改了,这就是const的作用。

const既然能修饰变量,那么是不是也能修饰指针变量呢,答案是可以的。

2.const修饰指针变量

一般来讲const修饰指针变量,有两种形式,可以放在*的左边,也可以放在*的右边,但两者之间的意义是不⼀样的。

1.const 放在*的左边做修饰

形式:

const int * pa

或者

int const * pa

如果我们想要改变*pa 和pa的值。 

我们可以看到当我们想要改变* pa的值时,我们是不被允许的,但是改变pa的值是被允许的。

2.const 放在*的右边做修饰

形式

int * const pa 

 如果我们想要改变*pa 和pa的值。 

我们会发现这时*pa被允许,pa不被允许了。

其实const还有第三种修饰方式,放在左右两边。

3.const 放在左右两边做修饰

形式:

const int * const pa

这时,如果我们想要改变*pa 和pa的值。

我们发现*pa和pa都不被允许改变了。

4.结论

  • 结论:const修饰指针变量的时候
  • const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本身的内容可变。
  • const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指 向的内容,可以通过指针改变。
  • const如果放在左右两边,修饰的是指针指向的内容和指针变量本身,保证了指针指向的内容和指针变量的内容不能被修改。

五.指针运算

指针的基本运算有三种:

  1. 指针+- 整数
  2. 指针-指针
  3. 指针的关系运算

1.指针+- 整数

我们可以举个例子

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++){printf("%d ", *(p + i));//p+i 这⾥就是指针+整数}return 0;
}

2. 指针-指针

我们将首地址传给指针变量p,如果指针解引用的值不等于/0,就让p++,直到找到\0的地址,指针就会停在\0的地址处,让p-s(起始地址)就是元素的个数

//指针-指针
#include <stdio.h>
int my_strlen(char* s)
{char* p = s;while (*p != '\0')p++;return p - s;
}
int main()
{printf("%d\n", my_strlen("abc"));return 0;
}

3.指针的关系运算

将数组的首地址赋给p,让p<arr+sz,每循环一次p++,让p往下走,打印数组里的值。

//指针的关系运算
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];int sz = sizeof(arr) / sizeof(arr[0]);while (p < arr + sz) //指针的⼤⼩⽐较{printf("%d ", *p);p++;}return 0;
}

六.野指针

野指针的形成

1.指针未初始化

#include <stdio.h>
int main()
{int* p;//局部变量指针未初始化,默认为随机值*p = 20;return 0;
}

2.指针越界访问

#include <stdio.h>
int main()
{int arr[10] = { 0 };int* p = &arr[0];int i = 0;for (i = 0; i <= 11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}

3.指针指向的空间释放

在这段代码中,test()函数中,将&n返回后,虽然我们将地址成功返回,但是再返回后n所申请的内存空间会被销毁,空间的到释放后,我们虽然能根据地址找到但是没有访问权限。

#include <stdio.h>
int* test()
{int n = 100;return &n;
}
int main()
{int* p = test();printf("%d\n", *p);return 0;
}

七.传值调用和传址调用

1.传值调用

这段代码就是经典的传值调用

#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int a, b;scanf("%d %d", &a, &b);int r = Add(a, b);printf("%d\n", r);
}

 

然而我们看看下一段代码

#include <stdio.h>
void Swap1(int x, int y)
{int tmp = x;x = y;y = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap1(a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}

 我们发现a和b的值并没有发生交换。这时我们可以试试另外一种方法。

2.传址调用

#include <stdio.h>
void Swap2(int* px, int* py)
{int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}

 

我们发现这时我们成功交换了a和b的值。那为什么第一种方法不行呢?

这是因为实参传递给形参的时候,形参会单独创建一份临时空间来接收实参,对形参的修改不影响实参

 第二种方法,我们是直接将地址传过去,让他们直接调用地址,通过解引用来改变值,从而达到交换的目的。

好了今天的分享就到这里吧,我们下一篇见。

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

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

相关文章

Stable Diffusion 提示词攻略

一、提示词作用 提示词所做的工作是缩小模型出图的解空间&#xff0c;即缩小生成内容时在模型数据里的检索范围&#xff0c;而非直接指 定作画结果。 提示词的效果也受模型的影响&#xff0c;有些模型对自然语言做特化训练&#xff0c;有些模型对单词标签对做特化训练&#xf…

负债了,打死也别干的六件事!逾期了,六句谎言千万别信!

负债这事儿&#xff0c;真是一言难尽&#xff0c;稍不留神&#xff0c;就可能让情况雪上加霜。今儿咱们聊聊&#xff0c;负债后那几件打死也别干的几件事&#xff0c;尤其是针对还没有逾期的朋友们&#xff0c;免得后悔莫及。 首先&#xff0c;千万别动歪脑筋&#xff0c;拿公款…

【Golang 面试 - 基础题】每日 5 题(十)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

YOLOV8-人员越界识别

原文:YOLOV8-人员越界识别 - 知乎 (zhihu.com) 一、人员越界识别背景描述 实际生活中某些场景下需要配合摄像头自动识别危险区域,并在发现有人员闯入危险区域(禁止进入区域)时进行报警。翻越围墙监测预警系统对监控区域内的护栏、围墙设定警戒围墙区域,一旦有可疑人员靠…

springboot电子产品销售系统-计算机毕业设计源码80294

摘 要 电子商务行业在全球范围内迅速发展&#xff0c;随之而来的是电子产品销售市场的快速增长和消费者对在线购物体验的需求提升&#xff0c;因此&#xff0c;电子产品销售系统应运而生。该系统旨在满足电子产品市场的需求&#xff0c;提供全面的购物功能和高效的管理操作。 …

高级及架构师高频面试题-基础型

1、设计模式有哪些原则&#xff08;待解释的更直白&#xff09; 单一职责原则&#xff1a;一个类或方法应只负责一项职责&#xff0c;避免一个类因为多个变化原因而改变。开闭原则&#xff1a;软件实体应对扩展开放&#xff0c;对修改封闭。比如要增加用户类别的时候可以新增一…

校车购票小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;我的乘车信息管理&#xff0c;车辆信息管理&#xff0c;座位管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;车辆信息&#xff0c;我的 开发系统…

【项目管理】高手项目经理都在用的6个SOP

SOP&#xff08;Standard Operating Procedure&#xff09;流程是一种标准化的操作指南&#xff0c;旨在确保组织或团队在各种情况下都能高效、一致地完成任务。SOP流程通常包括详细的步骤、关键控制点和责任分配&#xff0c;以确保质量和安全。SOP流程涉及从日常运营到危机管理…

28.x86游戏实战-初探XXX发包

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

GPT-4o mini小型模型具备卓越的文本智能和多模态推理能力

GPT-4o mini 是首个应用OpenAI 指令层次结构方法的模型&#xff0c;这有助于增强模型抵抗越狱、提示注入和系统提示提取的能力。这使得模型的响应更加可靠&#xff0c;并有助于在大规模应用中更安全地使用。 GPT-4o mini 在学术基准测试中&#xff0c;无论是在文本智能还是多模…

微信小游戏之三消(二)主要游戏控制方法

设计一个 game class。负责了游戏的核心控制逻辑&#xff0c;包括游戏状态管理、方块和道具的生成与效果处理&#xff0c;以及游戏的重新开始和复活流程。通过这些方法&#xff0c;脚本实现了游戏的基本玩法和用户交互。 主要游戏控制方法 gameStart()&#xff1a;开始游戏&am…

Java学习Day16:基础篇6

1.静态和非静态 2.调用静态和非静态的过程 注&#xff1a;在Java中&#xff0c;同类中&#xff0c;确实可以使用类的对象来调用静态方法&#xff0c;尽管这不是推荐的做法。静态方法属于类本身&#xff0c;而不是类的任何特定实例。因此&#xff0c;理论上讲&#xff0c;你应该…

【iOS】—— KVO与KVC

KVO与KVC 1. KVOKVO底层实现分析如何验证上面的说法&#xff1a;NSKVONotifyin_Person内部结构didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法 回答问题&#xff1a; 2. KVC简介&#xff1a;key和keyPath的区别key&#xff1a…

探索 Electron:如何进行网址收藏并无缝收录网页图片内容?

Electron是一个开源的桌面应用程序开发框架&#xff0c;它允许开发者使用Web技术&#xff08;如 HTML、CSS 和 JavaScript&#xff09;构建跨平台的桌面应用程序&#xff0c;它的出现极大地简化了桌面应用程序的开发流程&#xff0c;让更多的开发者能够利用已有的 Web 开发技能…

EtherNet/IP转CAN协议转化网关(功能与配置)

怎么样把EtherNet/IP和CAN两个协议连接起来?有很多朋友想要了解这个问题&#xff0c;那么作者在这里统一说明一下。其实有一个不错的设备产品可以很轻易地解决这个问题&#xff0c;名为JM-EIP-ECAT网关。接下来作者就从该设备的功能及配置详细说明一下。 一&#xff0c;设备主…

聊聊基于Alink库的主成分分析(PCA)

概述 主成分分析&#xff08;Principal Component Analysis&#xff0c;PCA&#xff09;是一种常用的数据降维和特征提取技术&#xff0c;用于将高维数据转换为低维的特征空间。其目标是通过线性变换将原始特征转化为一组新的互相无关的变量&#xff0c;这些新变量称为主成分&…

TinyMCE一些问题

1.element 在el-dialog中使用tinymce导致富文本弹窗在el-dialog后面的问题 原因是富文本的弹窗层级太低了 在APP.vue中添加样式即可解决 /* 富文本菜单 */ .tox-tinymce-aux {z-index: 9999 !important; }2.element 在el-dialog中点击富文本的功能栏报错 由于 aria-hidden 属…

Midjourney、Sora和硅谷机密-《分析模式》漫谈15

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 “Analysis Patterns”的Preface&#xff08;前言&#xff09;有这么一句&#xff1a; Kent Beck, Ward Cunningham, and Jim Coplein encouraged me to get involved with the commu…

虚假的互联网信息?不妨从IT的角度理解【景观社会】

博主前言&#xff1a;“我思故我在”&#xff0c;笛卡尔的这一哲学命题&#xff0c;大抵上次还比较熟络的时光还是高中亦或复习考研政治的岁月里。这是一个光怪陆离的社会——或者说网络社会&#xff0c;形形色色的消息充斥在脑海之时&#xff0c;你是否还能认识真正的自己&…