Динамический массив структур и вызов функции f (const struct_type * const data []) - PullRequest
1 голос
/ 11 мая 2009

Следующий код предупреждает о несовместимом типе. Как правильно решить этот код?

спасибо

typedef struct a_struct struct_type;

void f(const struct_type *const data[], unsigned n);

void test(unsigned n)
{
   struct_type **v;

   assert(n);
   v = malloc(n * sizeof(struct_type *));
   /* fill v with new struct_type (with a malloc per entry) */
   f(v, n); <- WARN on v
}

Ответы [ 4 ]

4 голосов
/ 11 мая 2009

Причина, по которой компилятор жалуется, - это первое const в объявлении f.

Попробуйте использовать

void f(struct_type *const data[], unsigned n);
/*...*/
f( v, n );

и вы не получите то же самое предупреждение. Кроме того, вы можете разыграть v, когда вы звоните f

void f(const struct_type *const data[], unsigned n);
/*...*/
f( (const struct_type * const *) v, n );

Это немного нелогично, но в C вы не можете передать pointer-to-pointer-to-nonconst для pointer-to-pointer-to-const. Они сделали специальное исключение, чтобы позволить вам передать pointer-to-nonconst для pointer-to-const.

Вот вопрос часто задаваемых вопросов «Почему я не могу передать char ** функции, которая ожидает const char **:

Вы можете использовать pointer-to-T (для любого типа T), где ожидается pointer-to-const-T. Однако правило (явное исключение), допускающее небольшие несоответствия в квалифицированных типах указателей, применяется не рекурсивно, а только на верхнем уровне. (const char ** равно pointer-to-pointer-to-const-char, и, следовательно, исключение не применяется.)

Причина, по которой вы не можете присвоить значение char ** указателю const char **, несколько неясна. Учитывая, что классификатор const существует вообще, компилятор хотел бы помочь вам выполнить ваши обещания не изменять значения const. Вот почему вы можете назначить char * для const char *, но не наоборот: совершенно очевидно, что добавить const -ность к простому указателю, но это опасно это прочь Однако предположим, что вы выполнили следующую более сложную серию заданий:

const char c = 'x';     /* 1 */
char *p1;           /* 2 */
const char **p2 = &p1;      /* 3 */
*p2 = &c;           /* 4 */
*p1 = 'X';          /* 5 */

В строке 3 мы присваиваем char ** const char **. (Компилятор должен пожаловаться.) В строке 4 мы присваиваем const char * a const char *; это явно законно. В строке 5 мы модифицируем то, на что указывает char * - это должно быть законно. Однако p1 в конечном итоге указывает на c, который равен const. Это произошло в строке 4, потому что * p2 был действительно p1. Это было установлено в строке 3, которая является назначением запрещенной формы, и именно поэтому строка 3 запрещена.

Назначение char ** для const char ** (как в строке 3 и в исходном вопросе) не является сразу же опасным. Но он создает ситуацию, в которой обещание p2 - что конечное значение не будет изменено - не может быть выполнено.

(C ++ имеет более сложные правила для назначения const -качественных указателей, которые позволяют выполнять больше видов назначений без появления предупреждений, но при этом защищают от непреднамеренных попыток изменить значения const. C ++ по-прежнему не позволяет назначать char ** для const char **, но это позволит вам присвоить char ** для const char * const *.)

В C, если вы должны назначить или передать указатели, которые имеют несоответствия в квалификаторах, отличные от первого уровня косвенности, вы должны использовать явное приведение (например, (const char **) в этом случае), хотя, как всегда, необходимость такое приведение может указывать на более глубокую проблему, которую на самом деле не решают.

Ссылки: ISO Sec. 6.1.2.6, гл. 6.3.16.1, гл. 6.5.3 H & S Sec. 7,9,1 с. 221-2

0 голосов
/ 11 мая 2009

Посмотрите, будет ли это работать для вас:

f(struct_type *data);

void test(unsigned n)
{
    struct_type *v = malloc(n * sizeof(struct_type *));
    f(v);
}

Пожалуйста, дайте мне знать, как вы ладите.

0 голосов
/ 11 мая 2009

f ожидает получить в качестве входных данных массив указателей (const struct_type * []). Вы передаете указатель на указатель struct (const struct_type **).

Лучше всего, IMO, изменить подпись f на:

void f(const struct_type *const* data);

Зачем вам нужно передавать массивы в качестве аргументов в функции?

0 голосов
/ 11 мая 2009

Отредактировано на основе ответа Rampion. Проблема в двойном const в объявлении f ().

Код с предупреждением:

struct_type ** v;
v = (struct_type **)malloc(10 * sizeof(struct_type *));      
f(v);

Это компилируется без предупреждения:

const struct_type *const* v;
v = (const struct_type **)malloc(10 * sizeof(struct_type *));
f(v);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...