目录
一、概述
数组
指针
二、数组
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; // 声明一个指针常量,指向变量x
int 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 = nullptr; // 声明一个空指针
if (p == nullptr) {
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 = nullptr;
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。通过指针,可以实现对链表的高效遍历和操作。