Неверный тип указателя с typedef

У меня возникают проблемы при вызове функции, принимающей в качестве параметра указатель на строку. Мне нужно получить имя элемента.

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

Позвольте мне представить структуры, с которыми я работаю (не написанные мной и являющиеся частью гораздо большего проекта, я не могу их модифицировать).

// 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;

Итак, в основном, я думал, что вызову функцию следующим образом:

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

Но это не работает (предупреждение: передается аргумент 1 «STR_toCsv» из несовместимого типа указателя). Я использую gcc с NetBeans 6.8.

Я много чего перепробовал...

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

... но закончилось тем, что имя было приведено к char ** :

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

Что не так с моим кодом? По сути, SYMBOL = CHAR *, верно? Почему СИМВОЛ* != СИМВОЛ**? Я почти уверен, что упускаю что-то простое, но сейчас... Ничего не пришло.

ИЗМЕНИТЬ Вот 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;
  }
}

person Isaac Clarke    schedule 29.06.2010    source источник
comment
Я не публикую в качестве ответа, так как мне нужно проверить, что я говорю. Но в основном SYMBOL = const char*, а не char*. Итак, SYMBOL* = const char**, что несовместимо с char**.   -  person Alexandre C.    schedule 29.06.2010
comment
Вы пробовали &(pData->pElement->name)?   -  person Mark Elliot    schedule 29.06.2010
comment
Разве вызов не должен быть getStringFromCsv(pData-›pElement-›name)? Поскольку pData уже является указателем, а pData является двойным разыменованием. РЕДАКТИРОВАТЬ: На самом деле я думаю, что согласен с Марком   -  person Il-Bhima    schedule 29.06.2010
comment
-> имеет более высокий приоритет, чем &.   -  person Secure    schedule 29.06.2010
comment
@Pmod: нет, это C, а не C++.   -  person Mike Seymour    schedule 29.06.2010
comment
Спасибо ребята. Майк прав, это определенно C, скомпилированный с помощью gcc. // Александр, возможно, ты прав, я думал об этом, не найдя подходящего теста или решения.   -  person Isaac Clarke    schedule 29.06.2010


Ответы (5)


Если вы хотите передать &pData->pElement->name функции, функция должна быть объявлена ​​как:

void getStringFromCsv(T_SYMBOL * str);

В качестве альтернативы вы можете использовать временный char * как предложенный Secure - но в этом нет особого смысла, потому что любые обновления значения этого char * не могут быть использованы - член ->name не может быть изменен, так как это массив.

Вы также можете просто объявить функцию как:

void getStringFromCsv( char * str );

... и назовите это как:

getStringFromCsv( pData->pElement->name );

(В этом случае функция по-прежнему может изменить содержимое массива ->name. Чего вы не можете сделать, так это изменить положение самого массива).


Помимо варианта Secure, есть еще один способ, если ваш компилятор поддерживает составные литералы C99:

getStringFromCsv( &(char *){ pData->pElement->name } );
person caf    schedule 29.06.2010
comment
К сожалению, изменение прототипов функций не возможно. Спасибо - person Isaac Clarke; 29.06.2010
comment
@Isaac Clarke: В этом случае вам нужно отредактировать свой вопрос, чтобы показать источник функции getStringFromCsv() или, по крайней мере, ее задокументированный интерфейс. - person caf; 30.06.2010
comment
Готово ;) (извините за задержку, я полагаю, мы не в одном часовом поясе) - person Isaac Clarke; 30.06.2010
comment
@Isaac Clarke: Поскольку функция только читает *data, а не изменяет его, временное решение char * является правильным для использования (но, как вы можете видеть, нет никакой земной причины, по которой функция не могла бы просто взять char * в первое место, используя data вместо *data!) - person caf; 30.06.2010
comment
Сам главный разработчик сказал мне, что это его ошибка. Изменение этого повлечет за собой массовый рефакторинг, так что это тоже не вариант. Я пойду с временным чаром, спасибо. - person Isaac Clarke; 30.06.2010
comment
Привет ! Метод C99 отлично работает! Это то, что я искал, спасибо! - person Isaac Clarke; 30.06.2010
comment
@Исаак Кларк: Нет проблем. Кстати, вам стоит заглянуть в Coccinelle — coccinelle.lip6.fr — утилита spatch semantic patch сделает короткая работа такого рефакторинга. - person caf; 30.06.2010
comment
О, французский инструмент :) Я знаю нескольких ребят из INRIA. Буду иметь в виду, спасибо! // Я думаю, что рефакторинг сам по себе не проблема, меня больше беспокоит желание моего коллеги измениться... Но это уже другой вопрос. - person Isaac Clarke; 01.07.2010

