共计 2633 个字符,预计需要花费 7 分钟才能阅读完成。
提醒:本文最后更新于 2023-10-07 17:34,文中所关联的信息可能已发生改变,请知悉!
什么是指针?
指针是一种变量,它存储了另一个变量的内存地址。我们可以通过指针来访问或修改它所指向的变量的值。指针的类型决定了它所指向的变量的类型,以及它可以进行的操作。
例如,下面的代码定义了一个整型变量 a
,一个整型指针p
,并将p
初始化为 a
的地址:
int a = 10; // 定义一个整型变量 a,并赋值为 10
int *p = &a; // 定义一个整型指针 p,并将其初始化为 a 的地址
在这里,我们使用了 &
运算符来获取变量的地址,也就是它在内存中的位置。我们也可以使用 *
运算符来获取指针所指向的变量的值,这称为 解引用 指针。例如:
printf("The value of a is %d\n", a); // 输出 a 的值
printf("The address of a is %p\n", &a); // 输出 a 的地址
printf("The value of p is %p\n", p); // 输出 p 的值,即 a 的地址
printf("The value of *p is %d\n", *p); // 输出 * p 的值,即 a 的值
输出结果可能是:
The value of a is 10
The address of a is 0x7ffeedb8f9ec
The value of p is 0x7ffeedb8f9ec
The value of *p is 10
从输出结果可以看出,p
和 &a
是相同的,都是 a
的地址;而 *p
和a
是相同的,都是10
。
指针有什么用?
指针有很多用途,其中最常见的有以下几个:
- 动态内存分配 :我们可以使用指针来分配和释放内存空间,从而实现动态地创建和销毁数据结构。例如,我们可以使用函数
malloc()
来分配一块内存,并返回一个指向该内存的指针;我们也可以使用函数free()
来释放一个指向已分配内存的指针。例如:
int *arr = malloc(10 * sizeof(int)); // 分配一个大小为 10 个整数的数组,并返回一个指向该数组首元素的指针
if (arr == NULL) {printf("Memory allocation failed\n"); // 如果分配失败,则输出错误信息
exit(1); // 并退出程序
}
for (int i = 0; i < 10; i++) {arr[i] = i + 1; // 使用下标运算符 [] 来访问和修改数组元素,等价于 *(arr + i) = i + 1;
}
for (int i = 0; i < 10; i++) {printf("%d ", arr[i]); // 输出数组元素
}
printf("\n");
free(arr); // 释放数组占用的内存空间
输出结果是:
1 2 3 4 5 6 7 8 9 10
- 传递参数:我们可以使用指针来传递参数给函数,从而实现对参数进行修改或节省复制参数所需的时间和空间。例如,我们可以使用指针来实现交换两个变量的值的函数:
void swap(int *x, int *y) { // 定义一个交换两个整型变量的值的函数,参数为两个整型指针
int temp = *x; // 定义一个临时变量,并将 * x 的值赋给它
*x = *y; // 将 * y 的值赋给 *x
*y = temp; // 将临时变量的值赋给 *y
}
int main() {
int a = 10, b = 20; // 定义两个整型变量 a 和 b,并赋值为 10 和 20
printf("Before swap: a = %d, b = %d\n", a, b); // 输出交换前的值
swap(&a, &b); // 调用交换函数,传入 a 和 b 的地址
printf("After swap: a = %d, b = %d\n", a, b); // 输出交换后的值
return 0;
}
输出结果是:
Before swap: a = 10, b = 20
After swap: a = 20, b = 10
- 实现数据结构:我们可以使用指针来实现一些复杂的数据结构,如链表、树、图等。这些数据结构通常由一些节点组成,每个节点包含一些数据和指向其他节点的指针。例如,我们可以使用指针来实现一个单向链表:
struct node { // 定义一个链表节点的结构体,包含一个整型数据和一个指向下一个节点的指针
int data;
struct node *next;
};
struct node *head = NULL; // 定义一个指向链表头节点的指针,并初始化为 NULL
void insert(int x) { // 定义一个在链表头部插入一个节点的函数,参数为要插入的数据
struct node *new_node = malloc(sizeof(struct node)); // 分配一个新节点的内存空间,并返回一个指向该节点的指针
if (new_node == NULL) {printf("Memory allocation failed\n"); // 如果分配失败,则输出错误信息
exit(1); // 并退出程序
}
new_node->data = x; // 将要插入的数据赋给新节点的数据域
new_node->next = head; // 将新节点的指针域指向原来的头节点
head = new_node; // 将头指针指向新节点
}
void print() { // 定义一个打印链表所有元素的函数
struct node *cur = head; // 定义一个当前节点的指针,并初始化为头节点
while (cur != NULL) { // 当当前节点不为空时,循环执行以下操作
printf("%d ", cur->data); // 输出当前节点的数据
cur = cur->next; // 将当前节点移动到下一个节点
}
printf("\n");
}
int main() {insert(10); // 在链表头部插入 10
insert(20); // 在链表头部插入 20
insert(30); // 在链表头部插入 30
print(); // 打印链表所有元素
return 0;
}
输出结果是:
30 20 10
指针有什么注意事项?
指针虽然强大,但也有一些需要注意的地方,否则可能会导致程序出错或崩溃。以下是一些常见的注意事项:
- 不要使用未初始化或无效的指针:如果一个指针没有被初始化或已经被释放,那么它可能会指向任意的内存地址,这可能会导致访问或修改不属于程序的内存空间,从而引发错误或崩溃。因此,在使用指针之前,我们应该确保它已经被正确地初始化或分配,并在释放后将其置为 NULL。
- 不要越过数组或字符串的边界 :如果一个指针是用来访问数组或字符串中的元素,那么我们应该确保它不越过数组或字符串的边界,否则可能会访问到无效的内存地址,从而引发错误或崩溃。这是一个常见的编程错误,也称为 缓冲区溢出 。为了避免这种错误,我们应该在使用指针时检查它是否在合法的范围内,或者使用一些安全的函数来操作数组或字符串,如
strncpy()
、strncat()
、snprintf()
等。
正文完