C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」目录一.变量的内存实质到1.1变量的实质1.2赋值给变量1.3变量在哪里?二.指针是个什么东西?三.二级指针(指针的指针)3.1定义与初始化3.2间接数据访问3.2.1.改变一级指针指向3.2.2改变N-1级指针的指向3.2.3二级指针的步长四.指针与数组4.1指针与数组名4.1.1通过数组名访问数组元素4….

大家好,又见面了,我是你们的朋友全栈君。

目录

一.变量的内存实质到

1.1变量的实质

1.2 赋值给变量

1.3 变量在哪里?

二. 指针是个什么东西?

 三. 二级指针(指针的指针)

3.1 定义与初始化

3.2 间接数据访问

3.2.1 .改变一级指针指向

3.2.2 改变 N-1 级指针的指向

3.2.3 二级指针的步长

四. 指针与数组

4.1 指针与数组名

4.1.1 通过数组名访问数组元素

4.1.2 通过指针访问数组元素

4.1.3 数组名与指针变量的区别

4.2 指针数组( 字符指针数组 )

4.2.1 定义

 4.2.2 代码实例

4.3 二级指针与指针数组 

4.3.1 .指针数组名赋给二级指针的合理性

4.3.2 完美匹配的前提(小尾巴 NULL)


在风起云涌的编程世界中,C/C++作为编程界的扛把子,以霸主地位而屹立不倒,究其原因,它有其他语言无法相媲美的“底牌”而存在,那就是——指针。指针被称为是C/C++中的精髓,也有人说由于指针的存在让C/C++变得更加难学,难懂,难消化。果真是这样吗?本篇文章让我们一起来揭开指针的真正面纱。

一.变量的内存实质到

1.1变量的实质

要理解指针,首先就要理解“变量”的存储实质,如果你已经深入理解“变量”的存储实质,直接跳过本小节……

首先我们来看一下内存空间图:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

如图所示,内存只不过是一个存放数据的空间,可以理解为装鸡蛋的篮子,装水果的箱子,或是装RMB的钱包,随便啦,反正就是这么一个很随意的玩意……现在我们把它想象成电影院的座位,电影院中的每个座位都要编号,而我们的内存要存放各种各样的数据,当然我们要知道我们的这些数据存放在什么位置吧!所以内存也要象座位一样进行编号了,这就是我们所说的内存编址(为内存进行地址编码)。座位可以是遵循“一个座位对应一个号码”的原则,从“第 1 号”开始编号。而内存则是按一个字节接着一个字节的次序进行编址,如上图所示。每个字节都有个编号,我们称之为内存地址

内存编址:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

当我们在程序中写下了这样的语言声明:

int i;
char a;

时,它其实是内存中申请了一个名为 i 的整型变量宽度空间(DOS 下的 16 位编程中其宽度为 2 个字节),和一个名为 a 的字符型变量宽度的空间(占 1 个字节)。

内存中的映象如下图所示:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

图中可看出,i 在内存起始地址为 6 上申请了两个字节的空间(我这里假设
了 int 的宽度为 16 位,不同系统中 int 的宽度可能是不一样的,最常用的win32环境下为4个字节),并命名为 i。
a 在内存地址为 8 上申请了一字节的空间,并命名为 a。阿欧……这样我们就有两个不同类型的变量了。变量有了接下来我们考虑的就是如何给变量进行赋啦。

1.2 赋值给变量

再看下面赋值:
i = 30;
a = ’t’;
你当然知道个两个语句是将 30 存入 i 变量的内存空间中,将“t”字符存入 a 变量的内存空间中。我们可以利用这样来形象理解:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

我们将30存在了以地址6(真正的地址可不是这样子哦,真正的地址如:0016FD14)为起始地址的两个字节空间里,a 在内存地址为 8 上申请了一字节的空间存入了‘t’,那么变量i和a在哪呢???

1.3 变量在哪里?

接下来我们来看看&i 是什么意思?是取 i 变量所在的地址编号嘛!我们可以这样大声读出来:返回 i 变量的地址编号。你记住了吗?如果没有,在读一遍:返回 i 变量的地址编号

以一小段简单代码来弱弱看下:

#include <iostream>
#include <stdlib.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int i = 30;

	std::cout << "&i = "<< &i << std::endl;
	std::cout << "i =  " << i << std::endl;

	system("pause");
	return 0;
}

输出结果为:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

输出的 &i 的值 0016FD14就是我们图示中内存空间编码为6的内存地址。接下来就进入我们真正的主题——指针。

二. 指针是个什么东西?

