惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
aimingoo的专栏
aimingoo的专栏
爱范儿
爱范儿
D
Docker
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Tailwind CSS Blog
D
DataBreaches.Net
月光博客
月光博客
N
Netflix TechBlog - Medium
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Visual Studio Blog
MyScale Blog
MyScale Blog
B
Blog
阮一峰的网络日志
阮一峰的网络日志
L
LangChain Blog
Recent Announcements
Recent Announcements
Microsoft Azure Blog
Microsoft Azure Blog
WordPress大学
WordPress大学

秋码分享

我是如何解决将 c++ 编译成可以在 node.js 中使用的 *.node,中间出现的一大堆问题的(指纹浏览器基石篇) eSIM Plus 爱沙尼亚手机号彻底翻车?“永久有效”悄然变成了一年! 接码平台 SMS-Activate 余额可以转移到新平台使用,截止日期:2026年1月29日 是时候将 hugo-theme-kiwi 主题提交到 themes.gohugo.io 站点上了 Flux2 刚开源就凉了?Z-Image 本地部署狠狠打了个样 声音的未来:Chatterbox —— 用「夸张度旋钮」提升表现力的开源 TTS 向导 还以为那只是换个背景?Qwen-Image-Edit 在 ComfyUI 中能做到更离谱的事 Windows 结合最新版 ComfyUI 部署阿里最新开源的 Qwen-Image 图像大模型 从零样本到跨场景:Seed-VC语音转换技术的革命性突破 大语音模型轻量化革命:MegaTTS3 如何重新定义文本生成语音的技术边界(windows篇) 竞赛级编程大模型OlympicCoder-7B之本地部署(Windows篇) 阿里开源了端到端全模态大模型Qwen-2.5-Omini-7B之本地部署(windows篇) 语音识别之whisper本地部署(实时语音之开篇) 甭管是个人还是企业都能部署的Mistral-Small3.1,远超同级别的模型 文生音乐开源项目DiffRhythm,8G显存本地部署之Windows篇 阿里QwQ-32B本地部署指南:用Ollama轻松运行320亿参数大模型 基于Qwen2.5大模型的Spark-TTS,零样本语音克隆,CPU可运行之本地部署(Windows篇) 智谱开源了文生图CogView4-6B模型,支持中文提示词之本地部署(Windows篇) 基于歌词生成整首歌的开源AI音乐模型,支持中、英、日、韩等多种语言,本地化部署YuE(windows篇) 阿里云开源的文生视频万相 Wan2.1之本地部署Wan2.1-T2V-1.3B模型 互动式开源AI图像编辑神器,Windows11本地部署 MagicQuill 本地部署Qwen2.5-VL-7B-Instruct多模态视觉大模型(Windows篇) 保持角色一致性的绘本生成AI开源项目之Story-Adapter本地部署Windows篇 本地部署 Stable Diffusion 3.5(最新 ComfyUI记录篇) 谁说Win7安装不了Node.js最新版的呢?都2025年,还不更新系统到Win11 vs code远程调试Linux服务器上的php代码 浏览器定制 | Windows11 编译 Chromium 133.0.6885.0(截稿前Chromium最新版之编译篇[一]) 解决windows下php8.x及以上版本,在Apache2.4中无法加载CURL扩展的问题 在 Windows8.1 下编译 Chromium (103.0.5060.68 之三) 安装 depot_tools 和 Windows 10 SDK 为在Windows下构建基于 chromium 的浏览器(103.0.5060.68 之二)
不说是彻底搞懂,至少让你不再惧怕c/c++指针,以及各种奇葩指...
一个游离于山间之上的江湖漫行者。A jungle wanderer who wanders above the mountain · 2024-11-29 · via 秋码分享

经历过C/C++Coder,说起指针,无不是摇头晃脑的。

然而,没弄懂指针,可以说是无法在C/C++世界里徜徉,毕竟指针可是C/C++的灵魂所在。

