一起学C程序设计第八课指针(一)
C语言指针
指针是C语言重要而且独特的一个概念。指针很灵活,学习指针必备的知识是要了解C语言中的数据存储方式。因篇幅有限,本节课先介绍指针的一些基本概念,以及用指针的方式去引用我们之前学过的基本数据类型、数组、函数等。
注意,请认真学习完《C程序设计(第五版)》第八章后再阅读本文会有更大的收获。 指针
指针即内存地址。变量在内存中初始化时被分配一个地址,之后再用到这个变量都是根据其地址找到对应的存储单元,再结合其数据类型进行取值。
直接访问
根据变量名来访问其在内存中的值。对于基本数据类型如整型、浮点型、字符型等一般都采用此种方式。
间接访问
把变量A的存储地址存在另一个变量B里,通过访问变量B获取变量A的地址,进而根据地址去访问A。指针变量
定义
用一个变量来存储某个数据的内存地址,那么这个变量就是指针变量。
为了区别于普通变量,定义指针变量的时候加上一个星号"*",但指针变量的变量名不包含星号。
引用
定义一个指针变量后,我们可以给它赋值,赋的值自然要是一个指针,通常指针通过取址符"&"获取。指针变量的引用形式有两种:不带星号,引用指针变量自身的值,即存储的指针可以做指针相关的运算,或者重新赋值一个同类型的指针带星号,引用指针变量存储的指针对应的变量,即上述的"间接访问"变量,等同于对变量进行运算,或者重新赋值
星号的位置
目前两种主流的写法: int* a; 星号紧跟数据类型之后int *a; 星号紧挨着变量名
这两种写法在编译时都能通过而且没有区别,主要是看个人习惯以及对指针变量定义的理解。
在Visual Studio里默认星号紧跟数据类型,想修改的话打开"工具"->"选项"进行调整如下:
调整指针的对齐方式
作为函数参数
指针变量作为函数参数的时候,实参传递给形参的是指针。改变形参的值不会改变实参,而通过"*p += 10"这样的运算则会改变其指针对应的变量的值,例如:
指针参数作为函数参数
在上面的代码里,指针变量p1和p2传入到fun1()函数中去做运算之后,并没有发生变化,而b的值却发生了变化,有疑惑的自己敲代码运行一下看输出和心理预期有没有出入。
指针变量的指针
变量的指针存储在指针变量里,指针变量和普通变量一样,也有它自己的指针,叫作指针变量的指针。
上面这句话有点像绕口令,其实不难理解,我们打个比方:一个柜子有很多个颜色各不相同的格子,每个格子上贴有不同的字母标记,有一个金币放在标记为A的红色格子中;另外有一个标记为B的蓝色格子,里面放一张纸条并写有字母A,得出以下类比:所有格子都是变量,里面存的东西就是变量的值字母A表示红色格子的指针,即变量的指针蓝色格子存放字母A的纸条,即存放红色格子的指针,所以蓝色格子是一个指针变量字母B表示蓝色格子的指针,即指针变量的指针借助断点理解指针
在Visual Studio编辑器中利用断点可以在指针变量赋值的过程中看到其值的变化,例如:
借助断点理解指针
以上代码里定义了两个指针变量p和p2:p是正确的定义方式,把变量a的内存地址赋值给pp2初始化定义没有问题,但是直接把整数11赋给p2是不对的;p2会把11转成16进制0x000000000000000b,然后根据这个地址找存储单元取值,结果自然是找不到的
提示:初学者不太容易接受和理解指针这个新的概念,当遇到"指针"这个词的时候,心里自动把它转成"地址"来过渡。数组与指针
通过上面的学习,初学者可能有点疑惑,基础变量的指针看起来好像就是引用的时候做一次转换,感觉不到特别有用的地方。接下来,通过指针引用数组就能体现出指针的便利之处了。
数组的指针
之前学习数组我们了解到:数组名就表示数组首个元素的存储地址,即首个元素的指针,也是数组的指针。
存放数组指针的指针变量,定义的时候和基础变量的指针一样,前面声明数组的数据类型,如下几种定义数组指针的方式都是等价的:
int a[10]; int *p1 = &a[0]; int *p2 = &a; int *p3 = a;
数组指针的运算
通过数组指针的运算来引用数组里的元素是我们学习的关键知识点。由于数组的在初始化的时候,被系统分配连续的内存来存储各个元素,所以知道了首个元素的指针p,通过加法运算得到第i个元素的指针p+i(i从0开始计数)。
对于初学者来说,为了熟悉数组指针的用法,要多练习写一些不同条件的for循环和while循环去通过指针引用数组的元素,并实现数组的排序、找最大值、最小值等算法。
数组作为函数参数
之前已经学习过数组作为函数参数是,形参数组的变化会影响实参数组。函数的形参不管是数组,还是数组指针,它们都是等价的,同样实参传递数组和数组指针也是等价的。
二维数组与指针
以上是一维数组的指针相关特性,那二维数组的指针如何呢?和一维数组一样,二维数组的名字表示首个元素的指针。二维数组元素的指针引用比一维数组稍微复杂,但是只要记住一点,在指针运算中,一般先计算出行(第一维的数组指针)指针,再根据行指针里存储的第二维数组的首元素指针,例如二维数组a的i行j列元素为:
a[i][j] = *(*(a + i) + j);
对于二维数组a[m][n],我们定义一个指针变量p,p里指针指向一个一维数组且有n个元素,使得p指向a[0],p+1指向a[1],p+i指向a[i],p的指针指向a[0][0],是一个整型元素,则p+1指向的是a[0][0]的下一个元素a[0][1],这时a[i][j] = *(p + n*i +j); 相当于指针从a[0][0]开始按行逐个移动到a[i][j] 如果定义 int (*p)[n] = a ,p的指针指向a[0],是一个包含n个元素的数组,则p+1指向a[0]的下一个元素a[1],这时a[i][j] = *(*(p + i) + j); 相当于指针从a[0]先移动到第i行a[i],在移动到第j列a[i][j]
这里注意的是,之前我们用的指针变量指向一个基本数据类型的变量,或者是一维数组内的(基本数据类型)元素,而这里定义一个指针变量指向的是一个一维数组,采用了新的定义方法:int (*p)[n]; ,虽然p也指向了一个数组的首元素,但p+i并不表示指向第i个元素,而是指向第i行。字符串与指针
字符串赋值
C语言可以直接用字符串赋值给一个指针变量,但是不能给一个指针变量直接赋值一个数组:char *s = "hello world"; 是可以的int *p = { 1, 2, 3 }; 是错误的
这个与C语言对字符串的特殊对待方式有关,C语言对字符串按照常量字符串处理,并为其开辟字符数组来存储,所以有了存储地址就等于有了指针了,而一个数组就没有这个待遇了~
字符指针的引用
和数组指针类似,自身也可以做指针运算,引用字符串中某一个字符,或者改变字符串中某一个字符,但是注意直接赋值给指针变量的字符串是不能改变其中的字符的,如下代码:
字符指针
因为str指针是直接字符串赋值,而C语言这种处理方式是把字符串作为常量存储在内存中的,故不能改变常量的值,只能引用。
字符指针作为函数参数
字符数组也是数组,因此形参改变也会影响实参。函数与指针
函数指针变量的定义
函数指针变量定义形式:数据类型 (* 指针变量名)(函数参数列表); 例如
void (*p)();
这里定义指针变量时也用括号把星号和变量名包含起来,再结合之前我们定义一个一维数组指针也是用括号包含星号和变量名,即可得知复杂类型数据的指针变量定义都要注意这点,其主要原因是星号的优先级普遍偏低。
注意的是函数指针变量指向函数体存储的入口地址,对函数指针变量做运算没有意义。
函数指针变量作为参数
有点类似于"函数式编程"的意思,比如JavaScript的回调函数,在一些高级语言中都有类似的"回调"语法规则。
函数指针的使用
在使用函数指针的时候,无论是直接调用还是当做参数传递到其他函数中调用,我们来看下面几种写法:
函数指针的调用
上面代码中的5种调用都能执行,而且结果一致,是不是有点疑惑呢?答案就在打印输出的地址中——指向函数的指针和函数里的指针是一致的。
类比上面格子的比喻,可以这样理解:标记为A的红色格子里面存放的纸条也写着A,所以无论你是从颜色(红色)找格子,还是从格子的标记(A)找格子,还是从格子里的纸条标记(A)再去找格子,最终找到的都是标记为A的红色格子。
初学者可能一时半会理解不透彻,可以去网上搜索"函数指针星号调用"查阅更多的资料。
函数返回指针
上一课学习函数,我们了解到函数的返回值要么为空,要么是一种基本的数据类型。如果返回一种基本数据类型的指针是否可行呢?答案是肯定的。
为了区别于普通自定义函数,我们在返回的指针函数定义时,在函数名前加一个星号:返回数据类型 *函数名(参数列表);
在使用的过程中,因为要接收函数的返回值,所以要定义一个同等类型的指针变量来作为函数返回值的赋值对象。下面是一个相当于字符串截取的参考示例,返回一个字符指针:
返回指针的函数指针数组和指向指针的指针
指针数组
指针数组指的是一个数组里的元素都是指针类型的数据,定义形式为:数据类型 *数组名[数组长度];
注意:区分和指向一维数组指针变量的定义区别。
指针数组最常用的使用场景是处理字符串数组,如果按照普通的二维数组去定义一个字符串数组是非常麻烦的,因为二维数组要制定列数,但是每个字符串(字符数组)的长度都不相同,有长有短,这样会造成存储空间的浪费,如果把字符串(字符数组)用字符指针代替存储到数组里,就能很好的解决这个问题了。
指向指针的指针
如果一个指针变量里存放的指针指向的不是一个具体的值,而是另外一个指针,那么这个指针就是"指向指针的指针"。继续拿格子举例:红色格子里存放纸条B,纸条B对应蓝色格子,而蓝色格子里又存放了纸条C,纸条C对应绿色格子,绿色格子里存放着金币,那么可以这么说:蓝色格子里存放金币所在格子的指针C红色格子里存放指向"指向存放金币格子的指针C"的格子的指针B指针B就是一个指向指针C的指针
main()函数的默认参数
在执行编译好的exe文件可以加上参数,main()默认两个参数:int argc,参数的总数量,至少为1个参数,就是执行的当前文件的路径char *argv[],参数组成的指针数组,即运行命令后跟的参数,以空格来隔开
可以在Visual Studio的项目配置里添加调试参数,点击"调试"->"你的项目名+属性调试"(最下面一个菜单),如下图所示:
添加外部调试参数总结
本节的知识点比较多,几乎是用指针把前面所学的东西又重新表达了一遍。使用指针要多练习,特别是二维数组指针的使用,很容易把定义一个指向一个一维数组整体的指针和一维数组首元素的指针搞混淆。
使用指针有时候能巧妙地解决一些复杂的问题,不要为了使用指针而使用,要保证程序的可读性和代码的可靠性,不必要故意写那些让人费解、故弄玄虚的代码。往期文章
一起学《C程序设计》第七课——函数及实战练习
一起学《C程序设计》第六课——数组、字符串及实战练习
一起学《C程序设计》第五课——循环控制及实战练习
一起学《C程序设计》第四课——if语句、switch语句及实战练习
一起学《C程序设计》第三课——数据结构、运算符、表达式和语句
潍坊这座小镇,有座保存完整的百年老火车站记者李牧青2023年3月23日上午,山东省组织的走文化廊道进经济园区看山东高质量发展行进式主题采访活动走胶济铁路文化体验线来到了位于潍坊的坊茨小镇。坊茨是德语对坊子的音译,坊茨小镇
日均起降476架次内蒙古航空助力全域旅游央广网呼和浩特3月23日消息(记者魏全民实习生刘贞怡)3月23日,为做强内蒙古自治区引客入蒙空中通道,做实百万人互游系列活动,进一步发挥航空旅游的合作优势,内蒙古地区2023年夏航
汉阳这场秀一定要给她捻翠低垂嫩萼,匀红倒簇繁英。春意渐深,在汉阳国博水系景观公园里的垂丝海棠渐入花期,花儿半醉半开,娇羞粉嫩,清丽迷人。今天这场秀就留给垂丝海棠。垂丝海棠又名有肠花,也有思乡草之名,象
祁阳节会经济助力乡村振兴红网时刻新闻3月23日讯(通讯员郑宏陈斌)祁阳市首届油菜花节暨农副产品交易会已经圆满落幕。节会期间,游客量达5。3万人次,实现旅游收入1215万元。节会经济成为助推乡村振兴的一个重
珲春以旅促兴谋发展,共建文旅朋友圈今年以来,珲春市充分发挥文旅融合效应,通过拓市场育特色创品牌,以人流带动客流,着力提振文旅市场消费潜力,促进全市文旅产业高质量发展。共拓文旅朋友圈,打造精品新线路。日前,G331上
蕉城春茶抢鲜开采北方客商纷至沓来眼下正值春茶采摘上市时节蕉城区各茶园开始热闹起来茶农茶企正抢抓时间采摘今年第一批春茶为抢鲜上市不少来自北方的茶叶爱好者客商纷纷深入茶山实地体验春茶采摘乐趣感受宁德天山茶的魅力蕉城区
毕摩文化之乡四川美姑县是全国最大的彝族聚居县头条创作挑战赛美姑县位于四川省西南部凉山彝族自治州东北部,地处大凉山黄茅埂西麓。县境东邻雷波县,西接越西县,南连昭觉县,北毗峨边彝族自治县,西北与甘洛县连界,东北同马边彝族自治县接
脱离中国的五国,现在如何了?清朝控制的最大领土疆域自秦汉以来,中国一直是亚洲的超级霸主,在接下来的一千多年里一直在对外扩张,实际控制领土一度超过千万平方千米。拥有最大面积的是元朝,土地面积为1372万平方公里
最高补贴500万元!海南这笔资金开始申报海南日报记者3月21日从海南省发改委获悉,2023年海南省现代物流业发展奖补资金(物流信息平台和冷链设施方向)开始申报,新建冷库(1万立方米及以上)等符合条件的项目,最高可获500
中国地质学会公布2022年度地质科技重要进展地质找矿重大成果!一志留纪特异埋藏化石库揭秘从鱼到人演化关键跃升有颌脊椎动物或有颌类的崛起代表了从鱼到人演化史上的一次极为关键的跃升。分子钟推断的有颌类起源与其最早化石之间存在约3000万年的巨大空
探寻三晋大地上的中华文明之源由2022年度山西六大考古重要发现说起运城夏县辕村新石器时代遗址太原小店郑村新石器时代遗址吕梁兴县碧村新石器时代遗址运城稷山东渠夏时期遗址运城绛县西吴壁遗址商代墓地临汾霍州陈村金元时期瓷窑址,无论是在时间跨度上,还是在
三伏将至,要学会忌嘴,少吃1瓜,多吃4菜,别不当回事即将步入农历6月,整个夏季最热的时期就要到来。酷暑难耐,食欲不振,再加上身体出汗量大,时间久了就会感觉浑身乏力,提不起精神。年轻人普遍都是这种情况,更别提上了年纪的老人。饮食问题愈
传荒野大镖客2将登陆Switch有望下周公布据西班牙游戏杂志Manual记者NachoRequena爆料,下一场任天堂直面会将会在下周举行,专注于第三方游戏,玩家可以期待来自大型发行商的新公布。我听说任天堂新直面会将在下周二
唐尚珺高考成绩出来了吗高考钉子户唐尚珺高考成绩出来了,有人说他考了703分,也有人说他考了517分。今年是他第十四次高考,这是他在高三的第十四年,人生能有多少个十四年,希望今年他能上上他所希望的那所大学
我的求婚誓词婚恋手册2019年的夏天,那个时候还没有疫情,也就是在那个夏天我在烟台长岛向我现在的夫人求了婚,誓词如下,希望对大家有用丫头陪你来到这里,我想起一首英文诗ilovethreethi
奉劝大家以后不要买商业保险,远离卖保险的人商业保险就是一个坑,要买就买社保。2007年我家先生买一份5000多,熟人卖的,自己也有点想买。2008年我买一份,2000多,我本不想,卖保险的(不是很熟),左一个哥,右一个嫂子
不要脸的人现在的人怎么这么不要脸,当初借你5万块钱我真是瞎眼了,说好借你10天,10天后还我,,现在一个多月了连个电话都不打给我,每次跟你要钱你总说快了快了,要么就说再过几天,我感觉你这个人
打人细节曝光,累累案底被挖,陈继志妻子声泪俱下感觉天塌了俗话说,不是一家人不进一家门。比如陈继志与他的妻子兰某刚就是一个很好的例子。在警方公布了打人案件的详细细节以及陈继志等人的累累前科后,原本口气狂妄气势嚣张的兰东,居然一改常态,梨花
妹子以后拍照还是远离玻璃吧,你的影子已经说明了一切,真尴尬不知道学习的怎么样,反正是挺认真的还是中国的烤鸭比较好吃城里来的女朋友非要体验一下农忙这才是我印象中的肉夹馍呀还能怎么办那,只能老老实实的在这待着安全出行,人人有责谁还不会画画呀,
美国两党开打了导读美国共和党,民主党开撕奥巴马希拉里佩洛西等民主党人士反对堕胎权被剥夺,而特朗普彭斯等共和党人士,则赞成堕胎权被否定。民主党的拜登发表讲话美国总统拜登在白宫就此发表全国讲话,拜登
泪目!他们的照片不用再打马赛克他们大多只能以马赛克示人当我们能看到面容时却常常是他们在人世间的最后一面1982年云南成立专业禁毒队伍40年来,禁毒战线共60人牺牲国际禁毒日即将到来之际云南警方公布60张禁毒英雄
足坛海国图志法国的大洋洲土地新喀里多尼亚,卡伦布的故乡2013年巴西联合会杯,代表大洋洲出战的球队塔希提实际上并不是一个主权国家,而是法国的海外领地。除了塔希提之外,法国还有一个海外领地既是国际足联的成员,也是大洋洲足联的成员。这支球