Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Differences when using ** in C

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;

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

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


Another question for the same problem:

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

like image 714
ChikChak Avatar asked Feb 06 '17 10:02

ChikChak


People also ask

IS & the same as * in C?

* can be either the dereference operator or part of the pointer declaration syntax. & can be either the address-of operator or (in C++) part of the reference declaration syntax.

What does * and & mean in C++?

Put simply. & means the address-of, you will see that in placeholders for functions to modify the parameter variable as in C, parameter variables are passed by value, using the ampersand means to pass by reference. * means the dereference of a pointer variable, meaning to get the value of that pointer variable.

What are * and & operator means?

Answer: * Operator is used as pointer to a variable. Example: * a where * is pointer to the variable a. & operator is used to get the address of the variable. Example: &a will give address of a.

What is the difference between and -> in structure?

-> is a shorthand for (*x). field , where x is a pointer to a variable of type struct account , and field is a field in the struct, such as account_number .


3 Answers

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.

like image 69
StoryTeller - Unslander Monica Avatar answered Oct 22 '22 02:10

StoryTeller - Unslander Monica


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.


How can I know if :

  • arr is a pointer to a pointer of an integer

It is always a pointer to pointer to 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

It can never be that. A pointer to an array of pointers to integers would be declared like this:

int* (*arr)[n]

It sounds as if you have been tricked to use int** by poor teachers/books/tutorials. It is almost always incorrect practice, as explained here and here and ( with detailed explanation about array pointers) here.

EDIT

Finally got around to writing a detailed post explaining what arrays are, what look-up tables are, why the latter are bad and what you should use instead: Correctly allocating multi-dimensional arrays.

like image 9
Lundin Avatar answered Oct 22 '22 02:10

Lundin