当前位置: 首页 > news >正文

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 语言指针提供一些帮助。

http://www.lryc.cn/news/602786.html

相关文章:

  • 快速构建基于React.js的用户注册与登录的Web应用程序
  • 图像识别边缘算法
  • 数学建模算法-day[13]
  • QGIS基于规则的道路分级制图及Leaflet集成展示实例
  • Polkadot 的 Web3 哲学:从乔布斯到 Gavin Wood 的数字自由传承
  • 开始记录一步步学习pcl
  • Go语言-->变量
  • DBAPI 实现分页查询的两种方法
  • 若依【(前后端分离版)SpringBoot+Vue3】
  • JVM 笔记:类加载、内存管理、垃圾收集与垃圾收集器
  • JVM 垃圾回收机制全景解析:从对象回收到收集算法
  • C++---初始化列表(initializer_list)
  • Flutter在购物场景中BLoC的应用
  • shell每日三题大神之路:第三天
  • 轻量级远程开发利器:Code Server与cpolar协同实现安全云端编码
  • AR眼镜:工业4.0时代高风险作业的安全守护者
  • 深度思考和搜索研究 最新的GSPO强化学习算法
  • 解决 xshell 无法连接win10 、win11的linux子系统
  • python每日一题练习
  • Java集合遍历的几种方式
  • 【docker】DM8达梦数据库的docker-compose以及一些启动踩坑
  • Thymeleaf th:object核心用法精解
  • LeetCode|Day28|67. 二进制求和|Python刷题笔记
  • OpenLayers 入门指南【四】:项目初始化
  • org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path
  • 下载k8s官方组件chart和容器镜像
  • 自动化测试常用函数
  • 网络编程概述与UDP编程
  • 关于前端的性能优化
  • 【数据架构09】人工智能及数据智能架构篇