C语言指针详解(1)(能看懂字就能明白系列)文章超长,慢慢品尝

news2024/10/7 8:19:43

目录

1、内存和地址

2、指针简介

与指针相关的运算符:

取地址操作符(&)

解引用操作符(间接操作符)(*)

​编辑 指针变量的声明

指针变量类型的意义

指针的基本操作

1、指针与整数相加(当然也可以相减):

2、指针与指针相加(或者相减)

3、比较

野指针

1、解引用未初始化的指针

2、指针越界访问

3、指针指向的空间释放

4、如何规避野指针

1、指针初始化

2、小心指针越界


1、内存和地址

什么是内存呢?我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的是数据是在内存中读取的,处理后的数据也会放回内存中,比如电脑上的内存是8GB/16GB/32GB等,那这些内存空间是如何高效的管理的呢?

其实也是把内存划分为一个个的内存单元,每个内存单元的大小取一个字节。

补充:计算机中常见的单位——一个比特位可以存储一个二进制的位1或者0

1byte = 8bit(bit-比特位,byte-字节)
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB

其实,每个内存单元,相当于一个学生宿舍,一个字节空间里面能放8个比特位,就好比一个宿舍里面住了八个人,每个人就是一个比特位。 

每个内存单元也都有一个编号(这个编号就相当于宿舍房间的门牌号),有了这个内存单元的编号,CPU就可以快速找到一个内存空间。生活中我们把门牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语言中给地址起了新的名字:指针。

所以我们可以理解为:

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

2、指针简介

指针?什么是指针?从根本上,指针(pointer)是一个值为内存地址的变量(或者数据对象)。比如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。

与指针相关的运算符:

那我们如何获得地址呢?

取地址操作符(&)

一般注解:后跟一个变量名,&给出该变量的地址

#include<stdio.h>
int main()
{
	int a = 10;
	&a;//这里是取出a的地址的意思
	printf("%p", &a);//p是打印地址的占位符
	return 0;
}

如图所示,0133F70C就是a的地址,(特别注意的是,上述代码在创建变量a的时候,会向内存申请4个字节的空间,用于存放整数10,其中每个字节都有地址,且&a取出的是a所占4个字节中地址较小的字节的地址。)虽然整型变量占4个字节,但我们只要知道了第一个字节地址,就能顺藤摸瓜访问到4个字节的数据。

我们把取到的数据(地址)放到指针变量存储,指针变量也是一种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。

解引用操作符(间接操作符)(*)

一般注解:后跟一个指针名或者地址时,*给出储存在指针指向地址上的值。

示例:

int a=10;

int *pa=&a;//这里pa左边写的是int*,*是在说明pa是指针变量,而前面的int是在说明pa指向的是整型(int)类型的对象。

*pa=20;//*pa等价于a,最终a的值等于20

 指针变量的声明

声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。另外,程序必须知道存储在指定地址上的数据类型。long和float可能占用相同的存储空间,但是它们存储数字却大相径庭。下面是一些示例:

int *pa  //pa是指向int类型变量的指针

char *pb //pb是指向char类型变量的指针

float *pc //pc是指向float类型变量的指针

类型说明符表示了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。

int*pa声明的意思是pa是一个指针,*pa是int类型。

指针变量类型的意义

指针变量的大小和类型无关,只要是指针变量,在同一个平台上,大小都是一样的,为什么还要各种各样的指针类型呢?

其实指针类型是有特殊意义的,例如以下代码:

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

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

 从上面两个代码可以看出,第一个代码会将n的四个字节全部改为0,但是第二个代码只是将n的第一个字节改为0。

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

比如:char*的指针解引用就只能访问一个字节,而int*类型的解引用就能访问四个字节。

指针的基本操作

1、指针与整数相加(当然也可以相减):

可以使用+运算符把指针和整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。

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

上面就是指针与整数相加的结果,另外也可以看出,char*类型的指针变量+1跳过一个字节,int*类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化。

另外再举一个数组的例子:

我们知道数组在内存中都是连续存放的,所以只要我们知道第一个元素的地址,就能顺藤摸瓜找到后面的所以元素。

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

2、指针与指针相加(或者相减)

int my_strlen(char* s)//定义一个求字符串长度的函数
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p-s;//这里就是指针减指针
}



#include<stdio.h>
int main()
{
	printf("%d", my_strlen("abc"));//传参后打印结果
	return 0;
}

3、比较

可以使用关系运算符比较两个指针的值,前提是两个指针都指向相同类型的对象。

