通过一个实际例子,数组和指针之间的异同
给出以下代码:
#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) ); } }
在这里,您可以使用指针读取矢量。 此外,使用指针算术,您可以迭代向量的内存地址。