深入理解指针 (1)

news2025/7/8 3:51:02

1.内存和地址

1.1内存

1.1.1内存的使用和管理 

(1)内存划分为一个个的内存单元,每个内存单元的大小是1个字节,一个内存单元可以存放8个bit。

(2)每个内存单元有一个编号,内存单元的编号在计算机中也叫地址,C语言中称为指针

内存单元的编号 == 地址 == 指针

1.2编址 

CPU访问内存中的某个字节空间,必须要知道内存单元的位置(给内存单元编号),地址信息被下达给内存,在内存上就可以找到该地址的对应数据,将数据通过数据总线传入CPU内寄存器

地址总线:传递信息,通过该信息定位内存单元

2.指针变量和地址

2.1变量创建的本质

int a = 20;
//变量创建的本质是在内存中申请空间
//向内存申请4个字节的空间,用来存放20这个数值
//这4个字节每个字节都有编号(地址)
//变量的名字是给程序员看的,编译器是通过地址找内存单元

2.2取地址操作符(&)

	int a = 20;
    //&a, &--取地址操作符,拿到变量a的地址
	printf("%p\n", &a);
	int* pa = &a;//pa是一个变量,用来存放地址(指针),pa叫指针变量

 理解int* pa = &a;

pa的类型是int*

pa是指针变量的名字

* 表示pa是指针变量

int表示pa指向的变量a的类型是int

 

2.3解引用操作符(*)

	int a = 20;
	int* pa = &a;
	*pa = 300;//*--解引用操作符(间接访问操作符)
	printf("%d\n", a);

2.4指针变量大小

指针变量用来存放地址

        一个32位机器,有32根地址总线,每根地址总线传递一个信号,32根地址总线产生32bit的地址,需要4字节存放。

        一个64位机器,有64根地址总线,每根地址总线传递一个信号,64根地址总线产生64bit的地址,需要8字节存放。

指针变量的大小只与机器的位长有关,与本身类型无关。

理解:一个char类型的地址是地址,同时一个int类型的地址同样也是地址,char*, int* 取地址时取的都是地址,所以与类型无关(通俗理解:苹果是水果,香蕉也是水果,当不论是拿苹果还是香蕉时都是拿的水果)

3.指针变量类型的意义

3.1指针的解引用

指针的类型决定了对指针解引用权限有多大。

如:char* 解引用能访问1个字节,int* 解引用能访问4个字节

3.2指针+-整数

指针类型决定了指针向前或向后一步走多远(距离)

如:一个char* 跳过1个字节,一个int* 跳过4个字节

理解:int* pa;        pa + 1——>  + 1 * sizeof(int)

                              pa + n——>  + n * sizeof(int) 

	int a = 10;
	int* pa = &a;
	char* pb = &a;

	printf("a = %p\n", &a);
	printf("pa = %p\n", pa);
	printf("pb = %p\n", pb);

	printf("a + 1 = %p\n", &a + 1);
	printf("pa + 1 = %p\n", pa + 1);
	printf("pb + 1 = %p\n", pb + 1);

3.3void* 指针

 无具体类型的指针(泛型指针),可以用来接收任何类型的地址,但是不能直接进行解引用操作和指针的加减整数操作。即:可接收不同类型地址但不能进行指针运算

void* 一般在函数参数的部分使用,用来接收不同数据类型的地址。使得一个函数能够处理多种类型数据。

4.const修饰指针

4.1const修饰变量

const修饰变量的时候叫:常变量。这个被修改的变量本质上还是变量,只是不能被修改

当使用指针变量可以对其const修饰的变量进行修改(门被关起来了,从窗户跳进去)

const int n = 20;

	int* p = &n;
	*p = 200;

4.2const修饰指针变量

4.2.1.const位于*左边

const放在*左边,限制的是指针指向的内容,即,不能通过指针变量来修改它所指向的内容

但是指针变量本身可以被修改

4.2.2.const位于*右边

const放在*右边,限制的是指针变量本身,即,指针不能改变它的指向

但是可以通过指针变量修改它所指向的内容

4.2.3.const位于*左右两侧

此时指针指向的内容和指针变量本身都不能被修改

:关于指针p有3个相关的值

1.p,p里面存放着一个地址

2.*p,p指向的那个对象

3.&p,表示的是p变量的地址

5.指针运算

5.1指针+-整数

1.指针类型决定了指针+1的步长

