C语言:指针的基础详解

目录

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

 

在函数内部要注意解引用


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计网体系结构

计算机网络的概述 概念 网络&#xff1a;网状类的东西或系统。 计算机网络&#xff1a;是一个将分散的、具有独立性功能的计算机系统&#xff0c;通过通信设备与线路连接起来&#xff0c;由功能完善的软件实现资源共享和信息传递的系统。即计算机网络是互连(通过通信链路互连…

Go语言的100个错误使用场景(40-47)|字符串函数方法

前言 大家好&#xff0c;这里是白泽。 《Go语言的100个错误以及如何避免》 是最近朋友推荐我阅读的书籍&#xff0c;我初步浏览之后&#xff0c;大为惊喜。就像这书中第一章的标题说到的&#xff1a;“Go: Simple to learn but hard to master”&#xff0c;整本书通过分析100…

ClickHouse--03--数据类型

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 数据类型1. Int2.FloattoFloat32(...) 用来将字符串转换成 Float32 类型的函数toFloat64(...) 用来将字符串转换成 Float64 类型的函数 3.DecimaltoDecimal32(value…

微服务中台架构的设计与实现

本文将探讨微服务中台架构的设计与实现&#xff0c;介绍如何通过微服务的方式进行系统拆分和组合&#xff0c;构建灵活、可扩展且易于维护的中台架构&#xff0c;以加速企业的数字化转型和提升竞争力。 ## 1. 引言 随着企业规模的不断扩大和业务的日益复杂化&#xff0c;传统…

RabbitMQ配置消息转换器

1. 默认转换器 Test public void testSendMap() throws InterruptedException {// 准备消息Map<String, Object> msg new HashMap<>();msg.put("name", "harry");msg.put("age", 21);// 发送消息rabbitTemplate.convertAndSend(&q…

问题:规范化过程主要为克服数据库逻辑结构中的插入异常、删除异常以及(??)的缺陷. #职场发展#职场发展#知识分享

问题&#xff1a;规范化过程主要为克服数据库逻辑结构中的插入异常、删除异常以及(??)的缺陷. 参考答案如图所示

JVM内存模型深度剖析与优化

JDK体系结构 Java语言的跨平台特性 JVM整体结构及内存模型 补充一个问题&#xff1a; 在minor gc过程中对象挪动后&#xff0c;引用如何修改&#xff1f; 对象在堆内部挪动的过程其实是复制&#xff0c;原有区域对象还在&#xff0c;一般不直接清理&#xff0c;JVM内部清理过程…

中国电子学会2023年12月份青少年软件编程Scratch图形化等级考试试卷四级真题(含答案)

2023-12 Scratch四级真题 分数&#xff1a;100 题数&#xff1a;24 分数&#xff1a;60min 一、单选题(共10题&#xff0c;共30分) 1.运行下列程序&#xff0c;输入“abcdef”&#xff0c;程序结束后&#xff0c;变量“字符串”是&#xff1f;&#xff08;B&#xff09;(3…

Vue 全组件 局部组件

一、组件定义和使用 1、全局组件 定义 <template> <div> <h1>This is a global component</h1> </div> </template> <script lang"ts"> </script> <style></style> 导入 全局组件在main.ts&#xff…

前端可能需要的一些安装

Node.js Node.js 官网 Node.js 中文网 Node.js is an open-source, cross-platform JavaScript runtime environment. Node.js是一个开源、跨平台的JavaScript运行时环境。Recommended for most users 推荐大多数用户使用哔哩哔哩安装视频 安装 node.js 的时候&#xff0c;会…

Write operation failed: computed value is readonly问题解决

源代码&#xff1a; // 封装倒计时逻辑函数 import { computed, ref } from vue import dayjs from dayjs export const useCountDown () > {// 1.响应式数据const time ref(0)// 格式化时间const formatTime computed(()>dayjs.unix(time.value).format(mm分ss秒))/…

Python·turtle库编程之:怎么画一个五角星?

文章目录 前言源码附&#xff1a; 前言 大家好&#xff0c;我是BoBo仔&#xff0c;这节课我要带来一期turtle库的使用教程——画五角星。话不多说&#xff0c;我们直接上代码。 源码 import turtle as t t.pencolor(yellow) t.fillcolor("yellow") t.penup() t.go…

算法刷题:和为s的两个数

和为s的两个数 .题目链接题目详情算法原理我的答案 . 题目链接 和为s的两个数 题目详情 算法原理 这里我们是利用单调性来使用双指针的对撞指针来解决问题 因为数组给的是有序递增的,因此我们设置两个指针left和right来解决问题,当nums[left]与nums[right]相加会有三种情况:…

电脑监控屏幕软件有哪些(监控电脑屏幕的软件)

随着信息技术的迅猛发展&#xff0c;电脑屏幕监控软件已成为企业、家庭以及教育机构保护数据安全、提升工作效率以及进行行为分析的重要工具。本文将详细介绍几款主流的电脑屏幕监控软件&#xff0c;包括它们的功能、特点以及适用场景&#xff0c;帮助读者更好地了解并选择合适…

【AIGC】Stable Diffusion之模型微调工具

推荐一款好用的模型微调工具&#xff0c;cybertron furnace 是一个lora训练整合包&#xff0c;提供训练 lora 模型的工具集或环境。集成环境包括必要的依赖项和配置文件、预训练脚本&#xff0c;支持人物、二次元、画风、自定义lora的训练&#xff0c;以简化用户训练 lora 模型…

React - 分页插件默认是英文怎么办

英文组件的通用解决方案 这里以分页插件为例&#xff1a; 大家可以看到&#xff0c;最后的这个页面跳转提示文字为Go to&#xff0c;不是中文&#xff0c;而官网里面的案例则是&#xff1a; 解决方案&#xff1a; import { ConfigProvider } from antd; import zhCN from an…

基于JAVA,SpringBoot和Vue二手房屋销售系统设计

摘要&#xff1a; 本研究旨在设计并实现一个基于JAVA, SpringBoot和Vue技术的二手房屋销售系统。该系统采用当前流行的前后端分离架构&#xff0c;后端使用SpringBoot框架快速搭建RESTful API&#xff0c;提供稳定且高效的服务端应用&#xff1b;前端则通过Vue.js框架构建动态…

幻兽帕鲁在腾讯云服务器中怎么修改配置?游戏难度、经验倍率等等

幻兽帕鲁的游戏配置文件应该是PalWorldSettings 找到这个文件&#xff0c;就可以修改里面的参数。 如果你是用腾讯云一键部署的幻兽帕鲁&#xff0c;则可以到轻量应用服务器管理界面&#xff0c;找到“应用管理”&#xff0c;里面有个可视化修改游戏参数的面板设置&#xff0…

计网物理层

通信基础 基本概念 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的传输媒体。 其主要任务是确定与传输媒体接口有关的一些特性&#xff0c;即定义标准。 机械特性&#xff1a;定义物理连接的特性&#xff0c;规定物理连接时所采用的…

UI文件原理

使用UI文件创建界面很轻松很便捷&#xff0c;他的原理就是每次我们保存UI文件的时候&#xff0c;QtCreator就自动帮我们将UI文件翻译成C的图形界面创建代码。可以通过以下步骤查看代码 到工程编译目录&#xff0c;一般就是工程同级目录下会生成另一个编译目录&#xff0c;会找到…