news 2026/4/23 10:12:26

深入理解C/C++指针

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入理解C/C++指针

1.指针理解

首先我们看一段程序,如下:

#include <stdio.h> int main(void) { char ch; char* pc = &ch; *pc = 'H'; printf("%c", ch); return 0; }

我们可以看到这是指针最基本的用法,那么我们现在来深入理解一下这段程序,char ch;编译器在执行了这句话后就会在内存中开辟一段内存,而对于该段内存就会有一个地址,这个地址就像是这段内存空间的一个门牌号用来帮助编译器在后面我们要用到该段程序时知道该空间。那么对于char就是类型,通过这个类型编译器可以知道要开辟的空间的大小即1个字节大小。

那么我们现在来思考一下ch是什么呢?ch是一个变量名对吧,但是编译器怎么通过这个变量名就找到了那块空间呢,我们前面讲到地址才是编译器找到内存空间的标识,那么我们来好好理解一些这个变量ch,变量的本质是标识符与内存地址的绑定,它代表了一个可以存储数据的内存空间,在编译时变量名会被编译器自动的与内存地址映射起来,就可以找到对应的空间了。这也就是c/c++这些高级语言的一个表现,变量名给我们使用的人看,但是底层编译会自动转化为地址。

所以接下来的char*pc=&ch,就好理解了,这也是语言的通行,就是开辟了一个char*类型内存大小的空间,pc就是这块空间的一个标识符,然后这块空间存放的就是ch这变量名绑定的那块空间的地址。所有通过pc我们并不能直接找到ch变量名的那块空间,故我们又要引用到了一个操作符*来通过该操作符,让其解引用然后就指向到了ch的那块空间,复制后ch空间下的值也被修改了。

我们可以通过下图来更好的理解:

2.指针类型理解

我们看下面这段程序:

#include <stdio.h> int main(void) { int num = 512; char* p = &num; printf("%d", *p); return 0; }

num是int类型,但是我们的指针是char*类型,运行后我们看输出结果如下:

可见所得是0,这是为什么呢?我们知道int类型开辟的空间是4个字节,但是char类型开辟的是1个字节,所以char*类型的指针指向的虽然是num空间,但是却只指向一个字节空间,我们看其中的空间分布图:

所以输出的就是0了。

我们再看下面这段程序:

#include <stdio.h> int main(void) { int num = 512; char* p = &num; *p += 1; printf("%d", num); return 0; }

输出结果如下:

可见用char*类型的指针来进值得操作时,也只会对它所指向得那块空间有影响。我们可以看看内存分布如下:

所以指针类型其实本质就是指向那块空间的大小。但是要注意的是在C++中上面这段程序会报错因为c++的语法更加严格了。

3.指针运算理解

我们看下面这段程序:

#include <stdio.h> int main(void) { int arr[4] = { 10,20,30,40 }; int* p1 = &arr[3]; int* p2 = &arr[0]; printf("%d", p1 - p2); printf("%d", (unsigned int)p1 - (unsigned int)p2); return 0; }

看结果:

我们可以看到两个运算得到的结果不一样了,首先一个直接对p1和p2指针进行减法运算,得到的是两个指针之间的元素个数,这个元素个数所说的其实就是你指定的指针类型的这样字的空间有几块,比如我的指针类型是int*所以两者相减的其实就是这两个指针间有几块4字节大小空间,我们可以验证一下如下我们将程序修改为下面的程序:

#include <stdio.h> int main(void) { int arr[4] = { 10,20,30,40 }; //int* p1 = &arr[3]; //int* p2 = &arr[0]; char* p1 = &arr[3]; char* p2 = &arr[0]; printf("%d\n", p1 - p2); printf("%d\n", (unsigned int)p1 - (unsigned int)p2); return 0; }

结果如下:

也就验证了我上面所说的观点。

