简体   繁体   English

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

[英]Similarities and differences between arrays and pointers through a practical example

Given the following code: 给出以下代码:

#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;
}

the following warnings are obtained when it is compiled (i686-apple-darwin9-gcc-4.0.1): 编译时获得以下警告(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 **'

but, why does an execution-error occur in the second printf , meanwhile it works for the first printf ? 但是,为什么在第二个printf中发生执行错误,同时它适用于第一个printf

Even more, why it is obtained the same output if the first scanf is substituted by scanf("%d", a); 更重要的是,如果用scanf代替第一个scanf(“%d”,a) ,为什么得到相同的输出; ?

Thank very much in advance 非常感谢提前

In most contexts, an expression of array type will be implicitly converted from an "N-element array of T" to "pointer to T" and its value will be set to point to the first element of the array. 在大多数情况下,数组类型的表达式将从“N元素数组T”隐式转换为“指向T”,其值将设置为指向数组的第一个元素。 The exceptions to this rule are when the array is an operand of the & or sizeof operators, or if the array is a string literal being used to initialize another array in a declaration. 此规则的例外情况是,当数组是&sizeof运算符的操作数时,或者数组是用于初始化声明中的另一个数组的字符串文字。

So how does all that relate to your code? 那么所有与你的代码有什么关系呢?

In the line 在线

scanf("%d", &a);

You are applying the & operator to the array. 您正在将&运算符应用于数组。 This suppresses the implicit conversion from "array of T" to "pointer to T" and returns a value of type "pointer to array of T", or T (*)[N] (hence your first warning). 这抑制了从“T数组”到“指向T的指针”的隐式转换,并返回类型“指向T数组的指针”或T (*)[N] (因此是您的第一个警告)。 Now it turns out that the value of a pointer to an array and the value of a pointer to the first element of the array are the same, they just have different types. 现在事实证明,指向数组的指针的值和指向数组的第一个元素的指针的值是相同的,它们只是具有不同的类型。 So assuming that a is at address 0x0001000: 假设a位于地址0x0001000:

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

That's why your first call to scanf() "works"; 这就是你第一次调用scanf() “工作”的原因; you're passing the right pointer value , but the compiler is complaining because the type of the expression doesn't match what the function expects. 你正在传递正确的指针 ,但编译器抱怨,因为表达式的类型与函数所期望的不匹配。 Had you written 如果你写的

scanf("%d", a);

you would not have received any warnings, since the type of a will be taken to be int * , which is what scanf() expects. 你不会收到任何警告,因为类型a将被视为是int * ,这是什么scanf()预期。 Note that this is identical to calling 请注意,这与呼叫相同

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

As for b ... 至于b ...

You explicitly declare b as a pointer to int and assign a block of memory to it. 您明确地将b声明为指向int的指针,并为其分配一块内存。 When you apply the & operator to it, what you get back is the address of the variable b with type int ** (hence the second warning), not the address that b points to. 当你对它应用&运算符时,你得到的是变量 b的地址,类型为int ** (因此是第二个警告),而不是b指向的地址。

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

For that case, you just pass the undecorated b : 对于这种情况,你只需通过未修饰的b

scanf("%d", b);

The array a is placed on the stack, the address for the first element is the same as the address of a. 数组a放在堆栈上,第一个元素的地址与a的地址相同。 &a[0] and &a is the same address &a [0]和&a是相同的地址

The array b is allocated with malloc, the address for the storage is on the heap, whereas the address for the pointer b is on the stack. 数组b分配有malloc,存储的地址在堆上,而指针b的地址在堆栈上。 &b[0] is not the same address as &b . &b [0]与&b的地址不同

That's why the first scanf and printf works but not the second. 这就是为什么第一个scanf和printf工作但不是第二个。

The C-FAQ explains it in much greater detail. C-FAQ更详细地解释了它。

In the first scanf you pass a reference to an array. 在第一个scanf中,您传递对数组的引用。 In C arrays are pointers to a memory block of the allocated type, in your case int * and an expression like a[0] gets translated into *(a + 0) (which btw gives rise to the funny variant 0[a] which will actually compile.) This array is allocated on the stack. 在C数组中是指向已分配类型的内存块的指针,在您的情况下int *和类似a[0]的表达式被转换为*(a + 0) (其中btw产生了有趣的变体0[a] ,将实际编译。)此数组在堆栈上分配。 The second array is allocated on the heap and the stack contains the pointer variable to that array. 第二个数组在堆上分配,堆栈包含该数组的指针变量。

In both cases you do not pass a pointer to the first array entry, but to the array and the pointer to the array respectively. 在这两种情况下,您都不会将指针传递给第一个数组条目,而是分别传递给数组和指向数组的指针。

Your first scanf overwrites that what is the array, as it is allocated on the stack, your value ends up (by luck) in the array. 你的第一个scanf覆盖了什么是数组,因为它在堆栈上分配,你的值最终(运气好)在数组中。

Your second scanf overwrites the pointer to the array, thereby changing the pointer to a memory address that probably does not exist in your data segment. 第二个scanf会覆盖指向数组的指针,从而将指针更改为数据段中可能不存在的内存地址。 This results in the execution error. 这导致执行错误。

That's normal ... 那是正常的......

First, scanf requires a pointer. 首先,scanf需要一个指针。 "a" and "b" already are pointers ! “a”和“b”已经成为指针! So : 所以:

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

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

Will work. 将工作。

Normally /* 1 */ shouldn't work. 通常/ * 1 * /不起作用。 But gcc transforms "&a" by "a" because "&a" doesn't have any sense. 但是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

You can't take the adress of a. 你无法接受一个地址。 But b is a "normal variable" of type pointer, and thus you can take it's address by "&b". 但是b是类型指针的“正常变量”,因此你可以通过“&b”获取它的地址。

On /* 2 */ you're putting the value entered by the user in b and thus, *b (or b[0]) will crash unless the user will enter a valid readable memory address. 在/ * 2 * /您将用户输入的值放在b中,因此* b(或b [0])将崩溃,除非用户输入有效的可读存储器地址。

In your case what is happening is that you are passing both variables a and b with the & operator to the scanf function. 在您的情况下,您正在使用&运算符将变量a和b传递给scanf函数。 What this operator does is "ask" the memory address of the variable and pass that address to the scanf function. 该运算符的作用是“询问”变量的内存地址,并将该地址传递给scanf函数。 But, as both of your variables are pointers, what they have indeed is an memory address, so when you pass &a or &b you are passing the memory of the pointer, not the memory address that it holds. 但是,由于你的两个变量都是指针,它们确实是一个内存地址,因此当你传递&a或&b时,你传递的是指针的内存,而不是它所拥有的内存地址。

Example: 例:

int x;
int *ptr;

x = 10;

suppose the memory address of x is 1000. You are storing the number 10 at the memory address 1000. Now you do this: 假设x的内存地址是1000.您将数字10存储在内存地址1000.现在执行此操作:

ptr = &x;

You are storing the address 1000 in the pointer. 您将地址1000存储在指针中。 But 1000, apart being an address, is a number itself, so the pointer, as does x, still needs a memory address to store that information. 但是,除了作为地址之外,1000本身就是一个数字,因此指针和x一样,仍然需要一个存储器地址来存储该信息。 Suppose the pointer memory location is 1004. Now look the example: 假设指针内存位置是1004.现在看一下示例:

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

So if you want to pass to scanf the variable x, but using the pointer, you need to pass the x address stored in it 因此,如果要传递scanf变量x,但使用指针,则需要传递存储在其中的x地址

scanf("%d", ptr);

Just to ilustrate another example of pointers and vectors 只是为了说明另一个指针和向量的例子

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

    ptr = vet;

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

Here you can read the vector using the pointer. 在这里,您可以使用指针读取矢量。 Also, using pointer arithmetics you can iterate over the memory addresses of the vector. 此外,使用指针算术,您可以迭代向量的内存地址。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM