为什么函数不调用左值

时间:2019-04-07 09:49:35

标签: c language-lawyer lvalue function-call

这应该很明显,但是在标准中我无法明确引用该函数调用是(不是)左值。有一些相关的question,但它与C ++有关,因此没有提供参考。

6.5.2.2(p5) Function calls中,我唯一能找到的就是

  

如果表示被调用函数的表达式具有指向的类型指针   函数返回对象类型,函数调用表达式具有   与该对象类型相同的类型,其值确定为   在6.8.6.4中指定

6.3.2.1(p1)指出

  

左值是一个表达式(对象类型为其他   thanvoid)可能指定一个对象

因此,我尝试查找函数调用是否指定了对象。如果函数调用结果具有存储持续时间和生存期,则在标准中未指定。由于任何对象都有存储期限和生存期,因此我得出结论,任何函数调用表达式都不会指定对象,因此也不是左值。

但这似乎令人困惑和复杂。我特别找到了一个示例6.5.2.3(p7)

  

示例1如果f是返回结构或联合的函数,而x是   该结构或联合的成员,f().x is有效的后缀   表达式,但不是左值。

通过此示例判断f()是否为左值f().x也为左值。但是例子很丰富,这让我感到困惑。

2 个答案:

答案 0 :(得分:2)

函数的返回值不是标准定义术语的左值,但是在某些上下文中它会提供一个的语义。

给出任何结构类型:

struct foo {...whatever... };

一个人可以编写一个函数,其返回值可以用需要struct foo类型的左值的方式使用(最典型的是将这样一个左值的地址传递给另一个函数)。

struct wrapped_foo {struct foo it[1];} wrap_foo(foo it)
{
  struct wrapped_foo ret = {it};
  return ret;
}
extern void do_something(int,int,int,struct foo const *p,int,int,int);
void demo_of_passing_address_of_a_foo(struct foo x)
{
  do_something(1,2,3,&(wrap_foo(x).it[0]),4,5,6);
}

请注意,虽然wrap_foo(x)的返回值不是左值,但wrap_foo(x).it[0]的返回值是1,可以使用其地址。由此确定的对象的生存期将通过对封闭表达式的评估(即对do_something的调用)进行扩展。如果将下标运算符本身定义为不会导致数组到指针分解而只是产生元素类型值的运算符,则仅当数组为1时才是左值,则{{1 }}不会是一个左值,并且生命周期的问题也将不相关。

虽然传递临时地址的功能很有用,但它要求编译器在将任何参数堆叠到wrap_foo(x).it[0]的返回值之前给上述wrap_foo返回值分配空间,从而增加了编译器的复杂性。外部函数调用。如果需要这样的编译器复杂性,那么也可以通过允许顶级参数表达式对任意类型的值使用&(产生指向对象的const限定指针,该对象的生存期为是外部封闭表达式的表达式。

答案 1 :(得分:1)

它不是左值,因为它在您引用的段落中被描述为“值”。该标准明确提及表达式何时具有作为左值的属性。例如:

  

6.5.3.2地址和间接操作符(强调我的意思)

     

4一元*运算符表示间接。如果操作数指向a   函数,结果是一个函数指示符; 如果它指向一个   对象,结果是一个左值指定对象。如果   操作数的类型为“要键入的指针”,结果的类型为“ type”。如果   无效的值已分配给指针,   一元*运算符未定义。

关于访问工会或会员。该标准不需要expr.id中的后缀表达式为左值。反之。整个成员访问权限具有与后缀表达式相同的值类别:

  

6.5.2.3结构和工会成员(强调我的观点)

     

3后缀表达式,后跟.运算符和标识符   指定结构或联合对象的成员。 值为   命名成员的值,如果第一个表达式为   左值。如果第一个表达式具有限定类型,则结果   具有指定成员类型的标准版本。

因此在您引用的示例中,f().x是一个值,而不是左值,因为f()本身不是左值。