接着我们来理解一下(unsigned int)p1 - (unsigned int)p2这句程序,该句程序就是将地址强转为了无符号整形然后进行减法运算,所以得到的就是整形减整形的值。对其的强转其实就是改变了编译器在访问时的访问类型。为什么上面两者的结果会这样子呢,我们来看看其中的汇编会更加清楚如下:

我们注意到其中一句汇编,sar eax,2可以在我们地址相减后得到的eax值,在执行的时地址减法运算时,eax右移了2位即除以了4,而在将地址转化为整数后再相减就没有做此处理,这也说明了其二者值不一样的原因。

4.指针和数组名的关系

我们知道了指针和数组的关系,如可以通过指针来操作数组,数组每个元素取出地址也可以赋值给指针操作,特别的我们知道数组名的地址和数组首元素地址一样,我们可以通通过指针来遍历数组也可以通过数组下标来遍历数组。下面我们来深入理解一下,数组名和指针的关系,看下面这段程序:

#include <stdio.h> int main(void) { int arr[4] = { 10,20,30,40 }; int* p1 = arr; printf("%d\n", *arr); printf("%d\n", *p1); return 0; }

运行后结果如下:

可见两者的输出结果都是10,但是这两者是一样的嘛,我们再看下面这段程序:

#include <stdio.h> int main(void) { int arr[4] = { 10,20,30,40 }; int* p1 = arr; printf("%d\n", arr); printf("%d\n", *p1); int a = 100; arr = &a; p1 = &a; return 0; }

运行后如下:

可见p1指针可以改变指向而arr却不能改变指向。再看下面这段程序:

#include <stdio.h> int main(void) { int arr[4] = { 10,20,30,40 }; int* p1 = arr; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(p1)); return 0; }

运行后:

可见数组名和指针有着相似性但是又不是一样的,接下来我们就来深入理解一下这两者。首先看下图:

可见p1的类型为int*但是arr的类型为int[4],p1指向的是单个的int但是arr指向的是4个int也就是整个数组,编译器在识别p1时就只会识别到一个int,而在识别arr时识别的时整个数组,这是在早年c语言创建时为了操作数组的高效性所定义的。

5.指针与const的应用

第一种就是我们不加const,我们称为自由指针可以更换指针的指向也可以更改指针指向内容的值。如下:

#include <stdio.h> int main(void) { int a = 100; int arr[4] = { 10,20,30,40 }; int* p1 = arr; p1 = &a; *p1 = 10; return 0; }

第二种就是在指针类型前面加上const,该指针可以改变指向,但是不能改变指向内容的值。如下:

第三种就是在指针变量前加上const ,该指针不可以改变指针的指向,但是可以改变指向的内容的值如下:

第四种在指针类型和指针变量之前都加上const,该指针既不可以改变指向,也不可以改变指向内容的值,如下:

指针与const的联动常用于在给程序员定义接口时使用,防止实现接口的程序员对不需要进行修改的值不小心进行了修改,导致出现意外错误。

6.函数参数传递二维数组

对于二维数组作为参数进行传递有下面几种常用方法:

第一种:

#include <stdio.h> void add(int array[][4] , int n) { int sum = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < 4; j++) { sum += array[i][j]; } } printf("sum=%d", sum); } int main(void) { int a = 100; int arr[2][4] = { 10,20,30,40, 50,60,70,80}; add(arr, 2); return 0; }

这种写法比较明确,可以很清楚的看出传入该函数的参数是一个二维数组。在编译时会将该参数理解为int (*array)[4]。

第二种:

#include <stdio.h> void add(int (*array)[4] , int n) { int sum = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < 4; j++) { sum += array[i][j]; } } printf("sum=%d", sum); } int main(void) { int a = 100; int arr[2][4] = { 10,20,30,40, 50,60,70,80}; add(arr, 2); return 0; }

这种写法也比较明确,而且可以明确的看出该array就是一个二维数组的指针。

第三种:

