I am checking out a tutorial video about heap from Bucky on YT.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int n, howMany;
int total;
float average = 0.0;
int * pointsArray;
printf("How many numbers do you want to average?\n");
scanf(" %d", &howMany);
pointsArray = (int *) malloc(howMany * sizeof(int));
printf("Enter numbers! \n");
for(n = 0; n<howMany; n++){
scanf(" %d", &pointsArray[n]);
total += pointsArray[n];
}
average = (float)total /(float)howMany;
printf("average is %f", average);
free(pointsArray);
return 0;
}
I don't understand how pointer-pointsArray changed from a pointer to a pointer array in the above for loop.
I have knowledge on rvalue and Ivalue in variable. I would appreciate if you may include these concept in the answer.
At what line did pointsArray become a pointer array?
pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory size of pointsArray.
Why complier knows how to input value in array when complier sees &pointsArray[n]?
I am able to identify my problem should lie in my confusion of array and pointer that becomes an array. May I have a clear definition so that I can separate them?
Thanks a lot. Please let me know if my question is unclear. I will try to improve it.
I don't understand how pointer-pointsArray changed from a pointer to a
pointer array in the above for loop. I have knowledge on rvalue and
Ivalue in variable. I would appreciate if you may include these
concept in the answer.
At what line did pointsArray become a pointer array? pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory
size of pointsArray.
First, let's start with the basics. A pointer is not an array, and an array is not a pointer, they are two separate types in C. An array type is converted to a pointer to the first element of the array on access as provided in C11 Standard - 6.3.2.1 Lvalues, arrays, and function designators(p3)
int * pointsArray;
...
pointsArray = (int *) malloc(howMany * sizeof(int));
What is pointsArray? (it is a pointer to type int).
Well, What is a Pointer?
A pointer is simply a normal variable that holds the address of something else as its value. In other words, a pointer points to the address where something else can be found. Where you normally think of a variable holding an immediate values, such as int a = 5;, a pointer would simply hold the address where 5 is stored in memory, e.g. int *b = &a; declares b as a pointer to type int and initializes its value to the address where 5 is stored in memory (e.g. b points to a -- where 5 is currently stored). It works the same way regardless what type of object the pointer points to.
Pointer arithmetic will work the same for all pointers regardless of the type because the type of the pointer controls the pointer arithmetic, e.g. with a char * pointer, pointer+1 points to the next byte after pointer. For an int * pointer (normal 4-byte integer), pointer+1 will point to the next integer at an offset 4-bytes after pointer. (so a pointer, is just a pointer.... where arithmetic is automatically handled by the type)
At what line did pointsArray become a pointer array? pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory
size of pointsArray.
Recall, "a pointer is not an array and an array is not a pointer." An array is declared either with:
type name[CONST]; /* declares an array of type with CONST elements */
type name[var]; /* declares a VLA C99+ with var no. of elements */
type name[] = { 1, 2, 3 }; /* declares & initializes an array of 3 elements */
Constrast with:
type *name; /* declares a pointer to type */
In the case of an array with a constant number of elements or with an initialization list, automatic-storage is provided for the array. Storage for the variable-length array (VLA) is similar but within another segment (generally within .bss [block stated by symbol]). On access (except when "it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array") an array is converted pointer to type that points to the initial element of the array object and is not an lvalue. 6.3.2.1(p3) (You will often see the phrase an "Array decays to a pointer" -- which is referring to this conversion)
(Note: it does not work in reverse, a pointer is never converted to an array)
The declaration type *name; declares name as a pointer to type. It is uninitialized and it holds an indeterminate address. Any attempt to access or otherwise make use of an uninitialized pointer invokes Undefined Behavior (frequently leading to a Segmentation Fault). The pointer must hold a valid address (e.g. it must point to valid storage) before you can access the memory. The easiest way to provide valid storage for a pointer is to dynamically allocate a block of memory and assign the starting address for the block to the pointer. This is done in your code with:
pointsArray = (int *) malloc(howMany * sizeof(int));
(note: there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?)
A better approach would be:
pointsArray = malloc (howMany * sizeof *pointsArray);
(pointsArray is type int*, so *pointsArray is type int. If you use the derefernced pointer as your type-size, you will eliminate the potential for type-size error)
Above, malloc reserves a valid block of memory guaranteed to be at least howMany * sizeof *pointsArray bytes in size. The starting address for the block of memory is assigned to pointsArray (e.g. pointsArray now points to that block) The memory has allocated storage duration and remains valid until you free() it, or the program ends. YOU are responsible for preserving a pointer holding the starting address of the block of memory so it can be freed when no longer needed.
You can now store up to howMany integers in the block of memory pointed to by pointsArray. To access the first address for an integer, you can use *(pointArray + 0) (which is just *pointsArray) using pointer notation or you can use pointsArray[0] using array index notation. (the key is to understand that [] acts as a defeference just the same as the '*' in *(name + offset) does. So to access the second element, you can use either *(pointsArray + 1) or pointsArray[1], etc.. While you can use array index notation with a pointer -- that does NOT make the pointer an array or cause it to be converted to one.
Why complier knows how to input value in array when complier sees &pointsArray[n]?
That has more to do with the requirements of scanf than anything funny with pointsArray. Think about it. From man 3 scanf() when using the %d format conversion specifier, scanf:
Matches an optionally signed decimal integer; the next pointer
must be a pointer to int.
So if instead you wanted to read an integer into the variable declared with int a;, your use of scanf would be:
if (scanf ("%d", &a) == 1) {
/* you have a good integer */
}
else {
/* a matching or input failure occurred */
}
For the integer value to be placed by scanf in a, you must provide a pointer to a. It is no different when you want to put the value in the integer pointsArray[n]. How do you provide a pointer to (address of) pointsArray[n]? (answer: you prefix it with the unary '&' address of operator -- &pointsArray[n])
Hopefully this will take care of your No. 3 as well. Look things over and let me know if you have further questions.
Array is contiguous block of memory and variable holds start of memory.
int* pointsArray
pointsArray = malloc(sizeof(*pointsArray) * howMany);
Now pointsArray holds (points to) some block of memory, in this case returned by malloc and you can normally use array annotation to access to memory entries.
Your pointer points to memory of type int which (I assume) is on your machine 4 bytes each. When you trying to access memory pointsArray[n] you are accessing memory locations:
pointsArray[0] = pointsArray + 4 * 0;
pointsArray[1] = pointsArray + 4 * 1;
pointsArray[n] = pointsArray + 4 * n;
If we look very generic way, each index has offset for sizeof(type), in your case type is int which is 4 bytes.
pointsArray[n] = pointsArray + sizeof(*pointsArray) * n
Please not that it is not a good idea to cast return of malloc and that it is better and safer to add sizeof(...) part to beginning of size calculation.
Do I cast the result of malloc?
At what line did pointsArray become a pointer array?
pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory size of pointsArray.
You are correct. It does not become a pointer array.
Why complier knows how to input value in array when complier sees &pointsArray[n]?
The compiler has nothing to do with this. The code is just reading input values to the extent of sizeof(int) (4 bytes) into the memory pointed to by pointsArray. At every iteration of the for loop it moves four bytes and writes the value of the input into that memory location.
I am able to identify my problem should lie in my confusion of array and pointer that becomes an array. May I have a clear definition so that I can separate them?
Since a pointer holds value of an address location, the address of any type including an array is a pointer. So pointer arithmetic can be applied on it.
Array is just a continuous memory location, so if you have address of the first element you can access other elements by adding to the first address.
So if you have a pointer which points to first element of the array like here
pointsArray = (int *) malloc(howMany * sizeof(int));
pointsArray points to the first element.
Now to access other elements you can use pointsArray[n] which translates to pointsArray + sizeof(*pointsArray) * n (address of the nth element) to access other elements.
Pointer is just an address (index) of a specific cell in the memory and it knows the basic length of its type, e.g. int32_t *p = 0x12345678 is an address at byte 0x12345678 with length 4 bytes. So when we do p+1, the result is 0x1234567c. Note Pointer itself is a type with a length of 4 or 8 bytes depending on your box.
For your question,
pointsArray is always and only a pointer of int. it doesn't know any information about the array, e.g. the length of the array. after the line pointsArray = (int *) malloc(howMany * sizeof(int)), pointsArray has a valid value (an address), which is the address of the memory allocated for an int array. (pointsArray doesn't know the length of the array).
for &pointsArray[n], compiler just write to the memory of address pointsArray + n.
The name of an array is very similar to a const pointer (the only difference I remember is that when you use sizeof to the name of an array you can get the size of the array while for a const pointer you get the size of the pointer). In C, I prefer not to use the concept of array, just consider it as a const pointer of some type.
Related
I'm very new to C and I have trouble understanding array pointers. I'm trying to make a array bigger,I copy all of its element to new bigger array but I can't make original variable to point the new array. I'm use to C# where you can do
double[] array1 = new double[5];
double[] array2 = new double[10];
array1 = array2;
I did something similar using int array
int array1 [5];
int array2 [10];
*array1 = &array2;
and it compile but crash the program. Same lines but double or char[] (I was told to use char[] instead of sting in C) do not even compile
[Error] incompatible types when assigning to type 'double' from type 'double (*)[(sizetype)(newsize)]'
The results I found on the topic told me to use double* array1 for variable type but this change the interactions with that variable.
If someone can explain the concept to me or at least tell me what to search for that will be huge help.
I do know the basics of pointers!
There are a few things you need to know about arrays (and pointers):
The first is that arrays and pointers are two different things;
The second is that an array can decay to a pointer to its first element. So if you use array1 (from your example) when a pointer is expected, that's the same as doing &array1[0]. The type of such a pointer is a pointer to a single element type (so for array1 the type will be int *);
The third thing is that for any array of pointer a and index i, the expression a[i] is exactly equal to *(a + i). That means *array1 (again from your example) is the same as array1[0] (*array1 is equal to *(array1 + 0) which is equal to array1[0]);
An array will have a fixed size. Once defined the size of an array can't change;
Lastly when you get a pointer to an array (as in &array2) then you get a pointer to the actual array, not to one of its elements. The type of e.g. &array2 is int (*)[10].
Now we can puzzle together the statement
*array1 = &array2;
If we do the array-indexing replacement for *array1 then we get
array[0] = &array2;
And here we can see a big problem: The type of a single element of array1 is a plain int. So what the assignment is trying to do is to assign a pointer to an array (of type int (*)[10]) to a single int.
If you want to copy all the elements from one array to another, then use the memcpy function. You're not allowed to assign between arrays.
But beware of the different sizes for array1 and array2. If you go out of bounds of an array (or other allocated memory) you will have undefined behavior.
In C there is no way to make an array variable "reference" a different variable. If you need to use "references" they can be emulated using pointers:
int *pointer1 = array1; // array1 here will decay to &array[0]
int *pointer2 = array2; // Same here for array2
With the above definition pointer1 is (in a way) "referencing" array1. You can now use pointer1 and array1 almost interchangeably.
One major difference between using pointers and arrays is how their sizes are calculated: When you do sizeof on an array you get the size (in bytes) of the whole array. Assuming 32-bit int (the most common) then sizeof array1 will return 5 * 4 (or 20) as the size. If you get the size of a pointer, you get the size of the pointer itself, not what it might point to. So sizeof pointer1 will return either 4 or 8 (depending on if you're in a 32-bit or 64-bit system).
Going back to references, we can now change where pointer1 is pointing:
pointer1 = pointer2; // Assuming pointer2 is unchanged, equivalent to pointer1 = array2
Now pointer1 and pointer2 are pointing to the same array, array2.
In C# you can overload the = to copy the arrays. In C it is just simple assignment.
In C arrays decays to pointers for the sake of simplicity. In C *(array + N) == array[N] and *array == array[0]
int array1 [5]; it is not the array of pointers only integers so *array1 = &array2; assigns array[0] with address of the first element of the the array2 converted to signed integer which generally doesn't make too much sense and it does not copy array2 to array
To copy array you need to use memcpy or the loop to copy the element. You need to make sure that the destination array is large enough to accommodate the second array. C will not change the destination array size.
The assignments that your are doing is wrong. Basically a pointer points to a block of memory. from your code I can understand that array1 = array2; and *array1 = &array2; is wrong.
Syntax in C is something like this data-type* pointer-variable = (data-type*)malloc(no. of bytes you want);
See consider you want 10 block of memory of type int
int *p = (int *)malloc(10 * sizeof(int))
sizeof(int) return 4 bytes.
Now p points to 10 * 4 = 40 bytes of memory, I multiplied by 4 because int is usually of 4 bytes and double is of 8 bytes and so on.
Follow this link to understand C - Data Types
Now regarding changing pointers refer below example and read the comments
int *q = NULL // declare a pointer of same type as the block of memory it is going to point
q = p; //now q and p point same memory of 40 bytes, means value at q[0] is equal to p[0]
When you have an integer pointer and you increment it by p++ it will point to next memory location p[1], pointer will be exactly incremented by 4 bytes as int size is 4 bytes and for double it will be 8 bytes, for char it will be 1 byte and so on.
Now if you want to increase the size of dynamically allocated memory you can use realloc please follow this link to understand more.
Dynamic Memory Allocation in C
int *p = NULL;
// Dynamically allocate memory using malloc()
p = (int*)malloc(no. of bytes, sizeof(int));
// Dynamically re-allocate memory using realloc()
p = realloc(p, (no. of bytes) * sizeof(int));
// Avoid memory leaks
free(p);
Syntax in C++ is something like this data-type* pointer-variable = new data-type[size];
See consider you want 10 block of memory of type int
int *p = new int[10]
Just use new operator to allocate block of memory and use delete to free allocated memory to avoid memory leaks.follow this link
new and delete operators in C++ for dynamic memory
Or If you are looking for containers where you don't know how much memory should be allocated the use standard template library vector, it helps creating dynamic arrays.follow this link
Vector in C++ STL
malloc() function forms a single block of memory (say 20 bytes typecasted to int), so how it can be used as an array of int blocks like as calloc() function? Shouldn't it be used to store just one int value in whole 20 bytes (20*8 bits)?
(say 20 bytes typecasted to int)
No, the returned memory is given as a pointer to void, an incomplete type.
We assign the returned pointer to a variable of pointer to some type, and we can use that variable to access the memory.
Quoting C11, chapter ยง7.22.3, Memory management functions
[....] The
pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to
a pointer to any type of object with a fundamental alignment requirement and then used
to access such an object or an array of such objects in the space allocated (until the space
is explicitly deallocated). [...] The pointer returned points to the start (lowest byte address) of the
allocated space. [....]
Since the allocated memory is contiguous, pointer arithmetic works, just as in case of arrays, since in arrays also, elements are placed in contiguous memory.
One point to clarify, a pointer is not an array.
There's an abstract concept in C formally known as effective type, meaning the actual type of the data stored in memory. This is something the compiler keeps track of internally.
Most objects in C have such an effective type at the point when the variable is declared, for example if we type int a; then the effective type of what's stored in a is int.
Meaning it is legal to do evil things like this:
int a;
double* d = (double*)&a;
*(int*)d = 1;
This works because the effective type of the actual memory remains an int, even though we pointed at it with a wildly incompatible type. As long as we access it with the same type as the effective type, all is well. If we access the data using the wrong type, very bad things will happen, such as program crashes or dormant bugs.
But when we call malloc family of functions, we only tell them to reserve n number of bytes, with no type specified. This memory is guaranteed to be allocated in adjacent memory cells, but nothing else. The only difference between malloc and calloc is that the latter sets all values in this raw memory to zero. Neither function knows anything about types or arrays.
The returned chunk of raw memory has no effective type. Not until the point when we access it, then it gets the effective type which corresponds to the type used for the access.
So just as in the previous example, it doesn't matter which type of pointer we set to point at the data. It doesn't matter if we write int* i = malloc(n); or bananas_t* b = malloc(n);, because the pointed-at memory does not yet have a type. It does not get one until at the point where we access it for the first time.
There is nothing special about memory returned from malloc compared to memory returned from calloc, other that the fact that the bytes of the memory block returned by calloc are initialized to 0. Memory returned by malloc does not have to be used for a single object but may also be used for an array.
This means that the following are equivalent:
int *p1 = malloc(3 * sizeof(int));
p1[0] = 1;
p1[2] = 2;
p1[3] = 3;
...
int *p2 = calloc(3, sizeof(int));
p2[0] = 1;
p2[2] = 2;
p2[3] = 3;
Both will return 3 * sizeof(int) bytes of memory which can be used as an array of int of size 3.
What malloc returns back to you is just a pointer to the starting memory address where the contiguous block of memory was allocated.
The size of the contiguous block of memory that you allocated using malloc depends on the argument you passed into malloc function. http://www.cplusplus.com/reference/cstdlib/malloc/
If you want to store int variable then you will do it by defining the pointer type you use to be of an int type.
example:
int p*; //pointer of type integer
size_t size = 20;
p = (int *) malloc(size); //returns to pointer p the memory address
after this, using the pointer p the programmer can access int (4 byte precision) values.
calloc only difference against malloc is that calloc initiallizes all values at this memory block to zero.
I'm currently learning C and I'm confused about memory layout and pointers.
In the following code, it is my understanding that the array is allocated on the stack.
#include <stdio.h>
int main () {
int x[4];
x[0] = 3; x[1] = 2; x[2] = 1;
printf("%p\n",x);
printf("%p\n", &x);
}
My question is, why do the two print calls output the same value?
I tried a similar snippet using malloc (allocate on the heap), and the values differ.
#include <stdio.h>
#include <stdlib.h>
int main () {
int *x = malloc(sizeof(int) * 4);
x[0] = 3; x[1] = 2; x[2] = 1;
printf("%p\n",x);
printf("%p\n", &x);
}
The reason is that unlike you were probably taught, arrays are not pointers. Arrays in C decay into pointers1 under some circumstances. When you pass an array to a function, it decays into a pointer to the first element. The address of that element is the same as the address of the entire array (an address is always to the first byte of an object).
What you get from malloc is not an array, but the address of a chunk of memory. You assign the address to a pointer. But the pointer and the chunk are separate entities. So printing the value of the pointer, as opposed to its address, yields different results.
(1) Decay is a fancy term for a type of implicit type conversion. When an array expression is used in most places (such as being passed as an argument to a function that expects a pointer), it automatically turns into a pointer to its first element. The "decay" is because you lose type information, i.e. the array size.
Your two print calls print the same value because one tries to print the array, which decays to a pointer to the array, and the other prints the address of the array. A pointer to the array contains the address of the array, so they're the same value.
In the second case, one prints the value of x, the other prints the address of x. Since x is a pointer to the block of memory you allocated, these must be different values.
So in the first case, all you have is an array (x). In the second case, you have an allocated block of memory (unnamed) and a pointer to that allocated block (x).
It is perhaps surprising that one can indeed take the address of a whole array, partly because one doesn't need to very often. The array in a sense is a single object, which has one address, which is the address of its first byte. Like with all objects, the address is obtained with the address operator, &.
The first element of an array (like all of its elements) has an address, too, which is the address of its first byte. A pointer to its first element is what the array type is "adjusted" to when it is passed as an argument to a function.
These two bytes are identical, and have the same address. But they have different types, which becomes obvious if you add 1 to them and print them again.
The pointer y, by contrast, is its own distinct object (probably 4 or 8 bytes in size; enough to store an address in it). Like any object it has an address which can be obtained with the & operator. Perhaps confusingly, it also contains an address, in this case the address of the first byte of the array. The two are of course not equal: The pointer object resides at a different location than the array (namely next to it on the stack, even if Olaf doesn't like that).
Minor remark: You use %p for printing pointers, which is good. If you do that, you should strictly spoken cast the pointer which you print to a void pointer: printf("%p\n", (void *)x);.
I have been following some examples that declare an int pointer
int *myInt;
and then turn that pointer into an array
myInt = (int*)malloc(1024);
this checks out
myInt[0] = 5;
cout << myInt[0]; // prints 5
myInt[1] = 7;
cout << myInt[1]; // prints 7
I thought an int pointer was a pointer to an int and never anything else. I know that pointers to strings just point to the first character of the string but it looks like the same sort of thing is happening here with an array of ints. But then if what we want is an array of ints why not just create an array of ints instead of a pointer to an int?
By the way I am interested in how this works in C not C++. This is in a C++ file but the relevant code is in C.
Is an int pointer an array of ints?
No.
I thought an int pointer was a pointer to an int and never anything else
That's right. Pointers are pointers, arrays are arrays.
What confuses you is that pointers can point to the first element of arrays, and arrays can decay into pointers to their first element. And what's even more confusing: pointers have the same syntax for dereferencing and pointer arithmetic that arrays utilize for indexing. Namely,
ptr[i]
is equivalent with
*(ptr + i)
if ptr is a pointer. Of course, similarly, arr[i] is the ith element of the arr array too. The similarity arises out of the common nature of pointers and arrays: they are both used to access (potentially blocks of) memory indirectly.
The consequence of this strong relation is that in some situations (and with some constraints), arrays and pointers can be used as if they were interchangeable. This still doesn't mean that they are the same, but they exhibit enough common properties so that their usage often appears to be "identical".
There is an alternative syntax for accessing items pointed by a pointer - the square brackets. This syntax lets you access data through pointers as if the pointer were an array (of course, pointers are not arrays). An expression a[i] is simply an alternative form of writing *(a+i)* .
When you allocate dynamic storage and assign it to myInt, you can use the pointer like a dynamic array that can change size at runtime:
myInt = malloc(1024*sizeof(int)); // You do not need a cast in C, only in C++
for (int i = 0 ; i != 1024 ; i++) {
myInt[i] = i; // Use square bracket syntax
}
for (int i = 0 ; i != 1024 ; i++) {
printf("%d ", *(myInt+i)); // Use the equivalent pointer syntax
}
* Incidentally, commutativity of + lets you write 4[array] instead of array[4]; don't do that!
Sort of, and technically no. An int pointer does point to the int. But an array of ints is contiguous in memory, so the next int can be referenced using *(myInt+1). The array notation myInt[1] is equivalent, in that it uses myInt pointer, adds 1 unit to it (the size of an int), and reference that new address.
So in general, this is true:
myInt[i] == *(myint + i)
So you can use an int pointer to access the array. Just be careful to look out for the '\0' character and stop.
An int pointer is not an array of ints. But your bigger question seems to be why both arrays and pointers are needed.
An array represents the actual storage in memory of data. Once that storage is allocated, it makes no significant difference whether you refer to the data stored using array notation or pointer notation.
However, this storage can also be allocated without using array notation, meaning that arrays are not necessarily needed. The main benefit of arrays is convenient allocation of small blocks of memory, i.e., int x[20] and the slightly more convenient notation array[i] rather than *(array+i). Thankfully, this more convenient notation can be used regardless of whether array came from an array declaration or is just a pointer. (Essentially, once an array has been allocated, its variable name from that point onwards is no different than a pointer that has been assigned to point to the location in memory of the first value in the array.)
Note that the compiler will complain if you try to directly allocate too big of a block of memory in an array.
Arrays:
represent the actual memory that is allocated
the variable name of the array is the same as a pointer that references the point in memory where the array begins (and the variable name + 1 is the same as a pointer that references the point in memory where the second element of the array begins (if it exists), etc.)
values in the array can be accessed using array notation like array[i]
Pointers:
are a place to store the location of something in memory
can refer to the memory that is allocated in an array
or can refer to memory that has been allocated by functions like malloc
the value stored in the memory pointed to by the pointer can be accessed by dereferencing the pointer, i.e., *pointer.
since the name of the array is also a pointer, the value of the first element in the array can be accessed by *array, the second element by *(array+1), etc.
an integer can be added or subtracted to a pointer to create a new pointer that points to other values within the same block of memory your program has allocated. For example, array+5 points to the place in memory where the value array[5] is stored.
a pointer can be incremented or decremented to point to other values with the same block of memory.
In many situations one notation will be more convenient than the other, so it is extremely beneficial that both notations are available and so easily interchanged with each other.
They are not the same. Here is the visible difference.
int array[10];
int *pointer;
printf ("Size of array = %d\nSize of pointer = %d\n",
sizeof (array), sizeof (pointer));
The result is,
Size of array = 40
Size of pointer = 4
If You do "array + 1", the resulting address will be address of array[0] + 40. If You do "pointer + 1", resulting address will be address of pointer[0] + 4.
Array declaration results in compile time memory allocation. Pointer declaration does not result in compile time memory allocation and dynamic allocation is needed using calloc() or malloc()
When you do following assignment, it is actually implicit type cast of integer array to integer pointer.
pointer = array;
I have more than one doubt so please bear with me.
Can someone tell me why this code fails?
#include<stdio.h>
void main(int argc,char **argv) /*assume program called with arguments aaa bbb ccc*/
{
char **list={"aaa","bbb","ccc"};
printf("%s",argv[1]);/*prints aaa*/
printf("%s",list[1]); /*fails*/
}
I assumed it had something to do with the pointer to pointer stuff, which i do not understand clearly. So i tried:
#include<stdio.h>
void main()
{
char **list={"aaa","bbb","ccc"};
char *ptr;
ptr=list;
printf("%s",ptr);/*this prints the first string aaa*/
/* My second question is how do i increment the value
of ptr so that it points to the second string bbb*/
}
What is the difference between char *list[] and char **list and in what situations are both ideal to be used?
One more thing confusing me is argv special? when i pass char **list to another function assuming it would let me access the contents the way i could with argv, it also failed.
I realize similar questions have been asked in the past, but i cant seem to find what i need. if so can someone please post the relevant links.
You should use char *list[]={"aaa","bbb","ccc"}; instead of char **list={"aaa","bbb","ccc"};. You use char* list[] = {...}; to declare the array of pointers, but you use char** to pass a pointer to one or more pointers to a function.
T* x[] = array of pointers
T** x = pointer to pointer
P.S. Responding to ejohn: There is only one use that I can think of for creating a pointer to a pointer (as an actual declared variable, not as a function parameter or temporary created by the unary & operator): a handle. In short, a handle is a pointer to a pointer, where the handl;e is owned by the user but the pointer it points to can be changed as needed by the OS or a library.
Handles were used extensively throughout the old Mac OS. Since Mac OS was developed without virtual memory technology, the only way to keep the heap from quickly getting fragmented was to use handles in almost all memory allocations. This let the OS move memory as needed to compact the heap and open up larger, contiguous blocks of free memory.
Truth is, this strategy at best just "sucked less". There are a huge list of disadvantages:
A common bug was when programmers would dereference the handle to a pointer, and use that pointer for several function calls. If any of those function calls moved memory, there was a chance that the pointer would become invalid, and dereferencing it would corrupt memory and possibly crash the program. This is an insidious bug, since dereferencing the bad pointer would not result in a bus error or segmentation fault, since the memory itself was still existent and accessible; it just was no longer used by the object you were using.
For this reason, the compiler had to be extra careful and some Common Subexpression Elimination optimizations couldn't be taken (the common subexpression being the handle dereference to a pointer).
So, in order to ensure proper execution, almost all accesses through handles require two indirect accesses, instead of one with a plain old pointer. This can hurt performance.
Every API provided by the OS or any library had to specify whether it could possibly "move memory". If you called one of these functions, all your pointers obtained via handles were now invalid. There wasn't a way to have the IDE do this for you or check you, since the moves-memory call and the pointer that became invalid might not even be in the same source file.
Performance becomes nondeterministic, because you never know when the OS will pause to compact your memory (which involved a lot of memcpy() work).
Multithreading becomes difficult because one thread could move memory while another is executing or blocked, invalidating its pointers. Remember, handles have to be used for almost all memory allocation to keep from fragmenting the heap, so threads are still likely to need access to memory via a handle even if they use none of the Mac OS APIs.
There were function calls for locking and unlocking the pointers pointed to by handles, however, too much locking hurts performance and fragments the heap.
There's probably several more that I forgot. Remember, all these disadvantages were still more palatable than using only pointers and quickly fragmenting the heap, especially on the first Macs, which only had 128K of RAM. This also gives some insight into why Apple was perfectly happy to ditch all this and go to BSD then they had the chance, once their entire product line had memory management units.
First of all, let's get the nitpicky stuff out of the way. main returns int, not void. Unless your compiler documentation specifically states that it supports void main(), use int main(void) or int main(int argc, char **argv).
Now let's step back a minute and talk about the differences between pointers and arrays. The first thing to remember is that arrays and pointers are completely different things. You may have heard or read somewhere that an array is just a pointer; this is incorrect. Under most circumstances, an array expression will have its type implicitly converted from "N-element array of T" to "pointer to T" (the type decays to a pointer type) and its value set to point to the first thing in the array, the exceptions being when the array expression is an operand of either the sizeof or address-of (&) operators, or when the array expression is a string literal being used to initialize another array.
An array is a block of memory sized to hold N elements of type T; a pointer is a block of memory sized to hold the address of a single value of type T. You cannot assign a new value to an array object; i.e., the following is not allowed:
int a[10], b[10];
a = b;
Note that a string literal (such as "aaa") is also an array expression; the type is N-element array of char (const char in C++), where N is the length of the string plus the terminating 0. String literals have static extent; they are allocated at program startup and exist until the program exits. They are also unwritable (attempting to modify the contents of a string literal results in undefined behavior). For example, the type of the expression "aaa" is 4-element array of char with static extent. Like other array expressions, string literals decay from array types to a pointer types in most circumstances. When you write something like
char *p = "aaa";
the array expression "aaa" decays from char [4] to char *, and its value is the address of the first 'a' of the array; that address is then copied to p.
If the literal is being used to initialize an array of char, however:
char a[] = "aaa";
then the type is not converted; the literal is still treated as an array, and the contents of the array are copied to a (and a is implicitly sized to hold the string contents plus the 0 terminator). The result is roughly equivalent to writing
char a[4];
strcpy(a, "aaa");
When an array expression of type T a[N] is the operand of the sizeof operator, the result is the size of the entire array in bytes: N * sizeof(T). When it's the operand of the address-of (&) operator, the result is a pointer to the entire array, not a pointer to the first element (in practice, these are the same value, but the types are different):
Declaration: T a[N];
Expression Type "Decays" to Value
---------- ---- ----------- ------
a T [N] T * address of a[0]
&a T (*)[N] address of a
sizeof a size_t number of bytes in a
(N * sizeof(T))
a[i] T value of a[i]
&a[i] T * address of a[i]
sizeof a[i] size_t number of bytes in a[i] (sizeof (T))
Note that the array expression a decays to type T *, or pointer to T. This is the same type as the expression &a[0]. Both of these expressions yield the address of the first element in the array. The expression &a is of type T (*)[N], or pointer to N-element array of T, and it yields the address of the array itself, not the first element. Since the address of the array is the same as the address of the first element of the array, a, &a, and &a[0] all yield the same value, but the expressions are not all the same type. This will matter when trying to match up function definitions to function calls. If you want to pass an array as a parameter to a function, like
int a[10];
...
foo(a);
then the corresponding function definition must be
void foo(int *p) { ... }
What foo receives is a pointer to int, not an array of int. Note that you can call it as either foo(a) or foo(&a[0]) (or even foo(&v), where v is a simple int variable, although if foo is expecting an array that will cause problems). Note that in the context of a function parameter declaration, int a[] is the same as int *a, but that's only true in this context. Frankly, I think the int a[] form is responsible for a lot of confused thinking about pointers, arrays, and functions, and its use should be discouraged.
If you want to pass a pointer to an array to a function, such as
int a[10];
foo(&a);
then the corresponding function definition must be
void foo(int (*p)[10]) {...}
and when you want to reference a specific element, you must dereference the pointer before applying the subscript:
for (i = 0; i < 10; i++)
(*p)[i] = i * i;
Now let's throw a monkey wrench into the works and add a second dimension to the array:
Declaration: T a[M][N];
Expression Type "Decays" to Value
---------- ---- ----------- ------
a T [M][N] T (*)[N] address of a[0]
&a T (*)[M][N] address of a
sizeof a size_t number of bytes in a (M * N * sizeof(T))
a[i] T [N] T * address of a[i][0]
&a[i] T (*)[N] address of a[i]
sizeof a[i] size_t number of bytes in a[i] (N * sizeof(T))
a[i][j] T value of a[i][j]
&a[i][j] T * address of a[i][j]
Note that in this case, both a and a[i] are array expressions, so their respective array types will decay to pointer types in most circumstances; a will be converted from type "M-element array of N-element array of T" to "pointer to N-element array of T", and a[i] will be converted from "N-element array of T" to "pointer to T". And again, a, &a, a[0], &a[0], and &a[0][0] will all yield the same values (the address of the beginning of the array), but not be all the same types. If you want to pass a 2d array to a function, like:
int a[10][20];
foo(a);
then the corresponding function definition must be
void foo(int (*p)[20]) {...}
Notice that this is identical to passing a pointer to a 1-d array (other than the size of the array in the examples being different). In this case, however, you would apply a subscript to the pointer, like
for (i = 0; i < 10; i++)
for (j = 0; j < 20; j++)
p[i][j] = i * j;
You don't have to explicitly dereference p in this case, because the expression p[i] implicitly deferences it (p[i] == *(p + i)).
Now let's look at pointer expressions:
Declaration: T *p;
Expression Type Value
---------- ---- ------
p T * address of another object of type T
*p T value of another object of type T
&p T ** address of the pointer
sizeof p size_t number of bytes in pointer (depends on type and platform,
anywhere between 4 and 8 on common desktop architectures)
sizeof *p size_t number of bytes in T
sizeof &p size_t number of bytes in pointer to pointer (again, depends
on type and platform)
This is all pretty straightforward. A pointer type holds the address of another object of type T; dereferencing the pointer (*p) yields the value at that address, and taking the address of the pointer (&p) yields the location of the pointer object (a pointer to a pointer). Applying sizeof to a pointer value will yield the number of bytes in the pointer, not the number of bytes in what the pointer is pointing to.
Now, assuming you've made it this far and haven't yet died of ennui, let's see how all of that applies to your code.
You're wanting to create an array of pointers to char and initialize it with three string literals, so you would declare it as
char *list[] = {"aaa", "bbb", "ccc"};
The list array is implicitly sized to hold 3 elements of type char *. Even though the string literals "aaa", "bbb", and "ccc" appear in an initializer, they are not being used to initialize an array of char; therefore, they decay from expressions of type char [4] to type char *. Each of these pointer values is copied to the elements of list.
When you pass list to a function, such as
foo(list);
the type of list decays from "4-element array of pointer to char" (char *[4]) to "pointer to pointer to char" (char **), so the receiving function must have a definition of
void foo(char **p) {...}
Since subscripting is defined in terms of pointer arithmetic, you can use the subscript operator on the pointer as though it were an array of char *:
for (i = 0; i < 3; i++)
printf("%s\n", p[i]);
Incidentally, this is how main receives argv, as a pointer to pointer to char (char **), not as an array of pointer to char. Remember, in terms of a function parameter declaration, a[] is identical to *a, so char *argv[] is identical to char **argv.
Now, because I can't seem to stop typing and get back to work (chasing down deadlocks is not fun), let's explore using pointers and dynamically allocated memory.
If you wanted to allocate your list dynamically at run time (i.e., you won't know how many strings are in your list ahead of time), you would declare list as a pointer to pointer to char, and then call malloc to actually allocate the memory for it:
char **list;
size_t number_of_strings;
...
list = malloc(number_of_strings * sizeof *list);
list[0] = "aaa";
list[1] = "bbb";
list[2] = "ccc";
...
Since these are assignments and not initializations, the literal expressions decay into pointers to char, so we're copying the addresses of "aaa", "bbb", etc., to the entries in list. In this case, list is not an array type; it is simply a pointer to a chunk of memory allocated somewhere else (in this case, from the malloc heap). Again, since array subscripting is defined in terms of pointer arithmetic, you can apply the subscript operator to a pointer value as though it were an array. The type of the expression list[i] is char *. There are no implicit conversions to worry about; if you pass it to a function as
foo(list)
then the function definition would be
void foo(char **list) {...}
and you would subscript list as though it were an array.
pssst...is he done?
Yeah, I think he's done.
char **x points to an array of char pointers, however this may not be how your compiler stores {"aaa","bbb","ccc"} in memory. char *x[] will cause the correct code to be generated no matter how the compiler stores an array of pointers.
The best source for learning the complexities of C is the book Expert C Programming by Peter van der Linden (http://www.amazon.co.uk/Expert-Programming-Peter-van-Linden/dp/0131774298).
The name of the book is misleading because it's very easily read by beginners I think.
"...assumed it had something to do
with the pointer to pointer stuff,
which i do not understand clearly."
How does an array of pointers to pointers work?