为什么我不需要向函数传递数组的地址?

3

你好,我刚开始学习C语言,正在学习指针。我正在编写一个简单的递归函数进行测试,它有两个参数:int *aint size。在我的主函数中,我使用 & 将我的数组第一个字符的地址传递给 print_array 函数。

然而这样似乎行不通,在编译时出现了“不兼容的指针类型”错误。我知道可以去掉 &,程序就能正常工作。我有一个问题:

为什么我不能用 & 从主函数把 my_array 的内存地址传递进去呢?我不应该只需要给函数数组的第一个元素的内存地址就行了吗?

谢谢,希望我的问题不太幼稚。

 #include <stdio.h>
 #include <stdlib.h>


void print_array(int *a, int size){
    if (size>0){
            printf("%d\n", a[0]);
            print_array(a+1, size-1);
    }
}

int main(void){
    int my_array[20];
    int i;

    for (i=0; i < 20; i++){
            my_array[i] = rand() % 20;
    }

    /*the contents of the array*/
    printf("The contents of the array\n");
    for (i=0; i < 20; i++){
            printf("%d\n", my_array[i]);
    }

    printf("The recursive method print\n");

    print_array(&my_array, 20);

    return EXIT_SUCCESS;

}
3个回答

7

是的,您可以将函数赋予第一个元素的地址,并让它处理其余部分。您可以采用以下任一方式:

print_array(my_array, 20);

...或者:

print_array(&my_array[0], 20);

不幸的是,尽管&my_array是合法的代码,但它会产生一个指向整个数组而不是指向数组第一个元素的指针。它们具有相同的地址,但是不同的类型,这就是导致错误的原因。
指针的类型决定了(除其他事项外)对该指针进行算术运算的方式。在您的情况下,print_array打印数组中的第一个int,然后将1添加到指针中。由于它是指向int的指针,因此该加法实际上会将int的大小添加到指针中的地址。
如果您使用指向整个数组的指针,则添加1将添加整个数组的大小到地址中。例如,假设int为4字节,并且my_array的基本地址为1000。在这种情况下,my_array+1将产生1004,因此它保存数组中第二个int的地址(正如您无疑想要的那样)。相比之下,&my_array将获取整个数组的地址,类型为“指向20个int的数组指针”。当您将其加1时,它将将指向类型的大小乘以1添加到地址中,因此您将获得1080(即整个数组为20 * 4 = 80字节)。这显然不是您想要的--与x+1指向数组中的第二个项目不同,它现在指向数组末尾,并尝试取消引用指针将导致未定义的行为。
因此,只需切换到上述其中一种形式(my_array&my_array[0])。作为更一般的观点,请意识到,在大多数情况下,数组的名称在评估时会作为指向数组第一个元素的指针--值得注意的例外是当您将数组的名称用作sizeof运算符或地址运算符的操作数时(就像您在这里所做的那样)。在这两种情况下,数组的名称仍然引用整个数组而不是指向第一个元素的指针。

如果print_array的第一个参数声明为int (*a)[20],那么你可以将&my_array传递给它。但是,int *a是指向整数的指针,而不是指向包含20个整数的数组的指针。这就是为什么你必须传递数组的第一个元素的地址,而不是数组本身的地址(即使它们具有相同的数值)。 - Wyzard
@Wyzard:是的,如果参数声明为int (*a)[20],你可以传递&my_array,但是在print_array内部的a+1将产生未定义的行为。 - Jerry Coffin
很遗憾,虽然&my_array是合法的代码,但它会产生一个指向整个数组的指针,而不是指向数组第一个元素的指针。它们具有相同的地址,但不同的类型,这就是导致错误的原因。您能详细说明一下吗?那么&my_array会给我一个指向整个数组的指针吗?如果它们都指向同一个块,它们怎么可能是不同的类型呢?谢谢。 - magna_nz
@magna_nz:我已经添加了更多关于那个的细节。 - Jerry Coffin

2
“您的函数需要一个指向int的指针,具体来说是数组的第一个元素(0号元素)的地址。
通过调用print_array(&my_array, 20);,您正在尝试传递整个数组的地址,这是一种类型为int(*)[20]的值,它与int*不同。它指向相同的内存位置,但它是不同类型的。
要传递第一个元素的地址,您可以编写:”
print_array(&my_array[0], 20);

或者,等价地说:
print_array(my_array, 20);

后一种方法可行,因为在大多数情况下(但不是所有情况),数组的名称会被隐式转换为指向其第一个元素的指针。
C和C++中数组和指针之间的关系可能会令人困惑。建议阅读:comp.lang.c FAQ的第6节。

0

由于数组自动退化为指针,以下内容摘自n1570草案:

6.3.2其他操作数

6.3.2.1左值、数组和函数设计者

  1. 除非它是sizeof运算符、_Alignof运算符或一元的&操作符的操作数, 或者是用于初始化数组的字符串字面量,否则具有类型“类型的数组”的表达式被转换为具有类型“指向类型的指针”的表达式, 该指针指向数组对象的初始元素而不是一个左值。如果数组对象具有寄存器存储类,则行为未定义。

请注意,关于 _Alignof 运算符的措辞是不正确的,并已从最终的 C11 标准中删除。与 sizeof 不同,_Alignof 只能应用于类型,而不能应用于表达式。(目前还不清楚为什么会这样。) - Keith Thompson
@KeithThompson 感谢您的评论,我并不拥有 C11 标准,只有草案。 - Iharob Al Asimi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接