But what I didn't expect was that the address of var_b
is <outofscope>
, which suggests that this variable might be allocated in other memory region different from the current stack frame.
What's the reason for this happening when allocating var_b
? Is there any way I can make it local?
When it comes to debugging, there's really no other way to read variables except to compile with -ggdb -O0
. ie: turn ON debug flags with -g
or -ggdb
or similar, and turn OFF optimization with -O0
. You must do both. See my answer here: What's the difference between a compiler's -O0
option and -Og
option?
Memory pools and pointer arithmetic
Whatever it is you're trying to do is very confusing. Some questions for you to think about:
- Why are you trying to "overflow a buffer" to change variable
var_b
? Why don't you just change var_b
directly?
memcpy(var_a, data_buffer, 8);
copies the first 8 bytes from data_buffer
into var_a
. Since var_a
is only 4 bytes, the first 4 bytes go into it successfully, and then the latter 4 bytes have undefined behavior by writing out of the bounds of the variable. Why don't you just write the first 4 bytes of data_buffer
into var_a
and the next 4 bytes into var_b
?
- If you think writing past the bounds of
var_a
should write into var_b
, why did you put var_b
first instead of var_a
first? You did:
uint32_t var_b;
uint8_t var_a[4];
The compiler can do whatever it wants when determining where to place variables in memory, so there's no guarantee here, but it seems more logical that you would have at least put var_a
first, like this:
uint8_t var_a[4];
uint32_t var_b;
Why didn't you?
Alright, that about covers it. Let's go over some things.
1. If you want to have guaranteed relative locations of variables, force it via a memory pool!
No matter what order you write your variables, the compiler is not compelled to abide by that order nor location, unless you use memory pools or otherwise manually specify the address for a given variable, such as a hardware register.
// How to force a memory layout of 4 bytes of `uint8_t var_a[4]` followed by 4
// bytes of `uint32_t var_b` (8 bytes total)
#define MEM_POOL_SIZE 8 // 8 bytes
// Step 1. Create an 8 byte memory pool. Choose **one** of the following
// options:
// Option 1: 8 byte memory pool **on the stack** (statically allocated)
uint8_t mem_pool[MEM_POOL_SIZE];
// Option 2: 8 byte memory pool **on the heap** (dynamically allocated)
uint8_t* mem_pool = malloc(MEM_POOL_SIZE);
if (mem_pool == NULL)
{
// do error handling here: out of memory
}
// Option 3: 8 byte memory pool **neither on the stack nor the heap**
// (`static` makes it take global RAM not allocated for either)
static uint8_t mem_pool[MEM_POOL_SIZE];
// Step 2: point `var_a` and `var_b` into the memory pool. Voila! They now
// magically take this pool of memory, with `var_b` **guaranteed** to be
// right after `var_a`.
uint8_t* var_a = mem_pool; // 4 byte array of uint8_t
// Note: the `+ 4` is pointer math: since `mem_pool` is a ptr to `uint8_t`,
// this moves forward `4*sizeof(uint8_t)` bytes.
uint32_t* var_b = mem_pool + 4;
// Step 3: use the variables! Here are some examples:
// write some bytes into the `var_a` array
var_a[0] = 0x01;
var_a[1] = 0x02;
var_a[2] = 0x03;
var_a[3] = 0x04;
// write a value into `var_b`
*var_b = 12345;
// write the first 4 bytes of data_buffer into `var_a` and the next 4 into
// `var_b`
memcpy(var_a, data_buffer, MEM_POOL_SIZE);
2. Just write the first 4 bytes of data_buffer
into var_a
and the next 4 bytes into var_b
directly
See my questions at the top of my answer. I don't understand what you are doing, or why. If you want to write the first 4 bytes of data_buffer
into var_a
and the next 4 bytes into var_b
, just do that! No need for any undefined-behavior "overflow" tricks, nor memory pools!
uint8_t var_a[4];
uint32_t var_b;
uint8_t data_buffer[16] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
memcpy(var_a, data_buffer, 4);
// remember, the `+ 4` is pointer arithmetic, and moves forward 4 x the size
// of the thing being pointed to, which is `uint8_t` (1 byte) in this case
memcpy(&var_b, data_buffer + 4, 4);
// Or (better), same thing as just above, but written more-clearly:
memcpy(var_a, data_buffer, sizeof(var_a);
memcpy(&var_b, data_buffer + sizeof(var_a), sizeof(var_b));