11.04学习
一、指针数组的相关知识点
1. 定义指针数组: 指针数组的声明需要指定数组中指针的类型以及数组的大小。例如:
int* ptrArray[10]; // 一个包含10个int类型指针的数组
2. 初始化指针数组: 指针数组的元素可以被初始化为指向特定变量的指针,或者为 NULL (表示空指针)。例如:
int a = 10, b = 20, c = 30;
int* ptrArray[3] = {&a, &b, &c};
3. 访问指针数组中的元素: 可以通过数组索引来访问指针数组中的元素,然后通过解引用操作符 * 来访问指针指向的值。例如:
printf("%d\n", *ptrArray[0]); // 输出a的值
4. 指针数组作为函数参数: 指针数组可以作为参数传递给函数,这在处理动态分配的数组时非常有用。例如:
void printArray(int* arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", *arr[i]);
}
printf("\n");
}
5. 动态分配指针数组: 指针数组的元素可以指向动态分配的内存。例如:
int* ptrArray[10];
for (int i = 0; i < 10; i++) {
ptrArray[i] = malloc(sizeof(int));
*ptrArray[i] = i;
}
6. 释放动态分配的内存: 如果指针数组的元素指向了动态分配的内存,那么在不再需要时,需要逐个释放这些内存。例如:
for (int i = 0; i < 10; i++) {
free(ptrArray[i]);
}
7. 指针数组和函数指针: 指针数组也可以包含函数指针。这在实现回调函数或事件驱动编程时非常有用。例如:
void (*funcArray[2])(void) = {func1, func2};
funcArray[0](); // 调用func1
8. 指针数组和多维数组: 指针数组可以用于模拟多维数组,尤其是在动态分配内存时。例如,可以创建一个指针数组,其中每个元素都是指向另一个数组的指针,从而模拟二维数组。
9. 指针数组和字符串数组: 指针数组经常用于处理字符串数组,其中每个元素都是一个指向 char 的指针,指向一个字符串。
10. 指针数组和结构体: 指针数组也可以包含指向结构体的指针,这在处理复杂的数据结构时非常有用。
二、多级指针的相关知识点
C语言中的多级指针,通常指的是指向指针的指针,这种指针可以用于多种高级编程技术,比如动态内存管理、函数指针、链表和树等数据结构。
1. 定义多级指针: 多级指针的定义需要连续使用多个星号(*)来表示指针的级别。例如,指向指针的指针(二级指针)可以这样定义:
int **p;
2. 初始化多级指针: 多级指针需要逐级初始化。首先初始化最内层的指针,然后是外层的指针,依此类推。
int var = 10;
int *p = &var;
int **pp = &p;
3. 访问指向的值: 要访问多级指针指向的值,需要逐级解引用。例如,通过 **pp 可以访问 var 的值。
printf("%d\n", **pp); // 输出10
4. 动态分配内存: 多级指针常用于动态分配多维数组。例如,动态分配一个二维数组:
int **arr = malloc(n * sizeof(int*));
for (int i = 0; i < n; i++) {
arr[i] = malloc(m * sizeof(int));
}
5. 释放动态分配的内存: 与动态分配内存相对应,释放内存时也需要逐级释放。
for (int i = 0; i < n; i++) {
free(arr[i]);
}
free(arr);
6. 函数参数: 多级指针可以作为函数参数传递,用于修改指针指向的值或者操作复杂的数据结构。
void updateValue(int ***p, int index, int value) {
(*(*p))[index] = value;
}
7. 指针数组和多级指针: 指针数组中的每个元素都是指针,因此可以通过多级指针来访问和操作这些指针数组中的元素。
8. 函数指针和多级指针: 多级指针可以指向函数指针,这在实现回调函数时非常有用。
void (*func)(int);
void (*func2)(int) = &someFunction;
func = func2;
9. 结构体和多级指针: 多级指针可以指向结构体指针,用于操作复杂的数据结构,如链表、树等。
struct Node {
int data;
struct Node *next;
};
struct Node **head;
10. 指针的指针和数组: 多级指针可以用于创建指针的数组,其中每个元素都是指向某个类型指针的指针。
11. 指针运算: 多级指针的运算需要特别注意,因为涉及到指针的地址和指针指向的值。
12. 指针的安全性: 使用多级指针时,需要特别注意指针的安全性,避免野指针、内存泄漏和越界访问等问题。
13. 指针和const: 当使用多级指针时,还可以使用 const 关键字来限制指针的修改,例如 const int **p 表示指向常量整数的指针的指针。
三、值传递和地址传递的相关知识点
在C语言中,函数参数的传递方式主要有两种:值传递(pass by value)和地址传递(pass by address)。
值传递(Pass by Value)
1. 基本机制:
值传递是指在调用函数时,将实际参数的值复制一份传递给函数的形式参数。
函数内部对参数的任何修改都不会影响到实际参数。
2. 参数复制:
当参数被传递到函数中时,会创建新的存储空间来存储这些值。
这意味着多个函数可以同时拥有同名局部变量,它们互不影响。
3. 性能考虑:
对于基本数据类型(如int、float等),值传递是高效的,因为复制这些值的成本很低。
对于大型数据结构(如结构体或数组),值传递可能会导致性能问题,因为需要复制整个数据结构。
4. 使用场景:
当你不希望函数内部修改参数值时,应该使用值传递。
当参数是基本数据类型或者小的数据结构时,值传递是合适的。
地址传递(Pass by Address)
1. 基本机制:
地址传递是指在调用函数时,将实际参数的地址传递给函数的形式参数。
函数内部可以通过这个地址来访问和修改实际参数。
2. 指针参数:
地址传递通常通过指针实现,即函数的形式参数是一个指针。
调用函数时,需要传递变量的地址(使用 & 操作符)。
3. 修改实际参数:
由于函数内部操作的是实际参数的地址,所以任何对参数的修改都会反映到实际参数上。
4. 性能优势:
对于大型数据结构,地址传递可以避免复制整个数据结构,从而提高效率。
5. 使用场景:
当你需要函数内部修改参数值,或者操作大型数据结构时,地址传递是合适的。
6. 指针和数组:
在C语言中,数组名在大多数情况下会被解释为指向数组首元素的指针,因此可以通过地址传递来操作数组。
7. 指针的指针:
地址传递也可以用于多级指针,允许函数修改指针本身的值。
8. const修饰符:
使用 const 关键字可以防止函数修改指针指向的值,即使参数是通过地址传递的。
总结
值传递适用于小数据和不需要在函数内部修改数据的场景。
地址传递适用于需要在函数内部修改数据,或者处理大数据结构以避免复制开销的场景。