#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]);
	while (p < arr + sz) //这里是指针大小的比较
	{
		printf("%d ", *p);
		p++;
	}
	return 0;
}

野指针

成因:

1、解引用未初始化的指针

int* pst; //未初始化的指针

*pst=5; //严重的错误

 为何不行呢,第二行的意思是把5存储在pst指向的位置。但是pst未被初始化未被初始化,其值是一个随机值,所以不知道将5存储在何处,这可能不会出什么错,也可能会擦写数据或代码,或者导致程序崩溃。

切记:创建一个指针时,系统只分配了存储指针本身的内存,并未分配存储数据的内存。因此,在使用指针之前,必须先用已分配的地址初始化它。

#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、指针指向的空间释放

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

4、如何规避野指针

1、指针初始化

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

#include <stdio.h>
int main()
{
int num = 10;
int*p1 = &num;
int*p2 = NULL;
return 0;
}
2、小心指针越界

一个程序向内存申请了哪些空间,,通过指针也就只能访问哪些空间,不能超过访问范围,超过了就是越界访问。

今天的笔记到此结束了

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

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

相关文章

解决 uniapp 开发微信小程序 不能使用本地图片作为背景图 问题

参考博文&#xff1a;uniapp微信小程序无法使用本地静态资源图片(背景图在真机不显示)的解决方法_javascript技巧_脚本之家 问题&#xff1a;uniapp 开发微信小程序&#xff0c;当使用本地图片作为 background-image 时&#xff0c;真机无法显示 解决&#xff1a; 方法一&am…

在线预览excel,luckysheet在vue项目中的使用

一. 需求 需要在内网项目中在线预览excel文档&#xff0c;并可以下载 二.在项目中下载并引入luckysheet 1.打开项目根目录&#xff0c;npm i luckyexcel 安装 npm i luckyexcel2.在项目的index.html文件中引入依赖 外网项目中的引入&#xff08;CDN引入&#xff09;&#…

Cesium:绘制地质剖面

作者:CSDN @ _乐多_ 本文记录了根据地质剖面的三角网的点、索引和颜色数组,绘制地质剖面的框架和部分代码。 效果如下图所示, 文章目录 一、算法逻辑二、代码一、算法逻辑 将三角网的点、索引和颜色数组按规则排列好,输入到第二节的代码中,可以绘制一个面。将这个绘制函…

如何确保消息不会丢失

本篇文章大家还可以通过浏览我的博客阅读。如何确保消息不会丢失 - 胤凯 (oyto.github.io)很多人刚开始接触消息队列的时候&#xff0c;最经常遇到的一个问题就是丢消息了。<!--more-->对于大部分业务来说&#xff0c;丢消息意味着丢数据&#xff0c;是完全无法接受的。 …

骨传导耳机的优缺点是什么?有什么值得入手的骨传导耳机吗?

骨传导耳机的优点还是挺多的&#xff0c;比如说&#xff1a;佩戴舒适、避免听力损伤、使用更安全灯&#xff0c;在详细了解骨传导耳机有什么优点和缺点之前&#xff0c;先来认识一下什么是骨传导耳机。 骨传导耳机是一种通过人体骨骼来传递声音的耳机&#xff0c;与传统的耳机相…

23111710[含文档+PPT+源码等]计算机毕业设计基于SpringBoot的体育馆场地预约赛事管理系统的设计

文章目录 **软件开发环境及开发工具&#xff1a;****功能介绍&#xff1a;****论文截图&#xff1a;****数据库&#xff1a;****实现&#xff1a;****代码片段&#xff1a;** 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 软件开发环境及…

嵌入式酒精壁炉:时尚生活的新宠

在这个注重品质与生活方式的时代&#xff0c;我们对于家居生活的要求早已不仅仅停留在实用性上。越来越多的人希望能够在家中营造一种时尚、温馨的氛围&#xff0c;而酒精壁炉恰好成为了这个潮流生活的代表。 如今&#xff0c;品质生活已经成为时尚的代名词。酒精壁炉以其精湛的…

图像分类系列(二) VGGNet学习详细记录

经典神经网络论文超详细解读&#xff08;二&#xff09;——VGGNet学习笔记&#xff08;翻译&#xff0b;精读&#xff09; 前言 上一篇我们介绍了经典神经网络的开山力作——AlexNet&#xff1a;经典神经网络论文超详细解读&#xff08;一&#xff09;——AlexNet学习笔记&a…