2.数组在内存中是连续存放

//顺序打印数组	
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(a) / sizeof(a[0]);
	int* p = &a[0];

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *p);
		p++;
        //printf("%d ", *(p + i));
	}
//逆序打印数组
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(a) / sizeof(a[0]);
	int* p = &a[sz - 1];

	for (int i = 0; i < sz; i++)
	{
		//printf("%d ", *p);
		//p--;
		printf("%d ", *(p - i));
	}

5.2指针-指针

指针-指针的绝对值指针之间元素的个数

指针-指针,计算的前提条件两个指针指向的是同一个空间

	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", &a[9] - &a[0]);
	printf("%d\n", &a[0] - &a[9]);

实例:写一个函数,求字符串长度

1.通过strlen函数求字符串长度

#include <string.h>
	
    char ch[] = "abcdef";
	size_t len = strlen(ch);
	printf("%zd\n", len);

2.写一个函数,模拟strlen函数实现求字符串长度

方法1:(指针+-整数操作)

size_t my_strlen(char* p)
{
	int count = 0;
	while (*p != '\0')    //while(*p)
	{
		count++;
		p++;
	}
	return count;
}

int main()
{
	char ch[] = "abcdef";
	size_t len = my_strlen(ch);	//数组名其实是数组首元素的地址 ch==&ch[0]
	printf("%zd\n", len);

	return 0;
}

方法2:(指针-指针操作)

size_t my_strlen(char* p)
{
	char* start = p;
	char* end = p;
	while (*end != '\0')    //while(*end)
	{
		end++;
	}
	return end - start;
}

int main()
{
	char ch[] = "abcdef";
	size_t len = my_strlen(ch);	//数组名其实是数组首元素的地址 ch==&ch[0]
	printf("%zd\n", len);

	return 0;
}

5.3指针的关系运算 

当一个指针指向一个数组时,指针向后移动,指针指向的地址由小变大

	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(a) / sizeof(a[0]);
	int* p = a;

	//while(p < a + sz)
	while (p < &a[sz]) //指针的大小比较
	{
		printf("%d ", *p);
		p++;
	}

6.野指针

指针指向的位置不可知(随机、不正确、无明确限制)

6.1野指针的成因

(1)指针未初始化

一个局部变量的值不初始化,值是随机的

如果将p中存放的值当作地址,解引用操作符会形成非法访问

int* p;    //指针未初始化
*p = 10;

(2)指针越界

注:数组下标是从0开始的,不要越界

(3)将指针指向的空间释放

局部变量进函数创建,出函数销毁,当它出函数时,内存空间归还给操作系统

int* test()
{
	int n = 10;
	return &n;
}

int main()
{
	int* p = test();
	printf("%d\n", *p);

	return 0;
}

6.2如何规避野指针

(1)指针初始化

 如果明确知道指向哪里就直接赋值地址,不知道指向哪里赋值NULL,NULL是C语言中的标识符常量,值为0,0也是地址,这个地址无法使用,读写该地址会报错。

int a = 10;
int* p = &a;    //直接复制地址
int* s = NULL;    //赋值NULL
*s = 100;    //error

(2)指针越界

一个程序申请了多少内存空间,指针也只能访问这些空间,超出范围访问就是指针越界。

(3)指针变量不再使用,及时置NULL,使用前检查有效性

只要指针是NULL就不进行访问,指针在使用之前先进行判空再进行解引用操作。

int a = 20;
int* p = &a;
if(*p != NULL)    //判空
{
    *p = 200;
}

(4)避免返回局部变量的地址 

不要返回局部变量的地址

7.assert断言

assert.h头文件定义了宏assert(),用于在运行确保程序符合指定条件,如果不符合就会报错终止运行,这个宏叫做断言。

assert()接收一个表达式作为参数,如果表达式为真不会产生任何作用,程序继续运行,如果表达式为假assert()会报错,在标准错误流stderr(屏幕上)写入一条错误信息,显示没通过的表达式,以及包含这个表达式的文件名和行号

优点:(1)自动识别文件和出错的行号

           (2)无需更改代码就能开启或关闭assert()机制

           (3)如果不需要使用时,在头文件定义宏NDEBUG

#define NDEBUG
#include <assert.h>

assert(*p != NULL);

缺点:增加了运行时间 

注:在VS的Release版本中会优化掉assert(),即不会起作用

