鱼C论坛

 找回密码
 立即注册
查看: 2563|回复: 17

[已解决]C语言中指针相关的初学问题

[复制链接]
发表于 2018-12-25 18:10:15 | 显示全部楼层 |阅读模式
5鱼币
#include <stdio.h>
#include <stdlib.h>

int main()
{
        char *pArray[]={//一个指针数组,存放的指针 
                        "Hello.",
                        "How are you?",
                        "Fine,thank you,and you?",
                        "I'm fine too."};
        
        char (*p)[4] = &pArray; //一个数组指针,指向pArray这个数组
                                           //然后这里也有一个warning
                                           //initialization from incompatible pointer type 
        
        printf("%s\n",*p);//一个*的*p表示pArray,数组名不就是首地址吗?,也就是Hello. 
                                 //但为什么运行后什么结果也没有呢? 
                                 //我想的是两个*的**p应该就是pArray[0]
                                //但是为什么验证后**p是null呢? 

        system("pause");
        return 0; 
 } 
最佳答案
2018-12-25 18:10:16
本帖最后由 行客 于 2018-12-26 22:33 编辑

借ba21的代码,我给你从汇编角度分析一下,希望能加深你的理解:
6:            char *pArray[]={// 指针数组:这个应该明白的吧?结合后面{}内的4个字符串,你可以理解成定义了一个4个char*的数组,就是pArray[0]是一个char *即指向"Hello.",pArray[1]是另一个char*指向"How are you?",后面的两个也一样。
7:                            "Hello.",
00401028   mov         dword ptr [ebp-10h],offset string "Hello." (00424064)        ;这句话的意思是,将"Hello."字符串的首地址00424064放在内存单元[ebp-10h]里;offset string "Hello." 就是指的'H'的地址(00424064)。这里首先要理解"Hello."本身是一个字符串常量,而字符串常量存放在静态存储区(你可以暂时理解为,有一个专门的地方用来存放各个字符串的,这个字符串的地址,如果没有通过编译器(IDE开发环境,就是类如VC6\VC2010等等的IDE编译工具))重新构建编译,这个字符串的地址是不变的)。C语言中的字符串常量以第一个字符的存放地址作标记,它本身就是一个地址常量(这里是00424064),这个地址常量只能作“指针右值”。也就是'H'这个字符的地址常量(00424064)就是这个字符串的标志,这个标志就是一个内存单元的地址(00424064),我们的编译器没办法直接用这个内存地址(00424064)作为代号(即变量名)来和我们开发者交互的,因为每次重新构建编译,这个地址是会发生改变的。所以这个地址(00424064)只能作为指针右值。而这个右值可以赋给一个“同类型”的“指针”。注意这里,我所说的同类型指针是包含了两个部分的,一个是指针,一个是同类型。所谓“指针”,也只是编译器定义了一个可以和我们开发者可以互相沟通的符号,其实也只是一个变量名,只是这个变量名(变量名代表的是一个地址)里存放的是我们字符串的地址,而在这里这个地址指向了字符串。所谓“同类型”,其实对于计算机底层来讲,指针本身是不分类型的,指针里面放的就是一个地址;同类型是编译器的概念,编译器就是将我们所定义的符号、语句编译成汇编,最终再链接成exe;同类型要解决的是能让编译器能识别数据宽度而能达到我们想要的编译结果的问题。在32位WIN的VS编译器默认情况下,指针存放地址(如(00424064))的内存单元的数据宽度为4字节,也就是dword宽度。那么指针本身的数据宽度为4字节。这里可以使用“printf("%d\n",sizeof(pArray[0]));”看一下结果验证。那么这个指针是怎么识别字符串长度的呢,字符串最后有一个我们看不到结束符为"\0"(内存中为16进制00),所以遇到"\0"后,就结束了。那么好,到这里我们可以继续往下看了:
8:                            "How are you?",
0040102F   mov         dword ptr [ebp-0Ch],offset string "How are you?" (00424054)        ;将"How are you?" 的首地址放在[ebp-0Ch]单元,以下类同
9:                            "Fine,thank you,and you?",
00401036   mov         dword ptr [ebp-8],offset string "Fine,thank you,and you?" (00424038)
10:                           "I'm fine too."};
0040103D   mov         dword ptr [ebp-4],offset string "I'm fine too." (00424028)
11:
12:           char *(*p)[4] = &pArray; //数组指针,你上面的变量pArray的类型是char *[],同时后面{}内确定了是4个元素,所以定义指针需要定义为char *(*p)[4] 
00401044   lea         eax,[ebp-10h]        ;将[ebp-10h]的地址放在eax。。
00401047   mov         dword ptr [ebp-14h],eax        ;将[ebp-10h]的地址通过eax又放在了[ebp-14h]里
13:
14:           printf("%s\n",**p);// 1层解引用行的地址,再次解引用,该行第1个元素的地址
0040104A   mov         ecx,dword ptr [ebp-14h]        ;
0040104D   mov         edx,dword ptr [ecx]        ;
0040104F   push        edx
00401050   push        offset string "%s\n" (00424024)
00401055   call        printf (004011b0)
0040105A   add         esp,8

