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

给出以下代码:

#include  #include  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) ,为什么得到相同的输出;

非常感谢提前

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

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

在线

 scanf("%d", &a); 

您正在将&运算符应用于数组。 这抑制了从“T的数组”到“指向T的指针”的隐式转换,并返回类型“指向T的数组的指针”或T (*)[N] (因此是您的第一个警告)。 现在事实certificate,指向数组的指针的值和指向数组的第一个元素的指针的值是相同的,它们只是具有不同的类型。 假设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声明为指向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); 

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

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

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

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

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

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

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

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

那是正常的……

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

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

将工作。

通常/ * 1 * /不应该工作。 但是gcc将“&a”改为“a”,因为“&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是类型指针的“正常变量”,因此你可以通过“&b”获取它的地址。

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

在您的情况下,您正在使用&运算符将变量a和b传递给scanf函数。 该操作符的作用是“询问”变量的内存地址,并将该地址传递给scanf函数。 但是,由于你的两个变量都是指针,它们确实是一个内存地址,因此当你传递&a或&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) ); } } 

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