在C语言中指针与数组是密不可分的核心知识点二者既有区别又有紧密联系。掌握它们的关联与用法能极大提升代码的灵活性和执行效率也是理解更复杂数据结构的基础。本文将围绕数组与指针的核心知识点结合具体程序示例逐点拆解讲解帮助大家吃透这一重点内容。一、数组名作为指针常量在C语言中数组名本质上是一个指针常量它指向数组的首元素地址且这个地址不可修改不能对数组名进行自增、自减等操作。需要注意的是数组名并非在所有场景下都等价于指针常量比如使用sizeof运算符计算数组大小时数组名表示整个数组而非首元素地址。下面通过程序段直观理解数组名的特性#include stdio.hint main() {int arr[5] {10, 20, 30, 40, 50};// 数组名arr指向数组首元素地址等价于arr[0]printf(数组首元素地址%p\n, arr);printf(数组首元素地址%p\n, arr[0]);// 利用数组名访问首元素的值printf(数组首元素值%d\n, *arr); // 等价于arr[0]// 错误示范数组名是指针常量不能修改其指向// arr; // 编译报错lvalue required as increment operand// sizeof(arr)计算整个数组的字节数而非指针大小printf(数组总字节数%zu\n, sizeof(arr)); // 输出205个int每个4字节printf(指针大小%zu\n, sizeof(int*)); // 输出864位系统return 0;}代码解析程序中arr作为数组名直接打印时输出首元素地址与arr[0]完全一致通过*arr可以访问首元素的值等价于arr[0]。但arr是常量无法进行自增操作否则会编译报错。此外sizeof(arr)计算的是整个数组的大小而sizeof(int*)计算的是指针的大小这也体现了数组名与普通指针的区别。二、指针访问数组元素既然数组名是指向首元素的指针那么我们可以定义一个指针变量指向数组首元素再通过指针的移动来访问数组的各个元素。指针访问数组元素有两种常用方式通过指针解引用访问或通过指针下标访问与数组下标用法一致。程序段示例#include stdio.hint main() {int arr[5] {1, 2, 3, 4, 5};int *ptr arr; // 指针ptr指向数组首元素等价于int *ptr arr[0]// 方式1指针解引用访问通过指针移动printf(指针解引用访问数组元素\n);for (int i 0; i 5; i) {printf(arr[%d] %d\n, i, *(ptr i)); // ptri指向第i个元素}// 方式2指针下标访问与数组下标用法完全一致printf(\n指针下标访问数组元素\n);for (int i 0; i 5; i) {printf(ptr[%d] %d\n, i, ptr[i]); // 等价于*(ptr i)}// 指针自增移动访问元素printf(\n指针自增访问数组元素\n);ptr arr; // 重置指针指向首元素while (ptr arr 5) {printf(%d , *ptr);ptr; // 指针自增指向后一个元素}return 0;}代码解析定义指针ptr指向数组arr的首元素后*(ptr i)等价于arr[i]表示访问数组的第i个元素同时ptr[i]的用法与arr[i]完全一致本质上也是对*(ptr i)的简写。通过指针自增ptr可以让指针依次指向数组的后续元素实现遍历。三、指针与数组的等价性由前面的知识点可知数组名本质是指针常量指针变量可以指向数组首元素因此在很多场景下指针与数组是等价的即arr[i]与*(arr i)等价ptr[i]与*(ptr i)等价。这种等价性是C语言中指针与数组关联的核心也是灵活使用二者的关键。需要注意的是等价性仅体现在“访问元素”的场景中数组名是指针常量不可修改而指针变量可以自由修改指向这是二者的核心区别。程序段示例验证等价性#include stdio.hint main() {int arr[4] {10, 20, 30, 40};int *ptr arr;// 验证arr[i]与*(arr i)等价printf(arr[0] %d, *(arr 0) %d\n, arr[0], *(arr 0));printf(arr[2] %d, *(arr 2) %d\n, arr[2], *(arr 2));// 验证ptr[i]与*(ptr i)等价printf(ptr[1] %d, *(ptr 1) %d\n, ptr[1], *(ptr 1));printf(ptr[3] %d, *(ptr 3) %d\n, ptr[3], *(ptr 3));// 区别指针变量可修改数组名不可修改ptr; // 指针指向arr[1]合法printf(\nptr自增后*ptr %d\n, *ptr); // 输出20// arr; // 编译报错数组名是常量不能自增return 0;}代码解析程序中多次验证了数组与指针的等价性arr[i]和*(arr i)、ptr[i]和*(ptr i)的输出结果完全一致。但指针变量ptr可以通过自增修改指向而数组名arr无法修改这也是二者最关键的区别需要重点区分。四、多维数组与指针多维数组以二维数组为例与指针的关联比一维数组更复杂核心是理解“多维数组的数组名指向什么”以及“指向数组的指针”与“指针数组”的区别。下面先明确二维数组的本质二维数组可以看作是“数组的数组”例如int arr[3][4]表示一个包含3个元素的数组每个元素又是一个包含4个int类型元素的一维数组。4.1 指向数组的指针int (*ptr)[n]指向数组的指针又称数组指针本质是一个指针它指向一个包含n个指定类型元素的一维数组。其声明格式为类型 (*指针名)[数组长度]括号不可省略否则会变成指针数组后面会讲解。数组指针常用于指向多维数组的行因为多维数组的数组名指向其第一行即第一个一维数组。程序段示例#include stdio.hint main() {// 二维数组3行4列看作3个包含4个int的一维数组int arr[3][4] {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};// 定义数组指针指向包含4个int元素的一维数组int (*ptr)[4] arr; // arr指向二维数组的第一行与ptr的类型匹配// 访问二维数组的元素printf(访问二维数组元素\n);// 遍历3行for (int i 0; i 3; i) {// 遍历每行的4个元素*(ptr i)指向第i行等价于arr[i]for (int j 0; j 4; j) {printf(%d , *(*(ptr i) j)); // 等价于arr[i][j]}printf(\n);}// 验证ptr的指向ptr指向第一行ptr1指向第二行printf(\nptr指向的地址%p\n, ptr);printf(ptr1指向的地址%p\n, ptr1); // 地址相差164个int每个4字节return 0;}代码解析int (*ptr)[4]声明了一个数组指针ptr它指向一个包含4个int元素的一维数组。二维数组名arr指向第一行即第一个一维数组因此可以将arr赋值给ptr。通过*(ptr i)可以获取第i行的地址再通过*(*(ptr i) j)可以访问第i行第j列的元素等价于arr[i][j]。ptr1的地址比ptr大164个int元素的总字节数说明ptr的步长是它所指向的数组的长度。4.2 指针数组int *ptr[n]指针数组本质是一个数组这个数组的每个元素都是一个指针指向指定类型的数据。其声明格式为类型 *指针数组名[数组长度]括号可以省略因为[]的优先级高于*与数组指针的声明有明显区别。指针数组常用于存储多个字符串字符串本质是字符数组字符串名是字符指针或多个独立变量的地址。程序段示例存储字符串代码解析char *strArr[3]声明了一个指针数组它包含3个元素每个元素都是char*类型的指针分别指向3个字符串的首地址。通过strArr[i]可以直接访问对应的字符串因为printf的%s格式符会自动根据指针地址打印字符串。指针数组的元素是指针变量因此可以修改其指向比如将strArr[0]重新指向newStr对应的字符串。4.3 数组指针与指针数组的区别数组指针与指针数组是极易混淆的两个概念核心区别在于“本质不同”数组指针是指针指向数组指针数组是数组元素是指针。下面通过表格和程序段进一步明确二者的区别。对比项数组指针int (*ptr)[n]指针数组int *ptr[n]本质指针指向一个包含n个int的数组数组包含n个int*类型的指针声明格式括号不可省略(*ptr)强调是指针括号可省略ptr[n]强调是数组sizeof计算结果指针大小64位系统为8数组总大小n * 指针大小用途指向多维数组的行处理多维数组存储多个指针如字符串、变量地址程序段示例对比二者区别代码解析数组指针arrPtr的sizeof结果是8指针大小而指针数组ptrArr的sizeof结果是162个指针每个8字节这直接体现了二者的本质区别。虽然二者都能访问二维数组的元素但arrPtr是通过修改指针指向来切换行而ptrArr是通过修改数组元素指针的指向来切换行逻辑完全不同。五、字符数组与字符串指针在C语言中字符串的存储有两种常用方式字符数组和字符串指针。二者都能实现字符串的存储和访问但在内存分配、可修改性等方面存在明显区别实际使用中需根据需求选择。5.1 字符数组存储字符串字符数组是最常用的字符串存储方式本质是一个char类型的数组字符串的每个字符依次存储在数组的各个元素中末尾自动添加结束符\0标志字符串结束。字符数组可以直接初始化也可以通过输入赋值其内容可以修改。程序段示例代码解析str1是字符数组初始化时直接赋值字符串编译器会自动在末尾添加\0str2手动初始化时必须添加\0否则printf无法判断字符串结束位置。字符数组的内容可以修改比如将str1[0]改为h这是字符数组的重要特性。5.2 字符串指针存储字符串字符串指针是一个char*类型的指针它指向字符串常量的首地址字符串常量存储在只读数据区。字符串指针的初始化是将字符串常量的地址赋值给指针而非将字符串内容复制到指针中因此其指向的内容不可修改修改会导致程序崩溃。程序段示例代码解析char *str Hello C中str是指针指向字符串常量Hello C的首地址该字符串存储在只读数据区因此不能修改其内容如str[0] h会报错。但可以修改指针str的指向让它指向另一个字符串常量如newStr指向的字符串这是字符串指针与字符数组的核心区别。5.3 字符数组与字符串指针的区别为了更清晰地区分二者整理核心区别如下对比项字符数组char str[]字符串指针char *str本质数组存储字符串的所有字符含\0指针指向字符串常量的首地址内存分配存储在栈区局部变量或全局区可修改指向只读数据区内容不可修改初始化方式可直接赋值字符串或逐个赋值字符需加\0只能赋值字符串常量地址不能逐个赋值字符sizeof结果数组总大小含\0指针大小64位系统为8指针与数组是C语言的核心二者的关联与区别可概括为数组名是指针常量指针可访问数组元素二者在访问元素时等价但数组名不可修改而指针可修改多维数组的核心是理解数组指针与指针数组的本质区别一个是指针一个是数组字符数组与字符串指针都能存储字符串但前者可修改内容后者只能修改指向。掌握这些知识点的关键是多写程序、多调试理解每段代码的内存分配和执行逻辑才能真正灵活运用指针与数组写出高效、规范的C语言代码。
3、C语言指针专题:指针与数组
在C语言中指针与数组是密不可分的核心知识点二者既有区别又有紧密联系。掌握它们的关联与用法能极大提升代码的灵活性和执行效率也是理解更复杂数据结构的基础。本文将围绕数组与指针的核心知识点结合具体程序示例逐点拆解讲解帮助大家吃透这一重点内容。一、数组名作为指针常量在C语言中数组名本质上是一个指针常量它指向数组的首元素地址且这个地址不可修改不能对数组名进行自增、自减等操作。需要注意的是数组名并非在所有场景下都等价于指针常量比如使用sizeof运算符计算数组大小时数组名表示整个数组而非首元素地址。下面通过程序段直观理解数组名的特性#include stdio.hint main() {int arr[5] {10, 20, 30, 40, 50};// 数组名arr指向数组首元素地址等价于arr[0]printf(数组首元素地址%p\n, arr);printf(数组首元素地址%p\n, arr[0]);// 利用数组名访问首元素的值printf(数组首元素值%d\n, *arr); // 等价于arr[0]// 错误示范数组名是指针常量不能修改其指向// arr; // 编译报错lvalue required as increment operand// sizeof(arr)计算整个数组的字节数而非指针大小printf(数组总字节数%zu\n, sizeof(arr)); // 输出205个int每个4字节printf(指针大小%zu\n, sizeof(int*)); // 输出864位系统return 0;}代码解析程序中arr作为数组名直接打印时输出首元素地址与arr[0]完全一致通过*arr可以访问首元素的值等价于arr[0]。但arr是常量无法进行自增操作否则会编译报错。此外sizeof(arr)计算的是整个数组的大小而sizeof(int*)计算的是指针的大小这也体现了数组名与普通指针的区别。二、指针访问数组元素既然数组名是指向首元素的指针那么我们可以定义一个指针变量指向数组首元素再通过指针的移动来访问数组的各个元素。指针访问数组元素有两种常用方式通过指针解引用访问或通过指针下标访问与数组下标用法一致。程序段示例#include stdio.hint main() {int arr[5] {1, 2, 3, 4, 5};int *ptr arr; // 指针ptr指向数组首元素等价于int *ptr arr[0]// 方式1指针解引用访问通过指针移动printf(指针解引用访问数组元素\n);for (int i 0; i 5; i) {printf(arr[%d] %d\n, i, *(ptr i)); // ptri指向第i个元素}// 方式2指针下标访问与数组下标用法完全一致printf(\n指针下标访问数组元素\n);for (int i 0; i 5; i) {printf(ptr[%d] %d\n, i, ptr[i]); // 等价于*(ptr i)}// 指针自增移动访问元素printf(\n指针自增访问数组元素\n);ptr arr; // 重置指针指向首元素while (ptr arr 5) {printf(%d , *ptr);ptr; // 指针自增指向后一个元素}return 0;}代码解析定义指针ptr指向数组arr的首元素后*(ptr i)等价于arr[i]表示访问数组的第i个元素同时ptr[i]的用法与arr[i]完全一致本质上也是对*(ptr i)的简写。通过指针自增ptr可以让指针依次指向数组的后续元素实现遍历。三、指针与数组的等价性由前面的知识点可知数组名本质是指针常量指针变量可以指向数组首元素因此在很多场景下指针与数组是等价的即arr[i]与*(arr i)等价ptr[i]与*(ptr i)等价。这种等价性是C语言中指针与数组关联的核心也是灵活使用二者的关键。需要注意的是等价性仅体现在“访问元素”的场景中数组名是指针常量不可修改而指针变量可以自由修改指向这是二者的核心区别。程序段示例验证等价性#include stdio.hint main() {int arr[4] {10, 20, 30, 40};int *ptr arr;// 验证arr[i]与*(arr i)等价printf(arr[0] %d, *(arr 0) %d\n, arr[0], *(arr 0));printf(arr[2] %d, *(arr 2) %d\n, arr[2], *(arr 2));// 验证ptr[i]与*(ptr i)等价printf(ptr[1] %d, *(ptr 1) %d\n, ptr[1], *(ptr 1));printf(ptr[3] %d, *(ptr 3) %d\n, ptr[3], *(ptr 3));// 区别指针变量可修改数组名不可修改ptr; // 指针指向arr[1]合法printf(\nptr自增后*ptr %d\n, *ptr); // 输出20// arr; // 编译报错数组名是常量不能自增return 0;}代码解析程序中多次验证了数组与指针的等价性arr[i]和*(arr i)、ptr[i]和*(ptr i)的输出结果完全一致。但指针变量ptr可以通过自增修改指向而数组名arr无法修改这也是二者最关键的区别需要重点区分。四、多维数组与指针多维数组以二维数组为例与指针的关联比一维数组更复杂核心是理解“多维数组的数组名指向什么”以及“指向数组的指针”与“指针数组”的区别。下面先明确二维数组的本质二维数组可以看作是“数组的数组”例如int arr[3][4]表示一个包含3个元素的数组每个元素又是一个包含4个int类型元素的一维数组。4.1 指向数组的指针int (*ptr)[n]指向数组的指针又称数组指针本质是一个指针它指向一个包含n个指定类型元素的一维数组。其声明格式为类型 (*指针名)[数组长度]括号不可省略否则会变成指针数组后面会讲解。数组指针常用于指向多维数组的行因为多维数组的数组名指向其第一行即第一个一维数组。程序段示例#include stdio.hint main() {// 二维数组3行4列看作3个包含4个int的一维数组int arr[3][4] {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};// 定义数组指针指向包含4个int元素的一维数组int (*ptr)[4] arr; // arr指向二维数组的第一行与ptr的类型匹配// 访问二维数组的元素printf(访问二维数组元素\n);// 遍历3行for (int i 0; i 3; i) {// 遍历每行的4个元素*(ptr i)指向第i行等价于arr[i]for (int j 0; j 4; j) {printf(%d , *(*(ptr i) j)); // 等价于arr[i][j]}printf(\n);}// 验证ptr的指向ptr指向第一行ptr1指向第二行printf(\nptr指向的地址%p\n, ptr);printf(ptr1指向的地址%p\n, ptr1); // 地址相差164个int每个4字节return 0;}代码解析int (*ptr)[4]声明了一个数组指针ptr它指向一个包含4个int元素的一维数组。二维数组名arr指向第一行即第一个一维数组因此可以将arr赋值给ptr。通过*(ptr i)可以获取第i行的地址再通过*(*(ptr i) j)可以访问第i行第j列的元素等价于arr[i][j]。ptr1的地址比ptr大164个int元素的总字节数说明ptr的步长是它所指向的数组的长度。4.2 指针数组int *ptr[n]指针数组本质是一个数组这个数组的每个元素都是一个指针指向指定类型的数据。其声明格式为类型 *指针数组名[数组长度]括号可以省略因为[]的优先级高于*与数组指针的声明有明显区别。指针数组常用于存储多个字符串字符串本质是字符数组字符串名是字符指针或多个独立变量的地址。程序段示例存储字符串代码解析char *strArr[3]声明了一个指针数组它包含3个元素每个元素都是char*类型的指针分别指向3个字符串的首地址。通过strArr[i]可以直接访问对应的字符串因为printf的%s格式符会自动根据指针地址打印字符串。指针数组的元素是指针变量因此可以修改其指向比如将strArr[0]重新指向newStr对应的字符串。4.3 数组指针与指针数组的区别数组指针与指针数组是极易混淆的两个概念核心区别在于“本质不同”数组指针是指针指向数组指针数组是数组元素是指针。下面通过表格和程序段进一步明确二者的区别。对比项数组指针int (*ptr)[n]指针数组int *ptr[n]本质指针指向一个包含n个int的数组数组包含n个int*类型的指针声明格式括号不可省略(*ptr)强调是指针括号可省略ptr[n]强调是数组sizeof计算结果指针大小64位系统为8数组总大小n * 指针大小用途指向多维数组的行处理多维数组存储多个指针如字符串、变量地址程序段示例对比二者区别代码解析数组指针arrPtr的sizeof结果是8指针大小而指针数组ptrArr的sizeof结果是162个指针每个8字节这直接体现了二者的本质区别。虽然二者都能访问二维数组的元素但arrPtr是通过修改指针指向来切换行而ptrArr是通过修改数组元素指针的指向来切换行逻辑完全不同。五、字符数组与字符串指针在C语言中字符串的存储有两种常用方式字符数组和字符串指针。二者都能实现字符串的存储和访问但在内存分配、可修改性等方面存在明显区别实际使用中需根据需求选择。5.1 字符数组存储字符串字符数组是最常用的字符串存储方式本质是一个char类型的数组字符串的每个字符依次存储在数组的各个元素中末尾自动添加结束符\0标志字符串结束。字符数组可以直接初始化也可以通过输入赋值其内容可以修改。程序段示例代码解析str1是字符数组初始化时直接赋值字符串编译器会自动在末尾添加\0str2手动初始化时必须添加\0否则printf无法判断字符串结束位置。字符数组的内容可以修改比如将str1[0]改为h这是字符数组的重要特性。5.2 字符串指针存储字符串字符串指针是一个char*类型的指针它指向字符串常量的首地址字符串常量存储在只读数据区。字符串指针的初始化是将字符串常量的地址赋值给指针而非将字符串内容复制到指针中因此其指向的内容不可修改修改会导致程序崩溃。程序段示例代码解析char *str Hello C中str是指针指向字符串常量Hello C的首地址该字符串存储在只读数据区因此不能修改其内容如str[0] h会报错。但可以修改指针str的指向让它指向另一个字符串常量如newStr指向的字符串这是字符串指针与字符数组的核心区别。5.3 字符数组与字符串指针的区别为了更清晰地区分二者整理核心区别如下对比项字符数组char str[]字符串指针char *str本质数组存储字符串的所有字符含\0指针指向字符串常量的首地址内存分配存储在栈区局部变量或全局区可修改指向只读数据区内容不可修改初始化方式可直接赋值字符串或逐个赋值字符需加\0只能赋值字符串常量地址不能逐个赋值字符sizeof结果数组总大小含\0指针大小64位系统为8指针与数组是C语言的核心二者的关联与区别可概括为数组名是指针常量指针可访问数组元素二者在访问元素时等价但数组名不可修改而指针可修改多维数组的核心是理解数组指针与指针数组的本质区别一个是指针一个是数组字符数组与字符串指针都能存储字符串但前者可修改内容后者只能修改指向。掌握这些知识点的关键是多写程序、多调试理解每段代码的内存分配和执行逻辑才能真正灵活运用指针与数组写出高效、规范的C语言代码。