7

I am having a difficult time fully understanding what is going on.

This is how I am reading the code at the bottom.

  1. Define A as a pointer to an address of a pointer that points to address of a double.

  2. Allocate 4 blocks of memory on the heap that can each hold an address to a double. Return the address of the first block allocated to A.

  3. Allocate 6 blocks of memory on the heap that can each hold a double data type and return the first block's address for each of the 4 blocks allocated earlier.

  4. Variable A still contains the address of the first block of the four pointers assuming that A[0] changed the value at the address pointed by A rather than A itself.

Now this is where the diagram comes in that is linked at the bottom. Lets say that I wanted to access the data stored in the red block. How would I do so?

Would the following code work?

double data = *(*(A + 3) + 2);

My logic is that A is a pointer and incrementing by 3 should increase the address by the number of bytes that is of the size (double *). Dereferencing the address (A + 3), I would obtain the address of first block (assuming it was not re-assigned). By increasing the resulting address by 2 and dereferencing that address, I should obtain the value at the red block.

So if I understand correctly, 1 + M pointers are being stored in memory. Why? If the goal is to store MxN data, why would I need 1 + M pointers to do so? The extra pointers seem like they would be helpful if I wanted to define rows of different sizes but the objective is to define a rectangular array of data.

const int M = 4;
const int N = 6;
double **A;

A = malloc(M*sizeof(double *));

for (i = 0; i < N; i++) {
    A[i]= malloc(N*sizeof(double));
}

Diagram:

enter image description here

Side Note: I am Electrical Engineering student that is unfamiliar with C to some degree and formal programming practices. I know that I'm being pedantic about the such a simple line of code, but I want to be sure that I understand pointers and memory allocation correctly. They are pretty hard to comprehend at first glance. This is part of a matrix multiplication code given to me by my professor and I want to pass the pointer to a function and have it access and modify the correct values in memory. Personally would of liked to see two-dimensional arrays for this code, but I am assuming that there is a good reason.

TLama
  • 75,147
  • 17
  • 214
  • 392
Altermeris
  • 71
  • 2

3 Answers3

3

You can also store this contiguously and just do the math instead of letting the compiler do it for you

So:

double * A = malloc (M * N * sizeof(double));

double get(double * what, cur_x, cur_y, max_x, max_y)
{
    return *(what+cur_x*max_y+cur_y);
}

this will save all the indirection since you know the size

If you don't need to allocate sizes dynamically this is what

double A[17][14] will do for you on the stack

Glenn Teitelbaum
  • 10,108
  • 3
  • 36
  • 80
2

Yes, double data = *(*(A + 3) + 2); would work, and access the "red" double.

I guess your professor will tell you soon that A[3][2] works as well, is much easier to read, and preferable to *(*(A + 3) + 2);

Lesson learned (not yet, but what your professor is trying to teach to you): in C, multidimensional arrays are not something like 'a table with N columns and M rows', in C, all arrays are really pointers, and multidimensional arrays are implemented as pointers to pointers. Whenever your write A[3][2], *(*(A + 3) + 2)is what really happens internally. This, in turn, explains why you can't allocate a N*M matrix in one statement; you have to allocate the "row pointers" first, then allocate the "columns" for each of the row pointer.

Guntram Blohm
  • 9,667
  • 2
  • 24
  • 31
  • *"C, all arrays are really pointers"* No. Arrays will decay into pointers and array-subscripts work for pointers but they are [not the same](http://stackoverflow.com/questions/7725008/pointers-difference-between-array-and-pointer). *"you can't allocate a N*M matrix in one statement"* yes you can: `double A[M][N];`, though for big matrices you don't want to allocate it on the stack. Using VLA's you can use `double (*A)[N] = malloc(M * sizeof *A);` though. – Kninnug Dec 08 '13 at 21:32
  • I think I just epimone, what I was talking about in the main post is a linked list right? All array are linked lists? – Altermeris Dec 08 '13 at 21:39
  • No, an array is a block of memory that may be addressed contigously: `int arr[10] = {...};` then you can access `arr[0]`, `arr[1]` up to `arr[9]`. Those statements are equivalent to: `*(&arr[0] + 0)`, `*(&arr[0] + 1)` etc. (`&arr[0]` is the address of the first element, had I written `*(arr + 1)` it would go 1 past the end of the array, you could also do: `int * ptr = arr;` and then use `*(ptr + 0)`, `*(ptr + 1)` due to array-to-pointer decay). The double-pointer looks like a linked-list but unlike a true LL there is no (other) data in the pointer itself except the address it points to. – Kninnug Dec 08 '13 at 21:46
  • @Altermeris Correction to my last comment: `*(arr + 1)` is equivalent to `*(&arr[0] + 1)` so taking the address of the first element is not necessary. I don't know why I thought otherwise. – Kninnug Dec 08 '13 at 22:02
2

malloc(M*sizeof(double *)) says: "Give me a pointer to M double pointers." malloc(N*sizeof(double)) says: "Give me a pointer to N doubles." Now notice the bug:

for (i = 0; i < N; i++) {
    A[i]= ...;
}

A points to an array of M double pointers, but you are writing N entries into it (where each entry is a pointer to N doubles). If M > N you waste space, and if M < N you use space that you haven't asked permission to write to.

John
  • 31
  • 2