Differences when using ** in C

I recently started to learn C, and I had a problem understanding the pointer syntax, for example, when I write the following line:

int ** arr = NULL; 

How to find out if:

  • arr is a pointer to an integer pointer

  • arr - pointer to an array of pointers to integers

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

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




Another question on the same issue:

If I have a function that takes char ** s as a parameter, I want to refer to it as a pointer to an array of strings, that is, to a pointer to an array of pointers to an array from chars , but is it also a pointer to a pointer to char ?

+45
c arrays syntax pointers
Feb 06 '17 at 10:18
source share
11 answers

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

You just discovered what could be considered a flaw in the type system. Each parameter you specify may be true. It is essentially based on a flat representation of program memory, where a single address can be used to refer to different logical memory schemes.

The way C programmers have dealt with this since the creation of C makes an agreement in place. Such as the required size parameters (parameters) for functions that take such pointers, and documenting their assumptions about the memory layout. Or requiring arrays to be interrupted with a special value, which allows for "jagged" buffer pointers to buffers.




I feel that a certain amount of clarification is in order. As you might see, when you consult with other very good answers, arrays are definitely not pointers . However, they break up into units in a sufficient context to justify a long mistake in learning about them (but I'm distracted).

What I originally wrote refers to the 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); 

Suppose func , and each piece of code is compiled into a separate translation unit. Each example (prohibiting any typos from me) is valid C. Arrays will decay to a "pointer to a pointer" when passed as arguments. How to define func to find out exactly what it was passed from the type of its parameter ?? The answer is that he cannot. The p_buff static type is int** , but it still allows func indirectly access (parts) of objects with significantly more efficient types.

+44
Feb 06 '17 at 10:25
source share

The int **arr declaration 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 one integer object. Since you can use pointer arithmetic with any level of indirection (ie *arr matches arr[0] , and **arr matches arr[0][0] ), the object can be used to access any of 3 of your question (that is, secondly, access to an array of pointers for integers, and for the third - access to an array of pointers to the first elements of whole arrays), provided that the pointers point to the first elements of the arrays ...




However, arr is still declared as a pointer to one pointer to one integer object. You can also declare a pointer to an array of specific dimensions. Here a declared as a pointer to a 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, pointers to arrays are most often used for transmitting constant measurements in functions in multidimensional arrays and for transmitting in arrays of variable length. The syntax for declaring a variable as a pointer to an array is rarely found, since whenever they are passed to a function, it is somewhat easier to use parameters like "array of undefined size", so instead of declaring

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

could use

 void func(int a[][10]) 

pass to a multidimensional array of arrays of 10 integers. Alternatively, typedef may be used to reduce headaches.

+27
Feb 06 '17 at 10:31 on
source share

How to find out if:

  • arr is a pointer to an integer pointer

It is always a pointer to an integer.

  • arr - pointer to an array of pointers to integers
  • arr - pointer to an array of pointers to arrays of integers

It can never be like that. A pointer to an array of pointers to integers will be declared as follows:

 int* (*arr)[n] 

It sounds like you were cheated to use int** poor teachers / books / textbooks. This is almost always a bad practice, as described here and here and (with a detailed explanation of pointers to arrays) here .

EDIT

Finally, I was going to write a detailed message explaining what arrays are there, what tables look like, why the latter are bad and what you should use instead: Proper distribution of multidimensional arrays .

+9
Feb 06 '17 at 10:54 on
source share

Having only a variable declaration, you cannot distinguish between three cases. You can still discuss whether it is possible to 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 arithmetic - be used in three different ways, each of which assumes a different memory location with a (good) chance of making an incorrect assumption.

Consider the following example where int ** 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 from int. Note that you cannot distinguish between these three values ​​only by the type that is int** for all three. But with different initializations, access to each of them inappropriately gives something unpredictable, with the exception of access to 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; 
+8
Feb 06 '17 at 11:29 on
source share

How to find out if:

arr is a pointer to an integer pointer

arr - pointer to an array of pointers to integers

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

You can not. It can be any of them. What ultimately depends on how you place / use it,

So, if you write code using these documents, document what you are doing with them, pass the size parameters to the functions it uses, and usually make sure that you have allocated it before using it.

+7
Feb 06 '17 at 10:27
source share

Pointers do not store information about whether they point to a single object or an object that is an element of an array. Moreover, for the pointer, arithmetic individual objects are considered as arrays of one element.

Consider these ads

 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. He does not know whether the address that he stores points to a single object, for example a , or to the first element of a1 , which has only one element or the first element of a2 , which has ten elements.

+6
Feb 06 '17 at 10:51 on
source share

Type of

 int ** arr; 

has only one valid interpretation. It:

 arr is a pointer to a pointer to an integer 

If you have more information than the declaration above, that's all you can know about it, that is, if arr is probably initialized, it points to another pointer, which, if possible, is initialized, points to an integer.

Assuming proper initialization, the only guaranteed valid way to use it is:

 **arr = 42; int a = **arr; 

However, C allows you to use it in several ways.

• arr can be used as a pointer to a pointer to an integer (ie the main case)

 int a = **arr; 

• arr can be used as a pointer to a pointer to an array of integers

 int a = (*arr)[4]; 

• arr can be used as a pointer to an array of pointers to integers

 int a = *(arr[4]); 

• arr can be used as a pointer to an array of pointers to arrays of integers

 int a = arr[4][4]; 

In the last three cases, it might look like you have an array. However, the type is not an array. The type is always just a pointer to a pointer to an integer - dereferencing is pointer arithmetic. This is not like a 2D array.

To find out what is appropriate for the current program, you need to look at the initialization of the arr code.

Update

For the updated part of the question:

If you have:

 void foo(char** x) { .... }; 

the only thing you know for sure is that **x will give char and *x will give you a char pointer (in both cases, the correct initialization of x assumed).

If you want to use x different way, for example. x[2] , in order to get the third char pointer, it requires the caller to initialize x , so that it points to a region of memory that has at least 3 consecutive char pointers. This can be described as a contract for calling foo .

+6
Feb. 06 '17 at 11:14
source share
Syntax

C is logical. An asterisk before an identifier in a declaration means a pointer to a variable type, two asterisks indicate a pointer to a pointer to a variable type.

In this case, arr is a pointer to a pointer to integer .

There are several ways to use double pointers. For example, you can imagine a matrix with a pointer to a vector of pointers. Each pointer in this vector points to a row of the matrix itself.

You can also create a two-dimensional array using this, e.g.

  int **arr=(int**)malloc(row*(sizeof(int*))); for(i=0;i<row;i++) { *(arr+i)=(int*)malloc(sizeof(int)*col); //You can use this also. Meaning of both is same. // arr[i]=(int*)malloc(sizeof(int)*col); } 
+3
Feb 06 '17 at 10:23
source share

When using pointers there is one trick, read it from the right side to the left side:

int** arr = NULL;

What you get: arr , * , * , int , so array is a pointer to a pointer to an integer.

And int **arr; matches int** arr; .

+3
Feb 06 '17 at 10:33
source share
 int ** arr = NULL; 

It tells the compiler arr is a double pointer of an integer and sets it to NULL .

+2
Feb 06 '17 at 11:21
source share

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

Visit the site, insert your ad and translate it into English.

In int ** arr; it says declare arr as pointer to pointer to int .

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

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

pour foo into the block (int, long long) returning double

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

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

It also converts English to C ads, which are prety neat - if you understand the description correctly.

-one
Feb 06 '17 at 15:55
source share



All Articles