通过实际例子,数组和指针之间的异同

时间:2009-11-23 10:51:10

标签: c arrays pointers

给出以下代码:

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

int main()
{
    int a[1];
    int * b = malloc(sizeof(int));

    /* 1 */
    scanf("%d", &a);
    printf("%d\n", a[0]);

    /* 2 */ 
    scanf("%d", &b);
    printf("%d\n", b[0]); 

    return 0;
}

编译时获得以下警告(i686-apple-darwin9-gcc-4.0.1):

array.c: In function 'main':
array.c:9: warning: format '%d' expects type 'int *', but argument 2 has type 'int (*)[0u]'
array.c:14: warning: format '%d' expects type 'int *', but argument 2 has type 'int **'

但是,为什么在第二个 printf 中发生执行错误,同时它适用于第一个 printf

更重要的是,如果第一个 scanf scanf(“%d”,a)替换为

,为什么会获得相同的输出?

非常感谢提前

5 个答案:

答案 0 :(得分:10)

在大多数情况下,数组类型的表达式将从“N元素数组T”隐式转换为“指向T”,其值将设置为指向数组的第一个元素。此规则的例外情况是,数组是&sizeof运算符的操作数,或者数组是否是用于初始化声明中的另一个数组的字符串文字。

那么与你的代码有什么关系?

在第

scanf("%d", &a);

您正在将&运算符应用于数组。这抑制了从“T的数组”到“指向T的指针”的隐式转换,并返回类型“指向T的数组的指针”或T (*)[N]的值(因此是您的第一个警告)。现在事实证明,指向数组的指针的值和指向数组的第一个元素的指针的值是相同的,它们只是具有不同的类型。假设a位于地址0x0001000:

expression      type          value         note
----------      ----          -----         ----
         a      int *         0x0001000     implicitly converted to pointer
        &a      int (*)[N]    0x0001000     
     &a[0]      int *         0x0001000

这就是你第一次打电话给scanf()“的原因”;你正在传递右指针,但编译器抱怨,因为表达式的类型与函数所期望的不匹配。你写过吗

scanf("%d", a);

您将不会收到任何警告,因为a的类型将被视为int *,这是scanf()所期望的。请注意,这与调用

相同
scanf("%d", &a[0]);

至于b ......

您明确地将b声明为指向int的指针,并为其分配一块内存。当您将&运算符应用于它时,您获得的是变量 b的地址,其类型为int **(因此是第二个警告),而不是b指向的地址。

expression      type          value         note
----------      ----          -----         ----
         b      int *         0x0040000     value contained in b
        &b      int **        0x0001000     address of b

对于这种情况,您只需传递未修饰的b

scanf("%d", b);

答案 1 :(得分:5)

数组a放在堆栈上,第一个元素的地址与a的地址相同。 &amp; a [0]和&amp; a是相同的地址

数组b分配有malloc,存储的地址在堆上,而指针b的地址在堆栈上。 &amp; b [0]与&amp; b 的地址不同。

这就是为什么第一个scanf和printf工作但不是第二个。

C-FAQ更详细地解释了它。

答案 2 :(得分:2)

在第一次扫描中,您传递对数组的引用。在C数组中是指向已分配类型的内存块的指针,在您的情况下为int *,并且类似a[0]的表达式会转换为*(a + 0)(btw会产生有趣的变体{{它将实际编译。)此数组在堆栈上分配。第二个数组在堆上分配,堆栈包含指向该数组的指针变量。

在这两种情况下,您都不会将指针传递给第一个数组条目,而是分别传递给数组和指向数组的指针。

你的第一个scanf覆盖了数组是什么,因为它在堆栈上分配,你的值最终(运气好)在数组中。

您的第二个scanf会覆盖指向数组的指针,从而将指针更改为数据段中可能不存在的内存地址。这会导致执行错误。

答案 3 :(得分:1)

这是正常的......

首先,scanf需要一个指针。 “a”和“b”已经成为指针! 所以:

/* 1 */
scanf("%d", a);
printf("%d\n", a[0]);

/* 2 */ 
scanf("%d", b);
printf("%d\n", b[0]);

会工作。

通常/ * 1 * /不起作用。但是gcc将“&amp; a”改为“a”,因为“&amp; a”没有任何意义。

printf("&a = %p\n", &a);
printf("a = %p\n", a);
printf("&b = %p\n", &b);
printf("b = %p\n", b);

&a = 0x7ffff6be67d0
a = 0x7ffff6be67d0
&b = 0x7ffff6be67c8
b = 0xb0b010

你无法接受一个地址。但是b是类型指针的“正常变量”,因此你可以用“&amp; b”来取它的地址。

开/ * 2 * /你将用户输入的值放在b中,因此* b(或b [0])将崩溃,除非用户输入有效的可读存储器地址。

答案 4 :(得分:1)

在你的情况下,发生的事情是你传递的变量a和b与&amp;操作符到scanf函数。该运算符的作用是“询问”变量的内存地址,并将该地址传递给scanf函数。但是,由于你的两个变量都是指针,它们确实是一个内存地址,因此当你传递&amp; a或&amp; b时,你传递指针的内存,而不是它所拥有的内存地址。

示例:

int x;
int *ptr;

x = 10;

假设x的内存地址是1000.您将数字10存储在内存地址1000.现在执行此操作:

ptr = &x;

您将地址1000存储在指针中。但是,除了作为地址之外,1000本身就是一个数字,因此指针和x一样,仍然需要一个存储器地址来存储该信息。假设指针内存位置是1004.现在看一下示例:

*ptr == 10;  //x content
ptr == 1000 //x memory address
&ptr == 1004 // ptr memory address. 

所以如果你想传递scanf变量x,但是使用指针,你需要传递存储在其中的x地址

scanf("%d", ptr);

只是为了说明另一个指针和向量的例子

int main
{
    int vet[5];
    int *ptr;

    ptr = vet;

    for(int i = 0; i < 5; ++i)
    {
        scanf("%d", (ptr+i) );
    }
}

在这里,您可以使用指针读取矢量。此外,使用指针算术,您可以迭代向量的内存地址。