由于C/C++Coder可以通过指针自由玩转内存,从而让外界给C/C++戴上了一顶内存不安全的语言。

相较于现代语言,诸如,JavagolangpythonPHP等这些具有垃圾回收机制的语言来说,C/C++把操控内存的主动权交给了Coder,之所以会被誉为内存不安全的语言,其最大责任便是Coder使用指针不当而引发内存崩溃。

什么是指针呢?

对于初学者而言,到底什么是指针(pointer)呢?我们光从字面上就大抵能猜出个一二,指针指针,无非就是一个事物指向另一个事物。

说起指针,便无法抛开内存而不谈。

我们知道数据(Data)+ 算法(algorithm)= 程序(program),那么程序的本质就是在操控数据,而内存便是存放数据(这里存放数据,不要与数据库存放的数据混淆了。)

由于变量类型作用域所限制,内存可分为:堆(heap)栈(stack)全局(global)以及静态存储(static)

无论是C/C++,抑或是Java等其他高级语言,最终都是要编译成汇编语言,而这种语言它可不管你为变量起了个多么好听的名字,在它那里统统都转变成了内存寻址

指针便是存放着某一块的内存地址,可以是某个基础类型变量的地址,也可以是复合类型变量的地址,甚至还可以是指针的地址。

#include <iostream>

using namespace std;

int main()
{
	int a = 10;
	int* ptr = &a; //声明一个int类型的指针,并指向变量a的地址
	cout << "变量 a 的地址=" << &a << endl;
	cout << "指针变量 ptr 存放着变量 a 的地址=" << ptr << endl;
	cout << "通过 * 解引用操作,间接获得变量 a 的值=" << *ptr << endl;

	*ptr = 15; //通过 * 解引用操作,来修改指针指向的内容。
	cout << "通过 * 解引用操作,间接修改变量 a 的值=" << a << endl;
	return 0;
}

image-20241125195242456

数组名就是指针

在初步了解了什么是指针的前提下,我们将继续探讨下一个话题,数组与指针到底有着怎样的联系呢?

而为什么说数组名称就是指针呢?我们知道,当我们以数组作为实参传入给函数形参时,它是以数组第一个元素的地址传入的,也就是以指针方式传入的。

#include <iostream>

using namespace std;

int main()
{
	//定义一个 int 类型数组
	int myArr[] = { 10,20,30,40,50,60 };

	//再定义一个 int 类型指针,并指向 myArr 数组
	int* ptr = myArr;

	//试着通过 *ptr 来修改数组里的元素的值。
	*ptr = 15;

	//随后,输出 myArr 的地址,以及指针变量存放的地址和它指向数组元素的值
	cout << "数组 myArr 的地址:" << myArr << endl;
	cout << "数组 myArr 第一个元素的地址:" << &myArr[0] << endl;
	cout << "指针变量 ptr 存放的地址:" << ptr << endl;
	cout << "指针变量 &ptr[0] 存放的地址:" <<&ptr[0] << endl;

	//通过遍历数组,输出所有元素
	for (int arr : myArr)
	{
		cout << '\t' << arr;
	}
	cout << endl;
	return 0;
}

image-20241125204453457

从上面输出的结果中,我们可以得出,数组名称就是数组首个元素的地址。例子中的myArr(数组名)与&myArr[0]都是指向第一个元素的地址,而若将它的地址存放在一个指针变量,那么,ptr&ptr[0]也是同样的指向数组首个元素的地址。

当然,我们是可以通过myArr[0]ptr[0]来获取数组的首个元素,而其他元素则可以通过数组下标获取,例如:myArr[1]*(myArr+1)以及*(ptr+1)都是获取数组的第二个元素。

#include <iostream>

using namespace std;