8.指针的调用

8.1传值调用和传址调用

8.1.1传值调用

传值调用就是在函数使用时,把变量本身的值直接传递给函数。实参传递给形参的时候,形参会单独创建一份临时空间来接收实参,此时对形参的修改不会影响对实参的改变

//传值调用(不能实现变量数值的交换)
void swap1(int x, int y)
{
	int z = 0;
	z = x;
	x = y;
	y = z;
}

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;
}

8.1.2传址调用

能建立函数和主调函数之间的联系,可以在函数内部修改主调函数的变量,从而使主调函数中的变量发生改变。

void swap1(int* pa, int* pb)
{
	int z = 0;
	z = *pa;
	*pa = *pb;
	*pb = z;
}

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;
}

注:如果只是需要主调函数的变量值来实现计算,就可以采用传值调用。如果函数内部要修改主调函数中的变量的值,就需要传址调用

未完待续……

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

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

相关文章

Leetcode98、230:二叉搜索树——递归学习

什么是二叉搜索树&#xff1a;右子树节点 > 根节点 > 左子树节点&#xff0c; 二叉搜索树中的搜索&#xff0c;返回给定值val所在的树节点 终止条件为传进来的节点为空、或者节点的值 val值&#xff0c;返回这个节点&#xff1b; 单程递归逻辑&#xff1a;定义一个resu…

15. LangChain多模态应用开发:融合文本、图像与语音

引言&#xff1a;当AI学会"看听说想" 2025年某智慧医院的多模态问诊系统&#xff0c;通过同时分析患者CT影像、语音描述和电子病历&#xff0c;将误诊率降低42%。本文将基于LangChain多模态框架与Deepseek-R1&#xff0c;手把手构建能理解复合信息的智能系统。 一、…

2022李宏毅老师机器学习课程笔记

机器学习笔记目录 1.绪论&#xff08;内容概述&#xff09;2.机器学习和深度学习的基本概念transformer 1.绪论&#xff08;内容概述&#xff09; 机器学习&#xff1a;让机器找一个函数&#xff0c;通过函数输出想要的结果。应用举例&#xff1a;语音识别&#xff0c;图像识别…

笔试强训:Day2

一、字符串中找出连续最长的数字串(双指针) 字符串中找出连续最长的数字串_牛客题霸_牛客网 #include <iostream> #include <string> #include <cctype> using namespace std;int main() {//双指针string str;cin>>str;int nstr.size();int begin-1,l…

linux合并命令(一行执行多个命令)的几种方式总结

背景&#xff1a; 最近安装配置机器&#xff0c;需要手打很多命令。又不能使用docker&#xff0c;所以就使用iTerm2连接多台服务器&#xff0c;然后move session到一个窗口中&#xff0c;shift command i使用XSHELL类似的撰写功能&#xff0c;就可以一次在多台服务器命令窗口…

基于归纳共形预测的大型视觉-语言模型中预测集的**数据驱动校准**

摘要 本研究通过分离共形预测&#xff08;SCP&#xff09;框架&#xff0c;解决了大型视觉语言模型&#xff08;LVLMs&#xff09;在视觉问答&#xff08;VQA&#xff09;任务中幻觉缓解的关键挑战。虽然LVLMs在多模态推理方面表现出色&#xff0c;但它们的输出常常表现出具有…

docker学习笔记5-docker中启动Mysql的最佳实践

一、查找目录文件位置 1、mysql的配置文件路径 /etc/mysql/conf.d 2、mysql的数据目录 /var/lib/mysql 3、环境变量 4、端口 mysql的默认端口3306。 二、启动命令 1、启动命令说明 docker run -d -p 3306:3306 -v /app/myconf:/etc/mysql/conf.d # 挂载配置目录 -v…

从零开始搭建Django博客③--前端界面实现

本文主要在Ubuntu环境上搭建&#xff0c;为便于研究理解&#xff0c;采用SSH连接在虚拟机里的ubuntu-24.04.2-desktop系统搭建&#xff0c;当涉及一些文件操作部分便于通过桌面化进行理解&#xff0c;通过Nginx代理绑定域名&#xff0c;对外发布。 此为从零开始搭建Django博客…

系统与网络安全------弹性交换网络(3)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 STP协议 环路的危害 单点故障 PC之间的互通链路仅仅存在1个 任何一条链路出现问题&#xff0c;PC之间都会无法通信 解决办法 提高网络可靠性 增加冗余/备份链路 增加备份链路后交换网络上产生二层环路 …

