2

I'm having troubles when calling a function taking a pointer to a string as a parameter. I need to get an Element's name.

// method
void getStringFromCsv( char ** str );

Let me introduce the structures I'm working with (not written by me and part of a much bigger project, I can't modify them).

// typedefs
typedef char      T_CHAR64[64];
typedef T_CHAR64  T_SYMBOL;

// generic element
typedef struct Element
{
  T_SYMBOL  name; 
} T_Element;

// csv element
typedef struct CsvElement
{
  Element * pElement;
  int   id;
} T_csvElement;

So, basically, I thought I would call the function like this :

T_Element * pData; // Not null, filled earlier
getStringFromCsv( &pData->pElement->name );

But this doesn't work (warning: passing argument 1 of ‘STR_toCsv’ from incompatible pointer type). I'm using gcc with NetBeans 6.8.

I tried many things...

T_SYMBOL foo = "foo";
T_SYMBOL * pFoo = &foo;

getStringFromCsv( pDef->name, &pFoo ); // error : passing from incompatible pointer type

T_CHAR * pBar = &foo;      // error : init from incompatible pointer type
T_CHAR * pBaz = &(foo[0]); // OK

getStringFromCsv( pDef->name, &pBaz ); // OK

T_SYMBOL * pFooTest = &(foo[0]); // error : init from incompatible pointer type

...but ended up casting name to a char ** :

getStringFromCsv( (char**) &pData->pElement->name );

What is wrong with my code ? Basically, SYMBOL = CHAR *, right ? Why is SYMBOL* != CHAR** ? I'm pretty sure I'm missing something simple but right now... Nothing came.

EDIT Here is getStringFromCsv :

void getStringFromCsv( char ** data )
{
  // pDesc is defined and not null
  csvDescriptorCat( pDesc, *data);
  csvDescriptorCat( pDesc, "\t");
}

void csvDescriptorCat( CsvDescriptor * pDesc, char* str)
{
  int len;
  if( str != NULL)
  {
    len = strlen(str);
    strcpy( &pDesc->line[pDesc->pos], str);
    pDesc->pos += len;
  }
}
6
  • I don't post as an answer since I would need to check what I say. But basically, SYMBOL = const char*, not char*. So SYMBOL* = const char** which is incompatible with char**. Commented Jun 29, 2010 at 12:13
  • 1
    Did you try &(pData->pElement->name)? Commented Jun 29, 2010 at 12:15
  • Shouldn't the call be getStringFromCsv( pData->pElement->name ) ? Since pData is already a pointer &pData is a double dereference. EDIT: Actually I think I agree with Mark Commented Jun 29, 2010 at 12:17
  • -> has a higher precedence than &. Commented Jun 29, 2010 at 12:26
  • @Pmod: no, this is C, not C++. Commented Jun 29, 2010 at 12:40

5 Answers 5

2

If you wish to pass &pData->pElement->name to the function, the function must be declared as:

void getStringFromCsv(T_SYMBOL * str);

Alternatively you can use a temporary char * as Secure offered - but there's not much point in doing this, because any updates to that char *'s value can't be used - the ->name member can't be modified, as it's an array.

You might as well just declare the function as:

void getStringFromCsv( char * str );

...and call it as:

getStringFromCsv( pData->pElement->name );

(In this case, the function can still change the contents of the ->name array. What you can't do is to change the position of the array itself).


As well as Secure's option, there's another way if your compiler supports C99 compound literals:

getStringFromCsv( &(char *){ pData->pElement->name } );
Sign up to request clarification or add additional context in comments.

8 Comments

Modifiying function prototypes is not an option, unfortunately. Thanks
@Isaac Clarke: In that case, you'll need to edit your question to show the source of the getStringFromCsv() function - or at least its documented interface.
Done ;) (sorry for the delay, we're not on the same time zone I presume )
@Isaac Clarke: Since the function is only reading *data, and not altering it, the temporary char * solution is the correct one to use (but as you can see, there's no earthly reason why the function couldn't just take a char * in the first place, using data in place of *data!)
The main developper himself told me it was an error of his. Changing this would imply a massive refactoring, so it's not really an option either. I'm gonna go with the temp char, thanks.
|
2

name is an array of chars, so &name gives you a pointer to char[64], as Vicky already answered. But casting makes things worse, because it tells the compiler to treat the first chars of the array as a pointer to the real array.

See the C-FAQ: http://c-faq.com/aryptr/aryptr2.html

I think you can use a temporary char* here:

char *tmp = pData->pElement->name; // array decays to pointer
getStringFromCsv(&tmp);

If this is expected by the function. Expecting a char**, make sure that it doesn't try to reallocate the memory. For simply filling it, a char* would be enough.

7 Comments

Thanks ! I don't like the idea of a temporary variable, though... Is the cast worse ?
The cast actually results in undefined behaviour and will most likely result in a crash. Read the documentation of getStringFromCsv carefully, as I said, as there usually is a reason why one level of indirection more than necessary is used. If you give us this documentation, then we can suggest specific workarounds, but without knowing it we can only guess.
I don't like having to use a temporary variable either. However, this solution is the only way that I can think of to safely do this. The ideal way would be to change the function prototype, but you mentioned that wasn't an option. As Secure mentioned, the function better not be trying to modify str itself or you can run into big problems.
What's wrong with a temporary variable? Function-local variables on the stack are cheap, and if I had to use pData->pElement->name more than once, then I would store it in a variable anyway for convenience and readability. Not calling it tmp, of course. Granted, here it is a sign that two designs are not matching properly and require some glue code.
I edited the first post to show you getStringFromCsv. // My main concern about creating a variable is reusability. My code will be used outside, and not only by the main developpers. I would like the users to be able to code, knowing only the headers. A cast would be "logical". Thanks for you help !
|
1

Alas, one of the little secrets of C that people fail to tell you, an array is not the same thing as a pointer. if x is defined as int x[5] or whatever, &x == x. Try out this code below:

#include <stdio.h>
int main(int argc, const char *argv[])
{
   char x [5];
   char *y;

   printf("%08x\n", x);
   printf("%08x\n", &x);
   printf("%08x\n", y);
   printf("%08x\n", &y);

   return 0;
}

Comments

0

Considering this : http://c-faq.com/decl/strlitinit.html

char  a[4] = "hello";
char* p = "hello";

Aren't the same thing (even if they seem to be). So my SYMBOL and CHAR* cannot be exchanged, right ?

Is there a workaround, or another solution ?

Comments

-2

Yes, under the covers T_SYMBOL is handled like a char *. But you've declared it as a char[64], so you're passing in a pointer to a char[64] not a pointer to a pointer to a char. The compiler is keeping track of that for you.

Personally in this situation I would just cast it as you did at the end of your question.

1 Comment

No. Casting it will not help, because it will simply crash when you try to dereference the nonsense char * value you get by dereferencing the casted value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.