c несовместимые типы в присваивании, проблема с указателями? - PullRequest
6 голосов
/ 05 апреля 2010

Привет, я работаю с C, и у меня есть вопрос о назначении указателей.

struct foo
{
   int _bar;
   char * _car[SOME_NUMBER]; // this is meant to be an array of char * so that it can hold pointers to names of cars
}

int foofunc (void * arg)
{
   int bar;
   char * car[SOME_NUMBER];

   struct foo * thing = (struct foo *) arg;

   bar = thing->_bar; // this works fine
   car = thing->_car; // this gives compiler errors of incompatible types in assignment
}

car и _car имеют одинаковое объявление, так почему я получаю сообщение об ошибке несовместимых типов? Я думаю, что это как-то связано с тем, что они являются указателями (потому что они являются указателями на массивы char *, верно?), Но я не понимаю, почему это проблема.

когда я объявил char * car; вместо char * car[MAXINT];, он компилируется нормально. но я не понимаю, как это было бы полезно для меня позже, когда мне нужно было получить доступ к определенной информации, используя индекс, было бы очень неприятно получить доступ к этой информации позже. на самом деле, я даже не уверен, правильно ли я поступаю, возможно, есть лучший способ хранить несколько строк вместо использования массива char *?

РЕДАКТИРОВАТЬ: я не хотел использовать INT_MAX (максимальное значение int), это просто какой-то другой int, который составляет около 20.

Ответы [ 4 ]

5 голосов
/ 05 апреля 2010

car и _car оба являются массивами, и вы не можете назначать массивы в C (кроме случаев, когда массив встроен в структуру (или объединение), и вы делаете присвоение структуры).

Они также являются массивами указателей на char, а не указателями на массивы char. То, что вы написали в коде, вероятно, то, что вы хотите - вы можете хранить указатели до максимум MAXINT имен в массиве. Тем не менее, вы должны правильно описать тип - как массив указателей на символ или указатель на символ.

Указатель на массив символов будет выглядеть так:

char (*car)[MAXINT];

И точка на массив символьных указателей (спасибо, Брайан) будет выглядеть так:

char *(*car)[MAXINT];

Будьте осторожны с MAXINT; это может быть очень большой массив (в Linux <values.h> определяет MAXINT как INT_MAX, что по крайней мере 2 31 -1).


Код выглядит так:

struct foo
{
   int _bar;
   char * _car[MAXINT];
}

int foofunc (void * arg)
{
   int bar;
   char * car[MAXINT];
   struct foo thing = (struct foo *) arg;
   bar = arg->_bar; // this works fine
   car = arg->_car; // this gives compiler errors of incompatible types in assignment
}

Ни назначение bar, ни car не должны компилироваться вообще - arg - это void *. Вы, вероятно, хотели использовать thing в той или иной форме. Как отметил Брайан, там тоже есть проблемы:

Вы либо хотите:

int foofunc(void *arg)
{
    int bar;
    char *car[MAXINT];
    struct foo thing = *(struct foo *)arg;
    bar = thing._bar; // this works fine
    car = thing._car; // this is still an array assignment
    ...other code using bar and car...
}

Или вы хотите:

int foofunc(void *arg)
{
    int bar;
    char *car[MAXINT];
    struct foo *thing = (struct foo *) arg;
    bar = thing->_bar; // this works fine
    car = thing->_car; // this is still an array assignment
    ...other code using bar and car...
}

Или, действительно:

int foofunc(void *arg)
{
    struct foo *thing = (struct foo *) arg;
    int bar           = thing->_bar; // this works fine
    char *car[MAXINT] = thing->_car; // this is still an array assignment
    ...other code using bar and car...
}

Наконец, имея дело с присваиванием массива, в C вы можете разумно использовать memmove() для этого:

int foofunc(void *arg)
{
    struct foo *thing = (struct foo *) arg;
    int bar           = thing->_bar; // this works fine
    char *car[MAXINT];
    memmove(car, thing->_car, sizeof(car));
    ...other code using bar and car...
}

Аналогичная функция memcpy() не имеет надежной семантики, если копируемые области перекрываются, тогда как memmove() имеет; проще всегда использовать memmove(), потому что он всегда работает правильно. В C ++ вы должны быть осторожны с использованием memmove() (или memcpy()). В этом коде это было бы достаточно безопасно, но понимание почему нетривиально.

Вы должны знать, что вы просто копируете указатели здесь - вы не копируете строки, на которые указывают указатели. Если что-то еще изменяет эти строки, это влияет как на значения, видимые через car, так и на переменную в вызывающем коде.

Еще один момент - пока: вы уверены, что вам нужен аргумент функции как void *? Он открывает код для всех видов злоупотреблений, которые можно предотвратить, если объявить, что функция принимает вместо 'struct foo *' (или даже 'const struct foo *').

3 голосов
/ 05 апреля 2010

Вы создаете новый массив размером MAXINT. Я думаю, что вы хотите создать указатель на массив размером MAXINT.

Создание указателя на массив символов *:

Ниже приведен массив размера MAXINT для элементов char *:

char * car[MAXINT]; 

Ниже приведен указатель на: массив размером MAXINT to char * elements:

char* (*car)[MAXINT];

ниже показано, как установить указатель на: массив размера MAXINT для элементов char *:

char* (*car)[MAXINT];
car = &arg->_car;

Другие синтаксические ошибки в вопросе:

  • Вам нужно использовать точку с запятой после определения структуры.
  • Вы должны использовать foo*, а не foo. Так и должно быть:
    struct foo* thing = (struct foo *) arg;
  • Вы должны использовать thing, а не arg:
    bar = thing->_bar;
    car = thing->_car;
2 голосов
/ 05 апреля 2010

Вы не можете назначить массив так, как вы делаете. Вы можете сделать поэлементную копию.

for(int i = 0; i < MAXINT; i++)
{
  car[i] = (arg->_car)[i]
}

Обратите внимание, что если строки не являются постоянными, вам может потребоваться использовать strcpy.

0 голосов
/ 05 апреля 2010

Обозначение массива в C законно сбивает с толку; Ваш код не означает, что вы думаете, что это значит.

arg->_car означает «адрес массива _car». Аналогично, car означает «адрес массива car». Если вы пытаетесь скопировать содержимое _car на машину, то это будет сделано:

memcpy(car, _car, MAXINT);

Но ваш реальный вопрос, я думаю, заключается в том, "как лучше всего хранить список строк?" Этот ответ: динамический список (тот, который увеличивается автоматически при добавлении элементов).

Вы бы объявили это так:

#define CARSIZE 65
int numCars = 0;
char **car; /* a list of addresses, each one will point to a string */

Чтобы добавить автомобиль:

char *newCar = malloc(CARSIZE); /* make room */
strncpy(newCar, "Mercedes", CARSIZE); /* newCar has the address of the string */
car[numCars++] = newCar; /* store it */

Чтобы перечислить автомобили:

int n;
for (n = 0; n < numCars; ++n)
    printf("%s\n", car[n]);

Чтобы снять автомобиль в позиции n:

free(car[n]); /* release the memory */
/* condense the list of pointers */
for ( ; n < numCars - 1; ++n)
    car[n] = car[n+1];

Это совершенно обычная процедура для C. ПРИМЕЧАНИЕ: Вышеприведенное неуместно и не скопировано из рабочей программы, поэтому я не могу обещать, что все * находятся в нужном месте. Я подозреваю, что это домашнее задание, поэтому я не хочу давать вам все ...

...