2

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

Community
  • 1
  • 1
J. Toran
  • 157
  • 2
  • 2
  • 14
  • 2
    Don't `#include "malloc.h"`. Do `#include `. Don't `include "inttypes.h"` but `#include ` – Eugene Sh. Jan 14 '16 at 14:10
  • Thank you, EugeneSh. I edited the question, but it made no difference to the results. – J. Toran Jan 14 '16 at 14:15
  • Also, how can I change the question to get more comments? – J. Toran Jan 14 '16 at 14:15
  • *note the triangle brackets. You will get more comments, don't worry – Eugene Sh. Jan 14 '16 at 14:16
  • I don't understand about the triangle brackets. You mean replace the quotes on the include files? Sheesh, I didn't notice on your first comment. – J. Toran Jan 14 '16 at 14:20
  • 1
    @J.Toran Yes, they are different. Quotes are for custom headers of project, triangles are for standard/compiler headers. – i486 Jan 14 '16 at 14:21
  • @i486 and Eugene Sh, Thanks I made the changes and it compiled fine. – J. Toran Jan 14 '16 at 14:24
  • Really? Did it solve the problem? I didn't expect that :) – Eugene Sh. Jan 14 '16 at 14:26
  • Oh, sorry. No the results were the same either way. – J. Toran Jan 14 '16 at 14:28
  • @J.Toran - The answer to your first linked-to question explains that with a pointer to an array you must write `(*LngArr1)[s] = s;` to dereference the pointer first and index the array second. – Bo Persson Jan 14 '16 at 15:19
  • @BoPersson: Thank you. However when I changed the lines as you suggested I still got `Error C4700 uninitialized local variable 'LngArr#' used` on the 2 assigns. Oddly, the 2 `printf` statements that followed did not have any error messages like they did before. – J. Toran Jan 14 '16 at 15:37
  • Rather than `(uint64_t *) SavPtrArr[0] = (uint64_t *) malloc(NBRPTR * sizeof(LngArr1[0]));`, consider the cleaner `p = malloc(sizeof *p * N)` style. In this case: `SavPtrArr[0] = malloc(sizeof *(SavPtrArr[0]) * NBRPTR);` Notice that no types are coded. – chux - Reinstate Monica Jan 14 '16 at 18:11
  • @chux: Thank you. I needed to have the `(uint64_t *)malloc` etc in order to get Visual Studio to compile it. I'm still working on the `(sizeof *(SavPtrArr[0] * NBRPTR);` part to see if I can understand it. – J. Toran Jan 14 '16 at 18:45
  • `)` missing in your comment. `(sizeof *(SavPtrArr[0] -->)<-- * NBRPTR)` – chux - Reinstate Monica Jan 14 '16 at 19:05
  • @chux: Ah, you are so right. And I think I got it. – J. Toran Jan 14 '16 at 20:08
  • @EugeneSh.: "Really? Did it solve the problem? I didn't expect that :)" I just now saw you were joking! – J. Toran Jan 14 '16 at 20:24
  • @J.Toran Actually I wasn't joking. These fixes were not supposed to fix these specific problems you had. so I was surprised. – Eugene Sh. Jan 14 '16 at 20:26
  • @EugeneSh.: Well, they didn't actually fix the problem, the results were the same. That's why I thought you were joking. :) – J. Toran Jan 14 '16 at 20:45

4 Answers4

3

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

