目录
1. 指针概念
地址和变量
指针
2. 指针的声明与初始化
3. 指针的使用
指针访问
指针的运算
指针与数组
指针与函数
4. 编程实例
5. 指针的常见陷阱与防御
6. 总结
1. 指针概念
地址和变量
在C语言中,地址和变量是两个基本但非常重要的概念。
1. 变量是指程序中用于存储数据值的命名位置。每个变量都有一个特定的数据类型,这决定了它可以存储的数据种类(如整数、字符、浮点数等)以及需要分配多少内存空间。创建变量时,系统会在计算机的内存中为该变量分配一块空间,并将你选择的名字与这块空间关联起来。之后,你可以通过这个名字来访问或修改这块内存中的数据。
int age = 25;
这里age就是一个变量,它的类型是int,表示它存储的是一个整数值。在这个例子中,age被初始化为25。
2. 地址指的是内存中的具体位置。当你声明一个变量时,系统会为其分配一定的内存空间,而这个空间的起始位置就是该变量的地址。可以通过取地址运算符&来获取变量的地址。地址是一个非常关键的概念,尤其是在处理指针时。指针是一种特殊的变量,它存储的是另一个变量或函数的地址,而不是直接存储数据值。
int age = 25;
printf("变量age的地址: %p", (void*)&age);
这段代码打印出变量age的地址。注意这里的(void*)是为了确保地址以正确的格式输出;在C语言中,使用%p格式说明符来打印指针(地址)。
变量是你用来命名并存储数据的地方。每个变量都有其特定的数据类型,并占用一定数量的内存。地址则是这些变量在内存中的具体位置。
指针
-
指针变量:指针变量是用来存储内存地址的变量。指针变量的类型决定了它所指向的数据类型。
-
地址运算符
&
:用于获取变量的内存地址。 -
间接寻址运算符
*
:用于访问指针所指向的内存地址中的数据。
指针指向的内存区域中的数据称为指针的目标。
2. 指针的声明与初始化
指针的声明格式如下:
数据类型 *指针变量名;
例如:
int *p; // 声明一个指向int类型数据的指针p
指针的初始化通常是通过将某个变量的地址赋值给指针:
int a = 10;
int *p = &a; // 将变量a的地址赋值给指针p
3. 指针的使用
指针访问
通过指针可以访问和修改它所指向的变量的值:
int a = 10;
int *p = &a;
*p = 20; // 通过指针p修改a的值为20
指针的运算
指针可以进行加减运算,运算的结果是指针向前或向后移动若干个元素的位置。指针的运算通常用于数组操作。
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // p指向数组的第一个元素
p++; // p现在指向数组的第二个元素
指针与数组
数组名本身就是一个指针,它指向数组的第一个元素。数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址。
通过指针可以遍历数组:
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
for (int i = 0; i < 5; i++) {printf("%d ", *(p + i)); // 输出数组的每个元素
}
假定指针变量px的地址值等于数组指针x(即指针变量px指向数组的首元数),则:x[i] 、*(px+i)、*(x+i) 和px[i]具有完全相同的功能:访问数组第i+1个数组元素。
指针与函数
指针可以作为函数的参数,使得函数能够修改实参的值:
void increment(int *p) {(*p)++; // 通过指针修改实参的值
}int main() {int a = 10;increment(&a); // 传递a的地址printf("%d", a); // 输出11return 0;
}
4. 编程实例
实例1:使用指针交换两个变量的值
#include <stdio.h>void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}int main() {int x = 10, y = 20;printf("Before swap: x = %d, y = %d\n", x, y);swap(&x, &y);printf("After swap: x = %d, y = %d\n", x, y);return 0;
}
实例2:使用指针遍历数组
#include <stdio.h>int main() {int arr[5] = {1, 2, 3, 4, 5};int *p = arr;for (int i = 0; i < 5; i++) {printf("%d ", *(p + i));}return 0;
}
实例3:使用指针作为函数参数修改数组元素
#include <stdio.h>void modifyArray(int *arr, int size) {for (int i = 0; i < size; i++) {arr[i] *= 2; // 将数组中的每个元素乘以2}
}int main() {int arr[5] = {1, 2, 3, 4, 5};modifyArray(arr, 5);for (int i = 0; i < 5; i++) {printf("%d ", arr[i]);}return 0;
}
实例4:函数指针实现排序策略
#include <stdio.h>// 比较函数类型
typedef int (*CompareFunc)(int, int);void bubbleSort(int arr[], int n, CompareFunc compare) {for (int i=0; i<n-1; i++) {for (int j=0; j<n-i-1; j++) {if (compare(arr[j], arr[j+1]) > 0) {int temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}
}int ascending(int a, int b) { return a - b; } // 升序
int descending(int a, int b) { return b - a; } // 降序int main() {int arr[] = {5, 2, 8, 1, 3};int n = sizeof(arr)/sizeof(arr[0]);// 使用升序排序bubbleSort(arr, n, ascending);for (int i=0; i<n; i++) printf("%d ", arr[i]); // 输出1 2 3 5 8// 使用降序排序bubbleSort(arr, n, descending);printf("\n");for (int i=0; i<n; i++) printf("%d ", arr[i]); // 输出8 5 3 2 1return 0;
}
5. 指针的常见陷阱与防御
-
野指针(Dangling Pointer)
-
指向已释放内存的指针,访问会导致未定义行为。
-
防御:释放后立即置空指针。
int *p = (int*)malloc(sizeof(int)); free(p); p = NULL; // 防止野指针
-
-
内存泄漏
-
分配内存后未释放。
-
防御:确保每个
malloc
都有对应的free
。
-
-
越界访问
-
指针操作超出合法内存范围。
-
防御:严格检查指针偏移量。
-
6. 总结
指针是C语言中非常强大的工具,它允许直接操作内存地址,提供了灵活的数据访问和操作方式。通过指针,可以实现高效的数据处理、动态内存管理以及复杂的数据结构(如链表、树等)。然而,指针的使用也需要谨慎,因为不当的指针操作可能导致程序崩溃或内存泄漏等问题。,每一个字节单元,都有一个编号,称为地址