int main()
{
	//定义一个 int 类型数组
	int myArr[] = { 10,20,30,40,50,60 };

	//再定义一个 int 类型指针,并指向 myArr 数组
	int* ptr = myArr;

	//试着通过 *ptr 来修改数组里的元素的值。
	*ptr = 15;


	cout << "数组首个元素值是:" << myArr[0] << endl;
	cout << "通过指针形式获取数组第一个元素内容:" << ptr[0] << endl;
	cout << "获取第二个元素的内容:" << myArr[1] << " | 或者另一种方式:" << *(myArr + 1) << endl;
	cout << "还是由ptr来获取数组第二个元素:" << *(ptr + 1) << endl;

	return 0;
}

image-20241125205604020

数组指针和指针数组

当你看到数组指针指针数组,脑瓜子是不是就开始生疼了。的确,我也是被这个回行文给整晕了。

数组指针

数组指针它从本质上来讲,还是一个指针,它是指向数组的指针(Pointer to an array)指向数组的指针也称为数组指针(array pointer)

其定义语法:

data_type (*variable_name)[size of array]
#include <iostream>

using namespace std;

int main()
{
    // Pointer to an array of five numbers
    //声明一个 指向包含5个int类型元素数组的指针
    int(*a)[5];

    int b[5] = { 1, 2, 3, 4, 5 };

    int i = 0;

    // Points to the whole array b
    //指向整个数组 b
    a = &b;

    for (int j = 0;j < 5;j++) {
        cout << "直接使用数组名得到的地址:" << &b[j] << " | 直接通过数组下标获取元素:" << b[j] << endl;
    }

    for (i = 0; i < 5; i++)
        cout <<"数组地址:"<< (*a + i)<<" | 获取数组元素:" << *(*a + i) << endl;


	return 0;
}

image-20241126203614684

数组指针有时也称为行指针,其意是当指针 ptr执行ptr+1运算时,指针是会指向数组的下一行。

#include <iostream>

using namespace std;

int main()
{
    //定义一个 3 行 4 列 的二维数组
    int arr[3][4]{ {1,2,3,4},{5,6,7,8},{9,10,11,12} };

    //声明一个 数组指针,指向了一个包含 4 个 int 类型元素数组的指针
    int (*ptr)[4];

    ptr = arr; //将数组首元素的地址赋值给 数组指针 变量ptr,即 arr[0] 或 arr[0][0]

    cout << "通过数组指针获取数组arr的元素:" << *(*ptr) << endl;

    ptr++;

    cout << "通过数组指针获取数组arr的下一行第一个元素:" << *(*ptr) << endl;

	return 0;
}

image-20241126210116013

指针数组

指针数组(Array of pointers)本质上是一个数组,数组中的所有元素都是一个指针。它是指针变量的数组,其声明语法:

data_type *variable_name[array_size];
#include <iostream>

using namespace std;

const int SIZE = 5;

int main()
{
    //定义一个数组
	int arr[]{ 10,20,30,40,50 };

	//声明一个 int 变量,以及一个 指针数组
	int i, * ptr[SIZE];

    for (i = 0; i < SIZE; i++) {

        // 将数组元素的地址分配给 指针数组 ptr 变量
        ptr[i] = &arr[i];
    }

    // 遍历输出数组元素,通过 指针数组 的方式
    for (i = 0; i < SIZE; i++) {

        cout << "Value of arr[" << i << "] = " << *ptr[i] << endl;
    }

	return 0;
}

image-20241126212157334

数组指针和指针数组的区别

那么,现在你对数组指针指针数组的区分,是不是有了大致的了解呢。

  • 1、声明不同:

    数组指针本质是一个指针,指向了一个数组

    指针数组本质是一个数组,该数组中的每个元素都是一个指针

  • 2、语法不同:

    数组指针:data_type (*variable_name)[n];

    指针数组:data_type *variable_name[n];

    区分方法:数组名带括号的就是数组指针不带括号的就是指针数组。(这个类似于函数指针指针函数的区别)

  • 3、可以通过从右到左结合来区分: ①对于数组指针 data_type (*variable_name)[n],因为括号优先级较高,因此*号数组名variable_name先结合,也就是说variable_name首先是一个指针,然后与[n]结合,表示指针variable_name指向了一个大小为n的数组,数组的类型为data_type。 ②对于指针数组 data_type *variable_name[n],variable_name[]先结合,因此variable_name首先是一个大小为n的数组,剩下的部分是数组的类型,即data_type*类型,也就是数组的每个元素都是一个data_type指针

