最后一天,聊聊关于指针自身的东西,在开始之前,我们再来明晰一次指针的属性:指针是一个储存地址的变量。
今天来引入一个新概念——指针数组。当数组的每一个元素都是指针的时候,就变成了指针数组:
char*SZname[]={"Fortran", "C", "C++"};
这个数组具有变量的性质,可以是静态的,全局的或者是局部的,指针数组具有类型,其存放的一定是和他类型相同的指针,(每个元素都存放在 data 区的 const 小区里)值得注意的是,和一般的数组一样,指针数组里的元素是连续排列存放于一起的,但是指针所指的元素有可能连续分布也有可能不是,但是与否都无关紧要,重点是第一点。
指针数组在内存情况
既然指针是一个变量,那么就有相对应的指针指向它,对于指针数组来说,一个数组的名字就是指针常量,根据前文所述,有指向常量的指针,那么数组的名就是指向指针的指针,即二级指针,如何定义一个二级指针呢?,例如就描述了一个二级指针:
char*pc[]={"a","b","c"}; char**ppc; ppc=&pc;
Amazing!二级指针就是多一个*。这段代码描述了一个指向字符指针的指针,注意,是指向字符指针,因为字符指针数组里,元素用得是“ ”双引号而不是单引号,单引号表示的是字符而非字符串,也就五字符数组一说了。(看不懂这一段的同学要复习字符串和字符数组的概念)再说,字符结尾没有‘\0’的标志,更加不是字符数组了。这点尤为注意!
在前面学习指针的时候我们提到,传递一个数组给函数就是传递一个指针,相同的,传递一个指针数组给函数就是传递一个二级指针给函数。下面的程序是应用实例:打印字符指针数组的元素
//second grade pointer 2021-7-21-21:28 #include<iostream> using namespace std; void print(char*[],int); int main() { char*cpn[]={"I","am","Iron","Man"}; int Long=sizeof(cpn)/sizeof(char*);//这里很重要,记得除以的是 char*指针的地址长度 print(cpn,Long);//数组名就是二级指针! return 0;//良好习惯! } void print(char*cp[],int len) { for(int n=0;n<len;n++) cout<<(int*)cp[n]<<" "//注意强制类型转换的形式 <<cp[n]<<endl; }
结果是:
"/Users/yuwenao/C++ Learning/cmake-build-debug/untitled5" 0x10014ff44 I 0x10014ff46 am 0x10014ff49 Iron 0x10014ff4e Man 进程已结束,退出代码为 0
值得注意的是,在 ISO C++11 里,出现了这样的 warning:
ISO c++ 11不允许从字符串到char *的转换
IDE 使用 C++11 的同学要注意,应该选择较低标准的 C++98,这是最主流的标准。
这里还有一个点要补充,就是:输出字符指针就是输出字符串
二级指针传递图,找了张内存图
这里有一个和他很像的概念,void*型指针,也就是空指针。
同学也许接触过 NULL,它代表无,空白,然后你就开始懵逼了,void 型指针似乎也是代表空白吧,他们是一个东西吗?
NULL 是一个值,代表空白,而 void 型指针代表一个指针类型,代表“不指向任何类型数据的指针”,就是“空指针”,而它的值可以是任何数,当然也可以是 NULL,自然,任何指针的值都可以是 NULL,且建议,在定义一个指针但是暂且没有用武之地时,将它赋予 NULL 值,避免它成为一个野指针,野指针十分危险,十分危险,十分危险!说了三次了,别创造野指针!当然,最好就是用的时候在定义,这样绝对安全。
在计算机看来,C++程序不过只是操作系统调用的函数。
额,说这个是不是有点恍惚,学了一大堆 C++结果说这些,敢情学的东西只是一点皮毛?
这个留待大家慢慢领悟了,我们继续往下走。
很多程序都需要从命令行输入参数,;如在 DOS(Win 下的命令提示符环境)中,copy 需要两个参数,type 需要一个参数
c>copy filea fileb c>type c:autoexec.bat
操作系统将命令行参数以字符串的形式传递给 main()函数,因此 main()的第一行形式为:
int main(int argc,char*argv[]) { //..... }
C/C++语言中的 main 函数,经常带有参数 argc,argv,如下:
int main(int argc, char** argv)
或者
int main(int argc, char* argv[])
在上面代码中,argc 表示命令行输入参数的个数(以空白符分隔),argv 中 存储了所有的命令行参数。假如你的程序是 hello.exe,如果在命令行运行该程序 运行命令为:
hello.exe Menou16
那么,argc 的值是 3,argv[0]是"hello.exe",argv[1]是"Menou",argv[2]是"16"。
自然,你可以吧 argc 和 agrv 称呼为“LittleDog”和“BigCat”,全凭你喜欢,额,最好不要,有点愚蠢。
剩余的命令行参数内容考虑日后专门出文解释。【其实是笔者学术水平所限,抱歉】
在程序运行中,全局变量存放在data区,局部变量存放在栈区,申请的动态空间存放在堆区。函数代码是程序的算法指令部分,它们同样也占有内存空间,存放在代码(code)区。
每个函数都有地址。指向函数地址的指针称为函数指针。函数指针指向代码区中的某个函数,通过函数指针可以调用相应的函数。
1、定义函数指针
int (*func)(char a, char b);
int 表示函数的返回类型,(*func)被括号括住的是*和函数的名字,表明这个名字是一个指针,后面的括号(char a, char b)表示正在进行函数说明,也就是声明,该函数具有两个字符型参数a,b;
函数也有静态,局部,全局之分,也占据内存空间,占 4 个字节的空间(32、64 位,不会有人用 16 位吧不会吧不会吧,16 位中是 2 个字节)。()优先级高于*,所以看清是否在进行函数指针的定义。
int*func(char a, char b); //这不是函数指针的定义,而是函数的声明
func 先于 ()结合成为函数的声明,再得到返回类型是 int*型指针。
2、函数指针内在差别
不含[ ]的数组名就是地址,不做函数调用的函数名(即有()的)就是地址,因此可以将函数名直接赋予函数指针。另一方面,函数也是有差异的,不同的参数和返回类型,造就了不同的函数,因此指针也有差异,这就导致了指针间的互动有限制。
剩余的函数指针内容考虑日后专门出文解释。【其实是笔者学术水平所限,抱歉】