int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
The third one is same as the first.
The general rule is operator precedence. It can get even much more complex as function pointers come into the picture.
Answer from Mehrdad Afshari on Stack OverflowI've been reading K&R and the syntax that differentiates an array of pointers vs a pointer to an array is confusing me. They say that
int *array[100];
is an array of 100 pointers to integers. On the other hand,
int (*array)[100];
is a pointer to an array of 100 integers.
Can someone elaborate on why this is the case?
It seems to me that it should be the other way around, since *(array[100]) reads like a pointer to an array with 100 elements, while (*array)[100] looks very much like it should be an array of 100 pointers.
What am I missing here?
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
The third one is same as the first.
The general rule is operator precedence. It can get even much more complex as function pointers come into the picture.
Use the cdecl program, as suggested by K&R.
$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>
It works the other way too.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
c - Pointer to an array and Array of pointers - Stack Overflow
c - Pointer to array of pointer vs pointer to an array - Stack Overflow
What’s the difference between an array and a pointer to an array in C?
Confused about pointer pointers and array pointers
What is a Pointer to an Array?
Can you resize the allocated memory of a pointer?
Pointer to an array
int a[10];
int (*ptr)[10];
Here ptr is an pointer to an array of 10 integers.
ptr = &a;
Now ptr is pointing to array of 10 integers.
You need to parenthesis ptr in order to access elements of array as (*ptr)[i] cosider following example:
Sample code
#include<stdio.h>
int main(){
int b[2] = {1, 2};
int i;
int (*c)[2] = &b;
for(i = 0; i < 2; i++){
printf(" b[%d] = (*c)[%d] = %d\n", i, i, (*c)[i]);
}
return 1;
}
Output:
b[0] = (*c)[0] = 1
b[1] = (*c)[1] = 2
Array of pointers
int *ptr[10];
Here ptr[0],ptr[1]....ptr[9] are pointers and can be used to store address of a variable.
Example:
main()
{
int a=10,b=20,c=30,d=40;
int *ptr[4];
ptr[0] = &a;
ptr[1] = &b;
ptr[2] = &c;
ptr[3] = &d;
printf("a = %d, b = %d, c = %d, d = %d\n",*ptr[0],*ptr[1],*ptr[2],*ptr[3]);
}
Output: a = 10, b = 20, c = 30, d = 40
Background
Think of pointers as just a separate data type. They have their own storage requirements -- such as their size -- they occupy 8 bytes on a x86_64 platform. This is the case of void pointers void*.
In those 8 bytes the information stored is the memory address of another piece of data.
The thing about pointers is that since they "point" to another piece of data, it's useful to know what type that data is too so you can correctly handle it (know its size, and structure).
In stead of having their own data type name such as pointer they compose their name based on the data type they refer to such as int* a pointer to an integer. If you want a plain pointer without type information attached to it you have the option of using void*.
So basically each pointer (to int, to char, to double) is just a void* (same size, same use) but the compiler knows the data being pointed to is of type int and allows you to handle it accordingly.
/**
* Create a new pointer to an unknown type.
*/
void* data;
/**
* Allocate some memory for it using malloc
* and tell your pointer to point to this new
* memory address (because malloc returns void*).
* I've allocated 8 bytes (char is one byte).
*/
data = malloc(sizeof(char)*8);
/**
* Use the pointer as a double by casting it
* and passing it to functions.
*/
double* p = (double* )data;
p = 20.5;
pow((double* )data, 2);
Pointer to array
If you have an array of values (let's say integers) somewhere in memory, a pointer to it is one variable containing its address.
You can access this array of values by first dereferencing the pointer and then operating some work on the array and its values.
/**
* Create an array containing integers.
*/
int array[30];
array[0] = 0;
array[1] = 1;
...
array[29] = 29;
/**
* Create a pointer to an array.
*/
int (*pointer)[30];
/**
* Tell the pointer where the data is.
*/
pointer = &array;
/**
* Access the data through the pointer.
*/
(*pointer)[1] = 999;
/**
* Print the data through the array.
* ...and notice the output.
*/
printf("%d", array[1]);
Array of pointers
If you have an array of pointers to values, the entire array of pointers is one variable and each pointer in the array refers to somewhere else in the memory where a value is located.
You can access this array and the pointers inside it without dereferencing it but in order to reach a certain value from it you will have to dereference one of the pointers inside the array.
/**
* Create an array containing pointers to integers.
*/
int *array_of_pointers[30];
array_of_pointers[0] = 0;
array_of_pointers[1] = 1;
...
array_of_pointers[29] = 29;
I’m trying to understand the distinction between an array and a pointer to an array in C.
For example:
int arr[5]; int *ptr1 = arr; // pointer to the first element int (*ptr2)[5] = &arr; // pointer to the whole array
I know that in most cases arrays “decay” into pointers, but I’m confused about what that really means in practice.
-
How are
arr,ptr1, andptr2different in terms of type and memory layout? -
When would you actually need to use a pointer to an array (
int (*ptr)[N]) instead of a regular pointer (int *ptr)? -
Does
sizeofbehave differently for each?
Any clear explanation or example would be really appreciated!
So if make an array like this:
int v[4][2];
I can’t assign v to:
int **pptr;
Instead I can assign it to:
int (*aptr)[2];
But I can pptr into a 2d array with malloc:
pptr = malloc( 4 * sizeof(int *));
for (int i = 0; i < 4; i++)
pptr[i] = malloc(2 * sizeof(int));Now I can assign a number to pptr[2][1] similar to v, but I can also assign pptr into another pointer pointer without any problems unlike v. This is so confusing, can someone explain why this happens?
If the code you reference in your question was given to you by your professor as an example of the use of pointer arrays of pointers to pointers, I'm not sure how much good that class will actually do. I suspect it was either provided as a debugging exercise or it may have been your attempt at a solution. Regardless, if you simply compile with Warnings enabled, you will find a number of problems that need attention before you advance to debugging your code.
Regarding the code you reference, while you are free to use a global text buffer, you are far better served by not using a global buffer and passing a pointer to your data as required. There are some instances, various callback functions, etc. that require global data, but as a rule of thumb, those are the exception and not the rule.
Your question basically boils down to "How do I properly use an array of pointers and double-pointers (pointer-to-pointer-to-type) variables. There is no way the topic can be completely covered in one answer because there are far too many situations and contexts where one or the other can be (or should be) used and why. However, a few examples will hopefully help you understand the basic differences.
Starting with the array of pointers to type (e.g. char *array[]). It is generally seen in that form as a function argument. When declared as a variable it is followed with an initialization. e.g.:
char *array[] = { "The quick",
"brown fox",
"jumps over",
"the lazy dog." };
char *array[]; by itself as a variable declaration is invalid due to the missing array size between [..]. When used globally, as in your example, the compiler will accept the declaration, but will warn the declaration is assumed to have one element.
The elements of array declared above are pointers to type char. Specifically, the elements are pointers to the string-literals created by the declaration. Each of the strings can be accessed by the associated pointer in array as array[0], ... array[3].
A pointer to pointer to type (double-pointer), is exactly what its name implies. It is a pointer, that holds a pointer as its value. In basic terms, it is a pointer that points to another pointer. It can be used to access the members of the array above by assigning the address of array like:
char **p = array;
Where p[1] or *(p + 1) points to "brown fox", etc.
Alternatively, a number of pointer to pointer to type can be dynamically allocated and used to create an array of pointers to type, that can then be allocated and reallocated to handle access or storage of an unknown number of elements. For example, a brief example to read an unknown number of lines from stdin, you might see:
#define MAXL 128
#define MAXC 512
...
char **lines = NULL;
char buf[MAXC] = {0};
lines = malloc (MAXL * sizeof *lines);
size_t index = 0;
...
while (fgets (buf, MAXC, stdin)) {
lines[index++] = strdup (buf);
if (index == MAXL)
/* reallocate lines */
}
Above you have lines, a pointer-to-pointer-to-char, initially NULL, that is use to allocate MAXL (128) pointers-to-char. Lines are then read from stdin into buf, after each successful read, memory is allocated to hold the contents of buf and the resulting start address for each block of memory is assigned to each pointer line[index] where index is 0-127, and upon increment of index to 128, index is reallocated to provide additional pointers and the read continues.
What makes the topic larger than can be handled in any one answer is that an array of pointers or pointer to pointer to type can be to any type. (int, struct, or as a member of a struct to different type, or function, etc...) They can be used linked-lists, in the return of directory listings (e.g opendir), or in any additional number of ways. They can be statically initialized, dynamically allocated, passed as function parameters, etc... There are just far too many different contexts to cover them all. But in all instances, they will follow the general rules seen here and in the other answer here and in 1,000's more answers here on StackOverflow.
I'll end with a short example you can use to look at the different basic uses of the array and double-pointer. I have provided additional comments in the source. This just provides a handful of different basic uses and of static declaration and dynamic allocation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void) {
/* array is a static array of 4 pointers to char, initialized to the
4 string-literals that a part of the declaration */
char *array[] = { "The quick",
"brown fox",
"jumps over",
"the lazy dog." };
/* p is a pointer-to-pointer-to-char assigned the address of array */
char **p = array;
/* lines is a pointer-to-pointer-to-char initialized to NULL, used
below to allocate 8 pointers and storage to hold 2 copes of array */
char **lines = NULL;
size_t narray = sizeof array/sizeof *array;
size_t i;
printf ("\nprinting each string-literal at the address stored by\n"
"each pointer in the array of ponters named 'array':\n\n");
for (i = 0; i < narray; i++)
printf (" %s\n", array[i]);
printf ("\nprinting each string using a pointer to pointer to char 'p':\n\n");
for (i = 0; i < narray; i++, p++)
printf (" %s\n", *p);
p = array;
printf ("\nprinting each line using a pointer to pointer"
" to char 'p' with array notation:\n\n");
for (i = 0; i < narray; i++)
printf (" %s\n", p[i]);
/* allocate 8 pointers to char */
lines = malloc (2 * narray * sizeof *lines);
/* allocate memory and copy 1st 4-strings to lines (long way) */
for (i = 0; i < narray; i++) {
size_t len = strlen (array[i]);
lines[i] = malloc (len * sizeof **lines + 1);
strncpy (lines[i], array[i], len);
lines[i][len] = 0;
}
/* allocate memory and copy 1st 4-strings to lines
(using strdup - short way) */
// for (i = 0; i < narray; i++)
// lines[i] = strdup (array[i]);
/* allocate memory and copy again as last 4-strings in lines */
p = array;
for (i = 0; i < narray; i++, p++)
lines[i+4] = strdup (*p);
p = lines; /* p now points to lines instead of array */
printf ("\nprinting each allocated line in 'lines' using pointer 'p':\n\n");
for (i = 0; i < 2 * narray; i++)
printf (" %s\n", p[i]);
/* free allocated memory */
for (i = 0; i < 2 * narray; i++)
free (lines[i]);
free (lines);
return 0;
}
Let me know if you have any questions. It a large topic with a relatively small set of rules that can be applied in whole lot of different ways and in different contexts.
My professor wrote that
**arrayis same as*array[]
That is true in some contexts and not true in other contexts.
If used in a function as argument,
void foo(int **array) {}
is the same as
void foo(int *array[]) {}
When declared as variables,
int **array;
is not the same as
int *array[];
Re comment "Why is int **array; not the same as int *array[];?"
Let's address the question with int *array and int array[].
Here are some of the valid and invalid ways to declare the variables.
int *array; // Ok. Uninitialized pointer.
int *array = nullptr; // ok.
int *array = new int; // ok.
int *array = new int[10]; // ok.
const size = sizeof(array); // Size of a pointer. For 32 bit
// pointers, this will be 4.
// For 64 bit pointers, this will be 8
Contrast that with
int array[]; // Not ok.
int array[] = nullptr; // Not ok.
int array[] = new int; // Not ok.
int array[] = new int[10]; // Not ok.
int array[] = {10, 20, 30}; // Ok.
int array[3] = {10, 20, 30}; // Ok. same as previous
const size1 = sizeof(array); // (size of int)*3
/// Unrelated to size of pointers
int array[5] = {10, 20, 30}; // Ok.
int array[5] = {10, 20, 30, 0, 0}; // Same as previus.
const size2 = sizeof(array); // (size of int)*5
/// Unrelated to size of pointers
More on the differences:
int *array;is sufficent to declare a variable whileint array[]is not. Size of the array must be known at compile time for the latter.sizeofoperator works diferently forint *array;andint array[];.A variable declared with
int *can be reassigned to point to a different location while a variable declared withint []cannot be reassigned.int *array = nullptr; array = new int; // Syntactically ok. array = new int[10]; // Syntactically ok.OTOH
int array[] = {10, 20, 30}; array = new int[3]; // Not ok. Compiler error. array = {50, 60, 70}; // Not ok. Compiler error.A variable declared with
int *can point to statically allocated memory as well as dynamically allocated memory. A variable declared withint []references only statically defined and constant size memory.
The differences apply equally to int **array; and int *array[];.