最佳答案

查看完整内容

借ba21的代码,我给你从汇编角度分析一下,希望能加深你的理解:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-25 18:10:16 | 显示全部楼层    本楼为最佳答案   
本帖最后由 行客 于 2018-12-26 22:33 编辑

借ba21的代码,我给你从汇编角度分析一下,希望能加深你的理解:
6:            char *pArray[]={// 指针数组:这个应该明白的吧?结合后面{}内的4个字符串,你可以理解成定义了一个4个char*的数组,就是pArray[0]是一个char *即指向"Hello.",pArray[1]是另一个char*指向"How are you?",后面的两个也一样。
7:                            "Hello.",
00401028   mov         dword ptr [ebp-10h],offset string "Hello." (00424064)        ;这句话的意思是,将"Hello."字符串的首地址00424064放在内存单元[ebp-10h]里;offset string "Hello." 就是指的'H'的地址(00424064)。这里首先要理解"Hello."本身是一个字符串常量,而字符串常量存放在静态存储区(你可以暂时理解为,有一个专门的地方用来存放各个字符串的,这个字符串的地址,如果没有通过编译器(IDE开发环境,就是类如VC6\VC2010等等的IDE编译工具))重新构建编译,这个字符串的地址是不变的)。C语言中的字符串常量以第一个字符的存放地址作标记,它本身就是一个地址常量(这里是00424064),这个地址常量只能作“指针右值”。也就是'H'这个字符的地址常量(00424064)就是这个字符串的标志,这个标志就是一个内存单元的地址(00424064),我们的编译器没办法直接用这个内存地址(00424064)作为代号(即变量名)来和我们开发者交互的,因为每次重新构建编译,这个地址是会发生改变的。所以这个地址(00424064)只能作为指针右值。而这个右值可以赋给一个“同类型”的“指针”。注意这里,我所说的同类型指针是包含了两个部分的,一个是指针,一个是同类型。所谓“指针”,也只是编译器定义了一个可以和我们开发者可以互相沟通的符号,其实也只是一个变量名,只是这个变量名(变量名代表的是一个地址)里存放的是我们字符串的地址,而在这里这个地址指向了字符串。所谓“同类型”,其实对于计算机底层来讲,指针本身是不分类型的,指针里面放的就是一个地址;同类型是编译器的概念,编译器就是将我们所定义的符号、语句编译成汇编,最终再链接成exe;同类型要解决的是能让编译器能识别数据宽度而能达到我们想要的编译结果的问题。在32位WIN的VS编译器默认情况下,指针存放地址(如(00424064))的内存单元的数据宽度为4字节,也就是dword宽度。那么指针本身的数据宽度为4字节。这里可以使用“printf("%d\n",sizeof(pArray[0]));”看一下结果验证。那么这个指针是怎么识别字符串长度的呢,字符串最后有一个我们看不到结束符为"\0"(内存中为16进制00),所以遇到"\0"后,就结束了。那么好,到这里我们可以继续往下看了:
8:                            "How are you?",
0040102F   mov         dword ptr [ebp-0Ch],offset string "How are you?" (00424054)        ;将"How are you?" 的首地址放在[ebp-0Ch]单元,以下类同
9:                            "Fine,thank you,and you?",
00401036   mov         dword ptr [ebp-8],offset string "Fine,thank you,and you?" (00424038)
10:                           "I'm fine too."};
0040103D   mov         dword ptr [ebp-4],offset string "I'm fine too." (00424028)
11:
12:           char *(*p)[4] = &pArray; //数组指针,你上面的变量pArray的类型是char *[],同时后面{}内确定了是4个元素,所以定义指针需要定义为char *(*p)[4] 
00401044   lea         eax,[ebp-10h]        ;将[ebp-10h]的地址放在eax。。
00401047   mov         dword ptr [ebp-14h],eax        ;将[ebp-10h]的地址通过eax又放在了[ebp-14h]里
13:
14:           printf("%s\n",**p);// 1层解引用行的地址,再次解引用,该行第1个元素的地址
0040104A   mov         ecx,dword ptr [ebp-14h]        ;
0040104D   mov         edx,dword ptr [ecx]        ;
0040104F   push        edx
00401050   push        offset string "%s\n" (00424024)
00401055   call        printf (004011b0)
0040105A   add         esp,8

