3

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.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Nicu Stiurca
  • 8,747
  • 8
  • 40
  • 48
  • The question is: Why are you *replacing* the pointers supplied? I know you're allowed to modify the content, but I'm not so sure about replacing the pointers. – paddy Oct 23 '13 at 23:33
  • @paddy, I think you are referring to my earlier version of the post; refresh to see the edit where I took out the extra pointer stuff. – Nicu Stiurca Oct 23 '13 at 23:35
  • Why are you keeping a reference that you are "never actually using"? – Shoe Oct 24 '13 at 01:25

2 Answers2

3

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.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Isn't `&args[1]` equivalent to `args+1`, in which case, it doesn't really matter if `args` is even a valid pointer? – Nicu Stiurca Oct 23 '13 at 23:34
  • 2
    @SchighSchagh: `args + 1` is only valid if [same as above]. You can't make up arbitrary pointers. Pointers must point to actual elements, or one-past-the-end. – Kerrek SB Oct 23 '13 at 23:34
  • The statement about `&args[1]` is true only for C, not C++. The language to permit this was added in C99 (I quoted the language in [this answer to another question](http://stackoverflow.com/questions/2896689/dereferencing-the-null-pointer/2896975#2896975)). C++ has no equivalent rule. – James McNellis Oct 23 '13 at 23:58
1

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.

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • Thank you, some quotes from the standard is what I was hoping for. As for C-style arrays... I don't like them either but I'm stuck with them. – Nicu Stiurca Oct 24 '13 at 19:44