c 函数的指针参数 和 数组参数 地址问题

时间:2022-09-05 18:48:25
先贴代码和运行结果
#include<stdio.h>
 int array(char p[])
 {
   printf("%x  %x  %x\n",&p,&(p[0]),&(p[1]));
   return 0;
  }
 int pointor(char *p)
 {
   printf("%x  %x  %x   %x\n",&p,&(p[0]),&(p[1]),++p);
   return 0;  
 }

void main()
 {
    char a[3]={'p','t','e'};
    array(a);
    pointer(a);
   printf("%x  %x  %x\n",&a,&(a[0]),&(a[1]));

}


多个运行结果
1
bfb9ee50  bfb9ee6d  bfb9ee6e
bfb9ee50  bfb9ee6e  bfb9ee6f   bfb9ee6e
bfb9ee6d  bfb9ee6d  bfb9ee6e

2
bfc0f050  bfc0f06d  bfc0f06e
bfc0f050  bfc0f06e  bfc0f06f   bfc0f06e
bfc0f06d  bfc0f06d  bfc0f06e

每个结果到最后一行输出到意思就是  a a[0] a[1] 的地址。

把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???

在pointor中a传进来同样也被转换成指针,在输出结果中p,(p[0]) 的值也不一样,和array的一样情况,其实我觉得pointor和array 前三个输出应该是一样的,可是不是这样,为什么啊???

最后一点就是输出第二行第四个++p的值其实就是a[1]的地址,在输出结果中也得到映证,可是我不理解到是++p的值和&(p[0])怎么是一样的啊????
以及array中到&(p[1])和pointor中的&(p[0])也是一样的???
完全不懂了,


这个看起来估计有点不容易理解吧,但是绝对值得思考啊,希望大牛能给点提示,linux码字不容易哦



12 个解决方案

#1


linux码字不容易哦
楼主,我只想知道到底有多不容易?你用vi吗?

#2


把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???

你打印的是 形参的地址,&p,&(p[0]),根本就是两码事


pointor函数中, 跟上面不一样原因在于 有个++p, 函数从右到左压栈的, ++p是第一个打印的,++p就相当于p+1,因为是前置++,所以在整个表达式语句结束后才会对p+1, 你把++p换成p++看看又会不一样了。

#3


看看 林锐的 《高质量 c /c ++ 编程》

c 中只有传值(by value)一种参数传递方式

#4


char a[]声明的数组数据放在常量区,在这里a不是指针,是常量,你打印&p只把形参地址打印出来了,p[0]才是要访问的地址,也就是a的地址

pointer()函数名你都能写错

printf("%x  %x  %x   %x\n",&p,&(p[0]),&(p[1]),++p);
这一句有缺陷,是在测试编译器么
http://bbs.csdn.net/topics/370153775

#5


引用 2 楼 wind_breez 的回复:
把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???

你打印的是 形参的地址,&amp;p,&amp;(p[0]),根本就是两码事


pointor函数中, 跟上面不一样原因在于 有个++p, 函数从右到左……


&p,&(p[0]),根本就是两码事 这是为什么啊???能仔细说明下吗,
你的pointor函数的说明我应该是看明白了,但是你最后说在表达式语句结束后才+1好像有问题吧,应该是表达式语句开始时候就+1吧,
很谢谢你的回答

#6


引用 4 楼 feiyuren 的回复:
char a[]声明的数组数据放在常量区,在这里a不是指针,是常量,你打印&amp;p只把形参地址打印出来了,p[0]才是要访问的地址,也就是a的地址

pointer()函数名你都能写错

printf("%x  %x  %x   %x\n",&amp;p,&amp;(p[0]),&amp;(p[1]),++p);
这一句有缺陷,是在测试编译器么
http:……

这个是在编译后复制粘贴时候改动的,

#7


引用 4 楼 feiyuren 的回复:
char a[]声明的数组数据放在常量区,在这里a不是指针,是常量,你打印&amp;p只把形参地址打印出来了,p[0]才是要访问的地址,也就是a的地址

pointer()函数名你都能写错

printf("%x  %x  %x   %x\n",&amp;p,&amp;(p[0]),&amp;(p[1]),++p);
这一句有缺陷,是在测试编译器么
http:……

那句话编译通过啊,没问题

#8



