一、指针的基本概念和用法
在 C 语言中,指针是一个变量,用来存储一个地址,这个地址指向内存中的另外一个变量。可以通过指针来访问或修改这个变量的值。
使用 * 运算符可以访问指针所指向的变量的值。
使用 & 运算符可以获取变量的地址。
下面是一个使用指针访问变量地址和值的示例代码:
#include <stdio.h> int main() { int num = 42; // 定义一个整数变量 num 并初始化为 42 int *ptr = # // 定义一个指针变量 ptr 并将其初始化为 num 的地址 printf("num 的地址是:%p\n", &num); printf("ptr 所指向的地址是:%p\n", ptr); printf("ptr 所指向的值是:%d\n", *ptr); return 0; }
输出结果如下:
在上面的示例代码中,我们首先定义了一个整型变量 num
并初始化赋值为 42
,然后定义一个整型指针变量 ptr
,并使用 &
运算符取 num
变量的地址初始化 ptr
指向 num
变量的地址。接着我们通过 printf
函数输出 num
变量的地址和 ptr
指向的地址,最后输出通过 *ptr
来访问 ptr
指向的变量 num
的值。
二、指针运算
2.1 指针的自增和自减运算
自增和自减运算仅适用于指向数组、字符串或分配内存的指针。
自增运算符
++
让指针指向下一个内存单元,即指针变量的值加上所指向的变量类型占用的字节数。自减运算符
--
让指针指向前一个内存单元,即指针变量的值减去所指向的变量类型占用的字节数。
示例代码:
#include <stdio.h> int main() { int arr[5] = {1, 2, 3, 4, 5}; int *p = &arr[0]; // 指向数组 arr 的第一个元素的指针 printf("当前指针 p 的值为: %p\n", p); p++; // 指针自增操作 printf("执行 p++ 后,p 指向的值为: %d\n", *p); p--; // 指针自减操作 printf("执行 p-- 后,p 指向的值为: %d\n", *p); return 0; }
输出结果如下:
这段代码中,我们定义了一个包含 5 个整型元素的数组 arr
,并定义一个指向数组第一个元素的指针 p
。通过执行 p++
和 p--
操作,指针 p
的值发生了变化,指向了数组中不同的元素,从而可以访问数组中不同的数据。
2.2 指针的自增和自减运算
加法和减法运算可以应用于指向数组或分配内存的指针。如果对指针进行加法或减法运算,会根据指针所指向类型的字节大小来确定位移量。
指针的加法运算(+)将指针的值增加多个字节数。
指针的减法运算(-)将指针的值减去多个字节数。
示例代码:
#include <stdio.h> int main() { int arr[] = {1, 2, 3, 4, 5}; int *p = &arr[2]; // 指向数组 arr 的第三个元素的指针 printf("当前指针 p 的值为: %p\n", p); p = p + 1; // 指针加法操作 printf("执行 p + 1 后,p 指向的值为: %d\n", *p); p = p - 2; // 指针减法操作 printf("执行 p - 2 后,p 指向的值为: %d\n", *p); return 0; }
输出结果如下:
在这个示例中,我们定义了一个数组 arr
,并将指针 p
初始化为指向数组 arr
的第三个元素。通过执行 p + 1
和 p - 2
操作,指针 p
的值发生了相应的变化,通过访问指针 p
所指向的值,我们可以看到指针指向了数组中不同的元素。
三、数组和指针
数组名本身就是指向数组头部的指针。
例如,如果有一个 int 类型的数组 arr
,那么在代码中使用 arr
和 &arr[0]
是等效的,在内存中它们所指向的都是数组的第一个元素。
示例代码:
#include <stdio.h> int main() { int arr[] = {1, 2, 3, 4, 5}; int *p = arr; // 指向数组 arr 的第一个元素的指针 printf("通过数组名访问数组的第一个元素:%d\n", arr[0]); printf("通过指针访问数组的第二个元素:%d\n", *(p + 1)); printf("通过指针访问数组的第三个元素:%d\n", p[2]); return 0; }
输出结果如下:
四、指针和函数
4.1 在函数中使用指针作为参数和返回值
4.1.1 使用指针作为函数参数
可以通过将指针作为函数的参数来传递数据。这样可以实现在函数内部修改传递给函数的变量的值,因为指针传递的是变量的地址。
示例代码:
#include <stdio.h> void changeValue(int *ptr) { *ptr = 10; // 修改指针指向的变量的值 } int main() { int num = 5; printf("函数调用前的值:%d\n", num); changeValue(&num); // 传递变量的地址 printf("函数调用后的值:%d\n", num); return 0; }
输出结果如下:
在这个示例中,我们在 changeValue
函数中使用了一个指针参数 ptr
,通过 *ptr
修改了变量 num
的值。在 main
函数中将 num
的地址传递给 changeValue
函数来实现修改 num
的值。
4.1.2 使用指针作为函数返回值
可以使用指针作为函数的返回值,以便将函数内部的计算结果返回给调用函数。
示例代码:
#include <stdio.h> #include <stdlib.h> // 包含 malloc 和 free 函数 int* createArray(int size) { int *arr = (int*)malloc(size * sizeof(int)); // 动态分配内存,并强制类型转换 for (int i = 0; i < size; i++) { arr[i] = i + 1; } return arr; } int main() { int size = 5; int *array = createArray(size); // 函数返回指针 for (int i = 0; i < size; i++) { printf("%d ", array[i]); } free(array); // 释放内存 return 0; }
输出结果如下:
在这个示例中,createArray
函数根据传入的参数 size
动态分配了一个整型数组,并将数组的首地址作为指针返回给调用函数。在 main
函数中,我们通过调用 createArray
函数来获取数组的地址,并使用指针 array
访问和输出数组的元素。最后,释放动态分配的内存。
4.2 指针参数的传值和传引用特性
4.2.1 指针参数的传值特性
当将指针作为函数参数进行传递时,实际上传递的是指针变量的值(即地址)。这意味着函数内部对指针本身的修改不会影响到调用该函数的代码。
示例代码:
#include <stdio.h> void changePointer(int *ptr) { int dummy = 10; ptr = &dummy; // 修改指针的值 } int main() { int num = 5; int *ptr = # printf("函数调用前的指针:%p\n", ptr); changePointer(ptr); printf("函数调用后的指针:%p\n", ptr); return 0; }
输出结果如下:
在这个示例中,changePointer
函数尝试将指针 ptr
指向一个新的变量 dummy
,但是在函数外部输出指针 ptr
的值时并未改变,验证了指针参数的传值特性。
4.2.2 指针参数的传引用特性
通过使用指针的指针或者指针的引用,可以实现对指针的引用传递,从而在函数内部对指针进行修改可以影响到调用该函数的代码。
示例代码:
#include <stdio.h> void changePointer(int **ptr) { int dummy = 10; *ptr = &dummy; // 修改指针指向的地址 } int main() { int num = 5; int *ptr = # printf("函数调用前的指针:%p\n", ptr); changePointer(&ptr); printf("函数调用后的指针:%p\n", ptr); return 0; }
输出结果如下:
在这个示例中,我们将指针 ptr
的地址传递给 changePointer
函数,并在函数内部修改了指针 ptr
的值为新的变量 dummy
的地址。在函数外部输出指针 ptr
的值时发现已经改变,验证了指针参数的传引用特性。