Ilya
  • 5,377
  • 2
  • 18
  • 33
  • Thank you. I believe that was what I was attempting with the lines I added to question, using LngArr3. I made the change on the assign, but still got the same 2 error messages I described above. – J. Toran Jan 14 '16 at 15:41
  • Looking at your answer some more, I see now how you're making the same correction Ian Abbot suggested, and that's correct. The `(uint64_t *)` in front is just the way I've seen it before. (I'm new at C and mainly go by copying examples I've seen.) But I see it's not necessary. – J. Toran Jan 14 '16 at 15:56
  • 1
    Yes it's surprising that it was compiling at all, you can't cast on the left side of the assignment. – Ilya Jan 14 '16 at 16:02
  • @IanAbbott and Ilya: Wrong how? Oh, I see you can't cast the left side of an assignment. Was it just luck that the program worked the same both with and without the leading `(uint64_t *)`? – J. Toran Jan 14 '16 at 16:02
  • It's like trying to assign `2 = 3`. The left hand side needs to be an lvalue and it isn't one in this case, so it's wrong. Similarly, a cast expression is not an lvalue, so you can't assign to it. – Ian Abbott Jan 14 '16 at 16:29
  • Some C compilers do allow a cast to be an lvalue if the operand of the cast is an lvalue (which isn't true in the `2 = 3` case, but is true in `(int)a = 3` when `a` is an `int`). For example, the GCC compiler allows this construct as an extension to the C language, but it is deprecated. – Ian Abbott Jan 14 '16 at 16:36
  • @J.Toran what is your compiler exactly? Is it really VS 2015 ? Don't you have warnings for this ? – Ilya Jan 14 '16 at 16:47
  • @Ilya: I'm using VS 2015 Community edition. No warnings on casting the left side of the `malloc` or `calloc` and compiles fine. By the way, as far as I know there is only 1 compiler for VS 2015. There's no separate compiler for C and C++. When I start a new project, the extension is always .cpp but I've never coded a lick of C++ and things seem to compile okay. – J. Toran Jan 14 '16 at 16:57
  • "the extension is always .cpp" => so VS assumes that your code is C++. It's the same compiler but I guess it has also a pure C mode. – Ilya Jan 14 '16 at 17:21
  • You can rename the source files in your project to use the .c extension; then they should be compiled in C mode. – Ian Abbott Jan 14 '16 at 17:38
  • @Ilya: Yes, that's what it looks like. To IanAbbott: When I tried to rename the source with the .c extension, the compile of the project still looked for the .cpp and wouldn't compile it. – J. Toran Jan 14 '16 at 17:44
  • 1
    You have to rename them in the IDE, not in Explorer. – Ian Abbott Jan 14 '16 at 17:53
  • @IanAbbott: Thank you. I didn't know you could rename it in the IDE. When I did that (and also renamed "stdafx.cpp" to "stdafx.c") it compiled okay. I could now compile without casting the `malloc`. – J. Toran Jan 14 '16 at 18:50
1

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.

Ian Abbott
  • 15,083
  • 19
  • 33
  • Thank you, Ian. You are correct. When I made the change I got a value of 0 no matter which variation of `calloc` I used. Can you offer any advice on how to assign values to the LngArr# array of 3 uint64_t that the pointers point to? – J. Toran Jan 14 '16 at 15:48
  • Well, `LngArr3 = calloc(NBRPTR, sizeof(LngArr3[0]));` ought to be valid, and `LngArr3 = calloc(1, sizeof(*LngArr3));` should also be valid. But if you insist on casting the result of `calloc` to the type of `LngArr3`, you can use `LngArr3 = (uint64_t (*)[NBRPTR])calloc(1, sizeof(*LngArr3));`. – Ian Abbott Jan 14 '16 at 16:07
  • After you have allocated `LngArr3` (perhaps with `malloc` instead of `calloc`), you can assign values to its 3 elements: `LngArr3[0] = 42; LngArr3[1] = 23; LngArr3[2] = 5;`. – Ian Abbott Jan 14 '16 at 16:15
  • Thank you again. I don't really understand the cast, but it did take care of the problem about needing a modifiable lvalue on the `calloc`. However for some reason I still get the `'LgnArr3': undeclared identifier` message. I don't see how that's possible when it seems to be declared right in front of the routine. – J. Toran Jan 14 '16 at 16:15
  • 1
    Because it's `LngArr3`, not `LgnArr3`! – Ian Abbott Jan 14 '16 at 16:17
  • I tried`malloc` instead of `calloc` but still got the same compile-time error message. – J. Toran Jan 14 '16 at 16:17
  • Man, sorry about that typo! We're getting close. When I tried assigning `LngArr3[s] = s;` I get error message `Error C3863 array type 'uint64_t [3]' is not assignable`. When I try `(*LngArr3)[s] = s;` that was suggested before I get `lngarr3s=7006384 lngarr3s=7006408 lngarr3s=7006432`. Those look like addresses instead of values? – J. Toran Jan 14 '16 at 16:25
  • I was wrong. `LngArr3` is a pointer to array 3 of `uint64_t`, so `LngArr3[s]` is an array 3 of `uint64_t`. An array is not assignable, as the compiler error says. `*LngArr3` dereferences the pointer to the array type (array 3 of `uint64_t`), and `(*LngArr3)[s]` applies the array subscripting operation to the array, giving element `s` of the array, which is of type `uint64_t`. An array element of type `uint64_t` can be used as an lvalue and assigned to. The parentheses are needed because the array subscripting operator `[]` has higher precedence than the dereference operator `*`. – Ian Abbott Jan 14 '16 at 17:18
  • 1
    In general, you're usually better off using pointers to the element type instead of pointers to the array type, as it's usually less messy! For example: `uint64_t *PtrLng = malloc(3 * sizeof(*PtrLng); if (PtrLng) { for (s = 0; s < 3; s++) { PtrLng[s] = s; } }`. – Ian Abbott Jan 14 '16 at 17:24
  • I summarized my results in a new version of the question. – J. Toran Jan 14 '16 at 17:25
0

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.

J. Toran
  • 157
  • 2
  • 2
  • 14
  • Shouldn't it be `(*LngArr3)[s]`? – Ian Abbott Jan 14 '16 at 17:36
  • I actually tried it both ways and the results were the same either way. – J. Toran Jan 14 '16 at 17:40
  • 1
    You were kind of lucky. Your `calloc` allocates space for 9 `uint64_t`s and you are assigning and printing the 0th, 3rd, and 6th `uint64_t`s. I'm guessing that's not what you intended, though I may be wrong. – Ian Abbott Jan 14 '16 at 17:48
  • What? Now I'm really confused, when I thought I had figured it out. What changes could I possibly make to get it to print the 1st, 2nd and 3rd values of `uint64_t`? I guess I still don't understand the `calloc`. – J. Toran Jan 14 '16 at 17:56
  • 1
    In your `calloc`, the number of elements is `NBRPTR` and the size of each element is `sizeof(LngArr3[0])` which is the same as `sizeof(*LngArr3)`. Since `LngArr3` is of type `uint64_t (*)[NBRPTR]`, `*LngArr3` is of type `uint64_t [NBRPTR]` (that's not valid C syntax, just a description of the type). So `sizeof(*LngArr3)` is `NBRPTR * sizeof(uint64_t)` and the `calloc` is allocating `NBRPTR` of those, so the size of the memory allocated by the `calloc` is `NBRPTR * NBRPTR * sizeof(uint64_t)`. – Ian Abbott Jan 14 '16 at 18:14
  • @J.Toran [] has higher precedence than * so without the parentheses it's like `*(LngArr3[s])`, so 1rst pointer, 2nd pointer, ... and the pointer is on a 3-int array, so the pointer increments 3 int addresses for each array index increments (and see comment by Ian above for the calloc). – Ilya Jan 14 '16 at 18:20
  • @IanAbbott: I was thinking along those lines, when I printed out the address of the three elements and they were all 24 bytes away from each other. But when I change the calloc to `calloc(NBRPTR, sizeof(uint64_t));` it compiles okay but when I `printf("%llu", *LngArr3[s]), I get 0 and 2 twenty digit values of apparent garbage. What should the `calloc` be? – J. Toran Jan 14 '16 at 18:22
  • 1
    So since `NBRPTR` is `3`, your `calloc`ed memory block contains 3 consecutive arrays of 3 `uint64_t`s, or 9 `uint64_t`s altogether. `LngArr3[0]` is the 0th array of 3, `LngArr3[1]` is the 1st array of 3, and `LngArr[2]` is the 2nd array of 3 (counting from 0). `*LngArr3[s]` is the same as `LngArr3[s][0]`, i.e. the 0th element of the `s`th array. Whereas `(*LngArr3)[s]` is the same as `LngArr3[0][s]`, i.e. the `s`th element of the 0th array. – Ian Abbott Jan 14 '16 at 18:22
  • @Ilya: I'm somewhat understanding, but I don't get what the `calloc` should be. Either that, or I still have the `printf` wrong. – J. Toran Jan 14 '16 at 18:25
  • @IanAbbott: Sorry, but my brain is fogging over. I think if I saw the correct `calloc` and `printf` statements, I might be able to understand better. – J. Toran Jan 14 '16 at 18:26
  • @Ilya: I want an arbitrary number of unsigned ints (actually 8-byte uint64_t). I have `#defined` to have 3, but that was just for the test. I would want more when doing actual work. – J. Toran Jan 14 '16 at 18:28
  • Okay, try `LngArr3 = calloc(1, sizeof(*LngArr3));`, `(*LngArr3)[s] = s;`, and `printf("lngarr3s=%llu", (*LngArr3)[s]);`. – Ian Abbott Jan 14 '16 at 18:29
  • 1
    But as I mentioned in a different comment, you're usually better off avoiding pointers to array types (unless you really need them) in favor of pointers to element types. It depends what you're trying to achieve though. – Ian Abbott Jan 14 '16 at 18:32
  • 1
    IanAbbott: yes @J.Toran: I think there's something fishy about the way you're declaring your pointers, as if the number of int had to be set in the pointer itself. If you want to take advantage of dynamic allocation at runtime, the pointers have to just be on the element type. – Ilya Jan 14 '16 at 18:45
  • @IanAbbott: I had already tried the `calloc` with on block. The values that got printed for element[0] was 0. But element[1] and [2] were 20-digit values of garbage. – J. Toran Jan 14 '16 at 18:54
  • To both of you. I'm not understanding what you mean by have a pointer to an element type. What I'm looking for is one pointer which is set by a `calloc` with enough room to hold x array elements of `uint64_t`s. x here defined as 3, but would be larger in actual practice. I don't want 3 pointers to individual elements, or I'd have to `calloc` 3 times of 1 block of 8 bytes. I'd rather `calloc` 3 blocks of 8 bytes at one time, to hold the entire array. If that is possible, that is. – J. Toran Jan 14 '16 at 18:57
  • @J.Toran: That's probably because you were using `*LngArr3[s]` instead of `(*LngArr3)[s]` and you were accessing memory past the end of the block you allocated. – Ian Abbott Jan 14 '16 at 18:57
  • @IanAbbott: I tried it both ways. What's really weird is that when I tried it with `(*LngArr3)[s] = s;` I got the garbage. When I tried it with `*LngArr3[s] = s;` I got the values 0, 1, 2 like I wanted. I thought it would be the other way around. – J. Toran Jan 14 '16 at 19:02
  • @J.Toran So just use a variable like `uint64_t *foo` and allocate the N elements using `foo = calloc(N, sizeof(foo[0]));`. Also understand that `foo[s]` is the same as `*(foo + s)` whether or not `foo` is an array or a pointer to the first element of an array. – Ian Abbott Jan 14 '16 at 19:03
  • Thanks again to both of you. The only thing is that when I do the `calloc` and I print the addresses of the array elements, they are still 24 apart, whether I `calloc` a block of or a block of NBRPTR. So it looks like I'm allocating a size of 9 `uint64_t`s. I don't understand how that's possible if I only allocate 1 block? – J. Toran Jan 14 '16 at 19:05
  • 1
    @J.Toran `sizeof(LogArr3[0])` is `24` (`NBRPTR * sizeof(uint64_t)`), so it shouldn't be surprising that `&LogArr3[1]` is 24 bytes apart from `&LogArr3[0]`. – Ian Abbott Jan 14 '16 at 19:08
  • 1
    @J.Toran, on the other hand, with the `uint64_t *foo` I posted above, `sizeof(foo[0])` is 8 (`sizeof(uint64_t)`) and `&foo[1]` is 8 bytes apart from `&foo[0]`. – Ian Abbott Jan 14 '16 at 19:18
  • 1
    @J.Toran If it hasn't sunk in yet, your element type is `uint64_t` and you want to allocate storage for _x_ consecutive `uint64_t`s from the heap and access those elements through a pointer to that storage. So the type of your pointer is `uint64_t *`. Let's declare the pointer variable as `uint64_t *foo;` and allocate the storage using `foo = calloc(x, sizeof(uint64_t));` or `foo = calloc(x, sizeof(foo[0]));` or `foo = calloc(x, sizeof(*foo));` (all the same), or `foo = malloc(x * sizeof(uint64_t));`, etc. (same, but uninitialized). (continued...) – Ian Abbott Jan 14 '16 at 19:33
  • 1
    (...) Then `foo` is a pointer to the zeroth element, `*foo` is the zeroth element itself,`foo + i` is a pointer to the `i`th element, `*(foo + i)` is the same as `foo[i]` and is the `i`th element itself. `&foo[i]` is the same as `&(*(foo + i))` which is the same as `(foo + i)`, i.e. a pointer to the `i`th element. – Ian Abbott Jan 14 '16 at 19:37
  • @IanAbbott: Thank you again. I'm going to study this last a lot more. (I'm still fairly new to C). But please see my new answer. I think I did it all right, but still had a few questions. – J. Toran Jan 14 '16 at 19:48
  • I think I got it. Just have those 3 questions in my new "answer". – J. Toran Jan 14 '16 at 20:00
0

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?

J. Toran
  • 157
  • 2
  • 2
  • 14
  • Okay, I see that `*foo` is the first element of foo, which is zero. But why is `&foo[s]` 00000000? – J. Toran Jan 14 '16 at 20:06
  • 1
    "Can I #define NBRPTR to be any arbitrary value " --> `0 <= NBRPTR <= SIZE_MAX` is the coding limit. Should `calloc()` return `NULL` or not is another question. – chux - Reinstate Monica Jan 14 '16 at 20:33
  • Where are the "3 foo pointers"? Only ` printf("foo =%p\n", foo);` prints a pointer. – chux - Reinstate Monica Jan 14 '16 at 20:34
  • 1
    Note: better to accept an answer and post a new question rather than continually improve code. – chux - Reinstate Monica Jan 14 '16 at 20:35
  • 1
    `printf("*foo=%llu\n", *foo);` is not portable. Use the matching printf specifier with type. – chux - Reinstate Monica Jan 14 '16 at 20:36
  • @chux: On testing, it appears the upper limit of NBRPTR of uint64_t is 170,000,000. Or 85,000,000 if you have two arrays. – J. Toran Jan 14 '16 at 20:50
  • @chux, I'm pretty new to SO, so I guess I should have posted some new questions. I never actually found the correct answer until one of the later comments, that wasn't posted as an answer. Anyhoo, I'll work on my delivery. – J. Toran Jan 14 '16 at 20:52
  • @chux: where I'm doing `printf("foos%d ptr=%p\n", s, foo[s]);` it prints 00000000. Even when I do `printf("foos%d ptr=%p\n", s, **&**foo[s]);` it does the same. – J. Toran Jan 14 '16 at 20:54
  • `s` is type `uint64_t`, why do you think `"%d"` is the matching print specifier? – chux - Reinstate Monica Jan 14 '16 at 21:59
  • Thank you, Chux. Just being dense. When I changed `s` to `int`, that not only fixed the problem the problem of printing `s` but also allowed the addresses to print the correct values. – J. Toran Jan 15 '16 at 10:49