Компилятору не удается переопределить переменную в условном блоке - PullRequest
2 голосов
/ 05 марта 2010
int bar = 2;
if (bar)
{
   int bar;
}

Ни gcc, ни Clang не удается выдать предупреждение (или ошибку) для этого, и программа сразу после запуска вылетает. Есть ли для этого веская причина? Не похоже, что это будет что-то трудно поймать. Это основы определения области блока: вложенная область наследует имена включающего блока ...

Есть объяснения?

РЕДАКТИРОВАТЬ: Оказывается, сбой был из-за использования Clang. Я проверял много раз взад и вперед, и кажется, что комбинация переопределения переменной и Clang вызывает сбой. Тем не менее, я не смог воспроизвести сбой в тестовом проекте, так что давайте разберемся.

Проблема оказалась связанной с Objective-C. Как отмечает Джонатан Леффлер, выполнение команды «int bar = bar» во внутренней области инициализирует переменную из себя, и это является причиной проблемы, когда инициализация выполняется с помощью вызова метода Objective-C.

Ниже показана ошибка в действии:

-(void)crasher
{
   NSNumber* bar = [NSNumber numberWithInt:2];
   if (bar)
   {
      NSString* bar = [self doit:bar];
   }
}

-(NSString*)doit:(NSNumber*)num
{
   NSString* str = [num stringValue];   // This line causes the crash
   return str;
}

Обратите внимание, что выполнение чего-то подобного в чистом C не приводит к сбою:

int bar = 2;
if (bar)
{
   char buff[10];
   int bar = sprintf(buff, "%d",bar);       
}

Ответы [ 5 ]

7 голосов
/ 05 марта 2010

Здесь нечего ловить. Переменная во внутреннем блоке является совершенно другой переменной, которая скрывает переменную во внешнем блоке. Это совершенно стандартная особенность языка, который существует там с незапамятных времен.

Авария, с которой вы столкнулись, не имеет абсолютно никакого отношения к опубликованному вами коду. Если вы не допустили ошибку в своем коде, работайте с внутренней переменной, предполагая, что вы работаете с внешней.

4 голосов
/ 05 марта 2010

Это основа вложенной области видимости: внутри вложенной области видимости можно скрыть что-либо, объявленное во внешней области видимости.У gcc есть опция для получения предупреждения об этом (-Wshadow), но она не активируется ни -Wall, ни -Wextra: предупреждение может появиться без изменений в коде (заголовок теперь имеет определение в глобальной области видимости дляидентификатор, используемый в функции).

1 голос
/ 05 марта 2010

Расширение ответа Дугласа Лидера:

#include <stdio.h>
static int xx(int foo)
{
    int bar = 2;
    if (foo > bar)
    {
        int foo = bar;
        int bar = bar;
        printf("inner: foo = %d, bar = %d\n", foo, bar);
    }
    printf("outer: foo = %d, bar = %d\n", foo, bar);
    return bar;
}
int main(void)
{
    xx(13);
    return(0);
}

Обратите внимание, что внутренний столбец инициализируется из самого себя, что дает неопределенное поведение.Но на MacOS X 10.6.2 (GCC 4.2.1) я получаю:

inner: foo = 2, bar = 0
outer: foo = 13, bar = 2

Вариант 1: вытаптывание из стека - A

Интересно, я получаю тот же вывод из этогокод, с функцией выталкивания стека, независимо от того, объявляю ли я i до или после a.

inner: foo = 2, bar = 20
outer: foo = 13, bar = 2

Код:

#include <stdio.h>
static void modify_stack(void)
{
    int a[20];
    int i;
    for (i = 0; i < 20; i++)
    {
        a[i] = 0xFFFFFFFF ^ i;
        printf("a[i] = 0x%08X\n", a[i]);
    }
}
static int xx(int foo)
{
    int bar = 2;
    if (foo > bar)
    {
        int foo = bar;
        int bar = bar;
        printf("inner: foo = %d, bar = %d\n", foo, bar);
    }
    printf("outer: foo = %d, bar = %d\n", foo, bar);
    return bar;
}
int main(void)
{
    modify_stack();
    xx(13);
    return(0);
}

Поскольку поведение не определено, этоРезультат в порядке.

Вариант 2: Вытаптывание в стеке - B

#include <stdio.h>
static int modify_stack(void)
{
    int a[20];
    int i;
    for (i = 0; i < 20; i++)
    {
        a[i] = 0xFFFFFFFF ^ i;
        printf("a[i] = 0x%08X\n", a[i]);
    }
    i = a[13];
    return(i);
}
static int xx(int foo)
{
    int bar = 2;
    if (foo > bar)
    {
        int foo = bar;
        int bar = bar;
        printf("inner: foo = %d, bar = %d\n", foo, bar);
    }
    printf("outer: foo = %d, bar = %d\n", foo, bar);
    return bar;
}
int main(void)
{
    int i = modify_stack();
    xx(13);
    return(i & 0xFF);
}

Вывод (кроме данных, напечатанных в цикле):

inner: foo = 2, bar = -14
outer: foo = 13, bar = 2
1 голос
/ 05 марта 2010

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

1 голос
/ 05 марта 2010
$ gcc 1.c

$ gcc -Wall 1.c
1.c: In function ‘main’:
1.c:6: warning: unused variable ‘bar’

$ cat 1.c
int main()
{
    int bar = 2;
    if (bar)
    {
        int bar;
    }
    return 0;
}

$ ./a.out ; echo $?
0

Компилирует для меня - с предупреждением под -Wall. И программа работает нормально.

Вы объявили две переменные, одну с именем bar, а другую также с именем bar. Компилятору все равно, если они находятся в разных областях.

...