name - это массив символов, поэтому &name дает вам указатель на char[64], как уже ответила Вики. Но кастинг усугубляет ситуацию, потому что он говорит компилятору рассматривать первые символы массива как указатель на реальный массив.

См. C-FAQ: http://c-faq.com/aryptr/aryptr2.html< /а>

Я думаю, вы можете использовать временный char* здесь:

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

Если это ожидается функцией. Ожидая char**, убедитесь, что он не пытается перераспределить память. Для простого заполнения достаточно char*.

person Secure    schedule 29.06.2010
comment
Спасибо ! Мне не нравится идея временной переменной, хотя... Разве актерский состав хуже? - person Isaac Clarke; 29.06.2010
comment
Приведение на самом деле приводит к неопределенному поведению и, скорее всего, приведет к сбою. Как я уже сказал, внимательно прочитайте документацию getStringFromCsv, так как обычно есть причина, по которой используется один уровень косвенности больше, чем необходимо. Если вы дадите нам эту документацию, то мы можем предложить конкретные обходные пути, но, не зная этого, мы можем только догадываться. - person Secure; 29.06.2010
comment
Мне тоже не нравится использовать временную переменную. Однако это решение - единственный способ безопасно сделать это, который я могу придумать. Идеальным способом было бы изменить прототип функции, но вы упомянули, что это невозможно. Как упоминал Secure, функции лучше не пытаться модифицировать str, иначе вы можете столкнуться с большими проблемами. - person bta; 29.06.2010
comment
Что не так с временной переменной? Локальные переменные функции в стеке дешевы, и если бы мне пришлось использовать pData->pElement->name более одного раза, я бы все равно сохранил ее в переменной для удобства и читабельности. Не называя это tmp, конечно. Конечно, здесь это признак того, что два дизайна не совпадают должным образом и требуют некоторого связующего кода. - person Secure; 29.06.2010
comment
Я отредактировал первый пост, чтобы показать вам getStringFromCsv. // Меня больше всего заботит возможность повторного использования при создании переменной. Мой код будет использоваться снаружи, а не только основными разработчиками. Я хотел бы, чтобы пользователи могли кодировать, зная только заголовки. Актерский состав был бы логичен. Спасибо за помощь! - person Isaac Clarke; 30.06.2010
comment
Вау, это главный WTF. Во-первых, тип char ** не подходит для использования здесь, const char * будет правильным выбором, потому что он копируется только в pDesc. Во-вторых, и это более важно, если это полный csvDescriptorCat, то при этом нет контроля длины. Особенно, когда этот код будет использоваться снаружи, это может привести к переполнению буфера. - person Secure; 30.06.2010
comment
В C я обнаружил, что приведение никогда не бывает логичным. Единственные обстоятельства, в которых приведения полезны и иногда необходимы, — это работа с различными целочисленными типами на битовом уровне или для определенных арифметических операций, а также при работе с функциями с переменным числом переменных. Большинство других приведений следует рассматривать как предупреждающий знак о том, что что-то может быть не так. Не забывайте, что приведение типов говорит компилятору, что я знаю, что делаю, поэтому заткнитесь и просто делайте это. Тем более здесь это ИМХО не так. Вместо этого он используется, чтобы скрыть фундаментальное непонимание системы типов в отношении массивов и указателей, тем самым вызывая UB. - person Secure; 30.06.2010

Увы, один из маленьких секретов C, о котором вам не говорят, состоит в том, что массив — это не то же самое, что указатель. если x определен как int x[5] или как-то так, &x == x. Попробуйте этот код ниже:

#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;
}
person Rannick    schedule 29.06.2010

Учитывая это: http://c-faq.com/decl/strlitinit.html

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

Это не одно и то же (даже если кажется). Так что мой SYMBOL и CHAR* нельзя обменять, верно?

Есть обходной путь или другое решение?

person Isaac Clarke    schedule 29.06.2010

Да, на самом деле T_SYMBOL обрабатывается как char *. Но вы объявили его как char[64], поэтому вы передаете указатель на char[64], а не указатель на указатель на char. Компилятор отслеживает это для вас.

Лично в этой ситуации я бы просто произнес это, как вы в конце своего вопроса.

person Vicky    schedule 29.06.2010
comment
Нет. Его приведение не поможет, потому что оно просто рухнет, когда вы попытаетесь разыменовать бессмысленное значение char *, которое вы получите, разыменовав приведенное значение. - person caf; 29.06.2010
comment
Спасибо за объяснение в любом случае. - person Isaac Clarke; 29.06.2010