carch
carch

Reputation: 13

About pointer of a pointer array

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.

  1. At what line did pointsArray become a pointer array? pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory size of pointsArray.
  2. Why complier knows how to input value in array when complier sees &pointsArray[n]?

  3. 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.

Upvotes: 1

Views: 270

Answers (5)

Jason CHAN
Jason CHAN

Reputation: 6805

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,

  1. 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).
  2. for &pointsArray[n], compiler just write to the memory of address pointsArray + n.
  3. 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.

Upvotes: 0

unalignedmemoryaccess
unalignedmemoryaccess

Reputation: 7441

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?

Upvotes: 1

David C. Rankin
David C. Rankin

Reputation: 84599

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.

  1. 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)

  1. 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.

  1. 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.

Upvotes: 1

Rajeev Singh
Rajeev Singh

Reputation: 3332

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.

Upvotes: 0

P.W
P.W

Reputation: 26800

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.

Upvotes: 0

Related Questions