访问数组

#include <iostream>

using namespace std;

const int SIZE = 5;

int main()
{
  
    //定义一个二维数组
	int arr[3][4]{ {1,2,3,4},{5,6,7,8},{9,10,11,12} };

	int* ptr[3];

	for (int i = 0;i < 3;i++)
	{
		ptr[i] = arr[i]; // 指针数组 ptr 有 3 个指针,指向了二维数组 arr 的每一行
	}

	//可以通过以下几种方式来访问
	cout << "使用传统的数组下标访问 arr[1][2] =" << arr[1][2] << endl;
	cout << "使用指针的第 1 种方式【*(ptr[1]+2)】 =" << *(ptr[1] + 2) << endl;
	cout << "使用指针的第 2 种方式【 *(*(ptr+1) + 2)】 =" << *(*(ptr+1) + 2) << endl;
	cout << "使用指针的第 3 种方式【(*(ptr+1))[2]】 =" << (*(ptr+1))[2] << endl;

	return 0;
}

image-20241127210529294

函数指针和指针函数

讲完了数组指针指针数组后,将进入另一个话题,函数指针指针函数,或许你还处于数组指针指针数组概念中,一时半会儿还绕不出哪个圈来。

函数指针

函数指针其本质是一个指针,该指针指向了函数。

简而言之,函数指针就是指向函数指针

以下是函数指针的声明格式:

data_type (*fun)(parameter_type,...) //parameter_type 是可选的
#include <iostream>

using namespace std;

//声明加法函数
int add(int, int);

//声明一个减法函数
int sub(int, int);

//声明一个 函数指针
int (*fun)(int, int);

int main()
{
	//第一种写法
	fun = add; //将 add 函数地址赋值给 函数指针 fun
	cout << "fun(3,4)的结果是:" << fun(3, 4) << endl;

	//第二种写法
	fun = &sub;
	cout << "(*fun)(3,4)的结果是:" << (*fun)(3, 4) << endl;

	return 0;
}

int add(int x, int y)
{
	return x + y;
}

int sub(int x, int y)
{
	return x - y;
}

image-20241128212836647

其实,函数名(函数标识符)就表示了它的地址,故而取地址运算符&不是必需的,这一点,与数组标识符就是数组的地址,倒是相同的。

指针函数

指针函数其本质上是一个函数,而该函数的返回值是一个指针

其声明格式如下:

return_type* func(parameter_type,[...]) //parameter_type是可选的

其实指针函数声明与普通函数声明,就是了多了*

#include <iostream>

using namespace std;

//声明 指针函数
int* fun();


int main()
{
	int* ptr = fun();
	cout << "输出 ptr 的值:" << *ptr << endl;

	return 0;
}

int* fun()
{
	static int a = 10;
	return &a;
}

image-20241129212250444

指针函数与函数指针的区别

我们也同指针数组数组指针的区别一样,同样是从以下几点来区别两者的不同点。

  • 1、从概念上来讲:

    指针函数其本质是函数,而该函数的返回值是指针

    函数指针其本质是指针,他指向了函数

  • 2、语法不同(语法不同):

    指针函数:return_type* func(parameter_type,[…])

    函数指针:return_type (* func) (parameter_type,[…])

其实,C/C++中的关于指针的奇葩变种远不止这些,限于篇幅有限(一整长篇大论,总是让人感觉怎么还没到文末呢,而C/C++又是个枯燥的东西),故而,至于c/c++指针的篇目还未终结……