Cursor 配置 MCP Tool

文章目录 1、MCP Tool 的集合2、一个 demo :Sequential Thinking2.1、搜索一个 MCP Tool 获取 command 命令2.2、在 Cursor 配置2.3、配置状态检查与修正(解决网络问题)检查解决办法 2.4、使用 1、MCP Tool 的集合 https://smithery.ai/ 2、一个 demo :Sequential Thinking …

【金仓数据库征文】-《深入探索金仓数据库:从基础到实战》

目录 前言 什么是金仓数据库&#xff1f; 金仓数据库的特点 金仓数据库的核心特点 金仓数据库与其他数据库的对比 金仓数据库的安装 常见的语句 总结 前言 为助力开发者、运维人员及技术爱好者快速掌握这一工具&#xff0c;本文将系统性地介绍金仓数据库的核心知识。内…

RocketMQ 主题与队列的协同作用解析(既然队列存储在不同的集群中,那要主题有什么用呢?)---管理命令、配置安装

学习之前呢需要会使用linux的基础命令 一.RocketMQ 主题与队列的协同作用解析 在 RocketMQ 中&#xff0c;‌主题&#xff08;Topic&#xff09;‌与‌队列&#xff08;Queue&#xff09;‌的协同设计实现了消息系统的逻辑抽象与物理存储分离。虽然队列实际存储在不同集群的 B…

从岗位依附到能力生态:AI革命下“什么叫就业”的重构与价值

在人工智能(AI)技术深刻重塑社会生产关系的当下,“就业”这一概念正经历着从“职业绑定”到“能力变现”的范式转移。本文将从传统就业观的解构、AI赋能艺术教育的价值逻辑、以及未来就业形态的进化方向三个维度,探讨技术驱动下就业的本质变革,并揭示AI技术如何通过教育创…

海外版高端Apple科技汽车共享投资理财系统

这一款PHP海外版高端Apple、科技汽车、共享投资理财系统phplaravel框架。

企业为何要禁止“片断引用开源软件代码”?一文看透!

开篇故事&#xff1a;一段“开源代码”引发的百亿级灾难 某电商平台为快速上线新功能&#xff0c;从GitHub复制了一段“高性能加密算法”代码到支付系统中。 半年后&#xff0c;黑客通过该代码中的隐藏后门&#xff0c;盗取百万用户信用卡信息。 事后调查&#xff1a;这段代…

【C++指南】告别C字符串陷阱:如何实现封装string?

&#x1f31f; 各位看官好&#xff0c;我是egoist2023&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f4ac; 注意&#xff1a;本章节只详讲string中常用接口及实现&#xff0c;有其他需求查阅文档介绍。 &#x1f680; 今天通过了…

国内ip地址怎么改?详细教程

在中国&#xff0c;更改IP地址需要遵守规则&#xff0c;并确保所有操作合规。在特定情况下&#xff0c;可能需要修改IP地址以满足不同需求或解决特定问题。以下是一些常见且合法的IP地址变更方法及注意事项&#xff1a; 一、理解IP地址 IP地址是设备在网络中的唯一标识&#x…

模式设计简介

设计模式简介 设计模式是软件开发中经过验证的最佳实践解决方案,它是针对特定问题的通用解决方案,能够帮助开发者提升代码的可维护性、可扩展性和复用性。设计模式并非具体的代码实现,而是一种解决问题的思路和方法论,它源于大量的实践经验总结,旨在解决软件开发过程中反…

众趣科技X世界读书日丨数字孪生技术赋能图书馆空间智慧化运营

4月23日&#xff0c;是第30个“世界读书日”&#xff0c;不仅是庆祝阅读的日子&#xff0c;更是思考知识传播未来的契机。 图书馆作为主要传播图书的场所&#xff0c;在科技的发展中&#xff0c;图书馆正面临前所未有的挑战&#xff0c;联合国数据显示&#xff0c;全球近30%的…

MySQL 事务(详细版)

目录 一、事务简介 1、事务的概念 2、事务执行的案例 3、对于事务的理解 二、事务操作 &#xff08;一&#xff09;未控制事务 &#xff08;二&#xff09;控制事务一 &#xff08;三&#xff09;控制事务二 三、事务四大特性 四、并发事务问题 五、事务隔离…