news 2026/7/18 11:46:54

【C语言】指针(1)——指针简单介绍及指针相关运算

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C语言】指针(1)——指针简单介绍及指针相关运算

目录

1.内存和地址

2.指针变量和地址

2.1取地址操作符(&)

2.2指针变量和解引用操作符

2.2.1指针变量

2.2.2如何理解 int * p (如何拆解指针类型)

2.2.3解引用操作符(*)

2.2.4指针变量的大小

3.指针变量类型的意义

3.1指针+-整数

3.2void*

4.const修饰指针

4.1 const修饰变量

4.2 const修饰指针变量

5.指针运算

5.1指针+-指针

5.2指针-指针

5.3指针的关系运算

6.野指针

6.1野指针成因

1.指针未初始化

2.指针越界访问

3.指针指向的空间释放

6.2如何规避野指针

6.2.1指针初始化

6.2.2小心指针越界

6.2.4避免返回局部变量的地址

7.assert断言

8.传值调用和传址调用


1.内存和地址

计算机中最小的单位是比特位bit,可以用来存储1个二进制位

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

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

计算机中的编址,不是把每个字节的地址记录下来,而是通过硬件设计完成的。

地址总线:32位机器有32根地址总线,可以表示2^32种含义,每种含义表示一个地址。

2.指针变量和地址

2.1取地址操作符(&)

在C语言中创建变量的本质就是向内存申请空间。

%p可以用来打印地址

使用&取出地址(单目操作符)

&a取出的是a所占四个字节中较小的字节的地址

#include<stdio.h> int main() { int a = 0; // 为a申请4个字节的空间 int *p = &a; return 0; }

2.2指针变量和解引用操作符

2.2.1指针变量

使用取地址操作符(&)取出的地址是一个数值,需要存储在指针变量中。

上述代码中,p是一个指针变量。指针变量也是变量,是用来存放地址的,存放在指针变量中的值会被理解为地址。

指针:地址

指针变量:存放地址的变量

口语中的指针一般指指针变量

2.2.2如何理解 int * p (如何拆解指针类型)

*说明p是一个指针变量

int说明p指向的对象是int类型

同样的,若p指向的类型是char类型,定义时要使用char*

2.2.3解引用操作符(*)

解作符(*)又叫间接访问操作符

*p表示通过p存放的地址找到p指向的对象

可以通过*p间接访问p指向的对象(例如可以通过*p=0将p指向的对象的值变为0)

*和&是可以相抵消的(*&a == a)

2.2.4指针变量的大小

32位机器假设有32根地址总线,每根地址总线的电信号转换成数字信号后都表示0或1,如果将32根地址总线产生的二进制序列当做一个地址,那么该地址就需要32个bit位进行存储,即32位机器的地址变量的大大小为4个字节。

同理,64位机器中的地址变量的大小为8个字节

指针变量的大小与其所指向的对象类型无关,只要是指针变量,在相同平台上的大小都是相同的。

如图,在x86(即32位)环境下,各种类型的指针变量大小均为4字节;在x64(即64位)环境下,均为8字节。

3.指针变量类型的意义

既然在同一个平台下各种类型的指针变量的大小是相同的,为什么还要有各种各样的指针类型呢?

因为指针类型决定了对指针进行解引用操作时有多大的权限。

比如:char*指针在解引用时只能访问一个字节,int*指针解引用时可以访问4个字节。

3.1指针+-整数

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

运行结果

可以看出,当int*类型的pa进行+1操作时,地址由006FFC08变为006FFC0C,向后移动了4个字节(sizeof(int));而char*类型的pc+1时,由006FFC08变为006FFC09,向后移动了1个字节(sizeof(char))。这就是不同指针变量类型的差别。

结论:指针类型决定了指针向前或向后一步有多大(字节)。

3.2void*

在指针类型中有一种特殊类型是void*类型的,可以理解为无具体类型的指针(或泛型指针)。

这种指针可以用来接受各种类型的地址,但是也有局限性:void*类型的指针不能直接进行指针+-整数和解引用的运算。

一般情况下,void*类型的指针使用在函数参数的部分,用来接收不同数据类型的地址,可以实现泛型编程的效果,使一个函数可以处理多种类型的数据。

4.const修饰指针

4.1 const修饰变量

const int a = 10; // a具有了常属性(不能被修改)

在c语言中,虽然a不能被修改了,但本质上还是变量(常变量);在c++中,const修饰的变量就是常量

被const修饰的变量可以通过指针修改(破坏规则)

4.2 const修饰指针变量

//代码1 void test1() { int n = 10; int m = 20; int* p = &n; *p = 20;//ok? p = &m; //ok? } void test2() { //代码2 int n = 10; int m = 20; const int* p = &n; *p = 20;//报错 p = &m; //ok? } void test3() { int n = 10; int m = 20; int* const p = &n; *p = 20; //ok? p = &m; //报错 } void test4() { int n = 10; int m = 20; int const* const p = &n; *p = 20; //报错 p = &m; //报错 } int main() { //测试⽆const修饰的情况 test1(); //测试const放在*的左边情况 test2(); //测试const放在*的右边情况 test3(); //测试*的左右两边都有const test4(); return 0; }

一般来说,const可以放在*的左边,也可以放在*的右边

当const放在*左边,修饰的是指针指向的内容,确保指针指向的内容不能通过指针改变,但指针变量内容本身可以改变。

当const放在*右边,修饰的是指针变量本身,保证了指针变量本身的内容不能修改,但是指针指向的内容可以通过指针改变。

也可以同时放在*左边和右边,表示指针指向的内容和指针变量本身均不能修改。

5.指针运算

指针运算有三种,分别是:

