* 符号的两个主要的含义
在声明变量时,* 表示该变量是一个指针。
例如: int *ptr; 表示 ptr 是一个指向 int 类型数据的指针。
在使用指针时,* 用于间接访问指针所指向的内存地址中的值。
例如: *ptr = 10; 表示将值 10 赋给 ptr 所指向的内存地址。
当出现两个 ** 时,它表示指向指针的指针,也就是二级指针。
例如: int **pptr; 表示 pptr 是一个指向 int * 类型数据的指针,也就是一个指向指针的指针。
二级指针的使用场景包括:
在函数中传递指针参数,并在函数内部修改指针的值。
处理动态分配的二维数组。
实现链表、树等复杂的数据结构。
例子
c
int main() {
int x = 10;
int *p = &x; // p 是一个指向 int 类型数据的指针
int **pp = &p; // pp 是一个指向 int* 类型数据的指针,也就是一个二级指针
printf("x = %d\n", x); // 输出: x = 10
printf("*p = %d\n", *p); // 输出: *p = 10
printf("**pp = %d\n", **pp); // 输出: **pp = 10
**pp = 20;
printf("x = %d\n", x); // 输出: x = 20
return 0;
}
在这个例子中:
我们首先声明了一个整型变量 x 并赋值为 10。
然后声明了一个指向 int 型变量的指针 p,并让它指向 x。
接下来声明了一个二级指针 pp,它指向指针 p。
现在来看这一行代码:
c
**pp = 20;
这行代码的执行过程如下:
首先通过 pp 找到它所指向的内存地址,也就是 p 的地址。
然后通过 *pp(解引用 pp)得到 p 的值,也就是 x 的地址。
最后通过 **pp(二次解引用 pp)得到 x 的值,并将其赋值为 20。
所以,当我们执行 **pp = 20; 时,实际上是在修改 x 的值,而不是 p 或 pp 本身。
这就是为什么在最后的 printf("x = %d\n", x); 语句中,x 的值变成了 20。
小结
总结一下,当我们使用二级指针 **pp 时,实际上是通过两次间接寻址,最终操作的是 x 这个变量本身。因此,修改 **pp 的值就相当于直接修改了 x 的值。
& 的主要意义
在 C 语言中,& 符号用于获取元素的内存地址操作,它的主要用途包括:
获取变量的地址
例如: int x = 10; int *p = &x; 这里 &x 表示获取变量 x 的内存地址,并将其赋值给指针变量 p。
作为函数参数,实现按引用传递
例如: void swap(int *a, int *b) { ... } 函数中的 int *a 和 int *b 就是指针参数,可以使用 & 操作符将变量的地址传递给它们,从而在函数内部修改变量的值。
取数组元素的地址
例如: int arr[] = {1, 2, 3}; int *p = &arr[0]; 这里 &arr[0] 表示获取数组 arr 的第一个元素的地址,并赋值给指针 p。
获取结构体成员的地址
例如: struct Person { int age; char name[20]; }; struct Person p; int *age_ptr = &p.age; 这里 &p.age 表示获取结构体变量 p 的 age 成员的地址。
code实例
c
#include <stdio.h>
#include <stdlib.h>
// 动态数组结构体
typedef struct {
int *data; // 指向数据的指针
int size; // 数组大小
} DynamicArray;
// 初始化动态数组
DynamicArray* init_dynamic_array() {
DynamicArray *arr = (DynamicArray*)malloc(sizeof(DynamicArray));
arr->data = NULL;
arr->size = 0;
return arr;
}
// 向动态数组中添加元素
void add_element(DynamicArray *arr, int value) {
// 分配新的内存空间
arr->data = (int*)realloc(arr->data, (arr->size + 1) * sizeof(int));
// 将新元素添加到数组末尾
arr->data[arr->size] = value;
arr->size++;
}
// 获取动态数组中的元素
int get_element(DynamicArray *arr, int index) {
// 检查索引是否越界
if (index < 0 || index >= arr->size) {
printf("Error: Index out of bounds.\n");
return 0;
}
// 返回指定索引的元素
return arr->data[index];
}
int main() {
// 初始化动态数组
DynamicArray *arr = init_dynamic_array();
// 向动态数组中添加元素
add_element(arr, 10);
add_element(arr, 20);
add_element(arr, 30);
// 获取动态数组中的元素
printf("Element at index 0: %d\n", get_element(arr, 0));
printf("Element at index 1: %d\n", get_element(arr, 1));
printf("Element at index 2: %d\n", get_element(arr, 2));
// 释放动态数组占用的内存
free(arr->data);
free(arr);
return 0;
}
在这个例子中,我们定义了一个 DynamicArray 结构体,它包含一个指向数据的指针 data 和一个表示数组大小的整型变量 size。
我们实现了三个函数:
docs
init_dynamic_array():初始化一个动态数组,分配内存并设置初始大小为 0。
add_element():向动态数组中添加一个新元素。它首先使用 realloc() 函数扩展数组的内存空间,然后将新元素添加到数组末尾。
get_element():获取动态数组中指定索引的元素。它会先检查索引是否越界,然后返回相应的元素值。
在 main() 函数中,我们演示了如何使用这些函数来创建、添加元素和获取元素。
值得注意的是,我们使用了指针 data 来动态分配和管理数组的内存空间。在最后,我们需要手动释放 data 指针和 DynamicArray 结构体占用的内存,以避免内存泄漏。
小结
总的来说,& 操作符是 C 语言中一个非常重要的符号,它允许我们获取变量、数组元素、结构体成员等的内存地址,从而可以使用指针来操作它们。这在很多场景下非常有用,特别是在涉及内存管理、函数参数传递等方面。