【问题标题】:Does C correctly handle sizeof(...) and sizeof ... in this case?在这种情况下,C 是否正确处理 sizeof(...) 和 sizeof ...?
【发布时间】:2018-04-28 02:45:50
【问题描述】:

在下面的代码中,testtest2 函数是等价的吗?

typedef int rofl;

void test(void) {
    rofl * rofl = malloc(sizeof(rofl)); // Is the final rofl here the TYPE?
}

void test2(void) {
    rofl * rofl = malloc(sizeof *rofl); // Is the final rofl here the VARIABLE?
}

换句话说:

  1. 由于括号,sizeof(rofl) 中的rofl 是否正确选择了rofl 类型
  2. 由于缺少括号,sizeof *rofl 中的rofl 是否正确选择了rofl 变量

注意:这是一个看起来很傻的示例,但实际上可能会发生您的类型名称与变量名称相同的情况。因此问题。

【问题讨论】:

  • 为什么人们投了反对票?这是一个完全合理的问题,鉴于 C 确实有多个名称空间,因此某些名称可以有多个可见的含义,因此您无法明确回答这个问题,除非您明确知道 typedef 名称和对象名称共享相同的命名空间,可以相互隐藏。
  • 简短的回答是否定的,括号没有区别。 sizeof 是一个运算符,而不是一个函数,所以括号只是多余的。
  • 顺便说一句,要明确回答您的问题 2,是的,缺少括号会迫使 sizeof 操作数只有一个选项:它必须是某种“一元表达式”,所以它不能是 typedef 名称。关于问题 1,括号不会导致编译器需要 typedef 名称。括号里的东西,如果它只是一个标识符,在那个时候只能是一个typedef、函数或对象的名称,所以编译器只会找到一个解析;它不需要选择。
  • @EricPostpischil 你自相矛盾。括号要么有区别,要么没有。 unary-expression 可以是一个标识符
  • @EJP:没有矛盾。当括号存在时,有两个句法选项是可能的。当括号不存在时,只有一个句法选项。使用括号中的标识符,sizeof (foo) 可以匹配 C 标准第 6.5.3 条中显示的“sizeofunary-expression”或“sizeof ( type-name )” 在同一子句中。然后根据标识符的含义而不是本地语法来确定匹配。没有括号,只有第一个是可能的。

标签: c sizeof unary-operator


【解决方案1】:

在这两种情况下,最后一个rofl 是变量名。变量名一出现就在范围内;对于当前范围的其余部分,普通上下文中的标识符(*)始终表示变量名。

sizeof 运算符不会为名称查找引入任何特殊情况。事实上,没有任何语言结构会使用标识符的隐藏含义。

在实践中,最好不要对类型和变量名使用相同的标识符。


(*) 标识符有三种特殊的上下文:标签名称、结构标记和结构成员。但在所有其他上下文中,所有标识符共享一个公共名称空间:类型名称、变量名称、函数名称等没有不同的标识符名称空间。

这是一个人为的例子:

typedef int A;      // "A" declared as ordinary identifier, meaning a type name

struct A { A A; };  // "A" declared as struct tag and member name -- OK as these are three different name spaces. Member type is "int"

A main()            // int main() - ordinary context
{
    struct A A();   // "A" declared as ordinary identifier, meaning a function name; hides line 1's A
    // A C;         // Would be error: ordinary A is a function now, not a typedef for int
    struct A B;     // OK, struct tags have separate name space
    A:+A().A;       // OK, labels and struct members have separate name space, calls function
    goto A;         // OK, label name space
}

【讨论】:

  • 您应该明确说明 rolf 作为对象的标识符(名称)隐藏了 rolf 作为 typedef 名称的标识符。这不清楚,因为 C 确实有多个名称空间。例如,将其用作标签不会隐藏对象的用途。人们可能认为“sizeof(rofl)”可以解析为 typedef 名称的大小或对象的大小;它们都可以看到它们位于不同的名称空间中。编译器如何选择?但是如果我们解释 typedef 名称是因为隐藏在同一个名称空间中而不可见,那么它是如何解析的就一目了然了。
  • 顺便说一句,C 标准使用“隐藏”而不是“阴影”。
  • @EricPostpischil 回复。您的第一条评论,我的文字“对于当前范围的剩余部分,该标识符始终表示变量名”确实已经传达了这一点吗?我什至试图在第二段中强调这一点,说从不使用隐藏的含义。
  • 好吧,“那个标识符总是意味着变量名”的说法实际上并不正确。标识符可以同时是对象的名称、标签的名称、多个结构或联合成员的名称,以及结构、联合或枚举的标记。这些可能性由句法上下文区分。所以这个答案并没有真正解释 C 名称空间在问题的上下文中是如何工作的——OP 的问题本质上是问sizeof 的两种语法形式是否区分类型名称和对象名称......
  • ... 所以我认为澄清这一点可能会有所帮助,尽管 C 有多个标识符名称空间,但 typedef 名称和对象名称确实共享一个名称空间(以及函数名称),因此可以隐藏另一个,不像标签。
【解决方案2】:

在此声明中

rofl * rofl = malloc(sizeof(rofl)); // Is the final rofl here the TYPE?

变量名称rofl 隐藏了typedef 名称rofl。因此在 sizeof 运算符中使用了指针 rofl,即表达式的类型为 int *

此声明同样有效

rofl * rofl = malloc(sizeof *rofl); 

除了使用带有取消引用指针rofl 的表达式,其类型定义名称为rofl,即int 类型。

似乎是由于这个 C 语法定义引起的混乱

sizeof unary-expression
sizeof ( type-name )

但是,unary-expression 可以是一个主表达式,它是一个用括号括起来的表达式。

来自 C 标准(6.5.1 主要表达式)

primary-expression:
    ( expression )
    //...

例如,如果x 是一个变量的名称,那么你可以写任何一个

sizeof x 

sizeof( x )

为清楚起见,您可以在 sizeof 运算符和主表达式之间插入空格

sizeof    ( x )
operator  primary expression

为了比较,考虑另一个一元运算符:一元加号。你可以写例如

+ x

+ ( x )

现在只需将一元加号替换为另一个一元运算符sizeof

对于隐藏名称,结构、联合和枚举的问题是可以解决的,因为它们的名称包含标签的关键字。

例如

typedef struct rofl { int x; } rofl;

void test(void) {
    rofl * rofl = malloc(sizeof( struct rofl));
}

在这个带有 sizeof 运算符的函数中,使用了类型名称struct rofl

在这个函数中

typedef struct rofl { int x; } rofl;

void test(void) {
    rofl * rofl = malloc(sizeof( rofl));
}

使用 sizeof 运算符,使用带有变量 rofl 的主表达式,其类型为 struct rofl *

【讨论】:

    【解决方案3】:

    不涉及“挑选”或“选择”。在这两种情况下,每个sizeof 调用中引用的rofl 都是变量,而不是类型,因为范围规则。该变量是在内部范围内声明的,因此会覆盖类型名称。 sizeof 运算符的参数括号是无关紧要的。

    祝你好运。

    【讨论】:

      猜你喜欢
      • 2020-08-20
      • 2020-04-05
      • 1970-01-01
      • 2013-03-21
      • 1970-01-01
      • 2013-09-19
      • 2011-11-02
      • 2020-02-18
      • 1970-01-01
      相关资源
      最近更新 更多