If I write this code in a function:
void func(void)
{
int x = 0;
int y = 3724832+x;
}
It would (probably) optimize the variable y out because it isn't being used. Even if I'm wrong about this specific case, the point I'm making is that the compiler has the ability to optimize out variables.
My question is does it also do this with local arrays?
Example:
void func(void)
{
uint8_t tempBuffer[2] = {0x00, 0x01};
sendBufferOverUSB(tempBuffer, 2); //sendBufferOverUSB(uint8_t* arr, int sizeArr);
}
Does the compiler then optimize this array out? Or does it keep this array in memory forever? Or is that even what optimization means? I know that arrays are created at compile time, so I'm trying to figure out if I should be using arrays in the way I've just used them or if I should make an array for every .c file as a buffer and simply use that whenever I need to store some bytes for transmission. Like this:
//example.c
#include "example.h"
#define MAX_EXAMPLE_BUFFER_SIZE 256
static uint8_t localBuffer[MAX_EXAMPLE_BUFFER_SIZE];
void func(void)
{
// using buffer
localBuffer[0] = 0x00;
localBuffer[1] = 0x01;
sendBufferOverUSB(localBuffer, 2); //sendBufferOverUSB(uint8_t* arr, int sizeArr);
}
I am going to have a bunch of functions which need to convert from Little Endian variables into Big Endian formatted bytes so i essentially need some buffer to move these bytes into, and I just didn't know if making an array of the size needed was better than using a buffer for that .c file.
3 Answers 3
"Optimized out" means the compiler realises the variable isn't necessary, and deletes the variable. For example, this code:
void func(void)
{
int x = 0;
int y = 3724832+x;
printf("%d\n", y);
}
might be compiled the same way as:
void func(void)
{
printf("%d\n", 3724832);
}
or even (if your compiler is smart enough)
void func(void)
{
puts("3724832");
}
and we would say that the variables x
and y
have been optimized out, because the optimizer has removed them from the program.
"Optimized out" does not mean that the variable gets destroyed when the function returns. All non-static
local variables get destroyed when the function returns. No exceptions. This includes arrays.
In your function:
void func(void) { uint8_t tempBuffer[2] = {0x00, 0x01}; sendBufferOverUSB(tempBuffer, 2); }
the variable tempBuffer
will be destroyed when the function returns. If you try to trick the compiler so you can use tempBuffer
after func
returns - for example, using a pointer - you might find that the memory that used to contain this variable has been overwritten, and it no longer contains {0x00, 0x01}
. You might even get a crash when you try to use it. This isn't because of optimization.
In your specific example, no, the array would not be optimized out because it gets used in the code (its address gets passed to sendBufferOverUSB
).
I know that arrays are created at compile time
Nothing's created at compile time. When you run the program, anything declared at file scope (outside the body of a function) or with the static
keyword is created when the program is loaded and released when the program exits (those objects have static
storage duration). Objects with auto
storage duration are created when their enclosing block is entered and released when that block is exited1. Objects with allocated
storage duration are created by a call to malloc/calloc/realloc
and released by a call to free
2.
In the code
void func(void)
{
uint8_t tempBuffer[2] = {0x00, 0x01};
sendBufferOverUSB(tempBuffer, 2); //sendBufferOverUSB(uint8_t* arr, int sizeArr);
}
tempBuffer
has auto
storage duration - it only exists for the lifetime of its enclosing function. Space for tempBuffer
is set aside on function entry and released on function exit (meaning if you try to return a pointer to that array, that pointer is not valid after the function exits). By contrast, in the code
void func(void)
{
static uint8_t staticBuffer[2] = {0x00, 0x01};
sendBufferOverUSB(staticBuffer, 2); //sendBufferOverUSB(uint8_t* arr, int sizeArr);
}
staticBuffer
has static
storage duration - space for it is set aside on program startup and released on program exit, and the initialization is only performed once at program startup.
- Logically speaking, anyway. It's common practice to allocate space for all
auto
variables at function start and release it at function exit, regardless of whether those variables are limited to an inner scope.
- There's also a
thread
storage duration, but I don't have enough experience with it to say anything meaningful about it.
Does the compiler then optimize this array out?
Probably not, because you're passing it to a function, which almost certainly means it is decaying to a pointer in the argument list. Taking the address of an object is one way to force the compiler to materialize it (ie, prevent elision).
If sendBufferOverUSB
happens to be inlined, the optimizer will work on the inlined block, and may again be able to prove the array isn't used, or that its address never leaves the inlined scope and that it can be replaced with literal values.
Or does it keep this array in memory forever? Or is that even what optimization means?
No, the array is automatic and only exists for the duration of this function anyway. The values to which the array is initialized will either be expressed as literals in the code, or there will be a static object somewhere copied into your local array. Anyway, the values 0x00
and 0x01
need to exist somewhere.
or if I should make an array for every .c file as a buffer
no, definitely don't use globals.
Mostly because they prevent later use of threading and re-entrancy, but they may also have a performance penalty (eg. an indirect load via the Global Offset Table or similar).
uint8_t tempBuffer[2] = {0x00, 0x01};
is declared in a function locally, so it will be stored on the stack while the function executes, and gets popped off the stack (discarded) when the function exits.