回 帖 发 新 帖 刷新版面

主题:一个怪程序

    这是一个在别人的书里摘下的程序,它的结果有些怪。不知道有没有人知道哪里怪和怎么怪??

char ga[]="abcdefghijklmn";
void my_array_func(char ca[10]){
    printf(" addr of array param = %#x \n",&ca);
    printf(" addr (ca[0]) = %#x \n",&(ca[0]));
    printf(" addr (ca[1]) = %#x \n",&(ca[1]));
    printf(" ++ca = %#x \n\n",++ca);
}
void my_pointer_fun(char *pa){
    printf(" addr of ptr param = %#x \n",&pa);
    printf(" addr (pa[0]) = %#x \n",&(pa[0]));
    printf(" addr (pa[1]) = %#x \n",&(pa[1]));
    printf(" ++pa = %#x \n\n",++pa);
}

main(){
    printf(" addr of global array = %#x \n",&ga);
    printf(" addr (ga[0]) = %#x \n",&(ga[0]));
    printf(" addr (ga[1]) = %#x \n",&(ga[1]));
    my_array_func(ga);
    my_pointer_fun(ga);
}

以上是程序,如果有人知道的话,麻烦告诉我。先谢了。

回复列表 (共18个回复)

沙发

//这是输出结果,楼主对哪个值感到不解?请逐条提出,我会给你满意的答复。
addr of global array = 0x94
addr (ga[0]) = 0x94
addr (ga[1]) = 0x95
addr of array param = 0xffc
addr (ca[0]) = 0x94
addr (ca[1]) = 0x95
++ca = 0x95

addr of ptr param = 0xffc
addr (pa[0]) = 0x94
addr (pa[1]) = 0x95
++pa = 0x95

板凳

    看一下你的结果,在第一行,全局数组的首地址是0x94,在参数数组的首地址
是0xffc.
    不是说这是传址调用吗?如果是这样的话,函数就应该是处理同一个数组才是。
    怎么首地址会不同呢?
    关于这个问题我有自己的看法。不过不知道大家的看法是不是一样。

3 楼

什么叫传址调用?

先说函数调用过程中参数传递的本质:
就是实际参数把自己的值传给形式参数,难道形式参数的地址也要跟实际参数一样吗?如果一样那不就是同一个变量了吗?

任何形式的参数调用(对C++中的引用调用不在此列)都是让形式参数的值保存实参的值而已。而传址调用只不过在这个机制上多加了一套外衣,仍然不会摆脱这个本质。
切看如何实现传址调用:
如果你打算进行传址调用,那么你把原本打算可以用传值调用的实参的地方改为该实参的地址,并把原来形式参数的类型改为该类型对应的指针类型,然后再进行调用。这样被调函数就得到了所谓的原来实参的地址,实质上这时的实参已经不是原来的“实参”了。而现在的实参难道不是使用了传值调用吗?只不过这个“值”变成了原来的“址”而已,在原来的传递参数的立场上可以叫做传址,难道现在参数类型都变了,还能叫传址吗?
如果不清楚我的解释,请继续看我的示例:

=====================================================
传值调用
=====================================================
调用代码:
int a = 100;
fun1(a);//这里的a可以直接换成100

被调用函数:
void fun1(int aa)
{
   aa++;
}
=====================================================
传址调用
=====================================================
调用代码:
int b=100;
fun2(&b);//使用变量名的地址
被调用函数:
void fun2(int *bb)//
{
    (*bb)++;
}

=============================================================================
对于第一种情况,被调函数的参数aa有自己不同于a的地址,而且该地址只有在fun1被调用时才有效,一旦fun1被调用,aa便生成了内存实例,该内存空间将保存实参a的数值,根本不会得到a的地址。所以,函数体内无论做什么操作,这里示例为aa++,都不会影响实参a。此之为传值调用。

对于第二种情况,被调函数的参数bb有自己不同于b的地址,而且该地址只有在fun2被调用时才有效,一旦fun2被调用,bb便生成了内存实例,该内存空间将保存实参的数值,(请注意,这时的实参你会认为是b吗?如果你这样认为,那你就大错特错了)该值是什么呢?就是用&b得到的b的地址“值”(之所以我把这个值加上引号,提请大家注意,这里仍然是传值)。因为指针bb得到了b的地址,*bb就理所当然的成了b的内容,对*bb的修改就成了对b的内容的修改,难道你们能做到“把b的内存地址也给修改了”这样的目的吗?所以,函数体内对*bb做什么操作,这里示例为(*bb)++,都将影响实参(&b)的值b。大家仔细看看,实际传进来的只是一个数值而已,只不过如果这个数值恰好是一个有效的内存地址的话,那么将会达到修改此内存的内容的目的而已。此之为传址调用。

再次声明:关于c++中的引用调用机制,不在此讨论范围之内。

4 楼

偶是菜鸟,不过在此斗胆说一句,meteor135你的说法不对!
这个函数形参接受的就是“实参”地址!只不过输出有问题,你若把&pa和&ca改成pa,ca就会发现这一点。
&pa和&ca是什么呢?是指针变量的地址啊。

5 楼

我已把我原本做的解释丰富化(没有修改本意),请楼上同仁及其他感兴趣人士参评!

6 楼

看了上面的内容我想问个问题:
char a[]="abcdedf";

