C语言:指针的基础详解

news2024/11/28 16:00:41

目录

1. 内存

2. 取地址&

3. 指针变量

4. 解引用

4.1 *解引用

4.2 []解引用

4.3 ->解引用

5. 指针变量的大小

5.1 结论

6. 指针运算

7. void* 指针

8. const修饰指针

8.1 const修饰变量

8.2 const修饰指针变量

8.3 结论

9. 野指针

9.1 为什么会出现野指针?

9.1.1 指针未初始化

9.1.2 指针越界访问

9.1.3 指针指向的空间释放

9.2 如何规避野指针

10. assert断言

11. 指针的作用


1. 内存

我们都知道,手机,电脑里都是有内存的,电脑CPU在处理数据时,需要的数据从内存中读取,处理后再放回内存中

每个内存单元为1个字节

具体的换算单位如下:

1byte = 8bit
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB

 CPU为了更好的处理数据,找到这个数据,就需要有地址,有了地址才能找到所需要的数据

每个字节都有属于自己的名字(地址),有了这个名字才能更好的找到它

那我们如何获得地址呢?

2. 取地址&

&符号当然可以取地址了

int a = 10;
printf("%p\n", &a);
//%p是打印地址的

 我们知道 int 有4个字节,这4个字节都有地址

如上图中,如果a是占这4个字节

那么打印出来的地址将会是较小的那个地址0x006FFD70

我们只要得到了较小的那个地址,剩下的三个地址就知道了

3. 指针变量

我们获得了地址之后,我们怎么存储起来方便我们后续使用呢?

所以就有了指针变量起作用的时候了

#include <stdio.h>

int main()
{
	int a = 10;
	int* p = &a;
	return 0;
}

在int的后面加上了个 * 号,它就是指针类型了,而这个p就是指针变量,且是整型的指针变量

我们看待这个指针的时候要把 int 和 * 看成一对,int* 就和上面的 int类似,都是个类型,它两组成了一个整型指针类型,这个p就是个名字,和 a 是一样的

然后我们就可以把整型a的地址放进这个指针变量里了

4. 解引用

我们现在有了地址,当然可以到地址里面去使用地址内部的东西了

具体使用方法需要解引用

当我们解引用后,就可以拿到内部的东西了

4.1 *解引用

#include <stdio.h>

int main()
{
	int a = 10;
	int* p = &a;
	printf("%d\n", *p);
	return 0;
}
输出:10

 对我们刚刚创建的指针变量名字前面加上个*即可解引用

4.2 []解引用

#include <stdio.h>

int main()
{
	int a = 10;
	int* p = &a;
	printf("%d\n", p[0]);
	return 0;
}
输出:10

是不是很像数组?其实指针变量也可以算是一个数组,这与我们直接创建一个数组是一样的

#include <stdio.h>

int main()
{
	int p[] = {1,2,3,4,5};
	printf("%d\n", p[0]);
	return 0;
}

数组名本质就是地址,地址加上解引用符号才能获得值,大同小异

4.3 ->解引用

可以把它叫做箭头解引用,要用到它得学到结构体指针才会用到它

#include <stdio.h>

struct s
{
	int n;
};

int main()
{
	struct s s1 = { 10 };
	struct s* s2 = &s1;
	printf("%d\n", s2->n);
	return 0;
}
输出:10

 把结构体指针解引用的符号就是使用箭头->

5. 指针变量的大小

指针变量也是有大小的,并且这个和运行环境有着部分关系

#include <stdio.h>

int main()
{
	printf("%zd\n", sizeof(char*));
	printf("%zd\n", sizeof(short*));
	printf("%zd\n", sizeof(int*));
	printf("%zd\n", sizeof(double*));
	return 0;
}

 

在x86的环境下结果是4,当我们改成x64后

结果变成了8

直接说结论

5.1 结论

1.指针变量的大小和类型无关,只要是指针类型变量,在相同的平台下,大小都是相同的

2.32位平台下地址是32个bit位,指针大小是4个字节

3.64位平台下地址是64个bit位,指针大小是8个字节

6. 指针运算

指针也是可以进行加减运算的,但是不能进行乘除运算,因为乘除运算没有意义

如果是int类型的指针,那么它每+1就跳过4个字节,char类型指针+1就跳过一个字节,由指针类型决定

#include <stdio.h>

int main()
{
	int a = 10;
	char b = 'a';
	printf("&a   = %p\n", &a);
	printf("&a+1 = %p\n", &a+1);
	printf("&b   = %p\n", &b);
	printf("&b+1 = %p\n", &b + 1);
	return 0;
}

 

7. void* 指针

void*指针是一种特殊的指针,并且它不能进行指针运算和解引用运算,但是它可以接受任意类型的地址

例如:

#include <stdio.h>

int main()
{
	int a = 10;
	int* pa = &a;
	char* pc = &a;
	return 0;
}

这样想在char* 里存一个整型地址会报警告 

 

但是我们可以使用void*放

#include <stdio.h>

int main()
{
	int a = 10;
	int* pa = &a;
	void* pc = &a;
	return 0;
}

8. const修饰指针