解密.locked1勒索病毒:专家级策略保护您的数据免受勒索攻击

导言&#xff1a; 在当今数字化的世界中&#xff0c;勒索病毒的威胁日益严峻。.locked1 勒索病毒作为其中的一种&#xff0c;采用高级的加密算法对用户文件进行加密&#xff0c;要求支付赎金以获取解密密钥。本文91数据恢复将介绍如何面对.locked1 勒索病毒&#xff0c;有效恢…

Python 3.12 版本有什么变化?

在前不久&#xff0c;python 3.12 正式发布了&#xff0c;那到底更新了哪些内容呢&#xff1f;一起来看看。 # 改善报错信息 来自官方标准库的模块现在可以在报NameError时提示问题原因&#xff0c;比如 >>> sys.version_info Traceback (most recent call last):Fi…

SpringBoot2—基础篇

目录 快速上手SpringBoot • SpringBoot入门程序开发 基于Idea创建SpringBoot工程&#xff08;一&#xff09; 基于官网创建SpringBoot工程&#xff08;二&#xff09; 基于阿里云创建SpringBoot工程&#xff08;三&#xff09; 手工创建Maven工程修改为SpringBoot工程&…

线程状态及线程之间通信

线程状态概述 当线程被创建并启动以后&#xff0c;它既不是一启动就进入了执行状态&#xff0c;也不是一直处于执行状态。在线程的生命周期中&#xff0c; 有几种状态呢&#xff1f;在 java.lang.Thread.State 这个枚举中给出了六种线程状态&#xff1a; 线程状态 导致状态发生…

Shopee活动名称怎么填写好?Shopee活动名称设置注意事项——站斧浏览器

虾皮活动名称的设定不仅是一个技巧性的问题&#xff0c;更是一门艺术。通过合理的活动名称设计&#xff0c;可以吸引更多的消费者参与活动&#xff0c;增加活动的曝光度和影响力。 shopee活动名称怎么填写好 简洁明了&#xff1a;活动名称应该尽量简洁明了&#xff0c;能够一…

北邮22级信通院数电:Verilog-FPGA(10)第十周实验 实现移位寄存器74LS595

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.代码部分 二.管脚分配 三.实现过程讲解及效…

上机练习 8: DataFrame 综合练习

记录一下做的练习题 目录 1)自定义一个 Series 并命名为 s1&#xff0c;自定义索引值&#xff0c;采用随机数作为其中数据尝试使用 s1.sum(计算其中所有数据的和,使用 s.mean(计算其中所有数据的平均值。 2)创建一个形状为4*6的 DataFrame 并命名为 df1,并指定行索引为[“a”…

多媒体领域顶会ACM MM 2023 获奖论文一览

ACM 国际多媒体会议是计算机科学领域中多媒体领域的顶级会议&#xff0c;属于CCF A类。今年的ACM MM 2023 已于2023年10月29日至11月2日在加拿大渥太华举行。 ACM MM会议专注于推动多媒体研究和应用&#xff0c;其研究领域广泛涉及触觉、视频、VR/AR、音频、语音、音乐、传感器…

23届计科,想找Java开发之类,真的是很难吗?

23届计科&#xff0c;想找Java开发之类&#xff0c;真的是很难吗&#xff1f; 你的投递信息(投递多少家&#xff0c;如何跟hr打招呼&#xff0c;已读不回如何应对等)都亮- -下才能知道问题出在 哪。最近很多小伙伴找我&#xff0c;说想要一些Java的资料&#xff0c;然后我根据…

asp.net在线考试系统+sqlserver数据库

asp.net在线考试系统sqlserver数据库主要技术&#xff1a; 基于asp.net架构和sql server数据库 功能模块&#xff1a; 首页 登陆 用户角色 管理员&#xff08;对老师和学生用户的增删改查&#xff09;&#xff0c;老师&#xff08;题库管理 选择题添加 选择题查询 判断题添加…

电商野路子:非标品中转仓项目

相信很多人之前都做过拼多多电商&#xff0c;抖音直播电商&#xff0c;淘宝虚拟电商&#xff0c;也做过淘宝传统电商&#xff0c;在童话看来&#xff0c;这些平台都已严重内卷&#xff0c;已经不再适合普通人进场了。凭你一没经验&#xff0c;二没背景&#xff0c;三没资源&…

成功解决:文档根元素 “mapper“ 必须匹配 DOCTYPE 根 “null“

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 文章目录 前言错误信息解决方法 前言 错误…