指针在c语言中的运用,怎么理解C语言中的指针,如何运用?
恰好我之前写了一系列介绍 C 语言的文章,介绍了什么是指针,以及为什么要使用指针,下面摘录一部分,感兴趣的话,可以点我了解更多。
什么是 C语言指针?
不同的数据类型的主要区别在于占用的存储空间不同。我们知道,C 程序是运行在计算机的内存中的,因此 C 程序的变量也是存在于内存中的。C 标准规定 char 类型占用一个字节的存储空间,对其他整型却没有做规定,现在为了解释的方便,我们假设 int 类型的数据占用内存 4 个字节。
假设我们如下定义了两个变量:signed char i = 3;int j = 8;
那么,i 占用了 1 字节的内存空间,j 占用了 4 字节的内存空间,请看下图。
方框表示内存空间,内部表示存储的值。我们把内存逐字节编号,方框外部的数字表示方框的编号(这样的内存“编号”即所谓的“内存地址”)。修改变量 i 的值,实际上就是修改地址为 4000 的内存空间里的值。那反过来呢?如果我修改了地址为 4000 的内存空间里的值,i 的值会相应改变吗?答案是肯定的,请继续往下看。
上图中的内存地址“4000”是我为了解释方便随意取的。那么,在实际应用中,变量 i 的地址如何获取呢?C 语言提供了“&”运算符,就是获取变量地址的。请看下面的例子:#include int main()
{
signed char i = 3;
int j = 8;
long p1 = (long)&i;
printf('p1: %ld ', p1);
return 0;
}
我们取出了 i 的地址,把它强制转换为 long 型(关于强制类型转换,可参考上一节),传递给 p1 了。编译执行,发现变量 i 的地址被打印出来了。这说明,C 程序变量的地址也是一个整数。
按照上面的说法,修改 i 的值除了直接对 i 赋值以外,还可以通过修改 p1 地址处的内存空间里的数值。那,怎样才能“通过修改 p1 地址处的内存空间里的数值”修改 i 的值呢?
上面的代码实例中,我们使用了 long 型变量 p1 存储了 i 的地址。事实上,C 语言有专门的数据类型存储地址,定义方式也很简单,就是:“类型描述符 * ”,例如,可以定义以下变量存储地址:signed char *p1 = &i;int *p2 = &j;
p1 和 p2 就是 C 语言中所谓的指针类型,因为 i 是 signed char 类型的,所以定义了 signed char * 类型的指针存储 i 的地址。j 是 int 类型的,所以定义了 int * 类型的指针存储 j 的地址。另外,C 语言提供了“&”运算符取变量地址,与之对应的,还提供了“ * ”运算符从相应地址内存里取出数值。
好了,了解了 C 语言的指针类型和“ * ”运算符,现在来看看如何“通过修改 p1 地址处的内存空间里的数值”修改 i 的值。请看如下代码:signed char *p1 = &i;
*p1 = 5;
printf('i=%d ', i);
编译运行,发现程序输出“i=5”,这样我们就实现了“通过修改 p1 地址处的内存空间里的数值”修改 i 的值。在定义变量时,” * “放在变量符号前,可以定义指针变量。在定义完指针变量后,“ * ”放在变量前,就表示从地址取值的运算符了。另外,“ * ”还可以表示乘法运算符,读者自己思考什么情况下,“ * ”表示乘法运算符。
以上的操作,实际上就是 C 语言的指针操作,可以看出它一点也不神秘,接下来几节,我们将继续讨论 C 语言的指针,比如为什么 int 类型的变量 j 的地址要使用 int* p2; 定义,而不能使用 signed char* p2; 定义,使用指针为何能写出紧凑、高效的 C 程序等等。
为什么要使用指针?
在开始讨论为什么使用 C 语言指针之前,先介绍一下复杂点的指针,这是新知识,也是铺垫。不想看铺垫可以往后翻一翻。
前面几节介绍了 C 语言中指针,也讨论了数组指针和指针数组的区别,但归根结底,至今我们说的都是基础数据类型定义的指针,C 语言有复合数据类型,那么它有复合数据类型的指针吗?答案是肯定的,事实上,在 C 语言中复合类型指针的使用相当广泛。
先来看看结构体指针。还是从实例出发,我们定义一个结构体类型,它有两个成员,分别是 sleep_time(睡觉时间) 和 work_time(工作时间),然后定义这种结构体类型的变量和指针:struct week{
double sleep_time;
double work_time;
};
struct week w;
struct week *pw = &w;
可以通过结构体指针 pw 访问 week 结构体的成员:(*pw).sleep_time = 7.0;
这样写有点麻烦,因此 C 语言非常贴心的提供了“->”运算符,所以我们还可以这样通过结构体指针访问成员:pw->sleep_time = 7.0;
为什么要使用 C 语言中的指针
好了,现在我们已经知道 C 语言中的结构体指针怎么使用了,铺垫完了。但是,明明使用结构体变量 w 就能很好的读写 week 结构体啊,为什么要用结构体指针呢?这不是麻烦了吗?为什么要使用结构体指针,其实可以延伸到“为什么要使用指针”,本节将以结构体指针为例讨论一下这个问题。
是的,仅仅访问 week 的 sleep_time 成员,只使用结构体变量 w 就足够了,再通过 pw 访问真的麻烦了。但是工具会不会带来方便,要看我们怎么使用,不能因为高射炮打蚊子不方便就说高射炮没用。恰当的使用结构体指针,有利于我们写出更加紧凑,效率更高的 C 程序。
一周有五天工作日,两天周末,一般来说,在工作日(weekday),人们的睡觉时间较短,工作时间较长,所以我们定义 weekday 函数来规划工作日的时间:void weekday(struct week wd)
{
wd.sleep_time = 7.0; // 7 小时
wd.work_time = 8.5; // 8.5 小时
}
而在周末(weekend)则反过来,工作间较短,睡觉时间较长,所以我们定义 weekend 函数来规划周末的时间:void weekend(struct week we)
{
we.sleep_time = 9.0; // 9 小时
we.work_time = 2.5; // 2.5 小时
}
这两个函数很好的规划了一周的睡觉和工作时间,但是却并不好用。为什么呢?因为它俩只在自己内部规划了,我们外界看不到啊!想在 main 函数把规划好的时间打印出来都办不到,因为它俩在自己内部规划好以后,就把“规划书”销毁了。这里把 weekday 和 weekend 函数的局部变量比作“规划书”,函数退出后,局部变量就自动销毁了。可以参考《c语言入门5,一文彻底弄懂函数的形参和实参》一节。
可能你会说,那我可以把“规划书”返回给 main 函数啊,让 weekday 和 weekend 函数有返回值就可以了:struct week weekday(struct week wd)
{
wd.sleep_time = 7.0; // 7 小时
wd.work_time = 8.5; // 8.5 小时
return wd;
}
struct week weekend(struct week we)
{
we.sleep_time = 9.0; // 9 小时
we.work_time = 2.5; // 2.5 小时
return we;
}
int main()
{
struct week w;
w = weekday(w);
printf('weekday, sleep time: %0.1f, work time: %0.1f ', w.sleep_time, w.work_time);
weekend(w);
printf('weekend, sleep time: %0.1f, work time: %0.1f ', w.sleep_time, w.work_time);
return 0;
}
是的,这的确是一个解决问题的办法,main 可以把 weekday 和 weekend 函数的“规划书”打印出来了。
但是这种解决问题的办法有一点臃肿,很多程序员把这样的代码称为“不优雅”的代码。你看,main 现在有一份空的“规划书”,需要 weekday 和 weekend 函数处理。weekday 和 weekend 函数能处理,但是它们要复制一份“规划书”回到自己内部做,这种复制就造成了空间浪费。此外,weekday 和 weekend 函数做完了规划书,还要把“规划书”再从自己内部取出,return 给 main,这就有时间浪费。
更节约资源,更有效率的做法是:weekday 和 weekend 函数处理这份“规划书”时,直接处理 main 里的“规划书”就可以了。不要复制后再处理,完事了还要在从自己内部传出。那,weekday 和 weekend 函数应该怎么修改呢?请看:void weekday(struct week *wd)
{
wd->sleep_time = 7.0; // 7 小时
wd->work_time = 8.5; // 8.5 小时
}
void weekend(struct week *we)
{ we->sleep_time = 9.0; // 9 小时
we->work_time = 2.5; // 2.5 小时
}
int main()
{
struct week w;
weekday(&w);
printf('weekday, sleep time: %0.1f, work time: %0.1f ', w.sleep_time, w.work_time);
weekend(&w);
printf('weekend, sleep time: %0.1f, work time: %0.1f ', w.sleep_time, w.work_time);
return 0;
}
看到了没,利用指针,整个 C 代码简洁多了。weekday 和 weekend 函数接收到的参数都是 main 里结构体变量 w 的地址,所以它俩都是直接操作 w 的。这样就不用在自己的栈帧里复制一份 w 再处理了,也不用在处理完毕还要 return 给 main 了。
看到这里,你可能会说,什么嘛,不就是用指针代替了结构体做参数吗?指针说不定比结构体还要耗空间呢!对吗?一起来看下:结构体变量 w,它占用内存至少两个 sizeof(double) 的空间(一个 double 型数据通常占用 8 字节空间)。而一个指针,不管它是什么类型的,在大多数 32 位计算机中,它只占 4 字节空间,在大多数 64 位计算机中,它也仅仅占 8 字节空间。所以使用指针做 weekday 和 weekend 函数的参数,在空间上,绝对是比直接使用 week 结构体节约空间的,何况指针还提升了效率,简洁了代码。如果是一个 char 型变量,它只占用 1 字节空间,这时使用指针的确更浪费空间。但是如果是一个非常复杂的结构体,它占用的内存空间甚至达几千字节,这时使用指针就非常节约空间了。所以说,工具是死的,人是活的。
到这里,相信你已经了解 C 语言指针在节约空间,提升程序效率方面的作用了。事实上,这里我们介绍的仅仅是指针的冰山一角,在以后的文章里,你会愈发觉得 C 语言指针的强大的。
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。
指针在c语言中的运用,怎么理解C语言中的指针,如何运用?相关推荐
- c语言int 转bool_深入理解C语言中的类型转换
今日份知识更新,即<深入理解C语言中的类型转换> 文章目录: 一.隐式转换 1.1.基本类型转换 整形提升 算数转换 1.2.指针转换 二.显式转换 隐式转换 基本类型转换 整形提升 表达 ...
- C语言递归调用return语句,理解C语言递归函数的逐级返回(return)
递归函数,也即调用自身的函数. C Primer Plus中有个例子很棒: /*理解C语言递归函数*/ #include void up_and_down(int); int main(void) ...
- 【C语言深度剖析】深入理解C语言中的移位操作符(代码+图解)
文章目录 移位操作符 原码 反码 补码 左移操作符 正数左移 负数左移 右移操作符 算术右移 正数算术右移 负数算术右移 逻辑右移 移位操作符 分为: 左移操作符:<< 右移操作符:> ...
- 【Attention】深度学习中的注意机制:理解序列模型中的注意机制How Attention works in Deep Learning
[学习资源] How Attention works in Deep Learning: understanding the attention mechanism in sequence model ...
- 传输层中UDP的深入理解以及socket中UDP的使用
目录 一.学习UDP的预备知识 1.熟悉网络通信中的五元组 (1)源IP地址和目的IP地址 (2)源端口号和目的端口号 (3)协议号 2.什么是客户端和服务端 二.socketAPI中UDP的使用 1 ...
- 详解c语言编程库题,详解C语言编程
C语言作为编程语言,其诞生已经很早,但是在编程语言多样化的今天,C仍然高居TIOBE编程语言排行榜的第一位(2014年5月),而C++语言排位第四.而位居第二位的Java本身就是脱胎于C++语言,第三 ...
- ArcEngine中IFeatureClass.Search(filter, Recycling)方法中Recycling参数的理解
转自 ArcEngine中IFeatureClass.Search(filter, Recycling)方法中Recycling参数的理解 ArcGIS Engine中总调用IFeatureClass ...
- 通俗理解决策树算法中信息增益的
转载自 通俗理解决策树算法中信息增益的 通俗理解决策树算法中的信息增益 在决策树算法的学习过程中,信息增益是特征选择的一个重要指标,它定义为一个特征能够为分类系统带来多少信息,带来的信息越多,说明 ...
- 理解C语言中指针的声明以及复杂声明的语法
昨天刚把<C程序设计语言>中"指针与数组"章节读完,终于把心中的疑惑彻底解开了.现在记录下我对指针声明的理解,顺便说下如何在C语言中创建复杂声明以及读懂复杂声明. 本文 ...
最新文章
- 将Mongodb部分数据导入mysql数据库
- 性能分析工具 Android TraceView
- 未发现数据源名称_在Power BI中管理数据源Analysis Services
- com技术内幕 代码_CFan科学院:零门槛极速抠图技术探秘
- nfs需要开通什么端口_创业板开通需要什么条件?开通创业板门槛,要怎么样开通创业板?...
- Kafka Streams简介: 让流处理变得更简单
- 虚拟机安装win7时遇到的问题及解决方案
- VMware esxi 阵列卡驱动下载
- visual studio发布网站的时候水晶报表rpt格式文件不是最新的解决
- Chrome Edge与Safari书签同步
- 利用Email包实现邮件的群收发功能
- PS制作橙子鱼缸、折纸效果字
- mac pro 系统升级带来的问题
- 网易云音乐的歌词无法在桌面歌词显示
- 了解设计模式 之 结构模式(四) -- 装饰模式
- python金融量化分析工资_量化金融分析师就业前景及薪酬怎么样?
- iphone12mini参数配置 iphone12mini多大尺寸
- 为知笔记 印象笔记 有道笔记 OneNote选择
- 【Markdown基础教程】Markdown介绍
- Python爬虫获取“房天下“房价数据(下)
热门文章
- Positional Encodings in ViTs 近期各视觉Transformer中的位置编码方法总结及代码解析 1
- CVE-2021-41773 CVE-2021-42013 Apache HTTPd最新RCE漏洞复现 目录穿越漏洞
- python无人机路径规划算法_RRT算法在Python中的实现,快速,拓展,随机,树
- hibernate native oracle,hibernate native 主键生成策略
- 计算机辅助抗体设计,计算机辅助设计提高单克隆抗体亲和力的研究
- python当前时间怎么弄_python获取当前时间
- boost序列化(Serialization)
- 蓝牙基础知识进阶——Physical channel
- Linux - xshell上传文件报错乱码
- CentOS7:JDK1.7.0_80安装