疑问:
在给一个函数传递一个运行参数时,不能使用argv+int的形式,必须使用*(argv+int)。
如果argv+int后,地址是字符串首字的地址,那么就不需要取值,而现在需要取值,那就说明argv+int不是字符串首字的地址。既然*(argv+int)所取到的值才是字符串首字的地址,那就说明argv+int的值是一个地址,假设为地址A,地址A中储存的值才是字符串首字的地址。
那二维字符数组(char name[x][y])的一维是否为指针数组?
分析:
创建指针,字符数组
char * pointer;
char ** pointer2;
char str[] = “Hello World”;
char *str2 = “Hello World”;
char string[5][6] = {“AAAAA”, “BBBBB”, “CCCCC”, “DDDDD”, “EEEEE”};
char *string2[5] = {“AAAAA”, “BBBBB”, “CCCCC”, “DDDDD”, “EEEEE”};
char * pointer;
char *pointer意思是pointer的值是一个地址,在64位程序上,会创建一个八字节的区域用来储存pointer的值。
char ** pointer2:
*pointer中的*是取值运算符,等价于*地址,意思是取出指定地址中的值,假设pointer代表地址A,因此*pointer等价于*地址A,会取出地址A中的值,假设取到的值为地址B。
**pointer,首先看成*(*pointer),上面已经说了,*pointer取到的是地址B,因此**pointer等价于*地址B。
char str[] = “Hello World”;
创建一个一维的字符数组,str在运行时会被实际上是一个地址,因此str的地址中储存着H的地址。
char *str2 = “Hello World”;
在程序运行时,”Hello World”将会被加载到内存当中,另外创建一个八字节的区域储存str2的值,str2的值将会是H的地址。
char string[5][6] = {“AAAAA”, “BBBBB”, “CCCCC”, “DDDDD”, “EEEEE”};
创建一个二维字符数组,一共三十字节,储存方式应该类似于下图:
A | A | A | A | A | \0 | B | B | B | B | B | \0 | C | C | C | C | C | \0 | D | D | D | D | D | \0 | E | E | E | E | E | \0 |
string在运行时实际上是一个地址,因此string的地址中储存着第一个A的地址。
char *string2[5] = {“AAAAA”, “BBBBB”, “CCCCC”, “DDDDD”, “EEEEE”};
类似于char *str2。
[]运算符优先级比*高,因此最终会创建一个一维字符串指针数组,64位程序,每个地址需要占用八字节,因此一共会占用四十字节,储存方式如图所示:
“AAAAA\0″中首字节的地址 | “BBBBB\0″中首字节的地址 | “CCCCC\0″中首字节的地址 | “DDDDD\0″中首字节的地址 | “EEEEE\0″中首字节的地址 |
做出假设:
根据上述的分析,char name[x][y]的一维并非是一个指针数组。
实验检验:
令pointer = string:
既然pointer得到的值是首A的地址,那就说明string储存的也是首A的地址,string本身就是一个char *,因此可以预言:pointer = *string的值为’A’
而实际上,pointer等于首A的地址:
预言不成立。
分析:
*string取得的是首A的地址,那就说明string储存着地址,假设该地址为“地址1”,因此”*string”等价于”*地址1″。地址1中储存着首A的地址,因此”*地址1″将会取到首A的地址,那么**string,将会是”*首A的地址”,值会是’A’
根据实验结果:
pointer = 0x41,也就是65,ASCII表中,65即A,预言成立!
分析:
假设string=地址A, *string=地址B,首A的地址=地址C
一:pointer = string得到的是首A的地址,那就说明:地址A=地址C
二:pointer = *string得到的是首A的地址,那就说明:地址B=地址C
根据一,string指向的是地址C,那么*string取到的应该是地址C的值即’A’;
根据二,string指向的是一个储存的地址C的地址,而不是地址C,否则*string就不可能得到地址C;
上述实验存在矛盾,无法得出结论。
这里要提起scanf(),放出数个正确的使用示例:
1 int num;
2 char str3[5];
3 char str4[2][5];
4 scanf(“%d”, &num);
5 scanf(“%s”, str3);
6 scanf(“%s”, str4[1]);
7 scanf(“%c”, &str4[0][0]);
我们都知道,scanf要求参数二及其之后的参数都是一个地址。
既然第五行的str3不需要使用取地址符,那就说明str3就是一个指针,str3中储存的地址是数组首元素的地址。
同理,str4[1]储存的地址是第二维数组首元素的地址,这不就说明了,二维数组的第一维度是一个指针数组吗?
第七行,当指向了具体元素时,就需要用取地址符号了,那就说明str4[0][0]本身代表一个具体值,而不是指向某个元素的地址。
这里似乎存在着一个隐式取值,我们知道程序运行时,变量实际上是一个地址,例如上述的num,运行时将会是一个地址,printf(“%d”, num);输出的是num的值,而不是num这个地址,即C会对变量隐式进行一次取值,num代表”*num所代表的地址”
因此第七行中的str4[0][0]本身代表着元素的地址,需要取地址运算符号。
为何str4[0]的值是一个地址,而str4[0][0]的值却是元素?不得不考虑一下C编译器对多维数组的处理了。
如果str4[0]是一个指针,即储存着指向str4数组第一个元素的地址,既然str4[0][0]可以代表一个元素,那么可以猜出,这里将会进行一次取值运算,即取出str4[0]的值,即一个地址,第二个[0]代表着地址偏移0个char单位,因此str4[0][0]本身是第一个元素的地址,要用取地址运算符。
那么岂不是可以同理推广到第一维度吗?如果str4储存的值是一个地址,那么str4[0]将会进行一次取值运算,取出str4的地址,另其偏移0个?单位,这个地址中,储存着数组首元素的地址,因此使用scanf时,隐式取值取到了首元素的地址。
那么也就是说,str4的值是指向数组中第一个指针的地址,该地址是数组首元素的地址,这里看出来,二维数组的一维的确是指针数组。
如果这样,那么pointer的值就不应该等于数组首元素的地址,存在矛盾。
那么只好提出另一种解释了。
根据scanf()的正确用法,无疑str3中保存着数组首元素的地址,当使用str3[0]时,需要使用取地址运算符,因此隐式取值说法应该是正确的。
那str4的值,会不会也是数组首元素的地址?为了得到验证,声明一个char指针,使其值=str4:
这里就很明显了,即使是二维数组,str4是一个指针,其值仍然是数组首元素的地址,而不是一个指针数组首元素的值。
但有一个矛盾未能解决,那就是为何*str4不是首元素的值而是首元素地址?
首先我们这里是对pointer指针进行操作,编译器肯定是按照一个普通指针处理我们的操作,因此可以排除是编译器针对数组进行的附加操作。
我们使用*str4时,是对数组操作,编译器可能看出这里是对多维数组取值,因此对我们的代码进行了修改,另我们的*str = str了。
为了得到验证,我们对pointer进行取值运算:
的确得到了数组首元素,解释得到了验证。
这里还存在一个问题,会不会是编译器知道我们在对多维数组进行操作,因此令pointer = string的值为string[0]的值?也就是让我们pointer的值变为指针数组第一个指针的值,毕竟*string = string嘛。
如果是这样,那么就说明一维数组是一个指针数组咯,那么令pointer偏移一个char指针单位,就可以取得string[1][0]的地址,对其取值,应该取到’B’。
如果string[0]是一个指向数组首元素的指针,那么&string[0]就可以得到储存这个指针的地址,令pointer = &string[0];
很可惜我最终还是数组首元素的地址,取不到这个“指针的地址”,也许,这个“指针的地址”根本就不存在。
暂且可以结论:二维字符数组的一维并非一个指针数组。