C语言基础第15天:从数组指针到指针函数
在 C 语言的学习旅程中,指针是一个绕不开的核心知识点,它既强大又容易让人混淆。本文将围绕 C 语言中的指针相关概念,包括数组指针、指针数组、字符数组与字符指针以及指针函数等内容进行详细讲解,并结合具体代码示例帮助大家更好地理解和运用这些知识。
数组指针
数组指针,顾名思义,是指向一个完整数组的指针。它存储的是单个指针,指向一个完整数组的首地址。
指向一维数组的指针
定义一个指向一维数组的指针,语法格式为数据类型 (*指针名)[数组长度]。例如,创建一个指向包含len个int元素的一维数组的指针p,可以写成int (*p)[len] = &arr,其中arr是被指向的一维数组。
通过数组指针遍历一维数组时,由于[]的优先级大于*,所以需要使用(*p)[i]的形式来访问数组中的元素。如以下代码所示:
int t_p1()
{// 创建一个一维数组int arr[] = {10,20,30,40,50};// 计算数组大小int len = sizeof(arr) / sizeof(arr[0]);// 创建一个数组指针指向一维数组arrint (*p)[len] = &arr;// 借助数组指针遍历数组for (int i = 0; i < len; i++){printf("%-4d", (*p)[i]);}printf("\n");
}
指向二维数组的指针
对于二维数组,有两种指向方式。一种是不推荐的int (*p)[][3] = &arr方式,另一种是推荐的用一维数组指针指向二维数组的首行,即int (*p1)[3] = arr。
在遍历二维数组时,可以通过多种方式访问元素,如p1[i][j]、(*(p1+i))[j]、*(p1[i]+j)以及*(*(p1+i)+j)等,这些方式都是等价的。例如:
int t_p2()
{// 创建一个二维数组int arr[][3] = {{10,20,30},{100,200,300},{1000,2000,3000}};// 获取行和列的容量int row_len = sizeof(arr) / sizeof(arr[0]);int col_len = sizeof(arr[0]) / sizeof(arr[0][0]);// 方式2,一维数组指针指向二维数组,本质上是一维数组指针指向二维数组的行(默认首行)推荐int (*p1)[3] = arr;for (int i = 0; i < row_len; i++){for (int j = 0; j < col_len; j++){printf("%-6d",p1[i][j]);printf("%-6d",(*(p1+i))[j]);printf("%-6d",*(p1[i]+j));// 列偏移printf("%-6d",*(*(p1+i)+j));}printf("\n");}
}
指针数组
指针数组本质是一个数组,只不过数组中的每一个元素都是指针。它的语法格式为数据类型 *数组名[容量]。
指针数组存储多个指针,每个元素指向不同的内存地址,每个指针元素独立分配内存,可能分散。在访问时,通过下标访问指针元素,再解引用,即*arr[i]。
以下是一个指针数组的示例代码:
int main(int argc,char *argv[])
{// 定义三个变量int a = 10, b = 20, c = 30;// 定义指针数组:先有指针,后有数组int *arr[3] = {&a, &b, &c};// 获取大小int len = sizeof(arr) / sizeof(arr[0]);// 遍历数组for (int i = 0; i < len; i++){printf("%-3d", *arr[i]);}printf("\n");return 0;
}
数组指针与指针数组的区别
对比项 | 指针数组 | 数组指针 |
定义 | 数组元素均为指针的数组 | 指向一个完整数组的指针 |
存储内容 | 存储多个指针,每个元素指向不同内存地址 | 存储单个指针,指向一个完整的数组(首地址) |
内存分配 | 每个指针元素独立分配内存,可能分散 | 指向的数组内存连续,指针本身存储数组首地址 |
语法示例 | int *arr[5](元素为 5 个 int * 指针) | int (*arr)[5](指向 5 个 int 的数组的指针) |
访问方式 | 通过下标访问指针元素,再解引用:*arr[i] | 先解引用指针得到数组,再访问元素:(*arr)[i] |
使用场景 | 管理多个独立指针(如字符串数组、动态结构体数组) | 操作多维数组(如传递二维数组的行指针) |
字符数组和字符指针
在 C 语言中,表示一个字符串有字符数组和字符指针两种方式。
字符数组
字符数组由元素组成,每个元素中存放一个字符。它在创建时会在内存中开辟内存空间,可存放字符数据。字符数组可以初始化,但不能整体赋值,只能对数组中的各个元素赋值。
例如:
void str_test1()
{// 定义一个字符串char str[] = "I LOVE YOU";// 数组名是一个常量,不支持赋值// str = "YUE QIAN"; // 编译报错,常量不支持修改,替代方案:strcpy(str,"YUE QIAN");printf("%s\n", str); // 数组在传参时,会被降级为指针
}
字符指针
字符指针是指向char类型的指针变量,它中存放的是地址。字符指针在创建时需要依赖于字符数组,它在内存开辟的空间中存放的是数组元素的地址。字符指针可以初始化,也可以赋值。
例如:
void str_test2()
{// 定义一个字符串char *str = "I LOVE YOU"; // 指针str指向一个字符串常量// 改变str的指向str = "YUE QIAN";printf("%s\n", str);
}
字符数组和字符指针的联系与区别
- 联系:字符数组和字符指针变量都能实现字符串的存储与运算。对于字符串中字符的存取,都可以用下标法或指针法。
- 区别:字符数组名是常量,其值不能改变;字符指针的值可以改变,即可以改变其指向。字符数组可以独立存在,而字符指针不能独立存在,它需要依赖于字符数组或字符串常量。
字符串作为形参
实参与形参都可以是字符数组,也可以实参用字符数组、形参用字符指针,还可以形参和实参都是字符指针(但在函数内部不能对字符串常量中的字符做修改)。
例如实参用字符数组,形参用字符指针的情况:
void fun(char *str, int len) {..}
void main()
{char str[] = "hello";int len = sizeof(str) / sizeof(str[0]);fun(str, len);
}
指针函数
指针函数本质上是函数,这个函数的返回值类型是指针。它的语法有两种写法:
// 写法1
返回类型* 函数名(形参列表)
{函数体;return 指针;
}
// 写法2
返回类型 *函数名(形参列表)
{函数体;return 指针;
}
需要注意的是,在函数中不要直接返回一个局部变量的地址,因为函数调用完毕后,随着栈帧的回收,变量空间会销毁,返回的地址就不明确,此时返回的指针叫做野指针。解决方案是给局部变量添加static,延长其生命周期。
以下是一个指针函数的示例,根据学号查找学生成绩:
float* search(float (*all)[4], int id)
{// 定义一个指针变量,用来接收查询到的学生的所有成绩float *pt;pt = *(all + id);// 行偏移return pt;
}
总结
指针是 C 语言中非常重要的概念,数组指针、指针数组、字符数组、字符指针和指针函数都是指针知识体系的重要组成部分。掌握它们的定义、用法以及相互之间的区别,对于深入理解 C 语言和进行高效的编程至关重要。通过不断地练习和实践,才能更好地运用这些知识解决实际问题。希望本文能为大家学习 C 语言指针提供一些帮助。