Let's say you have a function taking a string as an argument:
void foo(char *arg);
If we know for certain that the array (not to be confused with string length, thanks chux) will always have a certain size, let's say 8, then we can instead do:
void bar(char (*arg)[8]);
and then call it like this:
char str[8] = "Hello";
bar(&str);
We need to add the &
for this to work properly, but the above code will emit a warning if you pass an array of wrong size or type, which is exactly what I want to achieve. But we will obviously need to modify the body a bit. So my question is simply if this wrapper technique would work:
void bar(char (*arg)[8]) {
char *tmp = (char*) arg;
foo(tmp);
}
What I'm trying to achieve here is that warnings should be emitted if called with an array of wrong size. Is the above solution safe? Is it safe to cast pointer to array of char to pointer to char?
I tried it, and it works, and emits no warnings with -Wall -Wextra -pedantic
. And as soon as I change the size of str
I get:
<source>: In function 'main':
<source>:18:9: warning: passing argument 1 of 'bar' from incompatible pointer type [-Wincompatible-pointer-types]
18 | bar(&str);
| ^~~~
| |
| char (*)[9]
<source>:9:17: note: expected 'char (*)[8]' but argument is of type 'char (*)[9]'
9 | void bar(char (*arg)[8]) {
| ~~~~~~~^~~~~~~
which is exactly what I want. But is it safe, or is it UB? I would like to do this, not only via a wrapper, but also by rewriting the original function, like
void foo(char (*argaux)[8]) {
char *arg = *argaux;
// Copy body of original foo
I know that I can achieve basically the same thing using structs, but I wanted to avoid that.
Runnable code: https://godbolt.org/z/GnaP5ceMr
If you create an array by initializing its values directly, the size will be the number of elements in it. Thus the size of the array is determined at the time of its creation or, initialization once it is done you cannot change the size of the array.
The first one would be called like func1(foo, foo + CAPACITY) : passing in a pointer to the start of the array, and a pointer to just beyond the last element. The second would be called like func2(foo, CAPACITY) : passing in a pointer to the start of the array and the array size.
Once an array has been created, its size cannot be changed. Instead, an array can only be "resized" by creating a new array with the appropriate size and copying the elements from the existing array to the new one.
Arrays can either hold primitive values or object values. An ArrayList can only hold object values. You must decide the size of the array when it is constructed. You can't change the size of the array after it's constructed.
char *tmp = (char*) arg;
is wrong, these are not compatible pointer types. You can fix this easily though:
char *tmp = *arg;
*arg
gives a char[8]
which then decays into a pointer to its first element. This is safe and well-defined. And yes, pointers have much stronger "typing" in C than pass-by-value, so the compiler will recognize if an array of wrong size is passed.
Please note however that this leads to other problems: you can no longer have const correctness.
See Const correctness for array pointers?
This is not safe:
char *tmp = (char*) arg;
Because you're attempting to convert a char (*)[8]
to a char *
. While you might get away with it since a pointer to an array will (at least on x86-64) have the same numeric value as a pointer to the first member of an array, the standard doesn't guarantee that it will work. You would first need to dereference the parameter:
char *tmp = *arg;
In theory you should be able to do this:
void foo(char arg[static 8]);
This means that arg
must be an array of at least that size.
The description of this syntax is in section 6.7.6.3p7 of the C standard:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the
[
and]
of the array type derivation. If the keywordstatic
also appears within the[
and]
of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
However, most implementations don't enforce this restriction and it doesn't prevent you from passing an array larger than expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With