简体   繁体   English

为什么我们不需要在 malloc 数组的情况下取消引用指针值?

[英]Why we don't need to de-reference pointer value in case of malloc array?

I am coming across hardship trying to understand how C compiler interprets character arrays, strings, integers, and arrays in a shady way.我在试图了解 C 编译器如何以一种隐蔽的方式解释字符 arrays、字符串、整数和 arrays 时遇到了困难。 And thanks for helping me out.并感谢您帮助我。 Even many of reddit users pointed out that the whole pointer thing in C is a bit shady on how exactly it interprets commands.甚至许多 reddit 用户都指出,C 中的整个指针对于它如何准确地解释命令有点可疑。 So, I am trying to understand malloc with an example.所以,我想通过一个例子来理解malloc But before that this is what I understand.但在此之前,这是我所理解的。 Say *p is a variable.假设*p是一个变量。 Then p holds the address it's pointing towards.然后 p 保存它指向的地址。 and *p references the value that the address holds.并且*p引用地址所持有的值。 In case of dynamic allocation, When I do在动态分配的情况下,当我这样做时

int *p = (int *)malloc(sizeof(int)*5); //say we want 5 locations //

And manually loop as the user gives in all 5 values, I put in the values like并在用户输入所有 5 个值时手动循环,我输入的值如下

scanf("%d", p+i) // as p already holds address, I get that we don't have to provide the & . And i being the iterator.. //

and then print the same with another loop here's what happens and I have not a clue why.然后用另一个循环打印相同的内容,这就是发生的事情,我不知道为什么。

Say the user inputs 55,66,77,88,99假设用户输入55,66,77,88,99

When I print using this code,当我使用此代码打印时,

printf("%d", *p+i); // De-referencing the values in location p by using asterics, + the value of iteration,i //

I get the values as crazy as 55,56,57,58,59我得到的值和55,56,57,58,59一样疯狂

So from a little help from internet, I tried the printf code like this and it worked like a charm, but I didn't have to even de-reference.因此,在互联网的帮助下,我尝试了像这样的 printf 代码,它就像一个魅力,但我什至不必取消引用。 Why the heck is this so confusing?为什么这会如此令人困惑?

printf("%d", p[i]); // No asterics used. How does the compiler know I want the value and not the address? as p only should hold the addresses, and *p should give us the values in those addresses //

Also, somehow if I do printf("%d", *p++);另外,如果我做printf("%d", *p++); Then also it works.然后它也有效。 Why I am not getting how C works?为什么我不知道 C 的工作原理?

Also on the same note, if I try to do the same but this time with scanf("%d", p[i]);同样,如果我尝试做同样的事情,但这次使用scanf("%d", p[i]); , then syntax error. ,然后是语法错误。 I mean why?我是说为什么?

Thanks...谢谢...

Say *p is a variable说 *p 是一个变量

No. The variable is p and its type is int* .不,变量是p并且它的类型是int*


  • *p : the value found at address p *p : 在地址p找到的值
  • *p+i parsed as (*p) + 1 : the value at address p then add the value 1 to that value *p+i解析为(*p) + 1 :地址p处的值然后将值1添加到该值
  • p[i] equivalent with *(p + i) : the value at address p + i where p + i is the address of the i th element of an array of ints that starts at p . p[i]等效于*(p + i) :地址p + i处的值,其中p + i是从p开始的整数数组的第i个元素的地址。 Aka the i th element in the vector of ints starting at p也就是从p开始的整数向量中的第i个元素
  • *p++ parsed as *(p++) : increment the pointer p (ie make p point at the next element) and get the value from the old value of p . *p++解析为*(p++) :增加指针p (即使p指向下一个元素)并从p的旧值中获取值。
int *p = ....

Here, p is a variable of type int * , which means, variable p is a pointer which can hold the address of an int type.这里, p是一个int *类型的变量,也就是说,变量p是一个指针,它可以保存一个int类型的地址。

The in memory view of allocated and initialised memory, pointed by p , would be something like this:在 memory 视图中分配和初始化的 memory 由p指向,如下所示:

p ______
       |
      \|/
     --------------------------
     | 55 | 66 | 77 | 88 | 99 |
     --------------------------

You are correct on statement scanf("%d", p+i);您对语句scanf("%d", p+i);是正确的- -
// as p already holds address, I get that we don't have to provide the &.....

Question I:问题一:

When I print using this code,当我使用此代码打印时,

printf("%d", *p+i); // De-referencing the values in location p by using asterics, + the value of iteration,i //

I get the values as crazy as 55,56,57,58,59我得到的值和 55,56,57,58,59 一样疯狂

The precedence of unary * (indirection) operator is higher than binary + operator.一元* (间接)运算符的优先级高于二元+运算符。
So, the expression *p+i will be evaluated as - (*p)+i .因此,表达式*p+i将被评估为 - (*p)+i

