C/CPP, 编程

char **name,char *name[], char name[x][y], char *name[]

疑问:

在给一个函数传递一个运行参数时,不能使用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的地址:

Pointer

预言不成立。

分析:

*string取得的是首A的地址,那就说明string储存着地址,假设该地址为“地址1”,因此”*string”等价于”*地址1″。地址1中储存着首A的地址,因此”*地址1″将会取到首A的地址,那么**string,将会是”*首A的地址”,值会是’A’

根据实验结果:

Pointer-2

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:

Pointer-3

这里就很明显了,即使是二维数组,str4是一个指针,其值仍然是数组首元素的地址,而不是一个指针数组首元素的值。

但有一个矛盾未能解决,那就是为何*str4不是首元素的值而是首元素地址?

 

首先我们这里是对pointer指针进行操作,编译器肯定是按照一个普通指针处理我们的操作,因此可以排除是编译器针对数组进行的附加操作。

我们使用*str4时,是对数组操作,编译器可能看出这里是对多维数组取值,因此对我们的代码进行了修改,另我们的*str = str了。

 

为了得到验证,我们对pointer进行取值运算:

Pointer-4

的确得到了数组首元素,解释得到了验证。

 

这里还存在一个问题,会不会是编译器知道我们在对多维数组进行操作,因此令pointer = string的值为string[0]的值?也就是让我们pointer的值变为指针数组第一个指针的值,毕竟*string = string嘛。

如果是这样,那么就说明一维数组是一个指针数组咯,那么令pointer偏移一个char指针单位,就可以取得string[1][0]的地址,对其取值,应该取到’B’。

如果string[0]是一个指向数组首元素的指针,那么&string[0]就可以得到储存这个指针的地址,令pointer = &string[0];

Pointer-5

很可惜我最终还是数组首元素的地址,取不到这个“指针的地址”,也许,这个“指针的地址”根本就不存在。

 

暂且可以结论:二维字符数组的一维并非一个指针数组。

(95 Posts)

自信、努力、活出精彩;以前未所见的颜色,绘大千世界!

View all author’s posts

Leave a reply