Reputation: 714
So I understand arrays decay to pointers but isn't there an effective way of computing the array size particularly with compound literals?
say I am passing array of different sizes to a function; ideally i'd use an approach that determines the size dynamically without me having to hardoce the numbers but I can't do that inside the function to begin with. The other option left is the caller code, in which I can't really change static arrays for each function call
foo((uint8_t) {1,2});
foo((uint8_t) {1});
Upvotes: 1
Views: 81
Reputation: 58052
A variant on Chris Dodd's suggestion is to wrap your function in a macro:
void real_foo(uint8_t *arr, size_t len);
#define foo(...) real_foo((uint8_t[]) __VA_ARGS__, (sizeof (uint8_t[]) __VA_ARGS__)/sizeof(uint8_t));
// ...
foo({1});
foo({1,2});
Note the use of a variadic macro to work around the fact that {1,2}
is parsed as two macro arguments.
For slightly different syntax, you could also write { __VA_ARGS__ }
in the macro definition, so that you can invoke it as
foo(1);
foo(1,2);
You can also macro-ize supercat's solution, if you're willing to use gcc/clang statement expressions (a non-standard C extension):
#define foo(...) ({ static const uint8_t my_array[] = { __VA_ARGS__ }; real_foo(my_array, sizeof(my_array) / sizeof(uint8_t)); })
Or if real_foo()
returns void
, you can do the following which is standard C:
#define foo(...) do { static const uint8_t my_array[] = { __VA_ARGS__ }; real_foo(my_array, sizeof(my_array) / sizeof(uint8_t)); } while (0)
where the do/while
will eat a semicolon.
This of course assumes that real_foo()
doesn't need to modify the array. If it does, this won't work, even without the const
, since the array won't be re-initialized if the code is executed again.
Another option is to redesign your function so that it doesn't need to be told the size of the array. For instance, if there is some value that otherwise isn't valid as an entry of the array, you could use it as a sentinel:
foo((uint8_t []){1, 2, 0xff});
Upvotes: 1
Reputation: 81159
The the approach I'd suggest would be to declare a named object using standard initializer syntax.
static const uint8_t my_array[] = {1,2};
whereupon you can pass my_array
and sizeof my_array
. Using compound literals may seem more convenient, but unless they are small it will degrade efficiency because there is no way to create const-qualified compound literals with static duration. Creating objects with automatic duration and passing their address to outside code would generally make it necessary for a conforming compiler to actually create and populate the objects every time they're used, even if they're declared const
, while using static-duration const-qualified objects avoids such issues.
Upvotes: 1
Reputation: 126203
Your example is not valid C syntax, though gcc accepts it with the warning
warning: excess elements in scalar initializer
and then proceeds to pass just the first value in the braces as an argument. You could do this by adding a []
, but then you lose the size, as you note:
foo((uint8_t[]) {1,2});
You can compute the size and pass it as a second argument, but that requires duplicating the static array:
foo((uint8_t[]) {1,2}, sizeof((uint8_t[]) {1,2})/sizeof(uint8_t));
which may be acceptable if you define it as a macro or variable:
#define ARR (uint8_t[]) {1,2}
// or
uint8_t ARR[] = {1, 2};
foo(ARR, sizeof(ARR)/sizeof(ARR[0]));
Upvotes: 0