C++基础知识【5】数组和指针
目录
一、概述
数组
指针
二、数组
2.1、数组的声明
2.2、数组的初始化
2.3、数组的访问
2.4、多维数组
2.5、数组作为函数参数
三、指针
3.1、指针的声明
3.2、指针的赋值
3.3、指针的访问
3.4、指针运算
3.5、指针数组和数组指针
3.6、二级指针
四、数组和指针的关系
五、常量指针和指针常量
六、空指针和野指针
七、动态分配内存和释放内存
八、指针与函数
九、指针与字符串
十、指针与函数
一、概述
数组
- 数组是一组具有相同数据类型的元素的集合,可以通过一个名称和一个索引来引用其中的元素。
- 在声明数组时,必须指定数组的大小,这个大小在声明时是固定的,无法改变。
- 数组的元素可以是任何C++数据类型,例如int,double,char等。
- 数组名代表数组的第一个元素的地址,也称为指向数组的指针。
- 数组元素可以使用下标运算符[]访问,下标从0开始计数。
指针
- 指针是一个变量,用于存储另一个变量的内存地址。
- 指针必须声明为特定的数据类型,以便指向正确类型的内存地址。
- 可以使用*运算符来访问指针所指向的变量的值。
- 可以使用&运算符获取变量的内存地址,也称为取地址运算符。
- 可以将指针赋值为另一个指针,也可以将指针赋值为一个变量的地址。
在C++中,数组和指针之间有很多联系。例如,可以使用指针来操作数组元素,也可以使用指针来传递数组作为函数参数。另外,可以使用指针算术运算来遍历数组中的元素。例如,指针加一会使指针指向下一个元素。
二、数组
2.1、数组的声明
在C++中,数组的声明形式为:数据类型 数组名[数组长度]。其中,数组长度必须是一个常量表达式。例如:
int arr[10]; // 声明一个包含10个整数的数组double scores[5]; // 声明一个包含5个双精度浮点数的数组char name[20]; // 声明一个包含20个字符的数组
2.2、数组的初始化
在C++中,可以在声明数组的同时对数组进行初始化。数组初始化可以通过以下方式进行:
int arr[5] = {1, 2, 3, 4, 5}; // 使用花括号初始化数组int scores[5] = {0}; // 将数组中所有元素初始化为0
如果在声明数组时未进行初始化,则数组中的元素将被默认初始化为0(对于基本数据类型)或空(对于字符串类型)。
2.3、数组的访问
在C++中,可以使用下标运算符[]来访问数组中的元素。下标从0开始,依次递增。例如:
int arr[5] = {1, 2, 3, 4, 5};
cout << arr[0] << endl; // 输出数组的第一个元素,即1
cout << arr[3] << endl; // 输出数组的第四个元素,即4
2.4、多维数组
C++中支持多维数组的定义,例如:
int matrix[3][4]; // 定义一个3行4列的二维数组int cube[2][3][4]; // 定义一个2层3行4列的三维数组
多维数组可以通过多重下标运算符[]来访问。例如:
int matrix[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
cout << matrix[1][2] << endl; // 输出第2行第3列的元素,即7
2.5、数组作为函数参数
在C++中,可以将数组作为函数参数传递。当将数组作为函数参数传递时,实际上是将数组的地址传递给函数。因此,在函数中对数组的修改会影响到原始数组。
例如:
void printArray(int arr[], int len) { // 将数组作为函数参数传递for(int i = 0; i < len; i++) {cout << arr[i] << " ";}cout << endl;
}int main() {int arr[5] = {1, 2, 3, 4, 5};printArray(arr, 5); // 调用函数打印数组return 0;
}
数组做为函数参数时,建议把数组的大小也一起传入。
三、指针
3.1、指针的声明
在C++中,指针是一种特殊的变量类型,它用于存储另一个变量的地址。指针的声明形式为:数据类型* 指针变量名。例如:
int* p; // 声明一个指向整数的指针变量double* q; // 声明一个指向双精度浮点数的指针变量
3.2、指针的赋值
可以将指针赋值为另一个变量的地址,例如:
int x = 10;int* p = &x; // 将指针p指向变量x的地址
3.3、指针的访问
可以使用*运算符来访问指针所指向的变量的值。例如:
int x = 10;
int* p = &x;
cout << *p << endl; // 输出指针p所指向的变量x的值,即10
3.4、指针运算
指针可以进行算术运算,例如指针加法、指针减法、指针自增、指针自减等。指针运算的结果是一个指向新地址的指针。例如:
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr; // 将指针p指向数组arr的第一个元素
cout << *p << endl; // 输出数组arr的第一个元素,即1
p++; // 将指针p指向数组arr的第二个元素
cout << *p << endl; // 输出数组arr的第二个元素,即2
3.5、指针数组和数组指针
指针数组是一个数组,其元素均为指针类型。例如:
int x = 10;
int y = 20;
int* arr[2] = {&x, &y}; // 定义一个包含两个指向整数的指针数组
cout << *arr[0] << endl; // 输出指针数组的第一个元素,即x的值,即10
指针数组的应用场景很多,例如可以用来存储多个字符串的地址、存储多个函数指针等。
数组指针是一个指向数组的指针。例如:
int a[3] = {1, 2, 3};
int (*p)[3] = &a;
数组指针的应用场景也很多,例如可以用来传递多维数组的指针、动态分配多维数组等。
需要注意的是,指针数组和数组指针虽然组合了指针和数组的特性,但它们本身是不同的类型。指针数组是一个数组,数组中的每个元素都是指针,而数组指针是一个指针,指向一个数组。在使用指针数组和数组指针时,需要根据具体的应用场景选择合适的类型。
3.6、二级指针
二级指针是指一个指向指针的指针。例如:
int x = 10;
int* p = &x;
int** pp = &p; // 定义一个二级指针,指向指针p
cout << **pp << endl; // 输出指针pp所指向的变量x的值,即10
四、数组和指针的关系
数组名在表达式中会被转换为一个指向数组第一个元素的指针。例如:
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr; // 将指针p指向数组arr的第一个元素
cout << *p << endl; // 输出数组arr的第一个元素,即1
可以使用指针访问数组的元素,例如:
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr; // 将指针p指向数组arr的第一个元素
for (int i = 0; i < 5; i++) {cout << *(p+i) << " "; // 使用指针访问数组的元素
}
cout << endl;
还可以将指针作为函数参数传递,以便在函数中操作数组。例如:
void printArray(int* p, int len) {for (int i = 0; i < len; i++) {cout << *(p+i) << " ";}cout << endl;
}int main() {int arr[5] = {1, 2, 3, 4, 5};printArray(arr, 5); // 调用函数打印数组return 0;
}
五、常量指针和指针常量
在C++中,可以将指针声明为常量指针或指针常量,以限制指针的操作。
常量指针是指指针所指向的变量是常量,即不能通过指针修改变量的值。例如:
int x = 10;const int* p = &x; // 声明一个常量指针,指向变量x*p = 20; // 编译错误,不能通过指针p修改变量x的值
指针常量是指指针本身是常量,即不能修改指针指向的变量。例如:
int x = 10;int* const p = &x; // 声明一个指针常量,指向变量xint y = 20;p = &y; // 编译错误,不能修改指针p的值
六、空指针和野指针
空指针是指不指向任何有效地址的指针。在C++中,可以使用nullprt关键字来表示空指针。例如:
int* p = nullptr; // 声明一个空指针
野指针是指指向未知、未初始化或已经释放的内存的指针。使用野指针会导致程序崩溃或产生未知的结果,因此应该避免使用野指针。
七、动态分配内存和释放内存
动态内存分配是一种在程序运行时动态地分配内存的方式,可以使用new运算符来动态地分配内存。使用new运算符可以返回一个指向分配的内存空间的指针,从而可以通过指针来访问动态分配的内存。例如:
int* p = new int; // 动态分配一个整型变量的内存
*p = 10; // 修改指针指向的变量的值
cout << *p << endl; // 输出
动态分配的内存可以使用delete运算符来释放,例如:
int* p = new int; // 动态分配一个整型变量的内存
*p = 10;
delete p; // 释放动态分配的内存
使用动态内存分配时,要注意避免内存泄漏,即在动态分配内存后没有及时释放。此外,在使用动态内存分配时,要注意分配的内存大小,避免内存溢出。可以使用sizeof运算符来获取变量或数据类型的大小,例如:
int* p = new int[10]; // 动态分配一个包含10个整型变量的数组的内存
cout << sizeof(int) * 10 << endl; // 输出动态分配的内存的大小
delete[] p; // 释放动态分配的内存
在动态分配内存时,要注意使用delete[]运算符来释放数组的内存,而不是使用delete运算符。
八、指针与函数
指针可以作为函数参数,从而可以在函数内部修改指针指向的变量的值,或者在函数内部通过指针来操作变量。指针作为函数参数传递时,可以传递指向单个变量的指针,也可以传递指向数组的指针。
传递指向单个变量的指针的语法如下:
void func(int* ptr) {*ptr = 10; // 修改指针指向的变量的值
}int main() {int a = 5;int* p = &a; // 定义指向变量a的指针cout << a << endl; // 输出变量a的值func(p); // 将指向变量a的指针传递给函数cout << a << endl; // 输出变量a的新值return 0;
}
传递指向数组的指针的语法如下:
void func(int* ptr, int length) {for (int i = 0; i < length; i++) {*(ptr+i) = i+1; // 修改指针指向的数组元素的值}
}int main() {int arr[5] = {0}; // 定义一个包含5个整型变量的数组int* p = arr; // 定义指向数组arr的指针for (int i = 0; i < 5; i++) {cout << *(p+i) << " "; // 输出数组元素的初始值}cout << endl;func(p, 5); // 将指向数组arr的指针和数组长度传递给函数for (int i = 0; i < 5; i++) {cout << *(p+i) << " "; // 输出数组元素的新值}cout << endl;return 0;
}
需要注意的是,在使用指针作为函数参数时,要注意指针指向的变量或数组的生命周期,避免在函数外部使用已经被释放的指针。同时,指针作为函数参数时,要注意不要在函数内部改变指针的指向,否则可能导致程序出现未知的结果。
九、指针与字符串
在C++中,字符串可以用字符数组来表示,也可以用string类来表示。无论用何种方式表示字符串,都可以使用指针来访问字符串。
用字符数组表示字符串时,字符串实际上是一个以null字符('\0')结尾的字符数组。例如:
char str[] = "hello world"; // 定义一个包含字符串"hello world"的字符数组
char* p = str; // 定义指向字符数组str的指针
cout << p << endl; // 输出字符串"hello world"
用string类表示字符串时,可以使用string类的成员函数c_str()来获取一个以null字符结尾的字符数组,从而可以使用指针来访问字符串。例如:
string str = "hello world"; // 定义一个string对象,存储字符串"hello world"
const char* p = str.c_str(); // 获取字符串的以null字符结尾的字符数组
cout << p << endl; // 输出字符串"hello world"
在访问字符串时,可以使用指针的下标运算符或指针算术运算符来访问字符串中的单个字符。例如:
char str[] = "hello world";
char* p = str;
cout << *(p+6) << endl; // 输出字符"w"
cout << p[6] << endl; // 输出字符"w"
需要注意的是,在使用指针访问字符串时,要保证字符串以null字符结尾,否则可能导致程序出现未知的结果。此外,在使用指针访问字符串时,要注意字符串的长度,避免越界访问。可以使用标准库函数strlen()来获取字符串的长度。例如:
char str[] = "hello world";
cout << strlen(str) << endl; // 输出字符串的长度(不包括null字符)
十、指针与函数
指针可以作为函数的参数或返回值,从而实现对函数外部变量的修改或传递复杂的数据结构。在函数中,可以使用指针访问函数外部的变量或数据结构,也可以使用指针来传递函数内部的变量或数据结构到函数外部。
将指针作为函数参数时,需要使用指针类型来声明函数参数,例如:
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}int a = 10, b = 20;
swap(&a, &b); // 通过指针交换a和b的值
cout << a << " " << b << endl; // 输出"20 10"
在函数中,可以使用指针来访问函数外部的变量或数据结构,例如:
void func(int* p) {*p = 10; // 修改函数外部变量的值
}int a = 0;
func(&a); // 修改a的值为10
cout << a << endl; // 输出"10"
指针也可以作为函数的返回值,从而实现对函数内部变量的传递到函数外部。在函数中,可以使用关键字return返回指针类型的值。例如:
int* func() {int* p = new int;*p = 10;return p; // 返回动态分配的整型变量的指针
}int* p = func(); // 获取指向动态分配的整型变量的指针
cout << *p << endl; // 输出"10"
delete p; // 释放动态分配的内存
需要注意的是,在使用指针作为函数参数或返回值时,要避免空指针或野指针的问题,即指针没有指向有效的内存空间或指向未知的内存空间。可以使用NULL或nullptr来表示空指针,例如:
int* p = NULL; // 声明一个空指针
if (p == NULL) {cout << "p is a null pointer." << endl;
}
C++11引入了新的关键字nullptr来表示空指针,nullptr的类型是nullptr_t。使用nullptr可以避免与整型变量的混淆。
指针还可以用于实现动态数据结构,例如链表、树等。在动态数据结构中,指针可以指向同一数据结构中的其他节点或子节点,从而实现对数据结构的高效操作。例如,下面是一个简单的链表实现:
struct Node {int data;Node* next;
};Node* createList(int n) {Node* head = NULL;for (int i = 0; i < n; i++) {Node* node = new Node;node->data = i;node->next = head;head = node;}return head;
}void printList(Node* head) {for (Node* node = head; node != NULL; node = node->next) {cout << node->data << " ";}cout << endl;
}int main() {Node* head = createList(10); // 创建包含10个节点的链表printList(head); // 输出链表的值return 0;
}
以上代码中,createList函数创建一个包含n个节点的链表,并返回链表的头节点。printList函数输出链表的值。在链表中,每个节点都包含一个整型变量data和一个指向下一个节点的指针next。通过指针,可以实现对链表的高效遍历和操作。
相关文章:

C++基础知识【5】数组和指针
目录 一、概述 数组 指针 二、数组 2.1、数组的声明 2.2、数组的初始化 2.3、数组的访问 2.4、多维数组 2.5、数组作为函数参数 三、指针 3.1、指针的声明 3.2、指针的赋值 3.3、指针的访问 3.4、指针运算 3.5、指针数组和数组指针 3.6、二级指针 四、数组和指…...

Vim使用操作命令笔记
Vim使用操作命令笔记在普通模式下,输入 : help tutor 就可以进入vim的教学 在 terminal 中输入 vim 文件名 就可以打开文件 vim有两种模式 normal mode (普通模式)→ 指令操作 insert mode (输入模式&…...

【论文阅读】Robust Multi-Instance Learning with Stable Instances
1、摘要与引言 以往的MIL算法遵循i.i.d假设:训练样本与测试样本都分别来自于同一分布中,而这一假设往往与现实应用中有所出入。研究人员通过计算训练样本与测试样本之间的密度比对训练样本进行加权,以解决分布变化带来的问题。 分布的变化发…...

洛谷 P5116 [USACO18DEC]Mixing Milk B
题目链接:P5116 [USACO18DEC]Mixing Milk B - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 农业,尤其是生产牛奶,是一个竞争激烈的行业。Farmer John 发现如果他不在牛奶生产工艺上有所创新,他的乳制品生意可能就会受…...

华为OD机试 - 最左侧冗余覆盖子串(C 语言解题)【独家】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 使用说明本期题目:最左侧冗…...

实验7 图像水印
本次实验大部分素材来源于山大王成优老师的讲义以及冈萨雷斯(MATLAB版),仅作个人学习笔记使用,禁止用作商业目的。 文章目录一、实验目的二、实验例题1. 数字图像水印技术2. 可见水印的嵌入3. 不可见脆弱水印4. 不可见鲁棒水印一、…...

如何实现大文件断点续传、秒传
大家先来了解一下几个概念: 「文件分块」:将大文件拆分成小文件,将小文件上传\下载,最后再将小文件组装成大文件; 「断点续传」:在文件分块的基础上,将每个小文件采用单独的线程进行上传\下载&…...

备战蓝桥python——完全平方数
完全平方数 链接: 完全平方数 暴力解法: n int(input()) for i in range(1, n1):if(((i*n)**0.5)%10.0):print(i)break运用数论相关知识求解 任意一个正整数都可以被分解成若干个质数乘积的形式,例如 :2022∗5120 \ 2^{2}*5^{1}\,20 22∗51 由此…...

WebRTC中的NAT穿透
NAT简介 我们知道,WebRTC会按照内网、P2P、中转的顺序来尝试连接。在大部分的情况下,实际是使用P2P或者中转的。这里P2P的场景主要使用的技术就是NAT穿透。 我们先简单了解下NAT。NAT在真实网络中是常见的,它的出现一是为了解决ipv4地址不够…...

SpringCloud-高级篇(一)
目录: (1)初识Sentinel-雪崩问题的解决方案 (2)服务保护Sentinel和Hystrix对比 (3)Sentinel初始-安转控制台 (4)整合微服务和Sentinel 微服务高级篇 (1&…...

电脑自动重启是什么原因?详细解说
案例:电脑自动重启是什么原因? “一台用了一年的电脑,最近使用,每天都会一两次莫名其妙自动重启,看了电脑错误日志,看不懂什么意思,一直找不到答案。有没有高手知道怎么解决这个问题的。” 当…...

2023美国大学生数学建模竞赛E题思路
problem 背景: 光污染用于描述过度或不良使用人造光。我们称之为光污染的一些现象包括光侵入、过度照明和光杂波。在大城市,太阳落山后,这些现象最容易在天空中看到:然而,它们也可能发生在更偏远的地区。 光污染会改变我们对夜空…...

蓝桥杯三月刷题 第五天
文章目录💥前言😉解题报告💥数的分解🤔一、思路:😎二、代码:💥前言 上午没写,下午写了会被朋友拉出去耍,被冷风吹到了,而且被他坑了,根本没有玩骑…...

Echarts 水波图实现
开发的项目中需要实现这样一个水波图,例如下图在echarts官网中找了很久没找到,后面是在Echarts社区中找到的,实现了大部分的样式,但是还有一些数据的展示没有实现。水波图的数值展示是默认整数百分比,我的需求是需要保…...

逻辑优化基础-shannon decomposition
1. 简介 在逻辑综合中,香农分解(Shannon decomposition)是一种常用的布尔函数分解方法。它将一个布尔函数分解为两个子函数的和,其中每个子函数包含一个布尔变量的取反和非取反的部分。 具体来说,假设对于一个布尔函…...

Java中线程池的创建与使用
前言:默认线程池的弊端在线程池应用中,参考阿里巴巴java开发规范:线程池不允许使用Executors去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确&…...

关于HashMap与OkHttp的使用
写了一个okhttp的post请求方法,添加参数很麻烦,需要封装: //post请求public static void sendOkHttpRequestPost(String address , Callback callback) {OkHttpClient client new OkHttpClient();// 创建表单参数RequestBodyRequestBody fo…...

华为OD机试 - 单词倒序(C 语言解题)【独家】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 使用说明本期题目:单词倒序…...

搭建Samba服务器
搭建Samba服务器 文章目录搭建Samba服务器samba安装安装命令配置-ubuntu侧为samba服务器创建一个共享目录share创建使用该共享文件夹的账号修改samba服务器配置文件重启samba服务windows创建映射1.点击映射网络驱动器2.输入Ubuntu中的ip地址及其用户信息3.输入用户信息及其密码…...

Matlab进阶绘图第5期—风玫瑰图(WindRose)
风玫瑰图(Wind rose diagram)是一种特殊的极坐标堆叠图/统计直方图,其能够直观地表示某个地区一段时期内风向、风速的发生频率。 风玫瑰图在建筑规划、环保、风力发电、消防、石油站设计、海洋气候分析等领域都有重要作用,所以在一些顶级期刊中也能够看…...

【SQL开发实战技巧】系列(二十四):数仓报表场景☞通过执行计划详解”行转列”,”列转行”是如何实现的
系列文章目录 【SQL开发实战技巧】系列(一):关于SQL不得不说的那些事 【SQL开发实战技巧】系列(二):简单单表查询 【SQL开发实战技巧】系列(三):SQL排序的那些事 【SQL开发实战技巧…...

XILINX AXI总线学习
AXI介绍什么是AXI?AXI(高级可扩展接口),是ARM AMBA的一部分;AMBA:高级微控制器总线架构;是1996年首次引入的一组微控制器总线;开放的片内互联的总线标准,能在多主机设计中实现多个控…...

2022CCPC女生赛(补题)(A,C,E,G,H,I)
迟了好久的补题,,现在真想把当时赛时的我拉出来捶一拳排序大致按照题目难度。C. 测量学思路:直接循环遍历判断即可,注意角度要和2π取个最小值。AC Code:#include <bits/stdc.h>typedef long long ll; const int…...

【Nginx】Nginx的安装配置
环境说明系统:Centos 7一、编译安装Nginx官网下载地址nginx: download#安装依赖 [rootnginx nginx-1.22.1]# yum install gcc pcre pcre-devel zlib zlib-devel -y #从官网下载Nginx安装包,并进行解压、编译、安装 [rootnginx ~]# wget https://nginx.or…...

数学小课堂:统计时有效地筛选数据
文章目录引言I 被爆冷门的原因II 统计时有效地筛选数据2.1 统计数据的常见问题2.2 大数据的特征2.3 有效筛选数据的原则引言 在博弈论中很多结果有发生的概率,而概率这件事只是估计出来的,并不准确。因此,一旦加入博弈的选手多了之后&#x…...

MySQL安装优化
hello,大家好,我是小鱼 本文主要通过针对 MySQL Server(mysqld)相关实现机制的分析,得到一些相应的优化建议。主要 涉及 MySQL 的安装以及相关参数设置的优化,但不包括 mysqld 之外的比如存储引擎相关的参…...

RocketMQ系列开篇
RocketMQ系列开篇 今天开始学习RocketMQ相关系列源码。我会带着自己的目的去学习源码。所以不会像一般的技术博客一样,写一个完整的流程,介绍每一步干了啥。而是提出一个问题,然后去看代码里面是怎么实现的。说明一下,本次系列我…...

logback无法删除太久远的日志文件?logback删除日志文件源码分析
logback无法删除太久远的日志文件?logback删除日志文件源码分析 最近发现logback配置滚动日志,但是本地日志文件甚至还有2年前的日志文件,服务器是却是正常的! 网上搜索了一波没有发现,只找到说不能删除太久远的旧日志…...

【MyBatis-Plus】基于@Version注解的乐观锁实现
引入mybatis-plus依赖,注意这里的版本要求 since 3.4.0;(3.4.1,3.4.2已测) 3.2.0肯定是不支持的,无法引入MybatisPlusInterceptor; 乐观锁 当要更新一条记录的时候,希望这条记录没有被别人更新…...