12

Consider the below C code. I would have thought that the variable bar was being instantiated every time and would thus point to different addresses in memory, but it doesn't.

for (i = 2; i < 7; i++) {
    struct foo bar;
    printf("struct %u\n", bar);
}

Output:

struct 13205520
struct 13205520
struct 13205520
struct 13205520
struct 13205520

In case it is not obvious, what I want is to generate 5 different structs—well, actually 5 different pointers to structs—at 5 different locations. How can I do this?

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Lucky
  • 627
  • 5
  • 15
  • 3
    use `malloc()` instead – user12986714 May 03 '20 at 19:20
  • 1
    `struct foo bar[5];` – pmg May 03 '20 at 19:20
  • 3
    The code you show have *undefined behavior*. How could the format specifier for `unsigned int` be able to print a structure object instance? To print a pointer, you first of all need an actual pointer, for example by using the address-of operator `&` (as in `&bar`); Secondly, you need to use the format specifier `%p` to print a `void *`; Thirdly, you need to cast your pointer to `void *`. – Some programmer dude May 03 '20 at 19:21
  • @Paul D If each time the variable would have a different address then all the memory would be quickly exhausted.:) – Vlad from Moscow May 03 '20 at 19:22
  • You should instead do `printf("struct %p\n", (void *)&bar);`. – S.S. Anne May 03 '20 at 19:23
  • 1
    As for your "problem", why do you think that the structure should be getting a new location each iteration? Since the life-time of the object ends as the loop iterates, why not simply reuse the existing memory? – Some programmer dude May 03 '20 at 19:24

3 Answers3

9

The scope of bar only exists within one iteration of the loop. That means that when the next struct foo is created, it will be put in the same place as the old bar, because as far as the compiler sees it, the bar is no longer necessary. Looking at your example, it doesn't seem like you need to handle all of the bar's at once. So you might be okay with them all being at the same location. However, if you need to deal with multiple at a time, I can think of two possible solutions.

Putting the scope outside of the loop

To do this, you'll need an array of struct foo's. The scope of the array needs to be outside of the loop. For example:

struct foo bar_list[5];
for (i = 2; i < 7; i++) {
    printf("struct %p\n", (void *)&bar_list[i - 2]);
    // use the foo's here
}

Then each iteration of your for loop can modify one of the values

Allocating on the heap

If you're okay with storing five pointers in memory, you can allocate each bar somewhere on the heap. You would probably end up just using an array anyway, so this would probably only be useful if you needed to return the structs into another function. You'd need to do something like this:

struct foo* bar_list[5];
for (i = 2; i < 7; i++) {
    bar_list[i - 2] = malloc(sizeof(struct foo));
    printf("struct %p\n", (void *)bar_list[i - 2]);
    // do anything else you need to do
}

It's also worth mentioning, as someone else pointed out, that %p would be used to print a pointer address.

Botahamec
  • 263
  • 3
  • 8
  • 3
    Re “That means that when the next `struct foo` is created, it will be put in the same place as the old `bar`”: That is not what it means. It is a common consequence, but it is not implied by the fact that the lifetime (not scope) of `bar` is each execution of the block in the body of the loop. – Eric Postpischil May 03 '20 at 20:17
  • @EricPostpischil You're probably right that it might not necessarily have the same location, but I'm sure any reasonable compiler would put them in the same location. – Botahamec May 04 '20 at 23:23
  • Please do not tell people things that are not true. Students who are learning new concepts should not be expected to be able to weed out false statements since they do not yet have the knowledge required for it. And while it is a common consequence in simple code, there are circumstances in which it might not occur, such as, perhaps, when the code path includes definition of a variable length array. – Eric Postpischil May 04 '20 at 23:44
  • @EricPostpischil Would it be better if I changed it to something like, "It may/often is placed in the same location as the old `bar`"? – Botahamec May 05 '20 at 00:25
6

Your code has undefined behavior:

  • you cannot pass a structure to printf to print its address, the conversion specifier %u expects an unsigned int, not a structure.
  • furthermore, bar is uninitialized, passing it has undefined behavior.
  • each iteration instantiates a new structure that is uninitialized, possibly at the same location and it goes out of scope as soon as you leave the for body for the next iteration.
  • to print the location of the structure, use %p and pass (void *)&bar, but it is unspecified whether the address of bar will be the same for each iteration or not.

Most compilers will reuse the same space for bar at each iteration, but a compiler could conceivably generate code to randomise the address of bar in a proprietary way in order to make the code more resilient to exploits.

If you want all structures to reside in different addresses, you can define an array of structures outside the scope of the loop and print their addresses this way:

struct foo bar[5];
for (int i = 2; i < 7; i++) {
    printf("address of struct bar[%d] is %p\n", i - 2, (void *)&bar[i - 2]);
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
6

I would have thought that the variable bar was being instantiated every time

true, except that it is not instantiated, it is just that compiler sets some memory aside for it, or your program behaves as if it did set some memory aside for it.

and would thus point to different addresses in memory

which would be false. C only guarantees that objects do not overlap each other during their lifetimes. Your bar has automatic storage duration, its lifetime ends as soon as the program execution reaches the } at the end of the block.

There are two ways to escape this:

  • allocate an array of struct foo that has storage duration longer than that of the for loop; now you can use i to index this array.

  • use dynamic memory allocation with malloc - allocated storage duration guarantees that the object will be alive until memory is deallocated with free.


Notice that it is not proper at all to use

printf("struct %u\n", bar);

%u expects an unsigned int but you're passing in a struct. The behaviour of the construct is undefined and actually we have no way of telling what the meaning of the number 13205520 is or where it comes from.

To print the address of bar, you must use

printf("struct %p\n", (void *)&bar);