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

推荐订阅源

IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园_首页
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
ThreatConnect
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 聂微东
H
Help Net Security
T
Threat Research - Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale
A
Arctic Wolf
G
Google Developers Blog
量子位
U
Unit 42
I
InfoQ
V
V2EX
F
Fox-IT International blog
P
Privacy & Cybersecurity Law Blog
V
Visual Studio Blog
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
C
CERT Recently Published Vulnerability Notes
博客园 - 三生石上(FineUI控件)
T
The Exploit Database - CXSecurity.com
T
Tailwind CSS Blog
SecWiki News
SecWiki News
Know Your Adversary
Know Your Adversary
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
The Hacker News
The Hacker News
Project Zero
Project Zero
Application and Cybersecurity Blog
Application and Cybersecurity Blog
月光博客
月光博客
Recent Commits to openclaw:main
Recent Commits to openclaw:main
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
GRAHAM CLULEY
C
Cisco Blogs
I
Intezer
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
Recorded Future
Recorded Future
T
Tenable Blog
W
WeLiveSecurity
腾讯CDC
Stack Overflow Blog
Stack Overflow Blog
T
The Blog of Author Tim Ferriss
www.infosecurity-magazine.com
www.infosecurity-magazine.com
D
Docker
C
Cybersecurity and Infrastructure Security Agency CISA
PCI Perspectives
PCI Perspectives

文章列表

我是如何解决将 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 之二) Windows构建基于 Chromium 的浏览器之环境准备篇安装 Visual Studio(103.0.5060.68 之一) 为什么网站加载速度总是那么不尽如人意呢?(网站优化篇) 海外云服务器安装 Redis 6.2.x (Ubuntu 18.04 记录篇三) 海外云服务器安装 MariaDB 10.6.X (Ubuntu 18.04 记录篇二) 海外云服务器安装 JDK8 (Ubuntu 18.04 记录篇) 虚拟机 Linux 安装 JDK(Vagrant 之二 CentOS7 篇) 怎样快速搭建 Linux 虚拟机呢?(vagrant 篇) 个人站点迁移之gitlab.com pages 绑定自定义域名 不要你掏腰包,就能搭建个人网站之 gitlab.com Pages 托管服务 国内 gitee.com Pages 下线了,致使众多站长纷纷改用其他托管平台 canvas绘制文本时,该如何处理首行缩进、自动换行、多内容以省略号结束、竖排的呢? 其实,低成本甚至免费的,也是可以搭建个人网站的哦! 腾讯云COS托管静态网站,以及如何解决访问出现了下载网页的情况 错误分析 (Machine Learning 研习十九) 多类别分类器(Machine Learning 研习十八) 使用CSS计数器,在目录名称前加上了序号,让目录看起来更加井然有序 使用 golang 以及 Gin 框架,将上传的图片在不保存至本地的情况下添加水印,并上传至阿里云 OSS 绘制特征曲线-ROC(Machine Learning 研习十七) 网站引入 Prism,使得代码高亮显示,并一键复制代码块 精确率(召回率)的权衡(Machine Learning 研习十六) 对模型性能进行评估(Machine Learning 研习十五) 图像识别之入门案例之数字识别(Machine Learning 研习十四) 微调模型——续(Machine Learning 研习之十三) 微调模型(Machine Learning 研习之十二) 解决 github.com port 443: Timed out 的问题 选择和训练模型(Machine Learning 研习之十一) 回望这风雨飘摇的一年过后,我们终将要整束行囊继续前行 一套由 Hugo 驱动的博客主题 hugo-theme-kiwi 开源啦 机器学习中的 Transformation Pipelines(Machine Learning 研习之十) 特征缩放和转换以及自定义Transformers(Machine Learning 研习之九) 为机器学习算法准备数据(Machine Learning 研习之八) 端到端的机器学习项目之探索数据(Machine Learning 研习之七) 端到端的机器学习项目(Machine Learning 研习之六) 机机器学习的测试和验证(Machine Learning 研习之五) 机器学习的主要挑战和任务(Machine Learning 研习之四) 对于大量数据集的解决方案便是在线学习或是增量学习(Machine Learning 研习之三) 现实生活中机器学习的具体示例(Machine Learning 研习之二) 让机器学习不再是过门不入,带您一起详解机器学习(机器学习 Machine Learning 研习之一) 把握住golang中的template,方能驾驭得了Hugo主题的template 云服务器到期,站点迁移,Nginx配置SSL以备后续只需! 玩以太坊链上项目的必备技能(内联汇编 [inline assembly]-Solidity之旅十八) 玩以太坊链上项目的必备技能(库 [library]-Solidity之旅十七) 玩以太坊链上项目的必备技能(Constant 和 Immutable 状态变量-Solidity之旅十六) 玩以太坊链上项目的必备技能(修改器 [modifier]-Solidity之旅十五) 玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四) 玩以太坊链上项目的必备技能(函数及其可见性和状态可变性-Solidity之旅十三) 玩以太坊链上项目的必备技能(事件-Solidity之旅十二) 玩以太坊链上项目的必备技能(OOP-接口-Solidity之旅十一) 玩以太坊链上项目的必备技能(OOP-抽象合约-Solidity之旅十) 玩以太坊链上项目的必备技能(OOP-合约继承-Solidity之旅九) 玩以太坊链上项目的必备技能(流程控制-Solidity之旅八) 玩以太坊链上项目的必备技能(单位以及全局变量-Solidity之旅七) 玩以太坊链上项目的必备技能(基本类型转换以及推断-Solidity之旅六) 玩以太坊链上项目的必备技能(变量作用域-Solidity之旅五) 玩以太坊链上项目的必备技能(类型-映射类型-Solidity之旅四) 玩以太坊链上项目的必备技能(类型-引用类型-Solidity之旅三) 花了不到1块5,玩了下全网最火的ChatGPT 玩以太坊链上项目的必备技能(类型-值类型-Solidity之旅二) 玩以太坊链上项目的必备技能(初识智能合约语言-Solidity之旅一) 在构建 Web3 前,需先知道什么是区块链,毕竟 Web3 是基于区块链 Web3 来了,让我们展开双手拥抱它吧! Go 语言中的 Moduels 管理(Let's Go 三十四) Go 语言中的包(Let's Go 三十三) Go 语言中的错误处理(Let's Go 三十二) Go 语言中的带有缓冲 Channel(Let's Go 三十一) Go 语言中的单向 Channel(Let's Go 三十) Go 语言中的 Channel(Let's Go 二十九) Go 语言中的并发编程(Let's Go 二十八) Go 语言中的空接口(Let's Go 二十七) Go 语言中的类型断言(Let's Go 二十六)
不说是彻底搞懂,至少让你不再惧怕c/c++指针,以及各种奇葩指针变种
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++指针的篇目还未终结……