我们知道const修饰的变量是不可改变的,但是如果硬要改还是能改的

8.1 const修饰变量

#include <stdio.h>

int main()
{
	const int a = 10;
	a = 5;
	return 0;
}

 

但是如果我们使用指针变量来改变呢?

#include <stdio.h>

int main()
{
	const int a = 10;
	int* p = &a;
	*p = 5;
	printf("%d\n", a);
	return 0;
}

我们会发现它还是改变了

这是因为我们const的不够彻底,我们应该要让p就算拿到了a的地址也不能改变

8.2 const修饰指针变量

const的放置位置是有几种情况的

const int* p = &a;
int const * p = &a;
int* const p = &a;

 第一行和第二行的const都放在了*p的前面,所以限制的是*p,作用是*p不能改变

#include <stdio.h>

int main()
{
	int a = 10;
	const int* p = &a;
	//int const * p = &a;
	*p = 5;//不可改变
	printf("%d", *p);
	return 0;
}

 

第三行const仅仅只放在p的前面,那么它只能限制p,p的地址就不能改变

#include <stdio.h>

int main()
{
	int a = 10;
	int b = 9;
	int* const p = &a;
	//int const * p = &a;
	p = &b; //不可改变
	printf("%d", *p);
	return 0;
}

 

8.3 结论

主要是看const的位置在哪里,如果const是在*p的前面,限制的是*p,*p不可改变,如果是在*的后面p的前面,限制的是p,那么p不可改变

9. 野指针

概念:指针指向一片未知的区域

9.1 为什么会出现野指针?

9.1.1 指针未初始化

#include <stdio.h>

int main()
{
	int* p;
	*p = 20;
	return 0;
}

 *p并不知道指向了哪里,默认为随机值,会随机在一片地址把值改成20

9.1.2 指针越界访问

#include <stdio.h>

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* p = a; //等同于int* p = &a[0];
	for (int i = 0; i < 6; i++)
	{
		*(p++) = i;
	}
	return 0;
}

p一直加到了超越数组的位置,那么就是一片未知的区域,所以就变成了野指针 

9.1.3 指针指向的空间释放

#include <stdio.h>

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

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

这样*p虽然能获得值并且打印出来,函数每次调用都是建立栈空间,但是使用过后,函数建立的栈帧会消失, 那么地址就不存在了,p原本还指向这个函数,但函数消失后就变成了未知的区域,所以p就变成了野指针

9.2 如何规避野指针

1. 注意指针的初始化

2.小心指针越界访问

3.指针不使用时及时置空NULL

4.避免返回局部变量,如上述9.1.3

10. assert断言

#include <assert.h>
assert(p != NULL);

 assert函数的作用是如果括号内为真,这个函数不会有任何反应,但如果为假,那么会报错,并且告诉你在第几行代码出错

assert()的使用对程序员是非常友好的,其一就是能精确报错,其二就是如果已经确定程序没有任何问题,不需要再做任何断言,我们是可以一键注释的

#define NDEBUG
#include <assert.h>

我们只需要在头文件前包括上这个宏定义即可         

11. 指针的作用

在我们使用一个函数的时候如果不使用指针变量的话,只能传值,不能改变本身

例如:

#include <stdio.h>

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

int main()
{
	int x = 5;
	int y = 6;
	Swap1(x, y);
	printf("%d %d\n", x, y);
	return 0;
}

 

这样是无法使两个值改变的,因为我们这里传的x和y只是它的值,并不是x和y的本身,所以交换a和b是不起交换x和y的作用的

但如果我们使用指针传它的名字(地址)过去,就可以找到x和y本身,然后将它们交换了

#include <stdio.h>

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


int main()
{
	int x = 5;
	int y = 6;
	Swap(&x, &y);
	printf("%d %d\n", x, y);
	return 0;
}

 

在函数内部要注意解引用


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

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

相关文章

【AIGC】Stable Diffusion的模型微调

为什么要做模型微调 模型微调可以在现有模型的基础上&#xff0c;让AI懂得如何更精确生成/生成特定的风格、概念、角色、姿势、对象。Stable Diffusion 模型的微调方法通常依赖于您要微调的具体任务和数据。 下面是一个通用的微调过程的概述&#xff1a; 准备数据集&#xf…

optuna,一个好用的Python机器学习自动化超参数优化库

🏷️个人主页:鼠鼠我捏,要死了捏的主页 🏷️付费专栏:Python专栏 🏷️个人学习笔记,若有缺误,欢迎评论区指正 前言 超参数优化是机器学习中的重要问题,它涉及在训练模型时选择最优的超参数组合,以提高模型的性能和泛化能力。Optuna是一个用于自动化超参数优化的…

数据密集型应用系统设计

数据密集型应用系统设计 原文完整版PDF&#xff1a;https://pan.quark.cn/s/d5a34151fee9 这本书的作者是少有的从工业界干到学术界的牛人&#xff0c;知识面广得惊人&#xff0c;也善于举一反三&#xff0c;知识之间互相关联&#xff0c;比如有个地方把读路径比作programming …

springboot192中国陕西民俗网

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的中国陕西民俗网 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取…

2.15日总结