/*
(1)把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???
答:你也知道a被转换成了指针,那么int array(char p[]) == int array(char* p) 其实就跟pointer一样了
    对一个指针p取地址,相当于char** pp = &p;是一个指向指针的指针
    a[0]是p里面的首个元素,对这个元素取地址,获得的是这个元素的地址
    &a[0] == p 而不是 &a[0] == pp 明白了吧?

(2)在pointor中a传进来同样也被转换成指针,在输出结果中p,(p[0]) 的值也不一样,和array的一样情况,其实我觉得pointor和array 前三个输出应该是一样的,可是不是这样,为什么啊???
答:这个就有点学问了,正常来说,pointor和array 前三个输出应该是一样,可是千不该万不该,你偏偏在后面有
一个(++p),这就使p的指向变了,比如开始p指向第一个元素,++p之后,p就指向了第二个元素。
而在linux中,函数的参数是从右向左压栈的,所以会先计算++p,然后再计算&(p[1]),&(p[0]),&p,所以就出现了这
样的结果,当然压栈的顺序可能不同系统默认不一样,如果在某些系统中是从左向右压栈的,那么前三个输出是一样的

(3)最后一点就是输出第二行第四个++p的值其实就是a[1]的地址,在输出结果中也得到映证,可是我不理解到是++p的值和&(p[0])怎么是一样的啊????
以及array中到&(p[1])和pointor中的&(p[0])也是一样的???
答:++p你已经把p的地址改变了,现在的p[0]其实是以前的p[1],现在的p[1]其实是以前的p[2]!
*/

#9


引用 8 楼 wugui414 的回复:
C/C++ code?1234567891011121314151617/*(1)把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???答:你也知道a被转换成了指针,那么int array(char p[]) == int……

明白了,谢谢

#10


引用 7 楼 sx666777888 的回复:
引用 4 楼 feiyuren 的回复:char a[]声明的数组数据放在常量区,在这里a不是指针,是常量,你打印&amp;amp;p只把形参地址打印出来了,p[0]才是要访问的地址,也就是a的地址

pointer()函数名你都能写错

printf("%x  %x  %x   %x\n",&amp;amp;p,&amp;amp;(p[0]),&amp;amp;……


你这样写出来的表达式是有副作用的,里面几个子式的求值顺序变化会影响最后的结果,也就是表达式结果编译器相关
当换一个平台就可能是其它的结果了
一个表达式里是不应该对一个改变内容的变量进行一次以上引用的,在你这里,如果使用了p++,这个表达式里就不应该再有对p的访问

#11


理解和思考之前请先 “观察”

VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。
对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。
(Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)

想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”
但我又不得不承认:
 有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物;
 而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。
而我本人属前者。

这辈子不看内存地址和内存值;只画链表、指针示意图,画堆栈示意图,画各种示意图,甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗?本人深表怀疑!
这辈子不种麦不收麦不将麦粒拿去磨面;只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头,压面条,烤面包,……能从本质上理解面粉、理解面食吗?本人深表怀疑!!

提醒:
“学习用汇编语言写程序”

“VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习C和汇编的对应关系。”
不是一回事!

不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!

有人说一套做一套,你相信他说的还是相信他做的?
其实严格来说这个世界上古往今来所有人都是说一套做一套,不是吗?

不要写连自己也预测不了结果的代码!

电脑内存只是一个一维二进制字节数组及其对应的二进制地址;
人脑才将电脑内存中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……

#12


引用 11 楼 zhao4zhong1 的回复:
理解和思考之前请先“观察”!

VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。
对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的……



++++++++++++++++++

#1


linux码字不容易哦
楼主,我只想知道到底有多不容易?你用vi吗?

#2


把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???

你打印的是 形参的地址,&p,&(p[0]),根本就是两码事


pointor函数中, 跟上面不一样原因在于 有个++p, 函数从右到左压栈的, ++p是第一个打印的,++p就相当于p+1,因为是前置++,所以在整个表达式语句结束后才会对p+1, 你把++p换成p++看看又会不一样了。

#3


看看 林锐的 《高质量 c /c ++ 编程》

c 中只有传值(by value)一种参数传递方式

#4


char a[]声明的数组数据放在常量区,在这里a不是指针,是常量,你打印&p只把形参地址打印出来了,p[0]才是要访问的地址,也就是a的地址

pointer()函数名你都能写错

printf("%x  %x  %x   %x\n",&p,&(p[0]),&(p[1]),++p);
这一句有缺陷,是在测试编译器么
http://bbs.csdn.net/topics/370153775

#5


引用 2 楼 wind_breez 的回复:
把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???

你打印的是 形参的地址,&amp;p,&amp;(p[0]),根本就是两码事


pointor函数中, 跟上面不一样原因在于 有个++p, 函数从右到左……


&p,&(p[0]),根本就是两码事 这是为什么啊???能仔细说明下吗,
你的pointor函数的说明我应该是看明白了,但是你最后说在表达式语句结束后才+1好像有问题吧,应该是表达式语句开始时候就+1吧,
很谢谢你的回答

#6


引用 4 楼 feiyuren 的回复:
char a[]声明的数组数据放在常量区,在这里a不是指针,是常量,你打印&amp;p只把形参地址打印出来了,p[0]才是要访问的地址,也就是a的地址

pointer()函数名你都能写错

printf("%x  %x  %x   %x\n",&amp;p,&amp;(p[0]),&amp;(p[1]),++p);
这一句有缺陷,是在测试编译器么
http:……

这个是在编译后复制粘贴时候改动的,

#7


引用 4 楼 feiyuren 的回复:
char a[]声明的数组数据放在常量区,在这里a不是指针,是常量,你打印&amp;p只把形参地址打印出来了,p[0]才是要访问的地址,也就是a的地址

pointer()函数名你都能写错

printf("%x  %x  %x   %x\n",&amp;p,&amp;(p[0]),&amp;(p[1]),++p);
这一句有缺陷,是在测试编译器么
http:……

那句话编译通过啊,没问题

#8



/*
(1)把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???
答:你也知道a被转换成了指针,那么int array(char p[]) == int array(char* p) 其实就跟pointer一样了
    对一个指针p取地址,相当于char** pp = &p;是一个指向指针的指针
    a[0]是p里面的首个元素,对这个元素取地址,获得的是这个元素的地址
    &a[0] == p 而不是 &a[0] == pp 明白了吧?

(2)在pointor中a传进来同样也被转换成指针,在输出结果中p,(p[0]) 的值也不一样,和array的一样情况,其实我觉得pointor和array 前三个输出应该是一样的,可是不是这样,为什么啊???
答:这个就有点学问了,正常来说,pointor和array 前三个输出应该是一样,可是千不该万不该,你偏偏在后面有
一个(++p),这就使p的指向变了,比如开始p指向第一个元素,++p之后,p就指向了第二个元素。
而在linux中,函数的参数是从右向左压栈的,所以会先计算++p,然后再计算&(p[1]),&(p[0]),&p,所以就出现了这
样的结果,当然压栈的顺序可能不同系统默认不一样,如果在某些系统中是从左向右压栈的,那么前三个输出是一样的

(3)最后一点就是输出第二行第四个++p的值其实就是a[1]的地址,在输出结果中也得到映证,可是我不理解到是++p的值和&(p[0])怎么是一样的啊????
以及array中到&(p[1])和pointor中的&(p[0])也是一样的???
答:++p你已经把p的地址改变了,现在的p[0]其实是以前的p[1],现在的p[1]其实是以前的p[2]!
*/