指针,想说弄懂你不容易啊!我常常在思索它,为什么呢?其实生活中处处都有指针,我们也处处在使用它。有了它
我们的生活才更加方便了。没有指针,那生活才不方便。不信?你看下面的例子。

比如有天你说你要学习C++,要借我的这本 C++ Primer Plus,我把书给你送过去发现你已经跑出去打篮球了,于是我把书放在了你桌子上书架的第三层四号的位置。并写了一张纸条:你要的书在第 三 层 四号的书架上。贴在你门上。当你回来时,看到这张纸条,你就知道了我借与你的书放在哪了。你想想看,这张纸条的作用,纸条本身不是书,它上面也没有放着书。那么你又如何知道书的位置呢?因为纸条上写着书的位置嘛!聪明!!!其实这张纸条就是一个指针了。它上面的内容不是书本身,而是
书的地址,你通过纸条这个指针找到了我借给你的这本书。

那么我们 C/C++中的指针又戴上了啥面具呢?让我们拭目以待。下面看一条声明一个指向整型变量的指针的语句:
int *pi;

pi 是一个指针,当然我们知道啦,但是这样说,你就以为 pi 一定是个多么特别的东西了。其实,它也只过是一个变量而已。与上一篇中说的变量并没有实质的区别。好了,这就是指针。仅此而已,就这么简单。不信你看下图:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

(说明:这里我假设了指针只占 2 个字节宽度,实际上在 32 位系统中,指针的宽度是 4 个字节宽的,即 32 位。)
由图示中可以看出,我们使用“int *pi”声明指针变量 —— 其实是在内存的某处声明一个一定宽度的内存空间,并把它命名为 pi。你能在图中看出pi 与前面的 i、a 变量有什么本质区别吗?没有,当然没有!肯定没有!!真的没有!!!pi 也只不过是一个变量而已嘛!那么它又为什么会被称为“指针”?关键是我们要让这个变量所存储的内容是什么。现在我要让 pi 成为具有真正“指针”意义的变量。请接着看下面语句:
pi = &i;
你应该知道 &i 是什么意思吧!刚刚大声读的那句话,如果忘了,回头去在大声读,记住了!!!那这句代码怎么读呢?这样大声读:把 i 地址的编号赋值给 pi。并记下来。也就是你在 pi 里面写上 i 的地址编号。结果如下图所示:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

你看,执行完 pi=&i 后,在图示中的内存中,pi 的值是 6。这个 6 就是i 变量的地址编号,这样 pi 就指向了变量 i 了。你看,pi 与那张纸条有什么区别?pi 不就是那张纸条嘛!上面写着 i 的地址,而 i 就是那本厚书C++ Primer Plus。你现在看懂了吗?因此,我们就把 pi 称为指针。所以你要牢牢记住:指针变量所存的内容就是内存的地址编号 ! 本篇文章看完啥都可以没记住,这18个红字切记,切记,切记要牢牢刻在脑子里,也许可能或许大概在将来某个面试中人家问你指针是啥?这18个字足以得满分。也会随着你不断的学习对这句话会理解的越来越深。切记记住这18个红字!!好了废话太多了,现在我们就可以通过这个指针 pi 来访问到 i 这个变量了,请看代码:

#include <iostream>
#include <stdlib.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int i = 30;

	std::cout << "&i = "<< &i << std::endl;
	std::cout << "i =  " << i << std::endl;

	int *pi = &i;
	std::cout << "*pi = " << *pi << std::endl;

	system("pause");
	return 0;
}

输出结果如下: 

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

那么程序中*pi 什么意思呢?你只要这样大声读它:pi 内容所指的地址的内容(读上去好像在绕口令了),就是 pi 这张“纸条”上所写的位置上的那本 “书”—— i 。你看,Pi 的内容是 6,也就是说 pi 指向内存编号为 6 的地址。*pi嘛,就是它所指地址的内容,即地址编号 6 上的内容了,当然就是 30 这个“值”了。所以这条语句会在屏幕上显示 30。请结合上图好好体会吧!由于本人水平有限,对“指针是个什么东西?”的理解仅限于此,有问题我们在随时探讨喽。真想说:指针是个什么东西,这么难理解,脑仁疼疼的。
到此为止,你已经能大声读出类似&i、*pi 写法的含义了。也知道了具体的相关操作。总结一句话:我们的纸条就是我们的指针,同样我们的 pi 也就是我们的纸条!剩下的就是我们如何应用这张纸条了。如何用?大声读出下面的代码并正确理解含义。

char a,*pa;
a = 10;
pa = &a;
*pa = 20;

