是否在一个变量上使用了sizeof,其中同名的类型存在明确定义?

时间:2015-07-15 07:50:56

标签: c sizeof undefined-behavior

这是一个定义良好的行为,还是未定义/以其他方式定义foo(数据类型或标识符)sizeof将在何处运行?

typedef int foo;

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

如果它定义得很好,有没有办法可以获得数据类型foo的大小而不声明该类型的变量只在其上使用sizeof

4 个答案:

答案 0 :(得分:11)

  

这是明确定义的行为还是未定义

这是明确定义的行为。

在您的代码中

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

foo毫不含糊。 本地内部 foo“阴影”(或隐藏)外部 foo。所以,它基本上是

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

引用C11,章节§6.2.1,“标识符的范围”,(强调我的

  

如果标识符指定同名的两个不同实体   空间,范围可能重叠。如果是这样,一个实体(内部范围)的范围将严格地在另一个实体(外部范围)的范围之前结束。 在内部范围内,标识符指定在内部范围内声明的实体;在外部范围内声明的实体在内部范围内隐藏(并且不可见)。

现在,回答第二部分,

  

如果它定义得很好,有没有办法可以获得数据类型foo的大小而不声明该类型的变量只能在其上使用sizeof

嗯,内部 foo标识符的范围在定义后(作为char类型变量)启动。因此,如果您在变量foo的定义之前使用typedeffoo ed类型),那么您实际上将看到全局定义,直到那时,变量foo不存在以“遮蔽”全局标识符foo

正如other answer 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格式说明符来打印结果。

答案 1 :(得分:11)

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节中说:

  

如果标识符指定同名的两个不同实体   空间,范围可能重叠。如果是这样,一个实体的范围(   内部范围)将严格地在另一个实体的范围之前结束   (外部范围)。在内部范围内,标识符指定   在内部范围内声明的实体;在该中宣布的实体   外部范围在内部范围内隐藏(并且不可见)。 source

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

答案 2 :(得分:6)

本地char foo在其范围内完全隐藏typedef int foo。隐藏后,您无法观察名称foo。尝试创建第二个typedef foo foo_t,或重命名一些内容以避免&#34;碰撞。&#34;

答案 3 :(得分:2)

首先根据C标准(6.5.3.4 sizeof和alignof运算符)

  

2 sizeof运算符产生其操作数的大小(以字节为单位)   可以是表达式或类型的带括号的名称。 大小   是根据操作数的类型确定的。结果是   整数。如果操作数的类型是可变长度数组类型,   操作数被评估;否则,不评估操作数   结果是一个整数常量。

因此,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的全局定义。