Differences when using ** in C

前端 未结 11 869
半阙折子戏
半阙折子戏 2020-12-24 10:50

I started learning C recently, and I\'m having a problem understanding pointer syntax, for example when I write the following line:

int ** arr = NULL;


        
相关标签:
11条回答
  • 2020-12-24 11:25

    There are already good answers here, but I want to mention my "goto" site for complicated declarations: http://cdecl.org/

    Visit the site, paste your declaration and it will translate it to English.

    For int ** arr;, it says declare arr as pointer to pointer to int.

    The site also shows examples. Test yourself on them, then hover your cursor to see the answer.

    (double (^)(int , long long ))foo

    cast foo into block(int, long long) returning double

    int (*(*foo)(void ))[3]

    declare foo as pointer to function (void) returning pointer to array 3 of int

    It will also translate English into C declarations, which is prety neat - if you get the description correct.

    0 讨论(0)
  • 2020-12-24 11:30

    The declaration int **arr says: "declare arr as a pointer to a pointer to an integer". It (if valid) points to a single pointer that points (if valid) to a single integer object. As it is possible to use pointer arithmetic with either level of indirection (i.e. *arr is the same as arr[0] and **arr is the same as arr[0][0]) , the object can be used for accessing any of the 3 from your question (that is, for second, access an array of pointers to integers, and for third, access an array of pointers to first elements of integer arrays), provided that the pointers point to the first elements of the arrays...


    Yet, arr is still declared as a pointer to a single pointer to a single integer object. It is also possible to declare a pointer to an array of defined dimensions. Here a is declared as a pointer to 10-element array of pointers to arrays of 10 integers:

    cdecl> declare a as pointer to array 10 of pointer to array 10 of int;
    int (*(*a)[10])[10]
    

    In practice array pointers are most used for passing in multidimensional arrays of constant dimensions into functions, and for passing in variable-length arrays. The syntax to declare a variable as a pointer to an array is seldom seen, as whenever they're passed into a function, it is somewhat easier to use parameters of type "array of undefined size" instead, so instead of declaring

    void func(int (*a)[10]);
    

    one could use

    void func(int a[][10])
    

    to pass in a a multidimensional array of arrays of 10 integers. Alternatively, a typedef can be used to lessen the headache.

    0 讨论(0)
  • 2020-12-24 11:30

    Pointers do not keep the information whether they point to a single object or an object that is an element of an array. Moreover for the pointer arithmetic single objects are considered like arrays consisting from one element.

    Consider these declarations

    int a;
    int a1[1];
    int a2[10];
    
    int *p;
    
    p = &a;
    //...
    p = a1;
    //...
    p = a2;
    

    In this example the pointer p deals with addresses. It does not know whether the address it stores points to a single object like a or to the first element of the array a1 that has only one element or to the first element of the array a2 that has ten elements.

    0 讨论(0)
  • 2020-12-24 11:31

    Isn't it all the same with int **?

    You've just discovered what may be considered a flaw in the type system. Every option you specified can be true. It's essentially derived from a flat view of a programs memory, where a single address can be used to reference various logical memory layouts.

    The way C programmers have been dealing with this since C's inception, is by putting a convention in place. Such as demanding size parameter(s) for functions that accept such pointers, and documenting their assumptions about the memory layout. Or demanding that arrays be terminated with a special value, thus allowing "jagged" buffers of pointers to buffers.


    I feel a certain amount of clarification is in order. As you'd see when consulting the other very good answers here, arrays are most definitely not pointers. They do however decay into ones in enough contexts to warrant a decades long error in teaching about them (but I digress).

    What I originally wrote refers to code as follows:

    void func(int **p_buff)
    {
    }
    
    //...
    
    int a = 0, *pa = &a;
    func(&pa);
    
    //...
    
    int a[3][10];
    int *a_pts[3] = { a[0], a[1], a[2] };
    func(a_pts);
    
    //...
    
    int **a = malloc(10 * sizeof *a);
    for(int i = 0; i < 10; ++i)
      a[i] = malloc(i * sizeof *a[i]);
    func(a);
    

    Assume func and each code snippet is compiled in a separate translation unit. Each example (barring any typos by me) is valid C. The arrays will decay into a "pointer-to-a-pointer" when passed as arguments. How is the definition of func to know what exactly it was passed from the type of its parameter alone!? The answer is that it cannot. The static type of p_buff is int**, but it still allows func to indirectly access (parts of) objects with vastly different effective types.

    0 讨论(0)
  • 2020-12-24 11:31

    Having solely the declaration of the variable, you cannot distinguish the three cases. One can still discuss if one should not use something like int *x[10] to express an array of 10 pointers to ints or something else; but int **x can - due to pointer arithmetics, be used in the three different ways, each way assuming a different memory layout with the (good) chance to make the wrong assumption.

    Consider the following example, where an int ** is used in three different ways, i.e. p2p2i_v1 as a pointer to a pointer to a (single) int, p2p2i_v2 as a pointer to an array of pointers to int, and p2p2i_v3 as a pointer to a pointer to an array of ints. Note that you cannot distinguish these three meanings solely by the type, which is int** for all three. But with different initialisations, accessing each of them in the wrong way yields something unpredictable, except accessing the very first elements:

    int i1=1,i2=2,i3=3,i4=4;
    
    int *p2i = &i1;
    int **p2p2i_v1 = &p2i;  // pointer to a pointer to a single int
    
    int *arrayOfp2i[4] = { &i1, &i2, &i3, &i4 };
    int **p2p2i_v2 = arrayOfp2i; // pointer to an array of pointers to int
    
    int arrayOfI[4] = { 5,6,7,8 };
    int *p2arrayOfi = arrayOfI;
    int **p2p2i_v3 = &p2arrayOfi; // pointer to a pointer to an array of ints
    
    // assuming a pointer to a pointer to a single int:
    int derefi1_v1 = *p2p2i_v1[0];  // correct; yields 1
    int derefi1_v2 = *p2p2i_v2[0];  // correct; yields 1
    int derefi1_v3 = *p2p2i_v3[0];  // correct; yields 5
    
    // assuming a pointer to an array of pointers to int's
    int derefi1_v1_at1 = *p2p2i_v1[1];  // incorrect, yields ? or seg fault
    int derefi1_v2_at1 = *p2p2i_v2[1]; // correct; yields 2
    int derefi1_v3_at1 = *p2p2i_v3[1]; // incorrect, yields ? or seg fault
    
    
    // assuming a pointer to an array of pointers to an array of int's
    int derefarray_at1_v1 = (*p2p2i_v1)[1]; // incorrect; yields ? or seg fault;
    int derefarray_at1_v2 = (*p2p2i_v2)[1]; // incorrect; yields ? or seg fault;
    int derefarray_at1_v3 = (*p2p2i_v3)[1]; // correct; yields 6;
    
    0 讨论(0)
  • 2020-12-24 11:36

    How can I know if :

    arr is a pointer to a pointer of an integer

    arr is a pointer to an array of pointers to integers

    arr is a pointer to an array of pointers to arrays of integers

    You cannot. It can be any of those. What it ends up being depends on how you allocate / use it.

    So if you write code using these, document what you're doing with them, pass size parameters to the functions using them, and generally be sure about what you allocated before using it.

    0 讨论(0)
提交回复
热议问题