Выражение extern, T * v/s T []

Я видел следующий фрагмент кода в унаследованном проекте.

/* token.c */
struct token id_tokens[MAX_TOKENS];

/* analyse.c (v1) */
extern struct token *id_tokens; /* Raised my eyebrow, id_token declares a pointer */

Я настаивал на изменении analyse.c, чтобы содержать объявление, как показано ниже:

/* analyse.c (v2) */
extern struct token id_tokens[]; /* I am happy with this. id_tokens declares array of unspecified size. */

Я хочу v2, потому что pointer to T не то же самое, что array of T. Мой друг-счетчик аргументировал, что поведение обоих одинаково, поэтому не имеет значения, использую ли я v1 и v2.

Вопрос 1: Ли массив неполного типа заканчивается указателем?

Вопрос 2: Является ли мой друг правильным, что обе версии по своей природе гарантированно эквивалентны?

Ответ 1

/* token.c */
struct token id_tokens[MAX_TOKENS];

/*
 id_tokens
  +-----+-----+-----+-----+...+-----+
  |     |     |     |     |   |     |
  +-----+-----+-----+-----+...+-----+
    [0]   [1]   [2]   [3]  ...  [MAX_TOKEN-1]

  To access id_tokens[i], add offset of ith element
  i.e. i * sizeof(struct token) to the **address**
  of array token
 */

Итак, в вашем analyse.c с этим объявлением будут созданы следующие инструкции.

  • extern struct token id_tokens[];
    id_tokens [i]
    a. Адрес id_tokens, который может быть связан с другим модулем компиляции взято
    b. смещение я добавляется
    c. Значение ссылается
/* analyse.c (v1) */
extern struct token *id_tokens;

/*
 id_tokens
  +------+           +-----+...
  | addr |---------->|     |
  +------+           +-----+...


  To access id_tokens[i], fetch **contetnts** of pointer
  token, add offset of ith element i.e. i * sizeof(struct token)
  is added to this.
 */

Итак, в вашем analyse.c следующие инструкции будут сгенерированы с этим объявлением:

  1. extern struct token *id_tokens;
    id_tokens [i]
    a. Содержание с адреса id_tokens, связанного с другим блок компиляции принят.
     (Приведет к ошибке компиляции, если она присутствует в том же компиляторе из-за несоответствия типа)
    b. смещение я добавляется
    c. Значение ссылается

Предположим, что sizeof id_token[0] - это 2 байт, а sizeof указатель на id_token[0] - это 4 байт.

Ваше более позднее объявление может (неверно) интерпретировать id_tokens[0] и id_tokens[1] как адрес и добавлять к нему некоторое смещение (которое может быть уже существующим или несуществующим адресом, выровненным или неприсоединившимся адресом, который знает).

Если это ваш хороший день, программа может сразу же произойти сбой или segfault, и вы получите возможность исправить ошибку. Если это ваш плохой день, программа может просто испортить какую-то другую память или сообщить неправильное состояние в какой-либо модуль, что может привести к затруднению отслеживания ошибок и вызвать кошмар.


Теперь, я думаю, вы понимаете, почему вы получили (nil) как вывод в Mr. 32 ответ.

Ответ 2

Первая версия неверна. Массивы не являются указателями, декларация extern struct token *id_tokens; не соответствует типу определения struct token id_tokens[MAX_TOKENS];.

Ссылка: C FAQ: у меня было определение char a [6] в одном исходном файле, а в другом объявлено extern char * a, Почему он не работал?. Кроме того, см. this.

Ответ 3

позволяет понять тот же материал по программе

test.c

#include<stdio.h>
#include"head.h"
struct token id_tokens[10];
int main()
{
printf("In original file: %p",id_tokens);
testing();
}

head.h

struct token {
int temp;
};

test1.c с v1

#include<stdio.h>
#include"head.h"
extern struct token* id_tokens;
void testing () {
printf("In other file %p",id_tokens);
}

Выход: В исходном файле: 0x601040В другом файле (ноль)


test1.c с v2

#include<stdio.h>
#include"head.h"
extern struct token id_tokens[];
void testing () {
printf("In other file %p",id_tokens);
}

Вывод: В исходном файле: 0x601040В другом файле 0x601040


Это ясно показывает, что v1 неверен и v2 правильный.