Note that operator precedence is the priority for grouping different types of operators with their operands.请注意,运算符优先级是将不同类型的运算符及其操作数分组的优先级。

When you have printf("%d", *p+i);当你有printf("%d", *p+i); statement in a loop body which is iterating the given allocated array from i = 0 to i < 5 -循环体中的语句,它将给定的分配数组从i = 0迭代到i < 5 -
In the first iteration: ( i = 0 and pointer p is pointing to first element of array which is 55 ):在第一次迭代中:( i = 0和指针p指向数组的第一个元素,即55 ):

(*p) + 0  ->  55 + 0  ->  55

In the second iteration: ( i = 1 and pointer p is pointing to first element of array which is 55 ):在第二次迭代中:( i = 1和指针p指向数组的第一个元素,即55 ):

(*p) + 1  ->  55 + 1  ->  56

..... so on ..... 很快
..... ......

In the fifth iteration: ( i = 4 and pointer p is pointing to first element of array which is 55 ):在第五次迭代中:( i = 4和指针p指向数组的第一个元素,即55 ):

(*p) + 1  ->  55 + 4  ->  59

Hence you are getting output - 55,56,57,58,59.因此你得到 output - 55,56,57,58,59。

To get the expected output, group the operator with operands explicitely - *(p + i) .要获得预期的 output,请将运算符与操作数明确分组 - *(p + i)

With this, first the value of i will be added to pointer and then the resultant pointer (address) will be dereferenced.有了这个,首先i的值将被添加到指针,然后结果指针(地址)将被取消引用。

Question II:问题二:

I tried the printf code like this and it worked like a charm, but I didn't have to even de-reference.我尝试了像这样的 printf 代码,它就像一个魅力,但我什至不必取消引用。 Why the heck is this so confusing?为什么这会如此令人困惑?

printf("%d", p[i]); // No asterics used. How does the compiler know I want the value and not the address? as p only should hold the addresses, and *p should give us the values in those addresses //

From C Standard#6.5.2.1:从 C 标准#6.5.2.1 开始:

The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))).下标运算符[]的定义是E1[E2]等同于(*((E1)+(E2)))。

By this definition of subscript operator -通过这个下标运算符的定义 -

p[i] -> *((p) + (i)) -> *(p + i)

That means, p[i] and *(p + i) are equivalent.这意味着p[i]*(p + i)是等价的。
I have already showed you above in the answer that - why *(p + i) will give expected output.我已经在上面的答案中向您展示了 - 为什么*(p + i)会给出预期的 output。
Hence, when using p[i] you are getting expected output.因此,当使用p[i]时,您会得到预期的 output。

Question III:问题三:

Also, somehow if I do printf("%d", *p++);另外,如果我做printf("%d", *p++); Then also it works.然后它也有效。 Why I am not getting how C works?为什么我不知道 C 的工作原理?

Again, check the operator precedence table and you will find that precedence of ++ (postfix increment) operator is higher than unary * (indirection) operator.再次检查运算符优先级表,您会发现++ (后缀自增)运算符的优先级高于一元* (间接)运算符。 So, the expression *p++ will be evalauted as *(p++) .因此,表达式*p++将被评估为*(p++)

The post increment operator increase the value of operand by 1 but the value of the expression is the operand's original value prior to the increment operation.后自增运算符将操作数的值加1 ,但表达式的值是自增操作之前操作数的原始值。

When you have printf("%d", *p++);当你有printf("%d", *p++); statement in a loop body which is iterating the given allocated array -循环体中的语句,它正在迭代给定的分配数组 -
In the first iteration: pointer p is pointing to first element of array which is the element at 0 th index.在第一次迭代中:指针p指向数组的第一个元素,该元素是第0索引处的元素。

*(p++) -> p will be incremented but the value of p in the expression will be its value prior to the increment operation which is location of element at `0`th index
       -> *(0th location address) -> 55

In the second iteration: pointer p is pointing to second element of array which is the element at 1 st index.在第二迭代中:指针p指向数组的第二1元素,即第一个索引处的元素。

*(p++) -> p will be incremented but the value of p in the expression will be its value prior to the increment operation which is location of element at `1`st index
       -> *(1st location address) -> 66

...... so on ...... 很快
...... ……

In the fifth iteration: pointer p is pointing to fifth element of array which is the element at 4 th index.在第五次迭代中:指针p指向数组的第五个元素,即第4索引处的元素。

*(p++) -> p will be incremented but the value of p in the expression will be its value prior to the increment operation which is location of element at `4`th index
       -> *(4th location address) -> 99

and p is now pointing to one element past the end of array. p现在指向数组末尾之后的一个元素。

You will get expected output - 55,66,77,88,99.您将得到预期的 output - 55,66,77,88,99。

