Reputation: 8697
I am working with an API wherein the caller passes in an array of pointers to stuff, as well as the length of the array. To make my code more readable/maintainable, I would like to be able to effectively give names to each argument rather than refer to them as arg[0]
, arg[1]
, etc. Is it safe to declare references to all of the possible arguments even if the length of the passed-in array can be different sizes due to optional arguments?
I am trying to do something like this:
void myFunc(int out_args[], size_t nargs) {
int &foo = out_args[0];
int &bar = out_args[1]; // bar is optional argument. is this safe?
...
foo = 5;
if(2 >= nargs)
bar = 10;
...
}
Note that the arguments are output arguments, so I really want to have references to them. So, is it safe to have a dangling-ish reference to args[1] if I never actually use it?
My guess is that this is safe because I imagine the way references are implemented is to treat &
in the variable declaration of references as * const
, and whenever I use the references, then the compiler automatically dereferences the pointers for me. Ie, under the hood, I imagine that what I wrote is translated to something like
void myFunc(int out_args[], size_t nargs) {
int *const foo = &out_args[0];
int *const bar = &out_args[1]; // bar is optional argument. is this safe?
...
*foo = 5;
if(2 >= nargs)
*bar = 10;
...
}
In this case, I believe the code never actually accesses memory it shouldn't, so if the version above is equivalent to this, then I should be ok, right?
EDIT: I'm basically writing a plug in, and the API I'm using and can't do anything about can call my code with either something like
int ret_vals[1]; // only care about 1 return value
myFunc(ret_vals, 1);
or
int ret_vals[2]; // care about both return values
myFunc(ret_vals, 2);
or even
myFunc(NULL, 0); // ignore all return values; just marvel at the side effects
and my code needs to work in all cases.
Upvotes: 3
Views: 138
Reputation: 76280
So, is it safe to have a dangling-ish reference to args[1] if I never actually use it?
The standard impose that a reference shall be bound to a valid object in § 8.3.2:
There shall be no references to references, no arrays of references, and no pointers to references. [...] A reference shall be initialized to refer to a valid object or function. [ Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior. [...] — end note ]
It means that it is not safe.
My guess is that this is safe because I imagine the way references are implemented is to treat & in the variable declaration of references as * const, and whenever I use the references, then the compiler automatically dereferences the pointers for me.
No, don't do that. Again, the standard doesn't specify how references shall be implemented. In fact in § 8.3.2 it states that:
It is unspecified whether or not a reference requires storage.
As for your code: you could name the parameters once you are sure they exists.
void myFunc(int out_args[], size_t nargs) {
int &foo = out_args[0];
...
foo = 5;
if(nargs >= 2) {
int &bar = out_args[1];
bar = 10;
}
...
}
I feel obligated to make you notice that the use of C-style arrays in C++ is discouraged. It is generally a good idea to use std::vector
or std::array
instead.
Upvotes: 1
Reputation: 477444
It is undefined behaviour to evaluate the expression args[1]
if args
isn't a pointer to the first element of an array of at least two elements. Taking the address immediately, like &args[1]
, is valid only if args
points to the first element of an array of at least one element.
Basically, don't do it.
Upvotes: 3