views:

220

answers:

7

In short, I would like to do this:

const char **stringPtr = &getString();

However, I understand that you can't & on rvalues. So I'm stuck with this:

const char *string = getString();
const char **stringPtr = &string;

I can live with two lines. Am I introducing problems with this hack? I should have no fear of passing stringPtr out of the function it is declared in, right?

Edit: My apologies for not originally including the full context. I have taken on the summer project of building a video game from the ground up in C using OpenGL for graphics. I'm reading configuration data from a text file using libconfig.

One of the convenience functions for finding a specific string from your configuration file looks like this:

int config_setting_lookup_string(const config_setting_t *setting,
                                 const char *name, const char **value)
{
  config_setting_t *member = config_setting_get_member(setting, name);
  if(! member)
    return(CONFIG_FALSE);

  if(config_setting_type(member) != CONFIG_TYPE_STRING)
    return(CONFIG_FALSE);


  *value = config_setting_get_string(member);
  return(CONFIG_TRUE);
}

The way that value is assigned means that if you give the function an uninitialized value, it attempts to derefence undefined garbage, which pretty much always causes me a segfault. My current workaround for this issue is to initialize value to another pointer first, like so:

const char *dummyPtr;
const char **fileName = &dummyPtr;
config_setting_lookup_string(foo, "bar", fileName);

So I am trying to figure out the best way to rewrite the last part of the function so that I won't have to perform this two-step initialization. I was thinking that the changed function would look like this:

int config_setting_lookup_string(const config_setting_t *setting,
                                 const char *name, const char **value)
{
  config_setting_t *member = config_setting_get_member(setting, name);
  if(! member)
    return(CONFIG_FALSE);

  if(config_setting_type(member) != CONFIG_TYPE_STRING)
    return(CONFIG_FALSE);

  const char *string = config_setting_get_string(member);
  value = &string;
  return(CONFIG_TRUE);
}
A: 

You need the two lines. However, string is a local variable on the stack, once it goes out of scope, you may not have a pointer to the data returned by getString().

amrinder
A: 

If you return stringPtr, you will be returning a pointer to a local variable (string). So no, you can't do that.

Why are you trying to do this? That might allow us to make better suggestions.

Update: Okay, now I see what you're trying to do. You're doing it wrong:

value = &string;

If value is meant as an output parameter, the above line cannot work because you're assigning to a local variable.

Don't let the extra level of indirection confuse you. If you were writing a function that had an output parameter of type T, you'd write it as:

void foo(T* value)
{
    *value = GetT();
}

Now replace T with const char*:

...
*value = string;
...

And now you're not involving any temporary, local variables. Of course, that's how the code was originally written (and that part of it was correct), so that doesn't really help you. To address your intent, you should:

  1. Make config_setting_lookup_string do assert(value != NULL).
  2. Audit the callers of the function and fix them to stop passing garbage. They should be doing:

    const char* foo; config_setting_lookup_string(..., &foo);

and NOT:

const char** foo;
config_setting_lookup_string(..., foo);
jamesdlin
Edited and added the full context of what I'm trying to do. Basically, I'm trying to rewrite a library routine that returns a string through a const char ** to include some boilerplate initialization code which suits me better.
spirulence
+1  A: 

string in your case is a local, so taking the address of it is a bad idea since the memory for the local can (and likely will be) re-used for other purposes when you leave the method. In general, it is not a good idea to use the address of a local variable outside of its scope.

What are you trying to achieve?

Chris Schmich
Should have added that before. Thanks for your noob patience. Edited now.According to the documentation, "Storage for the string returned by config_lookup_string() is managed by the library and released automatically when the setting is destroyed or when the setting's value is changed; the string must not be freed by the caller."http://www.hyperrealm.com/libconfig/libconfig_manual.html#The-C-API
spirulence
+3  A: 

If you're calling a function that needs a const char**, you could do it like this:

const char *s = getString();
myFunction(&s);

Since s is allocated on the stack in the above example, if you want to return a const char** from your function, you will need to put it on the heap instead:

const char **sp = malloc(sizeof(const char *));
*sp = getString();
return sp;

HTH

nornagon
+1  A: 

No, you can't change config_setting_lookup_string() in the way you've described. You're returning a pointer to the string variable, but as soon as that function ends that variable goes out of scope and is destroyed.

You can, however, fix your initial problem quite easily. Leave the definition of config_setting_lookup_string() as it is, and call it like so:

const char *fileName = NULL;
config_setting_lookup_string(foo, "bar", &fileName);
caf
A: 

I like nornagon and caf's solution,

const char *fileName;
config_setting_lookup_string(foo, "bar", &fileName);

but if you can change config_setting_lookup_string you could also do it this way:

int config_setting_lookup_string(..., const char *&value)
{
  ...
  const char *string = config_setting_get_string(member);
  value = string;
  ...
}

const char *fileName;
config_setting_lookup_string(foo, "bar", fileName);
Beta
A: 

From the added information, it seems what you are trying to do is call a function which wants to return a string through one of the function arguments. The best way to do this in my opinion would be something like:

const char* fileName;
config_setting_lookup_string(..., &fileName);
(...)
return fileName;

This will allocate room for a const char* on the stack. The function call will fill the pointer with the address of the string it wants to return. This pointer value can then be passed out of the function if needed (unlike the pointer to the pointer, which would point to the stack, and be invalid when the function returns). Note that initializing fileName with "getString()" would presumably leak memory, since the pointer to the returned string would be overwritten, and the string never deallocated.

tengfred