评分

参与人数 1荣誉 +5 鱼币 +5 贡献 +3 收起 理由
sunnyrubik + 5 + 5 + 3

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-25 19:11:36 | 显示全部楼层
本帖最后由 csilovelxl 于 2018-12-25 19:20 编辑
#include <stdio.h>
#include <stdlib.h>

int main()
{
        char *pArray[]={//一个指针数组,存放的指针 
                        "Hello.",
                        "How are you?",
                        "Fine,thank you,and you?",
                        "I'm fine too."};
        
        char **p = pArray; //pArray是指向'H'的指针的指针,所以用指向字符指针的指针来指向
                                    
        
        printf("%s\n",*p);//一个*的*p表示pArray,数组名不就是首地址吗?,也就是Hello. 
                                 //但为什么运行后什么结果也没有呢? 
                                 //我想的是两个*的**p应该就是pArray[0]
                                //但是为什么验证后**p是null呢? 

        system("pause");
        return 0; 
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-25 19:22:07 | 显示全部楼层
1 //然后这里也有一个warning
(*p)[4]  是数组指针, 该指针指向的是一个数组而不是指针数组
那么正确的代码应该是 如:
char a[]{'a','b','c'}
char (*p)[4] = &a;

2:一个*的*p表示pArray,数组名不就是首地址吗?,也就是Hello.
char a[]{'a','b','c'}
a  是数组第一个元素的地址
&a 是数组的地址 (整个数组的首地址)

其它的看注释:
#include <stdio.h>
#include <stdlib.h>

int main()
{
        char *pArray[]={// 指针数组 
                        "Hello.",
                        "How are you?",
                        "Fine,thank you,and you?",
                        "I'm fine too."};
        
        char *(*p)[4] = &pArray; //数组指针,本该指向数组;而你上面是指针数组。所以该处应该是指针类型
        
        printf("%s\n",**p);// 1层解引用行的地址,再次解引用,该行第1个元素的地址

        system("pause");
        return 0; 
}

// 相对于一维数组来说 这是一行 { "Hello.","How are you?","Fine,thank you,and you?","I'm fine too."}
// 相于上面的{}这一行来说"Hello."是该行的第一个元素。

评分

参与人数 1荣誉 +3 鱼币 +3 贡献 +3 收起 理由
sunnyrubik + 3 + 3 + 3

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-12-25 21:41:16 | 显示全部楼层
本帖最后由 sunnyrubik 于 2018-12-25 21:59 编辑
ba21 发表于 2018-12-25 19:22
1 //然后这里也有一个warning
(*p)[4]  是数组指针, 该指针指向的是一个数组,而不是指针数组
那么正确 ...


您好,经过您的提点char *(*p)[4] = &pArray;我已经懂了。
但是
printf("%s\n",**p);// 1层解引用行的地址,再次解引用,该行第1个元素的地址
这句话我还是有点迷糊,第一行的地址不就是第一个元素的地址吗?
就比如说int array[2][3]={{1,2,3},
                                {4,5,6}};
&array[0][0](第一个元素的地址)和array(第一行的地址)不是一样的吗?
是不是说*p = pArray,pArray是Hello这个指针的首地址,所以需要再加一个*解引用得到Hello这个指针这个意思呢?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-25 23:19:07 | 显示全部楼层
sunnyrubik 发表于 2018-12-25 21:41
您好,经过您的提点char *(*p)[4] = &pArray;我已经懂了。
但是
printf("%s\n",**p);// 1层解引用行 ...

第一行的地址不就是第一个元素的地址
我说这个你迷糊啥哦。是一个值,不代表是一个意思对吧。要不也不会有行地址,和第一个元素的说法。既然有不同的说法肯定有不同的意义。

把思维放宽点就行了,行地址==第一个元素的地址,都知道。如果我再跟不懂的人说行地址==第一个元素的地址意义是不同的,我觉得人家也会懂。如果说1 == 1.0你能懂,难道你能不懂,1和1.0意义是不同的?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-26 18:36:45 | 显示全部楼层
楼主您好。可能以上几楼的答案有点抽象,我试试能不能说的通俗一点。
首先,回答第一个问题。
(1)char (*p)[4] = &pArray; //一个数组指针,指向pArray这个数组,关于赋值问题,这里我在vc6.0是直接报错的。正确的是  
char * (*p)[4] = &pArray;
首先就是,因为pArray是  char * 类型的数组,所以肯定是要数组名之前肯定是要char * 类型。
(2)一个*的*p表示pArray,数组名不就是首地址吗?
这里的 *p 相当于数组名,也就是说 一个*的p是数组的地址,**p才是数组的第一个元素。
(3)至于后面的问题就完全是由于之前的赋值错误引起的了。
若能解决楼主问题,希望能给个最佳。如果还有疑问,欢迎提问哦
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-12-26 19:20:31 | 显示全部楼层
本帖最后由 sunnyrubik 于 2018-12-26 19:42 编辑


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-26 19:33:01 | 显示全部楼层
sunnyrubik 发表于 2018-12-26 19:20
嗯,我的意思是说printf("%p   %p ",*p,**p);
为什么会结果不同呢?明明感觉地址都是相同的啊,就像您说 ...

我用了1个多小时给你写了汇编的分析,而且完全把你当小白写的回复。你能不能用心看看啊?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-12-26 19:36:32 | 显示全部楼层
小酒酒呢 发表于 2018-12-26 18:36
楼主您好。可能以上几楼的答案有点抽象,我试试能不能说的通俗一点。
首先,回答第一个问题。
(1)char  ...

请问后面方括号里的数字有意义吗?在小甲鱼老师用 整型二维数组距离时候,后面的数字主要是为了和矩阵的列数对应,我就盲目模仿了一下,但这里指针数组是一位数组,是不是没有必要啊?
另外,您知道为什么 *p的地址和**p的地址为什么不一样吗?我感觉**p已经不再是一个地址了,难道不是指向hello的指针了吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-12-26 19:38:29 | 显示全部楼层
本帖最后由 sunnyrubik 于 2018-12-26 19:40 编辑
行客 发表于 2018-12-26 19:33
我用了1个多小时给你写了汇编的分析,而且完全把你当小白写的回复。你能不能用心看看啊?


非常感谢你哈,但主要是看着你的回答特别长,我觉得有必要留更长的时间阅读。所以留在最后好好看看的哈。真的万分感谢(正准备看)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-12-26 20:04:58 | 显示全部楼层
行客 发表于 2018-12-26 13:20
借ba21的代码,我给你从汇编角度分析一下,希望能加深你的理解:

为什么地址424064  424054  424038  424028的差不一样呢?数组不是连续的吗?他们存的不都是一个指针吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-26 21:25:05 | 显示全部楼层
sunnyrubik 发表于 2018-12-26 20:04
为什么地址424064  424054  424038  424028的差不一样呢?数组不是连续的吗?他们存的不都是一个指针吗?

不是的,这几个地址是字符串的首地址,不是数组的地址。每一个字符串是一个以'\0'结束的字符串常量,这几个常量都存在静态存储区。这几个字符串的首地址是不连续的。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-26 21:26:58 | 显示全部楼层
本帖最后由 行客 于 2018-12-26 21:33 编辑

我觉得你对数组和指针了解的不是太清晰,我刚刚写了一个东西,希望能对你有帮助。这个和你的例子不一样,这里我们首先分析的是一维数组。你一定要先搞清楚这个:
#include <stdio.h>
#include <stdlib.h>

int main()
{
        char pArray[]={'h','e','l','l','o','.'}; //char类型的一维数组
        
        char (*p) [6]= &pArray; //一个与char pArray[]同类型的指针。
                                              //这里我们可以与int类型指针对照,加强理解:如下代码
                                              //int n=1; int *p=&n;
                                              //int *p=&n; 也可以变形为 int (*p)=&n; 就和上面一样了
                                              //这样就可以理解了

                //数组名本身就是一个常量指针,为了进一步加强理解,我们继续来看另一个指针定义的办法
                char *p1=pArray;                //pArray本身就是一个地址
        
                printf("%p\n",p); //打印0012FF78(可能机器不同,具体打印地址不同)
                printf("%p\n",p1); //打印0012FF78(可能机器不同,具体打印地址不同)
                printf("%p\n",pArray); //打印0012FF78(可能机器不同,具体打印地址不同)
                printf("%c\n",*p[0]); //打印h 这里需要注意,如果是printf("%c\n",*p); 或 printf("%c\n",p[0]); 是不能正常打印的
                printf("%c\n",*p1); //打印h
                printf("%c\n",*pArray); //打印h

        system("pause");
        return 0; 
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-26 21:58:21 | 显示全部楼层
sunnyrubik 发表于 2018-12-26 19:36
请问后面方括号里的数字有意义吗?在小甲鱼老师用 整型二维数组距离时候,后面的数字主要是为了和矩阵的 ...

首先,数字是有意义的,他跟指针的范围挂钩。
第二,*p是数组的地址,他是char ** 类型。**p对*p解一次指针,是char * 类型,也就是字符串。

评分

参与人数 1荣誉 +5 鱼币 +5 贡献 +3 收起 理由
sunnyrubik + 5 + 5 + 3

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-12-26 23:28:39 | 显示全部楼层
小酒酒呢 发表于 2018-12-26 21:58
首先,数字是有意义的,他跟指针的范围挂钩。
第二,*p是数组的地址,他是char ** 类型。**p对*p解一次 ...

谢谢你的回答,我的C语言之路还很漫长啊,哎
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-12-26 23:31:43 | 显示全部楼层
行客 发表于 2018-12-26 21:26
我觉得你对数组和指针了解的不是太清晰,我刚刚写了一个东西,希望能对你有帮助。这个和你的例子不一样,这 ...

谢谢您,费心了。我会继续努力的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-12-27 09:33:21 From FishC Mobile | 显示全部楼层
sunnyrubik 发表于 2018-12-26 23:28
谢谢你的回答,我的C语言之路还很漫长啊,哎

万事开头难。等你掌握了比较底层的c。再深入理解一下汇编。其他的高级语言对你都不是问题,加油吧,少年。你距离大牛已经不远了,一定别中途放弃
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-1-13 13:55

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表