Почему постоянная инициализация необходима для статического символа *, а не для статического символа ** - PullRequest
1 голос
/ 19 октября 2019

Может кто-нибудь объяснить, почему этот код ...

// main.c

#include <stddef.h>

static const int    g_a   = 1;
static const char*  g_b   = "hello";
static const char*  g_c[] = { "a",    "b",    NULL };

typedef struct Foo
{
  int           a;
  const char*   b;
  const char**  c;
} Foo;

static Foo f[] =
{
  { g_a,
    g_b,
    g_c }
};

int main( int argc, char* argv[] )
{
  return 0;
}

... выдает эту ошибку:

> gcc --version && gcc -g main.c 
gcc (GCC) 8.2.1 20181215 (Red Hat 8.2.1-6)
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

main.c:19:5: error: initializer element is not constant
     g_b,
     ^~~
main.c:19:5: note: (near initialization for 'f[0].b')

Я понимаю, что компилятор запрашивает константа для инициализации f[0].b, поэтому следующая инициализация является решением для ошибки компиляции:

static Foo f[] = { { g_a, "hello", g_c } };

Но почему компилятор не выдает аналогичную ошибку "константа требуется" дляинициализация f[0].c? (или, если на то пошло, f[0].a?) Почему это проблема только для f[0].b?

Ответы [ 2 ]

2 голосов
/ 19 октября 2019

f[0].a имеет тип "непостоянный int" и инициализируется значением g_a, имеющим тип "const int", который не может изменяться во время выполнения и известен во время компиляции. Поэтому здесь нет ошибок.

f[0].b имеет тип «неконстантный указатель на const char» и должен быть инициализирован значением g_b, которое также является «неконстантным указателем на const char»». Даже если он имеет инициализатор g_b, он может измениться во время выполнения, и IIRC последовательность инициализации не определена. Таким образом, его значение не известно во время компиляции, поэтому ошибка.

f[0].c имеет тип "неконстантный указатель на непостоянный указатель (и) на const char" и инициализируется g_cэто массив элементов типа «непостоянный указатель на const char». Символ массива может использоваться как постоянный указатель, который известен во время компиляции. Так что здесь нет ошибок.

Это то, что вам не хватает, я думаю: Если вы хотите, чтобы указатель был const, поместите модификатор в указатель, позади *, не в указанном значении, как это: [const] char * const pointer.

1 голос
/ 19 октября 2019

Формальные определения: посмотрите определение «адресной константы»: «Справочник по языку C 6.19»:

Константа адреса - это указатель на объект со статической продолжительностью хранения или указатель нафункция. Вы можете получить их с помощью оператора & или с помощью обычных преобразований имен массивов и функций в указатели, когда они используются в выражениях. Операторы [],., ->, & (address of) и * (разыменование указателя), а также приведение указателей могут использоваться в выражении, если они не требуют доступа к значению какого-либо объекта.

Практический ответ: «C» требует, чтобы статическая переменная была инициализирована постоянным выражением, которое можно вычислить во время компиляции. При расчете констант могут использоваться адреса статических / глобальных переменных, где фактический адрес неизвестен до времени ссылки.

В этих случаях (например, char * char_p = & char_var или аналогичные) компилятор будет генерировать сборкуинструкции, чтобы отметить вычисленное значение для «перемещения» во время соединения. Перемещение, выраженное статической / глобальной переменной. Во время соединения компоновщик добавит фактический адрес статического / глобального к сохраненному значению.

Обратите внимание: char char_var = 'A' ; char *char_p = &char_var;

        .file   "b.c"
        .text
        .globl  char_var
        .data
        .type   char_var, @object
        .size   char_var, 1

   # Char var initialized with a constant (65=A)
char_var:
        .byte   65
        .globl  char_p
        .section        .data.rel.local,"aw",@progbits
        .align 8
        .type   char_p, @object
        .size   char_p, 8

    # Initialize char_p to global symbol, actual address resolved at link time.
char_p:
        .quad   char_var

        .ident  "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
        .section        .note.GNU-stack,"",@progbits

Существует ограничение на возможность компоновщикарассчитать адрес во время ссылки. Он ограничен смещениями статического адреса +/-:

  1. & static_var
  2. & static_var + постоянная времени компиляции
  3. & static_var - постоянная времени компиляции

Но не '& static_var_1 - & static_var_2', что выдаст сообщение об ошибке, указывающее на ограничение: b.c:3:9: error: initializer element is not computable at load time int v = &char_var - &char_v2 ;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...