第一题&#xff1a;最小生成树 #include<bits/stdc.h> using namespace std; int n,m; //输入n个节点以及m条边 struct lu//结构体 {int start;//连接到第一个节点int end1;//第二个节点long long l;//输入图之间的距离 }a[2000005]; int f[100005]; long long sum;//最小…

【硬核】javascript轻松实现自动化批量取消某音用户关注功能

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…

JDBC教程+数据库连接池

JDBC 1.JDBC概述 ​ JDBC&#xff0c;全称Java数据库连接&#xff08;Java DataBase Connectivity&#xff09;&#xff0c;它是使用Java语言操作关系型数据库的一套API。 ​ JDBC本质是官方&#xff08;原SUN公司&#xff0c;现ORACLE&#xff09;定义的一套操作所有关系型数…

阿里云幻兽帕鲁服务器中据点帕鲁数量上限是修改哪个参数?

在阿里云的计算巢管理中&#xff0c;找到你的这台部署幻兽帕鲁的服务器实例&#xff0c;选择右上角的“修改游戏配置” 然后选择“基地内工作帕鲁的最大数量”改成20 不过也有同学说更改上面的数字&#xff0c;根本不起作用。 参考资料&#xff1a;大多数人现在都知道&#xf…

【C语言】socketpair 的系统调用

一、 Linux 内核 4.19socketpair 的系统调用 SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol,int __user *, usockvec) {return __sys_socketpair(family, type, protocol, usockvec); } 这段代码定义了一个名为 socketpair 的系统调用。系统调用是操作…

『运维备忘录』之 Zip 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

BDD - Python Behave 用户自定义配置文件

BDD - Python Behave 用户自定义配置文件 引言默认 behave.ini 配置文件自定义配置文件json 格式的配置文件ini 格式的配置文件 实例应用项目结构代码BDD/Features/user_data.feature 文件BDD/steps/user_data_steps.py 文件BDD/environment.py 文件默认配置文件 behave.ini自定…

京东护网面试题汇总

1 、JNI 函数在 java 中函数名为 com.didi.security.main,C 中的函数名是什么样的&#xff1f; com_didi_security_mian java.com.didi.security.main 2 、Frida 和 Xposed 框架&#xff1f; 3 、SSRF 利用方式&#xff1f; 4 、宏病毒&#xff1f; 5 、APP 加壳&a…

吐血整理!操作系统【处理机调度】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;OS从基础到进阶 1 基本概念1.1 总览1.2 什么是调度1.2.1 调度1.2.2 处理机调度 1.3 调度的三个层次1.3.1 高级调度1.3.2 中级调度&#xff08;内存调度&#xff09;1.3.3 低级调度&#xf…

如何给最小化安装的CentOS主机装个远程桌面?

正文共&#xff1a;888 字 18 图&#xff0c;预估阅读时间&#xff1a;1 分钟 前面我们领微软云Azure的免费主机时&#xff08;白嫖党618福利&#xff01;来Azure领200美刀&#xff01;外加云主机免费用一年&#xff01;&#xff09;&#xff0c;发现“有资格免费试用服务”的主…

无人机地面站技术,无人机地面站理论基础详解

地面站作为整个无人机系统的作战指挥中心&#xff0c;其控制内容包括:飞行器的飞行过程&#xff0c;飞行航迹&#xff0c; 有效载荷的任务功能&#xff0c;通讯链路的正常工作&#xff0c;以及 飞行器的发射和回收。 无人机地面站总述 地面站作为整个无人机系统的作战指挥中心…

Cannot resolve symbol ‘@+id/modifyAvatar‘

问题 Cannot resolve symbol id/modifyAvatar详细问题 笔者进行Android开发&#xff0c;创建组件id&#xff0c;报红。 鼠标放置报红处&#xff0c;提示 Cannot resolve symbol id/modifyAvatar解决方案 顶部菜单栏 → \rightarrow →Build → \rightarrow →Rebuild proje…

每日一题 力扣107 二叉树的层序遍历Ⅱ

107. 二叉树的层序遍历 II 题目描述&#xff1a; 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09; 示例 1&#xff1a; 输入&#xff1a;root [3,9,20…

《UE5_C++多人TPS完整教程》学习笔记6 ——《P7 在线会话控制(Online Sessions)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P7 在线会话控制&#xff08;Online Sessions&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xff08;也是译者&…

【Java多线程案例】定时器

1. 定时器简介 定时器&#xff1a;想必大家一定对定时器这个概念不陌生&#xff01;因为它经常出现在我们的日常生活和编程学习中&#xff0c;定时器就好比是一个"闹钟"&#xff0c;会在指定时间处理某件事&#xff08;例如响铃&#xff09;&#xff0c;而在编程世界…

Obsidian Publish的开源替代品Perlite

前几天就有网友跟我说&#xff0c;freenom 的免费域名不可用了&#xff0c;10 号的时候老苏进后台看了一下&#xff0c;还有一半的域名显示为 ACTIVE&#xff0c;似乎是以 2024年6月 为限。但到 11 号&#xff0c;老苏发现博客 (https://laosu.cf) 已经访问不了了&#xff0c;这…