这是一个定义良好的行为,还是未定义/以其他方式定义foo
(数据类型或标识符)sizeof
将在何处运行?
typedef int foo;
int main(int argc, char *argv[])
{
char foo;
printf ("%u\r\n", sizeof(foo));
return 0;
}
如果它定义得很好,有没有办法可以获得数据类型foo
的大小而不声明该类型的变量只在其上使用sizeof
?
答案 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
的定义之前使用typedef
(foo
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的全局定义。