#include <stdio.h> void add(int *array, int n , int m) { int sum = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { sum += array[i*m+j]; } } printf("sum=%d", sum); } int main(void) { int a = 100; int arr[2][4] = { 10,20,30,40, 50,60,70,80}; add(arr, 2,4); return 0; }

当使用第三种方式来对二维数组进行参数传递时,array就是一个指向数组首元素的地址,没有了二维数组的属性,所以不能使用上面两种通过array[i][j]的形式来访问二维数组了,所以我们可以通过上面的一维数组的访问形式来访问,其实也就是指针形式来访问,以为指针也可以通过数组下标形式来进行访问。

7.函数指针

函数指针是一个指向函数入口地址的指针,通过该指针可以实现对函数的调用。那么该函数指针的声明类型应该如何写呢?如下:<数据类型> (*<函数指针名称>)(<参数说明列表>)。其中数据类型就是函数的返回值类型,函数指针名称就是你相想要给该指针取得名称,参数说明列表就是你函数得形参列表。程序如下:

#include <stdio.h> int add(int a, int b) { return a + b; } int main(void) { int m = 10; int n = 20; int (*p)(int,int) = NULL; p = add; printf("%d", (*p)(m, n)); return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 10:09:37

Bagisto电商平台容器化实战:从单机到集群的完整部署方案

Bagisto电商平台容器化实战&#xff1a;从单机到集群的完整部署方案 【免费下载链接】bagisto Free and open source laravel eCommerce platform 项目地址: https://gitcode.com/gh_mirrors/ba/bagisto Bagisto作为基于Laravel框架的开源电商系统&#xff0c;通过容器化…

作者头像 李华
网站建设 2026/4/23 10:10:11

每日Java面试场景题知识点之-单例模式

每日Java面试场景题知识点之-单例模式 一、单例模式概述 单例模式&#xff08;Singleton Pattern&#xff09;是Java中最简单也是最常用的设计模式之一。它保证一个类只有一个实例&#xff0c;并提供一个全局访问点来访问这个实例。在Java企业级项目中&#xff0c;单例模式广泛…

作者头像 李华
网站建设 2026/4/23 10:09:58

MCP PL-600 Agent架构深度拆解(多模态融合技术大揭秘)

第一章&#xff1a;MCP PL-600 多模态Agent架构概述MCP PL-600 是一种先进的多模态智能体&#xff08;Agent&#xff09;架构&#xff0c;专为处理复杂、异构的环境交互任务而设计。该架构融合了视觉、语音、文本与传感器数据等多种输入模态&#xff0c;并通过统一的语义理解层…

作者头像 李华
网站建设 2026/4/23 10:09:33

【Dify Agent工具注册机制深度解析】:掌握高效插件集成的5大核心步骤

第一章&#xff1a;Dify Agent工具注册机制概述Dify Agent 是一个用于连接大语言模型与外部系统的智能代理工具&#xff0c;其注册机制是实现 Agent 可扩展性和安全调用的核心环节。通过标准化的注册流程&#xff0c;开发者可以将自定义功能模块快速接入 Dify 平台&#xff0c;…

作者头像 李华
网站建设 2026/4/23 10:09:54

低代码平台的扩展能力:活字格服务端编程实战

低代码平台的扩展能力&#xff1a;活字格服务端编程实战 引言 在当今数字化转型浪潮中&#xff0c;低代码平台因其快速开发和易用性而备受青睐。然而&#xff0c;企业级应用往往需要处理复杂的业务逻辑和特殊需求&#xff0c;这些需求可能超出标准低代码功能的范畴。活字格低…

作者头像 李华
网站建设 2026/4/23 6:42:05

Strix AI安全测试工具:新手快速上手终极指南

Strix AI安全测试工具&#xff1a;新手快速上手终极指南 【免费下载链接】strix ✨ Open-source AI hackers for your apps &#x1f468;&#x1f3fb;‍&#x1f4bb; 项目地址: https://gitcode.com/GitHub_Trending/strix/strix Strix作为开源的AI驱动安全测试工具&…

作者头像 李华