(1)指针+-指针

(2)指针-指针

(3)指针的关系运算

5.1指针+-指针

因为数组在内存中是连续存放的,所以只要知道第一个元素的地址,就可以找到后面所有元素。

Type * p;

p+1 --> 跳过 1*sizeof(type)

p+n --> 跳过 n*sizeof(type)

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

以上代码可用于输出数组元素

5.2指针-指针

|指针-指针| = 两指针间的元素个数

前提:两指针指向同一块空间(如指向同一个数组)

不存在指针+指针(无意义)

5.3指针的关系运算

指针的关系运算实际上就是指针和指针比较大小

可以用来判断指针是否指向数组内、

6.野指针

概念:野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)

6.1野指针成因

1.指针未初始化

指针变量未初始化,默认为随机值

2.指针越界访问

eg.当指针指向的范围超出数组范围时就成为野指针

3.指针指向的空间释放

eg.函数结束后指针指向的变量被释放,p成为野指针

6.2如何规避野指针

6.2.1指针初始化

指针初始化有两种情况:

1.明确知道指针应该指向哪里,初始化一个明确的地址

2.暂时不知道指针应该指向哪里,初始化为NULL

NULL是C语言中定义的标识符常量,值为0,0也是地址,这个地址是无法使用的,读写该地址会报错

6.2.2小心指针越界

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

6.2.3指针变量不再使用时及时置为NULL,指针使用前检查有效性。

不再使用某个指针时要将指针置为NULL,使用指针前先判断指针是否为NULL

6.2.4避免返回局部变量的地址

7.assert断言

assert.h头文件定义了宏assert(),用于在运行时确保特定程序符合指定条件,如果不符合,就报错终止运行,这个宏常被称为“断言”。

assert(p) //判断p是否为空指针,等价于assert(p!=NULL)

assert()宏接受一个表达式作为参数(任意表达式)。如果该表达式为真(返回值非零),assert()不会有任何反应,程序继续运行;如果该表达式为假(返回值为零),assert()就会报错,在标准错误流stderr中写入一条错误信息,显示错误的表达式及其所在的文件名及行号。

使用assert()的好处:

1.可以自动标识文件和出问题的行号

2.如果已经确保程序没有问题,不再需要assert()断言,就在#include<assert.h>前定义一个宏NDEBUG。然后重新编译,编译器就会自动禁用文件中的所有assert()语句。

#define NDEBUG #include<assert.h>

assert()的缺点:因为引入了外部的检查,增加了程序的运行时间。

在vs中,Release版本直接将assert()优化掉了,在linux版本中,Release版本仍会起作用。

8.传值调用和传址调用

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

传址调用:把变量的地址传递给函数。可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量。

如果函数中只是需要主调函数中的变量值进行运算,可以采用传值调用;如果函数内部要修改主调函数变量的值,就要采用传址调用。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 11:46:39

如何在生产环境中部署gemma-4-e2b-it-mxfp4:最佳实践

如何在生产环境中部署gemma-4-e2b-it-mxfp4&#xff1a;最佳实践 【免费下载链接】gemma-4-e2b-it-mxfp4 项目地址: https://ai.gitcode.com/hf_mirrors/mlx-community/gemma-4-e2b-it-mxfp4 gemma-4-e2b-it-mxfp4是一款基于Gemma4架构的多模态AI模型&#xff0c;支持文…

作者头像 李华
网站建设 2026/7/18 11:46:37

LOGITacker与Unifying工具集成:终极密钥提取技术指南

LOGITacker与Unifying工具集成&#xff1a;终极密钥提取技术指南 【免费下载链接】LOGITacker Enumerate and test Logitech wireless input devices for vulnerabilities with a nRF52840 radio dongle. 项目地址: https://gitcode.com/gh_mirrors/lo/LOGITacker LOGIT…

作者头像 李华
网站建设 2026/7/18 11:46:34

Spring Cloud Contract消费者端测试:如何使用契约存根验证调用

Spring Cloud Contract消费者端测试&#xff1a;如何使用契约存根验证调用 【免费下载链接】spring-cloud-contract Support for Consumer Driven Contracts in Spring 项目地址: https://gitcode.com/gh_mirrors/sp/spring-cloud-contract Spring Cloud Contract是实现…

作者头像 李华
网站建设 2026/7/18 11:46:25

城通网盘加速工具终极指南:3分钟解决限速烦恼的完整方案

城通网盘加速工具终极指南&#xff1a;3分钟解决限速烦恼的完整方案 【免费下载链接】ctfileGet 获取城通网盘一次性直连地址 项目地址: https://gitcode.com/gh_mirrors/ct/ctfileGet 你是否遇到过这样的场景&#xff1f;朋友分享了一个重要的学习资料&#xff0c;点击…

作者头像 李华
网站建设 2026/7/18 11:46:05

Privasis-Cleaner-0.6B社区贡献指南:如何参与模型改进与扩展

Privasis-Cleaner-0.6B社区贡献指南&#xff1a;如何参与模型改进与扩展 【免费下载链接】Privasis-Cleaner-0.6B 项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/Privasis-Cleaner-0.6B 欢迎来到Privasis-Cleaner-0.6B开源项目&#xff01;这是一个专注于文本隐…

作者头像 李华
网站建设 2026/7/18 11:45:33

Go静态文件打包方案对比与实践指南

1. Go静态文件打包的核心价值在Go语言开发中&#xff0c;静态资源打包是个老生常谈却常被忽视的问题。我经历过太多凌晨三点的部署灾难——明明本地测试完美的程序&#xff0c;放到服务器上就报"template file not found"。这种问题往往源于静态文件路径的硬编码依赖…

作者头像 李华