Question IV:问题四:

Also on the same note, if I try to do the same but this time with scanf("%d", p[i]);同样,如果我尝试做同样的事情,但这次使用scanf("%d", p[i]); , then syntax error. ,然后是语法错误。 I mean why?我是说为什么?

Revisit the definition of subscript operator above in the answer and recall that you are aware of fact that scanf() expect the address of variable (pointer) as argument.在答案中重新审视上面下标运算符的定义,并回想一下您知道scanf()期望变量(指针)的地址作为参数的事实。

p[i] -> *((p) + (i)) -> *(p + i)

*(p + i) ==> dereferencing the pointer p + i which will give value at that location. *(p + i) ==> 解除指针p + i的引用,这将在该位置给出值。
So, p[i] is not the address of i th location of array but its the value at i th location of array.因此, p[i]不是数组第i位置的地址,而是数组第i位置的值。 Hence, you are getting error when passing p[i] as argument to scanf() .因此,将p[i]作为参数传递给scanf()时会出错。

To get the address of i th location, just add & operation before p[i] -要获取第i位置的地址,只需在p[i]之前添加&操作 -

scanf("%d", &p[i]);

This will work as expected.这将按预期工作。 Again confused, why??又一头雾水,为什么??

Check this -检查这个 -

&p[i] -> &(p[i]) -> &(*((p) + (i)) -> ((p) + (i)) -> p + i
          |                |
  Precedence of operator   |
  [] is higher than        |
  & operator               |
                           |
      The operator & is used to get the address and the operator * is used for dereferencing.
      These operators cancel the effect of each other when used one after another. 

Hence, the statement因此,声明

scanf("%d", &p[i]);

is equivalent to this statement相当于这个语句

scanf("%d", p + i);

and you know very well why p + i works fine when given as argument in scanf() .你很清楚为什么p + iscanf()中作为参数给出时效果很好。

Let me know if you have any further question or confusion.如果您有任何进一步的问题或困惑,请告诉我。

The expression p[i] is exactly equivalent to *(p + i) - given a starting address p , compute the address of the i 'th object (not byte.) following that address and dereference the result.表达式p[i]完全等同于*(p + i) - 给定起始地址p ,计算该地址后面的第iobject的地址(不是字节。)并取消引用结果。

You don't use the * operator because there's an implicit dereference in the subscript operation.您不使用*运算符,因为下标操作中有隐式取消引用。

*p + i is equivalent to writing p[0] + i - you dereference p and add the value of i to the result, which is why you got the sequence 55, 56, 57, 58, 59 when you were expecting 55, 66, 77, 88, 99 . *p + i相当于写p[0] + i - 你取消引用p并将i的值添加到结果中,这就是为什么你得到序列55, 56, 57, 58, 59而你期望55, 66, 77, 88, 99

Life is easier when you use array notation - just write p[i] when you want the i 'th object in the sequence.当您使用数组表示法时,生活会更轻松 - 当您想要序列中的第i个 object 时,只需编写p[i]即可。

I think a small runnable code will help you better understand than me explaining.我认为一个小的可运行代码将帮助您更好地理解而不是我解释。

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

int main() {
    int *p = (int *)malloc(sizeof(int)*5);
    int *q = (int *)malloc(sizeof(int)*5);
    int i = 0;
    int test_val = 100;

    for(i = 0; i < 5; i++) {
        *(p + i) = test_val;
        q[i] = test_val;
        test_val = test_val + 10;
    }

    for(i = 0; i < 5; i++)
        printf("p[%d] = %d\n", i, p[i]);

    printf("p[i] gives the value in array p at index i \n\n");

    for(i = 0; i < 5; i++)
        printf("q[%d] = %d\n", i, q[i]);
    
    printf("Note that the values in p and q are equal which means that *(x + 1) and x[i] are 2 ways to access the same values \n\n");

    for(i = 0; i < 5; i++)
        printf("*p + %d = %d\n", i, *p + i);

    printf("*p + i gets value pointed by p and adds i to it in each iteration \n\n");

    for(i = 0; i < 5; i++)
        printf("*(p + %d) = %d\n", i, *(p + i));

    printf("*(p + i) gets value pointed by p + i \n\n");

    int *j = (int *)malloc(sizeof(int)*5);
    int *k = (int *)malloc(sizeof(int)*5);
    int *l = (int *)malloc(sizeof(int)*5);

    for(i = 0; i < 5; i++) {
         j[i] = test_val;
         k[i] = test_val;
         l[i] = test_val;
    }
    printf("(*j)++ = %d\n\n", (*j)++);
    printf("*(k++) = %d\n\n", *(k++));
    printf("*l++ = %d\n\n", *l++);
    
    for(i = 0; i < 5; i++) {
         j[i] = test_val;
         k[i] = test_val;
         l[i] = test_val;
    }
    printf("test_val = %d\n\n", test_val);
    printf("++(*j) = %d\n\n", ++(*j));
    printf("*(++k) = %d\n\n", *(++k));
    printf("*++l = %d\n\n", *++l);
    return 0;
}

Following is the output:以下是 output:

p[0] = 100
p[1] = 110
p[2] = 120
p[3] = 130
p[4] = 140
p[i] gives the value in array p at index i 

q[0] = 100
q[1] = 110
q[2] = 120
q[3] = 130
q[4] = 140
Note that the values in p and q are equal which means that *(x + 1) and x[i] are 2 ways to access the same values 

*p + 0 = 100
*p + 1 = 101
*p + 2 = 102
*p + 3 = 103
*p + 4 = 104
*p + i gets value pointed by p and adds i to it in each iteration 

*(p + 0) = 100
*(p + 1) = 110
*(p + 2) = 120
*(p + 3) = 130
*(p + 4) = 140
*(p + i) gets value pointed by p + i 

test_val = 150

(*j)++ = 150

*(k++) = 150

*l++ = 150

test_val = 150

++(*j) = 151

*(++k) = 150

*++l = 150

For the scanf question, scanf always needs a pointer as a parameter.对于scanf问题,scanf总是需要一个指针作为参数。 p[I] is the value at (p + i) and thus is incorrect. p[I] 是 (p + i) 处的值,因此不正确。 Ideally, you should use scanf("%d", &(p[i]))理想情况下,您应该使用 scanf("%d", &(p[i]))

" When I print using this code printf("%d", *p+i); ... I get the values as crazy as 55 , 56 , 57 , 58 , 59 " 当我使用此代码打印时printf("%d", *p+i); ... 我得到的值与55 , 56 , 57 , 58 , 59一样疯狂

When you use *p + i , p is dereferenced first (which gains the value of the first array element, which is 55 ) and then i is added to the value 55 .当您使用*p + i时,首先取消引用p (获得第一个数组元素的值,即55 ),然后将i添加到值55 *p + i is equal to (*p) + i . *p + i等于(*p) + i

This is why you get the output of: 55 , 56 , 57 , 58 , 59 and not 55 , 66 , 77 , 88 , 99这就是为什么你得到 output 的原因: 55 , 56 , 57 , 58 , 59而不是55 , 66 , 77 , 88 , 99

You just add i to the value of the first element in each iteration and never access the following 4 array elements.您只需将i添加到每次迭代中第一个元素的值,并且永远不会访问以下 4 个数组元素。

" I tried the printf code like this and it worked like a charm, but I didn't have to even de-reference. .... printf("%d", p[i]); " 我尝试了像这样的printf代码,它就像一个魅力,但我什至不必取消引用...... printf("%d", p[i]); "

When you use p[i] , you get the value at the i th element of the array as it offsets the pointer itself before dereferencing.当您使用p[i]时,您会在数组的第i个元素处获得值,因为它在取消引用之前会偏移指针本身。 It is equal to *(p + i) .它等于*(p + i)

" Also, somehow if I do printf("%d", *p++); Then also it works. " "另外,如果我执行printf("%d", *p++);那么它也可以工作。 "

When you use *p++ , p is actual dereferenced and the value at *p is gained but thereafter p is incremented.当您使用*p++时, p实际被取消引用,并且*p处的值被获得,但此后p增加。 Means at the next iteration p points to the next array element.意味着下一次迭代p指向下一个数组元素。

After the loop has been completed, p points one past the array.循环完成后, p指向数组后一格。


" If I try to do the same but this time with scanf("%d", p[i]); , then syntax error. I mean why? " 如果我尝试做同样的事情,但这次使用scanf("%d", p[i]); ,那么语法错误。我的意思是为什么?

For scanf() the things are a little bit different.对于scanf() ,情况有些不同。

When you use scanf("%d", p[i]);当您使用scanf("%d", p[i]); , p[i] is equal to *(p + i) as it is also in the printf() calls but as you try to dereference and gain an int object and not a pointer to int , which is needed for %d at scanf() , it is a syntax error. p[i]等于*(p + i) ,因为它也在printf()调用中,但是当您尝试取消引用并获得int object 而不是指向int的指针时, %dscanf() ,这是一个语法错误。

In scanf() , %d requires an argument of type int * , not int .scanf()中, %d需要int *类型的参数,而不是int


For your main question:对于您的主要问题:

" Why we don't need to de-reference pointer value in case of malloc array? " 为什么我们不需要在 malloc 数组的情况下取消引用指针值?

It has nothing to do with malloc() ed or dynamic memory allocation in particular.它与malloc() ed或动态 memory 分配无关。 It has something to do with pointer arithmetic syntax allowed in C.它与 C 中允许的指针算术语法有关。

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

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