是否对存在明确定义的同名类型的变量使用 sizeof?

Is using sizeof on a variable where a type of the same name exists well defined?

这是明确定义的行为还是未定义/以其他方式定义了 foo(数据类型或标识符)sizeof 将在其上运行?

typedef int foo;

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

如果定义明确,有没有一种方法可以获得数据类型 foo 的大小,而无需声明该类型的变量仅在其上使用 sizeof

本地 char foo 在其范围内完全隐藏了 typedef int foo。一旦名称 foo 被隐藏,您将无法观察到它。尝试创建第二个 typedef foo foo_t,或重命名一些东西以避免 "collision."

Is this well defined behaviour or is it undefined

这是定义明确的行为。

在你的代码中

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

foo 是明确的。 local or inner foo "shadows" (或隐藏) outer foo。所以,它本质上是

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

引用 C11,章节 §6.2.1,"Scopes of identifiers",(强调我的

If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope.

现在,回答第二部分,

If it is well defined, is there a way I could obtain the size of the datatype foo without declaring a variable of that type to only use sizeof on it?

好吧,inner foo 标识符的范围开始于 它的定义之后(作为 char类型变量)。所以,如果你在定义变量foo之前使用footypedefed类型),你实际上是看到全局定义,直到那时,变量foo不存在于"shadow out"全局标识符foo

by 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 格式说明符来打印结果。

首先按照C标准(6.5.3.4的sizeof和alignof操作符)

2 The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

因此,决定操作数大小的是sizeof运算符的操作数类型。因此,您可以互换使用表达式或某种类型定义。

考虑到未计算 sizeof 运算符中使用的表达式。

例如你可以这样写

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

并且在 printf 之后 x 将与 printf 之前具有相同的值 10。

至于你的代码片段,你似乎犯了一个错误。我认为你的意思如下

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;
}

然后函数 main 的块作用域中的标识符名称 foo 隐藏了全局作用域中的类型定义 foo 和语句

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

等同于

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

在 C 中,您不能在这种情况下引用 foo 的全局定义。

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 的范围尚未打开,因为编译器尚未达到其声明。 (编译器所做的只是在堆栈上为变量分配 space)。

所以foo在那个时候的用法仍然参考全局定义的typedef

当您点击#2 时,变量已声明且变量的生命周期正式开始,这意味着标识符现在正用于不同的实体。它从 foo.

的全局定义中屏蔽了当前块作用域(由函数声明开始)

这是有据可查的行为;网上有C标准草案,但必须购买已发布的标准。草案在第 6.2.1 节中说:

If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope. source

请注意,这不是魔法或任何东西......这都是在编译时完成的。编译器有一个 table 标识符和它们引用的东西,新的范围为这些创建新的 table。碰巧在上面代码的#1 点,编译器还没有用新的 char foo 填充 table(这是由于线性编译)。因此,当它翻译第一个 printf 行时,它会查看所有活动范围以找到标识符 foo,并查看 typedef 并使用它。在第二个 printf,它查看所有活动范围并找到最近使用的标识符 foo 并使用它。