指针专题教学讲义
简介
指针是C++语言中非常重要的一种数据类型,它能够让我们实现动态内存分配,并进行高效的数据操作。在本教学讲义中,我们将会学习到指针的基本定义和初始化、指针的高级用法、指针的运算、指针与数组、指针与函数、指针与结构体、指针与类以及指针的应用。
指针的定义和初始化
指针是一个特殊的变量,它存储的是内存地址,而不是实际的数据值。在C++中,声明指针变量时需要在类型前加上一个*号,表示这是一个指针变量。例如,如果我们要定义一个指向整型数据的指针变量,可以使用以下语句:
int* ptr;
这里的ptr是一个指向整型数据的指针变量。我们也可以使用nullptr来初始化指针:
int* ptr = nullptr;
这样做可以避免指针不确定的地址值出现。
我们还可以使用&符号来获取一个变量的地址,并将其存储到指针变量中。如下示例:
int x = 10;
int* ptr = &x;
这里的ptr指向了变量x的地址。
指针的高级用法
1. void指针
void指针是一种万能指针,它可以指向任何类型的数据。void指针在指针运算、类型转换等操作中具有灵活的应用。例如:
void* ptr;
int x = 10;
float y = 3.14;
ptr = &x;
ptr = &y;
这里的ptr既可以指向整型变量x的地址,也可以指向浮点型变量y的地址。
2. 常量指针
常量指针是指向常量的指针,在定义时需要在类型前加上const关键字。常量指针指向的数据不能被修改,但指针本身的值可以被修改。例如:
const int* ptr;
int x = 10;
int y = 20;
ptr = &x;
// *ptr = 30; // 错误,不能修改指针指向的数据
ptr = &y; // 正确
这里的ptr是一个指向常量整型的指针变量,指向的数据不能被修改。
3. 指针常量
指针常量是指针类型的常量,在定义时需要在指针变量名前加上
const关键字。指针常量的指针值不能被修改,但指向的数据可以被修改。例如:
int x = 10;
int y = 20;
int* const ptr = &x;
*ptr = 30; // 正确,可以修改指针指向的数据
// ptr = &y; // 错误,不能修改指针的值
这里的ptr是一个指向整型数据的指针常量,指针值不能被修改。
4. const指针常量
const指针常量是指针类型的常量,同时指向的数据也是常量,即指针和指针指向的数据都不能被修改。在定义时需要在类型前加上const关键字,并在变量名前加上*号,表示这是一个指针变量。例如:
const int* const ptr = &x;
// *ptr = 30; // 错误,不能修改指针指向的数据
// ptr = &y; // 错误,不能修改指针的值
这里的ptr是一个指向常量整型数据的指针常量,指针和指针指向的数据都不能被修改。
指针的运算
指针可以进行递增和递减运算,也可以进行指针之间的加减运算。递增或递减指针时,需要注意指针指向的数据类型,因为指针的移动单位是根据指向的数据类型来计算的。例如:
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = &arr[2];
ptr++; // 移动到arr[3]
ptr--; // 移动回arr[2]
float* fptr = (float*) &arr[0];
fptr++; // 移动到arr[1],需要进行类型转换
指针之间的加减运算会根据指针指向的数据类型计算它们之间的距离,例如:
int arr[5] = {1, 2, 3, 4, 5};
int* ptr1 = &arr[0];
int* ptr2 = &arr[3];
int diff = ptr2 - ptr1; // diff为3,即两个指针相差3个元素
指针与数组
指针和数组具有紧密联系,数组名本身就是一个指向数组首地址的指针。我们可以使用指针来访问数组中每个元素的值,也可以使用指针进行数组的遍历操作。例如:
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr;
for(int i = 0; i < 5; i++){
cout << *ptr << " ";
ptr++;
}
这里的ptr指向了数组arr的首地址,并通过递增指针的方式遍历了整个数组。
指针与函数
指针和函数也有着紧密的联系。我们可以使用指针作为函数参数,让函数能够直接修改指针所指向的数据。
1. 指针作为函数参数
指针作为函数参数时,我们可以将指针传递给函数,并让函数通过指针修改原始数据。
void increment(int* ptr){
(*ptr)++;
}
int x = 10;
increment(&x);
cout << x << endl; // 输出 11
这里的increment函数接受一个指向整型数据的指针,通过解引用指针修改了原始数据。
2. 返回指针的函数
函数可以返回指针,让我们能够动态分配内存并将指针返回给函数调用者使用。注意返回的指针必须在堆上分配空间,否则在函数调用结束时会被自动释放。
int* createArray(int n){
int* arr = new int[n];
for(int i = 0; i < n; i++){
arr[i] = i + 1;
}
return arr;
}
int* ptr = createArray(5);
for(int i = 0; i < 5; i++){
cout << ptr[i] << " ";
}
delete[] ptr; // 记得释放内存
这里的createArray函数动态分配了一个长度为n的整型数组,并将数组首地址作为返回值返回给调用者。
指针与结构体
指针可以用来访问结构体中的数据成员,也可以在结构体之间传递指针。
struct Student {
string name;
int age;
};
Student* s = new Student;
s->name = "Tom";
s->age = 20;
cout << s->name << " " << s->age << endl; // 输出 Tom 20
这里的s是一个指向结构体Student的指针变量,通过箭头符号->访问结构体中的数据成员。
指针与类
指针和类也有着紧密的联系,在C++中,类的对象是由new运算符分配的内存,返回值是指向该内存的指针。例如:
class Person {
public:
string name;
int age;
};
Person* p = new Person;
p->name = "Jack";
p->age = 30;
cout << p->name << " " << p->age << endl; // 输出 Jack 30
这里的p指向了一个Person类的实例,通过箭头符号->访问类中的数据成员。
指针的应用
指针在C++中有着广泛的应用,包括动态内存分配、链表、树等数据结构的实现等等。以下是一些指针的常见应用示例:
1. 动态内存分配
指针可以用来动态分配内存,避免在程序中使用过多的栈空间。通过new和delete运算符,我们可以在堆上分配和释放内存。例如:
int* arr = new int[5]; // 在堆上分配长度为5的整型数组
delete[] arr; // 释放内存
2. 字符串
在C++中,字符串通常是由char类型的指针表示的,我们可以通过指针来访问字符串中的每个字符。
char* str = "Hello";
cout << str << endl; // 输出 Hello
3. 函数指针
指针还可以用来表示函数的地址,这被称为函数指针。我们可以使用函数指针来动态选择调用哪个函数。
void func1(){
cout << "This is function 1" << endl;
}
void func2(){
cout << "This is function 2" << endl;
}
void chooseFunction(bool flag){
void (*funcPtr)();
if(flag){
funcPtr = &func1;
} else {
funcPtr = &func2;
}
(*funcPtr)();
}
chooseFunction(true); // 输出 This is function 1
chooseFunction(false); // 输出 This is function 2
这里的chooseFunction函数接受一个bool类型的参数,表示是否调用func1函数。如果为true,则调用func1函数,否则调用func2函数。
总结
本教学讲义介绍了C++语言中指针的基本定义和初始化、指针的高级用法、指针的运算、指针与数组、指针与函数、指针与结构体、指针与类以及指针的应用等内容。指针作为C++语言中非常重要的一种数据类型,具有着广泛的应用场景。在使用指针时,需要注意指针的类型、指针指向的数据类型和指针的值等重要因素。