C语言指针
指针是C语言重要而且独特的一个概念。指针很灵便,学习指针必备的知识是要了解C语言中的数据存储方式。因篇幅有限,本节课先介绍指针的一些基本概念,和用指针的方式去引用咱们以前学过的基本数据类型、数组、函数等。文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
注意,请当真学习完《C程序设计(第五版)》第八章后再浏览文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
指针
指针即内存地址。变量在内存中初始化时被分配一个地址,以后再用到这个变量都是依据其地址找到对应的存储单元,再结合其数据类型进行取值。文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
直接走访文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
依据变量名来走访其在内存中的值。对于基本数据类型如整型、浮点型、字符型等一般都采取此种方式。文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
间接走访文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
把变量A的存储地址存在另外一个变量B里,通过走访变量B获取变量A的地址,进而依据地址去走访A。文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
指针变量
定义文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
用一个变量来存储某个数据的内存地址,那么这个变量就是指针变量。文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
为了区分于普通变量,定义指针变量的时候加之一个星号“*”,但指针变量的变量名不包括星号。文章源自微观生活(93wg.com)微观生活-https://93wg.com/6623.html
引用
定义一个指针变量后,咱们可以给它赋值,赋的值自然要是一个指针,通常指针通过取址符“&”获取。指针变量的引用情势有两种:
- 不带星号,引用指针变量本身的值,即存储的指针可以做指针相关的运算,或者从新赋值一个同类型的指针
- 带星号,引用指针变量存储的指针对应的变量,即上述的“间接走访”变量,等同于对变量进行运算,或者从新赋值
星号的位置
目前两种主流的写法:
- int* a; 星号紧跟数据类型以后
- int *a; 星号紧挨着变量名
这两种写法在编译时都能通过而且没有区分,主要是看个人习惯和对指针变量定义的理解。
在Visual Studio里默许星号紧跟数据类型,想修改的话打开“工具”->“选项”进行调剂如下:
调剂指针的对齐方式
作为函数参数
指针变量作为函数参数的时候,实参传递给形参的是指针。扭转形参的值不会扭转实参,而通过“*p += 10”这样的运算则会扭转其指针对应的变量的值,例如:
指针参数作为函数参数
在上面的代码里,指针变量p1以及p2传入到fun1函数中去做运算以后,并无产生变化,而b的值却产生了变化,有纳闷的自己敲代码运行一下看输出以及心理预期有无出入。
指针变量的指针
变量的指针存储在指针变量里,指针变量以及普通变量同样,也有它自己的指针,叫作指针变量的指针。
上面这句话有点像绕口令,并不难理解,咱们打个譬如:一个柜子有不少个色彩各不相同的格子,每一个格子上贴有不同的字母标记,有一个金币放在标记为A的红色格子中;此外有一个标记为B的蓝色格子,里面放一张纸条并写有字母A,得出下列类比:
- 所有格子都是变量,里面存的东西就是变量的值
- 字母A表示红色格子的指针,即变量的指针
- 蓝色格子寄存字母A的纸条,即寄存红色格子的指针,所以蓝色格子是一个指针变量
- 字母B表示蓝色格子的指针,即指针变量的指针
借助断点理解指针
在Visual Studio编辑器中应用断点可以在指针变量赋值的进程中看到其值的变化,例如:
借助断点理解指针
以上代码里定义了两个指针变量p以及p2:
- p是正确的定义方式,把变量a的内存地址赋值给p
- p2初始化定义没有问题,然而直接把整数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] = * + 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] = *; 至关于指针从a[0][0]开始按行逐个挪动到a[i][j]
- 如果定义 int [n] = a,p的指针指向a[0],是一个包括n个元素的数组,则p+1指向a[0]的下一个元素a[1],这时候a[i][j] = * + j); 至关于指针从a[0]先挪动到第i行a[i],在挪动到第j列a[i][j]
这里注意的是,以前咱们用的指针变量指向一个基本数据类型的变量,或者是一维数组内的(基本数据类型)元素,而这里定义一个指针变量指向的是一个一维数组,采取了新的定义办法:int [n]; ,尽管p也指向了一个数组的首元素,但p+i其实不表示指向第i个元素,而是指向第i行。
字符串与指针
字符串赋值
C语言可以直接用字符串赋值给一个指针变量,然而不能给一个指针变量直接赋值一个数组:
- char *s = &34;; 是可以的
- int *p = { 1, 2, 3 }; 是过错的
这个与C语言对字符串的特殊对待方式有关,C语言对字符串依照常量字符串处理,并为其开拓字符数组来存储,所以有了存储地址就等于有了指针了,而一个数组就没有这个待遇了~
字符指针的引用
以及数组指针相似,本身也能够做指针运算,引用字符串中某一个字符,或者扭转字符串中某一个字符,然而注意直接赋值给指针变量的字符串是不能扭转其中的字符的,如下代码:
字符指针
由于str指针是直接字符串赋值,而C语言这类处理方式是把字符串作为常量存储在内存中的,故不能扭转常量的值,只能引用。
字符指针作为函数参数
字符数组也是数组,因而形参扭转也会影响实参。
函数与指针
函数指针变量的定义
函数指针变量定义情势:数据类型 ; 例如
void ;
这里定义指针变量时也用括号把星号以及变量名包括起来,再结合以前咱们定义一个一维数组指针也是用括号包括星号以及变量名,便可得知繁杂类型数据的指针变量定义都要注意这点,其主要缘由是星号的优先级普遍偏低。
注意的是函数指针变量指向函数体存储的入口地址,对函数指针变量做运算没成心义。
函数指针变量作为参数
有点相似于“函数式编程”的意思,比如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程序设计》第三课——数据结构、运算符、表达式以及语句
以上就是微观生活(93wg.com)关于“一块儿学《C程序设计》第八课——指针”的详细内容,希望对大家有所帮助!
评论