void main()
{
char *cp=a;
printf("\n  a=%#x",a);
printf("\n  cp=%#x",cp);
printf("\n*******");
printf("\n  &a=%#x",&a);
printf("\n  &cp=%#x",&cp);
}   
这里的a是地址常量吗?就象其他类型常量一样吗?
为什么printf("\n   &a=%#x",&a):里的&a就能用但char *cp=&a;就编译不了?
  请各位老师指点一下,  谢谢!

7 楼

    在你的程序里,a表示的是数组的首地址。对首地址a进行&运算,是一个所谓“未定义行为”。
    编译器对未定义行为的自由度很大,它可以对此行为发出警告,或者认为是一个错误,又或者是接受。
    对你这个问题,我用了几个编译器作了尝试。为了适应不同的编译器,我对你的程序作了下面的修改:

#include "stdio.h"
char a[]="abcdedf";

int main(void){
char *cp=a;
printf("\n  a=%d\n",(int)a);
printf("\n  cp=%d\n",(int)cp);
printf("\n*******");
printf("\n  &a=%d\n",(int)&a);
printf("\n  &cp=%d\n",(int)&cp);
}   


    编译结果如下:
    char* cp=a的情况:
dev-cpp:通过
lcc:通过
Miracle C:认为
printf("\n  &a=%d\n",(int)&a);
中的
&a
是错误,中断编译
Pacific C:发出警告,认为&a不必要。
TC2.0:通过
BC3.0:通过
dmc:通过
    char* cp=&a的情况:
dev-cpp:认为char* cp=&a的赋值不匹配,中止编译
lcc:通过
Miracle C:上面说了。
Pacific C:说了
TC2.0:发出警告,认为char* cp=&a是一个可疑指针
BC3.0:和上面一个一样
dmc:发出警告,认为要显式的类型转换。


    由此可见,这不是一个简单的问题。数组名进行&运算是一个涉及到可移植性的问题。不同的编译器有不同的处理。也说明了一个语言的实现不大可能完美。我们要守标准的规矩,尽量做出可以在各种环境下都可运行的程序。不然,ANSIC就没有用了。

8 楼

To好好学习:
看了上面的内容我想问个问题:
char a[]="abcdedf";

void main()
{
char *cp=a;
printf("\n  a=%#x",a);
printf("\n  cp=%#x",cp);
printf("\n*******");
printf("\n  &a=%#x",&a);
printf("\n  &cp=%#x",&cp);
}   
这里的a是地址常量吗?就象其他类型常量一样吗?
/*
a是一个地址常量,因而不可以对a进行诸如a++,a--之类的操作。
*/
为什么printf("\n   &a=%#x",&a):里的&a就能用但char *cp=&a;就编译不了?
只要你这样定义就可以了char **cp=&a;就可以了。
/*
要知道,a本身是数组名,也就是指针,对指针求地址必须定义匹配的数据类型接受这个值,char **p就是定义了指向指针的指针p,这样才可以。你原来定义的数据类型不匹配,因而编译通不过。
*/

=======================================================================
to 林杰杰:
针对你的话:
-----------------------------------------------------------------------
在你的程序里,a表示的是数组的首地址。对首地址a进行&运算,是一个所谓“未定义行为”。
-----------------------------------------------------------------------
a表示的数组的首地址,这种说法没错,但是不要忘了,它仍然是一个量,只不过是常量,跟变量没什么区别,都要占用内存空间的,因此必然有地址。因而可以进行&运算。

如果你认为我说的对,那么你可以自行修改你后面的说法。

针对你后面的程序,我只想说一点,请自己看我对“好好学习” 的回复,一切都会明白。你和他犯了同一个错误!
=======================================================================
to 网事:
自己修改你的说法吧!

9 楼

to 网事:
只有这个是对的:a==&a[0]==&*(a+0)
--------------------------
&(a+0)====>这个东西是什么?
a永远不会和&a相等的。
--------------------------
还有,对于函数参数表中的指针和数组名是一样的,都作为指针看待。
因而,上面的数组名的地址就是char **类型。




10 楼

不妨你再看看这个:
int main(void)
{
  int a[20];
  int *p=a;
  int **q=&a;
  printf("a=%#x\n",a);//a的值就是数组元素首地址
  printf("&a=%#x\n",&a);//&a的值就是数组地址,因为数组地址就是数组元素首地址,两个值相等就不为怪了。
  printf("p=%#x\n",p);
  printf("&p=%#x\n",&p);
  printf("q=%#x\n",q);
  printf("&q=%#x\n",&q);
  getch();
  return 0;
}
//这是输出结果,自己看看吧:
a=0xffa2
&a=0xffa2
/*
上面两个相等说明不了什么,一个指针完全可以保存自己的地址值,而且这一步也只有在数组初始化时完成,你不可能在后期完成。它们的数据类型永远不是相同的,而且你也不可能再对一个数组名重新赋值。
*/
p=0xffa2
&p=0xffca
q=0xffa2
&q=0xffcc
=======================================================================
char *p;
char p[];
这两个语句都是定义一个指向字符的指针变量p,他们的效果是相同的,同样你也可以做个小程序试一下。
-----------------------------------------------------------------------
不用再解释了,数组名和指针最大的区别就是一个是常量一个是变量!怎么会都是“指向字符的指针变量”?“效果是相同的”这样的话能说明什么?“效果”在编程这一行里又代表什么?

无语~~~~

我来回复

您尚未登录,请登录后再回复。点此登录或注册