假设你已经完全掌握在此之前的所有内容,我们接着看看“指针的指针”它是个什么东西?即对int **ppa;中 ppa 的理解。

 三. 二级指针(指针的指针)

二级指针,是一种指向指针的指针。我们可以通过它实现间接访问数据,和改变一级指针的指向问题。

3.1 定义与初始化

以一张简图说明问题:(看不懂看第二节)

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

例子代码:

#include <iostream>
#include <stdlib.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int i = 30;

	std::cout << "&i = "<< &i << std::endl;
	std::cout << "i =  " << i << std::endl;

	int *pi = &i;
	std::cout << "*pi = " << *pi << std::endl;

	int **ppi = &pi;
	std::cout << "**ppi = " << **ppi << std::endl;

	system("pause");
	return 0;
}

 输出结果:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

 思考:**pi 怎么读?代表的含义是什么?

3.2 间接数据访问

3.2.1 .改变一级指针指向

例子代码:

#include <iostream>
#include <stdlib.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int i = 30;

	int *pi = &i;
	std::cout << "一级指针*pi = " << *pi << std::endl;       //一级指针

	int **ppi = &pi;
	std::cout << "二级指针**ppi = " << **ppi << std::endl;   //二级指针

	*pi = 20;
	std::cout << "改变一级指针内容: *pi = " << *pi << std::endl;  //改变一级指针值
	std::cout << "一级指针*pi = " << *pi << std::endl;       //二级指针

	int b = 10;
	*ppi = &b;
	std::cout << "改变一级指针指向*pi = " << *pi << std::endl;   //改变一级指针的指向
	std::cout << "二级指针**ppi = " << **ppi << std::endl;   

	system("pause");
	return 0;
}

输出结果:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

3.2.2 改变 N-1 级指针的指向

  • 可以通过一级指针,修改 0  级指针(变量)的内容。
  • 可以通过二级指针,修改一级指针的指向。
  • 可以通过三级指针,修改二级指针的指向。
  •  ·····
  • 可以通过 n  级指针,修改 n-1 

3.2.3 二级指针的步长

所有类型的二级指针,由于均指向一级指针类型,一级指针类型大小是 4,所以二级指针的步长也是 4,这个信息很重要

四. 指针与数组

4.1 指针与数组名

4.1.1 通过数组名访问数组元素

看下面代码:
int i, a[] = {3,4,5,6,7,3,7,4,4,6};
for (i = 0; i <= 9; i++)
{

    std::cout << a[i] std::endl;
}
很显然,它是显示 a 数组的各元素值。
我们还可以这样访问元素,如下:
int i, a[] = {3,4,5,6,7,3,7,4,4,6};
for (i = 0; i <= 9; i++)
{

std::cout << *(a+i) << std<<endl;;
}
它的结果和作用完全一样。

4.1.2 通过指针访问数组元素

int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a; /*请注意数组名 a 直接赋值给指针 pa*/
for (i = 0; i <= 9; i++)

{

 std::cout <<  pa[i] << std::endl;
}
很显然,它也是显示 a 数组的各元素值。

另外与数组名一样也可如下:
int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a;
for (i = 0; i <= 9; i++)
{

   std::cout <<  *(pa+i) << std::endl;
}

4.1.3 数组名与指针变量的区别

请看下面的代码:
int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a;
for (i = 0; i <= 9; i++)
{

printf(“%d\n”, *pa);
pa++; /*注意这里,指针值被修改*/
}

可以看出,这段代码也是将数组各元素值输出。不过,你把循环体{}中的 pa改成 a 试试。你会发现程序编译出错,不能成功。看来指针和数组名还是不同的。其实上面的指针是指针变量,而 数组名只是一个指针常量。

4.2 指针数组( 字符指针数组 )

4.2.1 定义

指针数组的本质是数组,数组中每一个成员是一个指针。定义形式如下:
char * pArray[10];
语法解析:pArray 先与“[ ]”结合,构成一个数组的定义,char *修饰的是数组的内容,即数组的每个元素。

图示:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

 4.2.2 代码实例

#include <iostream>
#include <stdlib.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	char * pArray[] ={"apple","pear","banana","orange","pineApple"};
	for(int i=0; i<sizeof(pArray)/sizeof(*pArray); i++)
	{
		std::cout << pArray[i] << std::endl;
	} 

	system("pause");
	return 0;
}

输出结果:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

4.3 二级指针与指针数组 

4.3.1 .指针数组名赋给二级指针的合理性

