J. Toran
J. Toran

Reputation: 177

Still don't understand pointers to an array of numbers in C

As the title says, I'm dense. I'm trying to allocate space on the heap to store the values of an array of uint64_t. (It could be any kind of number but that's what I used to differentiate values from pointers.) I got the answer to one of my questions here. I think I understand. What I'm trying to do is get a pointer to an array of numbers, and then assign values to the array at run-time.

Edit: updated 12:15pm EST. To save people reading, I've left the original question below, and based on comments and answers narrowed down the questions to the following. This is my C code:

#include "stdafx.h"
#include <stdlib.h>
#include <stdint.h>

#define NBRPTR 3

int main()
{
    uint64_t    (*LngArr3)[NBRPTR];         //  pointer to array of 3 uint64_t
    uint64_t    s;                          //  subscript

//  WHen the next line of code is not commented, I get the following message:
//  Error   C2440   '=': cannot convert from 'uint64_t *' to 'uint64_t (*)[3]'
//  LngArr3 = (uint64_t *)calloc(NBRPTR, sizeof(LngArr3[0]));
    LngArr3 = (uint64_t(*)[NBRPTR])calloc(NBRPTR, sizeof(*LngArr3));    //  this compiles fine
    printf("&lngarr3=%p\n", &LngArr3);
    printf("LngArr3 =%p\n", LngArr3);
    printf("*LngArr3=%llu\n", *LngArr3);
    for (s = 0; s < NBRPTR; s++) {
//  The following line causes:  Error   C3863   array type 'uint64_t [3]' is not assignable
//      LngArr3[s] = s;
        *LngArr3[s] = s;
        printf("lngarr3s=%llu   ", LngArr3[s]);
    }
    putchar('\n');

    return 0;
}

My output is the following:

&lngarr3=002BFE88
Lngarr3 =0042E8C0
*LngArr3=4384960
lngarr3s=4384960   lngarr3s=4384984   lngarr3s=4385008

I have the following questions: I'm new and really don't understand: what does the cast on the calloc do? Also, the values of lngarr3 seem to be addresses separated by 24 rather than the numbers 0, 1, 2 like I'd hoped. How can I assign values at run-time to the 3 elements of the LngArr3 array? Contrary to what I've been told it doesn't make any difference if the assignment is (*LngArr3)[s] = s; rather than the above. I've tried it both ways and the only difference is the addresses produced.

I'm running VS 2015 Community Edition; compiled in x86 mode.

===========================================================================

For historical purposes I've left the original question as follows. My Visual Studio C code is below:

#include "stdafx.h"
#include <stdlib.h>
#include <stdint.h>

#define NBRPTR 3
#define NBRSAVPTR 2

int main()
{
    uint64_t    (* LngArr1)[NBRPTR];        //  pointer to array of 3 uint64_t
    uint64_t    (* LngArr2)[NBRPTR];        //  pointer to array of 3 uint64_t
    uint64_t    * (SavPtrArr[NBRSAVPTR]);   //  array of 2 pointers, hopefully pointing to above
    uint64_t    * PtrLngArr[NBRPTR];        //  array of 3 pointers to uint64_t
    uint64_t    s;                          //  subscript

    (uint64_t *) SavPtrArr[0] = (uint64_t *) malloc(NBRPTR * sizeof(LngArr1[0]));
    printf("&savptrarr0=%p\n", &SavPtrArr[0]);
    printf("savptrarr0 =%p\n", SavPtrArr[0]);
    printf("*savptrarr0=%llu\n", *SavPtrArr[0]);
    for (s = 0; s < NBRPTR; s++) {
//  get compile time error: "uninitialized local variable 'LngArr1' used" on next 2 lines
//      *LngArr1[s] = s;
//      printf("%llu  ", LngArr1[s]);
//  get compile time warning: "'printf': %u in format string conflicts with 
//      argument 1 of type 'unint64_t' *" on above line
// is it trying to print an address instead of a value?
    }
    putchar('\n');

    (uint64_t *) SavPtrArr[1] = (uint64_t *) calloc(NBRPTR, sizeof(LngArr2[0]));
    printf("&savptrarr1=%p\n", &SavPtrArr[1]);
    printf("savptrarr1 =%p\n", SavPtrArr[1]);
    printf("*savptrarr1=%llu\n", *SavPtrArr[1]);
    for (s = 0; s < NBRPTR; s++) {
//  get compile time error: "uninitialized local variable 'LngArr2' used" on next 2 lines
//      *LngArr2[s] = s;
//      printf("%llu  ", *LngArr2[s]);
    }
    putchar('\n');

    for (s = 0; s < NBRSAVPTR; s++)
        free(SavPtrArr[s]);

    putchar('\n');
    putchar('\n');
    for (s = 0; s < NBRPTR; s++) {
//  the next line gives *PtrLinArr[s] values of the same 20 digits of garbage numbers
        (uint64_t *)PtrLngArr[s] = (uint64_t *)calloc(1, sizeof(PtrLngArr[0]));
//  the next line gives all three *PtrLinArr[s] values of 0
//      (uint64_t *)PtrLngArr[s] = (uint64_t *)calloc(NBRPTR, sizeof(PtrLngArr[0]));
        printf("&ptrlngarrs=%p\n", &PtrLngArr[s]);
        printf("ptrlngarrs =%p\n", PtrLngArr[s]);
        printf("*ptrlngarrs=%llu\n", *PtrLngArr[s]);
        putchar('\n');
        free(PtrLngArr[s]);
    }

    return 0;
}

My results are below:

&savptrarr0=003BF6EC
savptrarr0 =004EE8B0
*savptrarr0=14829735431805717965

&savptrarr1=003BF6F0
savptrarr1 =004F0C50
*savptrarr1=0



&ptrlngarrs=003BF6D8
ptrlgnarrs =004EE8B0
*ptrlgnarrs=18302063723772116992

&ptrlngarrs=003BF6DC
ptrlgnarrs =004EE8B0
*ptrlgnarrs=18302063723772116992

&ptrlngarrs=003BF6E0
ptrlgnarrs =004EE8B0
*ptrlgnarrs=18302063723772116992

First off, I understand from this answer that I'm not supposed to cast the pointer for malloc and calloc, however that gives me the compile time error of C2440 '=': cannot convert from 'void *' to 'uint64_t *'. I don't know if this is particular to Windows or Visual Studio.

Anyway, I almost understand the results. The SavPtrArr contains 2 pointers to different addresses. The value of element 0 is garbage and element 1 is 0 because I used calloc instead of malloc. But what I want to do is to assign different values to the different 3 elements of LngArr1 and LngArr2 at run time (not by assigning values in the definition of the variables). I don't know how to do this because I don't understand the correct way to assign a value to a pointer to an array.

The less important question is why the 3 elements of pointers to uint64_t (PtrLngArr) point to the same address, and why the different number of blocks on the calloc make a difference in the values of the results. Why garbage in one case and 0 in the other case.

I hope this isn't too long, but I wanted to show the confusion I have on this, and why the results surprised me. Can anyone change the test code to show how to assign values to the 3 element arrays? Or change it to better explain how this stuff works. Thank you.

Edit: I also tried adding the following lines but it wouldn't even compile:

uint64_t    (*LngArr3)[NBRPTR];         //  pointer to array of 3 uint64_t

(uint64_t *)LngArr3 = (uint64_t *)calloc(NBRPTR, sizeof(LngArr3[0]));
printf("&lngarr3=%p\n", &LngArr3);
printf("LngArr3 =%p\n", LngArr3);
printf("*LngArr3=%llu\n", *LngArr3);
for (s = 0; s < NBRPTR; s++) {
    LgnArr3[s] = s;
    printf("lngarr3s=%llu", LngArr3[s]);
}
putchar('\n');

I got C2106 '=': left operand must be l-value on the calloc and somehow got C2065 'LgnArr3': undeclared identifier on the assign of the value of LgnArr3[s].

Upvotes: 2

Views: 445

Answers (4)

J. Toran
J. Toran

Reputation: 177

Thanks to both Ian Abbott and Ilya. A lot of views but you two are almost all the comments. A new attempt:

#include "stdafx.h"
#include <stdlib.h>
#include <stdint.h>

#define NBRPTR 3

int main()
{
    uint64_t    * foo;
    uint64_t    s;      //  subscript

    foo = calloc(NBRPTR, sizeof(foo[0]));   //  this compiles fine as C code

    printf("sizeof(foo[0])=%zd\n", sizeof(foo[0]));
    printf("&foo=%p\n", &foo);
    printf("foo =%p\n", foo);
    printf("*foo=%llu\n", *foo);
    for (s = 0; s < NBRPTR; s++)
        printf("foos%d ptr=%p   ", s, foo[s]);
    putchar('\n');
    for (s = 0; s < NBRPTR; s++) {
        foo[s] = s;
        printf("foos=%llu   ", foo[s]);
    }
    putchar('\n');

    return 0;
}

New results:

sizeof(foo[0])=8
&foo=0022F920
foo =0041E8C0
*foo=0
foos0 ptr=00000000   foos1 ptr=00000000   foos2 ptr=00000000   
foos=0   foos=1   foos=2

I'm almost satisfied. Still some more questions I'm afraid. How many elements can foo hold? Can I #define NBRPTR to be any arbitrary value and not have to change the definition of foo? Also, why is *foo and the 3 foo pointers all equal to 0? I'm sure I'm allocating only 24 bytes total, but I just wondered about the pointers not showing up.

Finally I changed that last print statement to do this:

printf("foos%d=%llu   ", s, foo[s]);

just to print which element s was printing. The last line of the result became:

foos0=0   foos1=4294967296   foos2=8589934592

I'm not making this up! It doesn't make much difference in the whole scheme of things. The main questions I had were the first two. But is there any reason for this new result?

Upvotes: 0

J. Toran
J. Toran

Reputation: 177

Primarily due to the help of Ian Abbott the code at the beginning of the revised question works with the following change to the printf.

printf("lngarr3s=%llu ", LngArr3[s]); needed to be changed to printf("lngarr3s=%llu ", *LngArr3[s]); I still don't understand the cast on the calloc, but I can study that at my leisure. The desired output was:

&lngarr3=0043FCA0
LngArr3 =0055E8C0
*LngArr3=5630144
lngarr3s=0   lngarr3s=1   lngarr3s=2

Thanks to all.

Upvotes: 0

Ilya
Ilya

Reputation: 5557

First off, I understand from this answer that I'm not supposed to cast the pointer for malloc and calloc

That's for C, you have to cast for a C++ compiler, and that's what Visual Studio is defaulting to (not sure about that, please someone correct me if I'm wrong).

don't know how to do this because I don't understand the correct way to assign a value to a pointer to an array.

For LngArr1 and 2, you have to allocate the actual int array, I think it's missing:

LngArr2 = (uint64_t *) malloc(NBRPTR * sizeof(uint64_t));

Then write the int:

(*LngArr2)[s] = s;

NB about the last array, it's an array of pointers to int, so for each pointer you have to allocate an int:

PtrLngArr[s] = (uint64_t *)calloc(1, sizeof(uint64_t));

Instead, in your code you allocate the size of a pointer that's probably less than the size of uint64_t:

(uint64_t *)PtrLngArr[s] = (uint64_t *)calloc(NBRPTR, sizeof(PtrLngArr[0]));

And what is the (uint64_t *)... in front ...?

Upvotes: 3

Ian Abbott
Ian Abbott

Reputation: 17438

Judging from the output, you are compiling and running a 32-bit Windows program. PtrLngArr[s] is of type uint64_t *, so sizeof(PtrLngArr[0]) will be 4 (since all pointers are size 4 in this program). So PtrLngArr[s] points to an area of memory 4 bytes long, allocated and initialized to all-zeros by calloc.

When you print *PtrLngArr[s], you get the uint64_t value pointed to by PtrLngArr[s]. However, sizeof(uint64_t) is 8, but the object pointed to is only 4 bytes long. This is undefined behavior.

If instead, you had set PtrLngArr[s] = calloc(1, sizeof(*PtrLngArr[0])), you would have allocated 8 bytes of memory initialized to all-zeros, and *PtrLngArr[s] would have the value 0.

Upvotes: 1

Related Questions