Интересная проблема объема, объяснение? - PullRequest
1 голос
/ 28 мая 2009

Я только что обнаружил ошибку, когда код выглядел примерно так:

char *foo = malloc(SOME_NUM * sizeof(char));
if (!processReturnsTrueOrFalse(foo)) {
    free(foo);
    char *foo = malloc(SOME_NUM * sizeof(char));
    // More stuff, whatever
}

Это компилируется, но странно, что мне разрешено определять две переменные в одной и той же функции, но компилятор, по-видимому, определяет их по-разному.

Если бы это было так, как я могу дифференцировать внутреннее foo с внешним? Откуда компилятор узнал, что в free перед моим вторым объявлением я пытался освободить внешний foo, но затем, когда я переопределил внутренний foo, он не дал мне ошибки?

Спасибо за любую информацию. Это, вероятно, довольно очевидный вопрос новичка.

Ответы [ 5 ]

12 голосов
/ 28 мая 2009

C ++ определяет новую область видимости для переменных каждый раз, когда вы используете { }. Посмотрите на этот пример здесь.

const char *foo = "global";

int main(int argc, char* argv[])
{
    const char *foo = "hello";

    {
        cout << foo << endl;
        const char *foo = "world";
        cout << foo << endl;
        cout << ::foo << endl;
    }
    cout << foo << endl;
}

Когда вы запустите это, вы получите:

 hello
 world 
 global 
 hello

Когда вы открываете новую область и объявляете переменную с тем же именем, что и переменная во вложенной области, вы скрываете переменную во внешней области, пока вы остаетесь в текущей области. Как только вы покидаете внутреннюю область, внешняя переменная снова становится видимой. Если внешняя переменная является глобальной переменной, вы можете получить доступ к глобальному пространству имен ::foo. Если внешняя переменная является переменной класса, вы можете использовать className::foo. Если внешняя переменная является просто локальной переменной, доступ к ней невозможен, пока вы не покинете область, в которой вы объявили переменную, которая ее скрыла.

Я давно не использовал C, поэтому C99, скорее всего, будет другим, но в более старом C нет доступа к скрытому имени.

const char *foo = "global";

int main(int argc, char* argv[])
{
    const char *foo = "hello";

    {
        char *foo = "world";
        printf("%s\n", foo);
    }
    printf("%s\n", foo);
    return 0;
}

Когда вы запустите это, вы получите:

 world 
 hello
5 голосов
/ 28 мая 2009

Как писали другие авторы, вы shadow переменную, когда объявляете переменную с тем же именем во внутренней области видимости, например, функция или блок.

Но есть способ получить доступ к затененной глобальной нестатической переменной в C:

int foo = 1;                       // global

int bar(void) {

    printf ("%d", foo);            // print global
    {
        int foo = 2;               // global foo shadowed
        printf ("%d", foo);        // print local
        {
             extern int foo;       // local foo shadowed
             printf("%d", foo);    // print global
        }                          // global foo shadowed
        printf ("%d", foo);        // print local
    }                              // end of scope for local foo
}

Я сделал несколько фиктивных блоков, потому что с компиляторами до C99 вы не можете объявить переменную в середине блока.

3 голосов
/ 28 мая 2009

Это не ошибка, можно объявлять переменные с одинаковыми именами в разных областях.

Компилятор знает, что foo внутри свободной функции является внешней, потому что на данный момент это единственный foo программы.

После объявления второго foo следует использовать

::foo = ...

для доступа к внешнему foo, (редактировать), если внешний foo объявлен как глобальный (вне какой-либо функции).

3 голосов
/ 28 мая 2009

Область действия вашего второго 'foo' начинается с его объявления и продолжается до конца блока, в котором он объявлен. Когда вы вызываете free (foo), он действует на первый 'foo', потому что второй foo не имеет был объявлен еще.

После объявления второго «foo» нет никакого доступа к внешнему «foo». Вы по существу замаскировали имя.

2 голосов
/ 28 мая 2009

Это из-за областей видимости и таблиц символов.

Пример, который вы здесь привели, более сложен, чем обычная область видимости (и я настоятельно советую не использовать такие конструкции). Но объяснение состоит в том, что компилятор обновляет таблицу символов в точке, в которой вы объявляете новую переменную, пока поиск таблицы символов не упадет во внешнюю область.

Как видно из этого примера, адрес указателя изменяется:

void *var = NULL;
{
    std::cout << "From outer scope: " << &var << std::endl;

    void *var = NULL;

    std::cout << "From inner scope: " << &var << std::endl;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...