Затенение переменных в C - почему компилятор запутался? - PullRequest
0 голосов
/ 06 августа 2020

Мне нужно разобрать этот код JSON. В структуре есть ключ, val и указатель на следующую структуру. Из-за вложенности указатель val иногда указывает на структуру jss.

Код ниже

struct jss {
    uint8_t type;
    char *key;
    char *val;
    struct jss *next;
};

void my_f() {
    ...
    struct jss *js = (struct jss *)malloc(sizeof(struct jss));
    ...
    while(js) {
        struct jss *js1 = (struct jss *)js->val;
        ...
    }
}

компилируется и работает нормально и имеет следующую сборку:

struct jss *js = (struct jss *)malloc(sizeof(struct jss));
 4ea:   bf 20 00 00 00          mov    $0x20,%edi
 4ef:   e8 00 00 00 00          callq  4f4 <Init+0x407>
 4f4:   48 89 45 e8             mov    %rax,-0x18(%rbp)
 
    ...
    
        char *t, *f, *h;
        struct jss *js1 = ((struct jss *)(js->val));
 522:   48 8b 45 e8             mov    -0x18(%rbp),%rax
 526:   48 8b 40 10             mov    0x10(%rax),%rax
 52a:   48 89 45 b0             mov    %rax,-0x50(%rbp)

Мы посмотрите, что rbp-0x18, имеющий структуру addr js, перемещается в rax, rax затем добавляет 0x10, чтобы указать на адрес js -> val, и результат сохраняется в rbp-0x50, который содержит js1. Пока все хорошо!

Но если я изменю код на этот (js1 заменяется на js):

struct jss {
    uint8_t type;
    char *key;
    char *val;
    struct jss *next;
};

void my_f() {
    ...
    struct jss *js = (struct jss *)malloc(sizeof(struct jss));
    ...
    while(js) {
        char *t, *f, *h;
        struct jss *js = (struct jss *)js->val;
        ...
    }
}

У меня есть эта сборка:

struct jss *js = (struct jss *)malloc(sizeof(struct jss));
     4ea:   bf 20 00 00 00          mov    $0x20,%edi
     4ef:   e8 00 00 00 00          callq  4f4 <Init+0x407>
     4f4:   48 89 45 e8             mov    %rax,-0x18(%rbp)

    ...

            char *t, *f, *h;
            struct jss *js = ((struct jss *)(js->val));
     522:   48 8b 45 c8             mov    -0x38(%rbp),%rax
     526:   48 8b 40 10             mov    0x10(%rax),%rax
     52a:   48 89 45 c8             mov    %rax,-0x38(%rbp)

Что компилируется нормально, но с ошибками: вместо загрузки адреса структуры js (rbp-0x18) в rax загруженный адрес - это адрес новой структуры, которую я создаю ... тогда нет ничего удивительного в том, почему происходит сбой.

Вопрос в том, что не так во втором коде. Я знаю о переменном затенении, и это действительно мое намерение. Почему компилятор запутался (я использую g cc)?

Ответы [ 2 ]

4 голосов
/ 06 августа 2020

Рассмотрим эту строку вашего кода:

struct jss *js = (struct jss *)js->val;
//          ^                  ^ 
//          |                  |
//          this js  and this js are the same

Вы объявляете js, а затем разыменовываете js. Вторая js - это та же переменная, что и объявленная, и она, конечно, не инициализирована, следовательно, segfault.

Если у вас

struct jss *js1 = (struct jss *)js->val;

, то js относится к js объявлено во внешней области видимости, что вам и нужно.

Это точно такая же ситуация, как и в этой более простой ситуации:

int foo = 3;
...
{
   int foo = foo;
   ... // you expect foo to be three here, but actually
       // you're just assigning the unininitialized foo to itself
}

BTW clang вызывает очень явное предупреждение в этой ситуации , но не g cc.

0 голосов
/ 06 августа 2020

Согласно стандарту C (6.2.1 Области действия идентификаторов)

7 Теги структуры, объединения и перечисления имеют область действия, которая начинается сразу после появления тега в спецификаторе типа. который объявляет тег. Каждая константа перечисления имеет область действия, которая начинается сразу после появления определяющего ее перечислителя в списке перечислителей. Любой другой идентификатор имеет область видимости, которая начинается сразу после завершения его декларатора.

Итак, внутри этого оператора while

while(js) {
    char *t, *f, *h;
    struct jss *js = (struct jss *)js->val;
    ...
}

объявлен идентификатор js, который ссылается на себя в инициализаторе. То есть в выражении инициализатора используется неопределенное значение объявленного идентификатора js, скрывающего объект с таким же идентификатором., Объявленным во внешней области видимости перед оператором while.

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