#9


引用 8 楼 wugui414 的回复:
C/C++ code?1234567891011121314151617/*(1)把a传入array()中 a被转换成指针,array的输出到结果到意思应该是 a指针值的地址 a[0]指针值的地址,a[1]指针值的地址,可是结果显示 a a[0]存储到地址不一样!! 这是怎么回事啊???答:你也知道a被转换成了指针,那么int array(char p[]) == int……

明白了,谢谢

#10


引用 7 楼 sx666777888 的回复:
引用 4 楼 feiyuren 的回复:char a[]声明的数组数据放在常量区,在这里a不是指针,是常量,你打印&amp;amp;p只把形参地址打印出来了,p[0]才是要访问的地址,也就是a的地址

pointer()函数名你都能写错

printf("%x  %x  %x   %x\n",&amp;amp;p,&amp;amp;(p[0]),&amp;amp;……


你这样写出来的表达式是有副作用的,里面几个子式的求值顺序变化会影响最后的结果,也就是表达式结果编译器相关
当换一个平台就可能是其它的结果了
一个表达式里是不应该对一个改变内容的变量进行一次以上引用的,在你这里,如果使用了p++,这个表达式里就不应该再有对p的访问

#11


理解和思考之前请先 “观察”

VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。
对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。
(Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)

想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”
但我又不得不承认:
 有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物;
 而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。
而我本人属前者。

这辈子不看内存地址和内存值;只画链表、指针示意图,画堆栈示意图,画各种示意图,甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗?本人深表怀疑!
这辈子不种麦不收麦不将麦粒拿去磨面;只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头,压面条,烤面包,……能从本质上理解面粉、理解面食吗?本人深表怀疑!!

提醒:
“学习用汇编语言写程序”

“VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习C和汇编的对应关系。”
不是一回事!

不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!

有人说一套做一套,你相信他说的还是相信他做的?
其实严格来说这个世界上古往今来所有人都是说一套做一套,不是吗?

不要写连自己也预测不了结果的代码!

电脑内存只是一个一维二进制字节数组及其对应的二进制地址;
人脑才将电脑内存中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……

#12


引用 11 楼 zhao4zhong1 的回复:
理解和思考之前请先“观察”!

VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。
对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的……



++++++++++++++++++