Подтвердить что ты не робот

Использует sizeof для переменной, где тип одного и того же имени существует хорошо определенным?

Является ли это хорошо определенным поведением или это undefined/как-то иначе определено, какой foo (тип данных или идентификатор) sizeof будет работать на?

typedef int foo;

int main(int argc, char *argv[])
{
    char foo;
    printf ("%u\r\n", sizeof(foo));
    return 0;
}

Если он определен корректно, существует ли способ получить размер типа данных foo без объявления переменной этого типа только для использования sizeof на нем?

4b9b3361

Ответ 1

C не имеет явного разрешения области, поэтому идентификаторы (имена переменных, имена typedef, имена структур и т.д. и т.д.) могут быть повторно использованы и переопределены при открытии новой области. Когда идентификатор повторно используется, предыдущий контекст, содержащийся с этим идентификатором, больше не отображается.

В вашем конкретном коде область typedef является глобальной, поэтому typedef отображается повсюду в вашем компилируемом пакете. Тем не менее, вы открываете новую область с объявлением функции, и в этой новой области вы определяете переменную, которая использует тот же идентификатор, что и typedef. Теперь этот идентификатор относится к переменной вместо типа; значение, пока область действия переменной (конец функции), typedef полностью не скрыта.

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

#include <stdio.h>
typedef int foo;

int main()
{
    printf ("%zu\n", sizeof (foo)); /* #1 */

    char foo;
    printf ("%zu\n", sizeof foo); /* #2 */

    return 0;
}

В точке # 1 обратите внимание, что область действия переменной char foo еще не открыта, так как компилятор не достиг своего объявления. (Весь компилятор будет делать это выделение пространства в стеке для переменной).

Таким образом, использование foo в этой точке по-прежнему относится к глобально определенному typedef.

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

Это хорошо документированное поведение; есть проект стандарта C в Интернете, но опубликованный стандарт должен быть приобретен. Проект гласит в разделе 6.2.1:

Если идентификатор обозначает два разных объекта с тем же именем пространство, области могут перекрываться. Если это так, объем одного объекта ( внутренний объем) полностью прекратится до сферы действия другого объекта (внешний охват). Внутри внутренней области идентификатор обозначает объект, объявленный во внутренней области; субъект, объявленный в внешний охват скрыт (и не отображается) во внутренней области. источник

Обратите внимание, что это не волшебство или что-то еще... все это делается во время компиляции. У компилятора есть таблица идентификаторов и то, что они ссылаются, а новые области создают для них новые таблицы. Так получилось, что в точке №1 в коде выше компилятор еще не заполнил таблицу новым char foo (это связано с линейной компиляцией). Поэтому, когда он переводит первую строку printf, он просматривает все активные области, чтобы найти идентификатор foo, и видит typedef, и использует это. На втором printf он просматривает все активные области и находит более недавнее использование идентификатора foo и использует его.

Ответ 2

Является ли это хорошо определенным поведением или оно undefined

Это хорошо определенное поведение.

В вашем коде

printf ("%u\r\n", sizeof(foo));

foo недвусмыслен. Локальный или внутренний foo "теней" (или скрывает) внешний foo. Таким образом, это существенно

printf ("%u\r\n", sizeof(char));

Чтобы процитировать C11, раздел §6.2.1, "Области идентификаторов", (выделено мной)

Если идентификатор обозначает два разных объекта с тем же именем пространство, области могут перекрываться. Если это так, объем одного объекта (внутренний объем) будет полностью закрыт перед областью действия другого объекта (внешней области). Внутри внутренней области идентификатор обозначает объект, объявленный во внутренней области; объект, объявленный во внешней области, скрыт (и не отображается) внутри внутренней области.

Теперь, чтобы ответить на вторую часть,

Если он определен корректно, существует ли способ получить размер типа данных foo без объявления переменной этого типа, чтобы использовать только sizeof на нем?

Ну, область внутреннего идентификатора foo начинается после его определения (как переменной типа char). Итак, если вы используете foo (тип typedef ed) перед определением переменной foo, вы фактически увидите глобальное определение, так как до этой точки переменная foo отсутствует в "shadow out" глобальный идентификатор foo.

Как упоминалось в другом ответе Mr. Purag, вы можете сделать что-то вроде

#include <stdio.h>

typedef int foo;

int main(void)
{
    printf ("%zu\n", sizeof foo); /* will cosider the global */
    char foo = 0;                 /* Global definition gets shadowed here*/
    printf ("%zu\n", sizeof foo); /* The local definition of 'foo' is only visible*/ 
                                  /* till the end of the block (function)*/

    return 0;
}

Тем не менее, в качестве примечания, тип результата оператора sizeof равен size_t. Вы должны использовать спецификатор формата %zu для печати результата.

Ответ 3

Локальный char foo полностью скрывает typedef int foo в своей области. Вы не можете наблюдать имя foo после его скрытия. Попробуйте создать второй typedef foo foo_t или переименовать что-то, чтобы избежать "столкновения".

Ответ 4

Прежде всего, согласно стандарту C (6.5.3.4 Операторы sizeof и alignof)

2 Оператор sizeof дает размер (в байтах) своего операнда, который может быть выражением или именем в скобках типа. Размер определяется по типу операнда. Результат - это целое число. Если тип операнда - это тип массива переменной длины, операнд оценивается; в противном случае операнд не оценивается и результатом является целочисленная константа.

Таким образом, это тип операнда оператора sizeof, который определяет размер операнда. Таким образом, вы можете использовать взаимозаменяемое выражение или определение определенного типа.

Учтите, что выражение, используемое в операторе sizeof, не оценивается.

Например, вы можете написать

int x = 10;
printf( "%zu\n", sizeof( ++x ) );

и после того, как printf x будет иметь то же значение 10, что и раньше, printf.

Что касается вашего фрагмента кода, кажется, что вы допустили ошибку. Я думаю, вы имеете в виду следующее

typedef int foo;

int main(int argc, char *argv[])
{
    foo exp;
    printf ("%zu\r\n", sizeof( exp ));
    return 0;
}

Конечно, нет необходимости создавать объект типа foo. Вы можете просто написать

    printf ("%zu\r\n", sizeof( foo ));

Что касается вашего исходного кода

typedef int foo;

int main(int argc, char *argv[])
{
    char foo;
    printf ("%u\r\n", sizeof(foo));
    return 0;
}

тогда имя идентификатора foo в области блока функции main скрывает определение типа foo в глобальной области и заявлении

    printf ("%u\r\n", sizeof(foo));

эквивалентно

    printf ("%u\r\n", sizeof( char ));

В C вы не можете ссылаться на глобальное определение foo в этом случае.