二级指针与指针数组名等价的原因:
char **p 是二级指针;
char* array[N]; array = &array[0]; array[0] 本身是 char*型;
char **p = array;

例子代码:

#include <iostream>
#include <stdlib.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	char * pArray[] ={"apple","pear","banana","orange","pineApple"};
	std::cout << "**********pArray[i]************" << std::endl;
	for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
	{
		std::cout << pArray[i] << std::endl;
	}
		
	char **pArr = pArray;
	std::cout << "**********pArr[i]************" << std::endl;
	for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
	{
		std::cout << pArr[i] << std::endl;
	}
	system("pause");
	return 0;
}

输出结果:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

4.3.2 完美匹配的前提(小尾巴 NULL)

数组名,赋给指针以后,就少了维度这个概念,所以用二级指针访问指针数组,需要维度,当然了,也可以不用需要。

实例代码:

#include <iostream>
#include <stdlib.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	//演绎 1
	std::cout << "******演绎 1*****" << std::endl;
	int arr[10] = {1};
	for(int i=0; i<10; i++)
	{
		std::cout << arr[i] << std::endl;
	}

	int *parr = arr;
	for(int i=0; i<10; i++)
	{
		std::cout << *parr++ << std::endl;
	}


	//演绎 2
	std::cout << "*****演绎 2*****"<<std::endl;
	char *str = "china";
	while(*str)
	{
		std::cout << *str++ << std::endl;
	}

	char * pArray[] ={"apple","pear","banana","orange","pineApple",NULL};
	char **pa = pArray;
	while(*pa != NULL)
	{
		std::cout << *pa++ << std::endl;
	}
	system("pause");
	return 0;
}

输出结果:

C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

【下一篇:】C/C++指针详解之提高篇(史上最全最易懂指针学习指南!!!!)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/143274.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月21日 下午11:40
下一篇 2022年5月22日 上午6:00


相关推荐

  • asio ssl 笔记

    asio ssl 笔记Asio 包含了一个类以及类模板用于基本的 SSL 支持 这个类使得加密通信成为可能 在已经存在的流上层做了一层加密 比如加密一个 TCP 套接字 在创建一个加密流之前 必须要构造一个 SSLcontext 对象 这个对象用于设置 SSL 选项 认证模式 认证文件等 下面用一个客户端的初始化作为说明 ssl contextctx ssl context sslv23 ctx set v

    2026年3月17日
    1
  • java sdk怎么配置_Java SDK环境配置教程

    java sdk怎么配置_Java SDK环境配置教程1 安装 JDK 双击打开刚刚下载的 JDK 安装包 安装目录可以为任意 只要你记着就好 当然 C 盘是比较好 但由于小祺是一名程序员 装了比较多开发相关的软件及环境 因此装在 D 盘 2 JAVA 环境变量配置安装好 JDK 后 找到我的电脑 按右键 点属性选项 按高级选项卡 点击环境变量 Win7 或 vista 为 计算机 属性 高级系统设置 高级 环境变量接着 大家会看到这一界面 下面 我们在系统变量中找到三个环境变

    2026年3月26日
    2
  • 易语言执行mysql命令_易语言执行sql进度条 易语言mysql

    易语言执行mysql命令_易语言执行sql进度条 易语言mysql易语言的进度条怎么使用?我就让进度条每秒进一格,一百秒进度条满!用了一个时钟组件。.版本2.程序集窗口程序集3.子程序__启动窗口_创建完毕.子程序_按钮1_被单击.如果(编辑框1.内容≠“”或编辑框2.内容≠“”)时钟1.时钟周期=1000.否则信息框(“请输入内容”,0,).如果结束.子程序_时钟1_周期事件.如果(进度条1.位置<进度条…

    2022年7月13日
    54
  • 电脑怎么远程连接到服务器?

    电脑怎么远程连接到服务器?

    2021年9月18日
    56
  • 操作系统常见面试题

    操作系统常见面试题引论什么是操作系统?可以这么说,操作系统是一种运行在内核态的软件。它是应用程序和硬件之间的媒介,向应用程序提供硬件的抽象,以及管理硬件资源。操作系统主要有哪些功能?操作系统最主要的功能:处

    2022年7月4日
    19
  • Azure 上通过 SendGrid 发送邮件

    Azure 上通过 SendGrid 发送邮件SendGrid 是架构在云端的电子邮件服务 它能提供基于事务的可靠的电子邮件传递 并且具有可扩充性和实时分析的能力 本文从 Azure 上创建 SendGrid 账号开始 介绍如何通过 SendGrid 发送邮件

    2026年3月16日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号