首页 > 试题广场 >

int a=5,则 ++(a++)的值是?

[单选题]
int a=5,则 ++(a++)的值是?
  • 5
  • 6
  • 7
  • 编译出错
++(a++); 
error: lvalue required as increment operand

发表于 2016-08-24 17:31:03 回复(0)
++ 是一目运算符,自增运算,它只能用于一个变量,即变量值自增1, 不能用于表达式。
++(a++) 里,小括号优先。
(a++) 是 表达式,按运算规则,不能对 表达式 作 自增运算.

-- 摘自百度知道

发表于 2016-08-18 16:41:42 回复(9)
a++返回一个值也就是5
++操作符只能作用于变量,而不能是一个数字 你可以试试 ++5
发表于 2016-09-07 18:59:28 回复(7)
++ 是一目运算符,自增运算,它只能用于一个变量.
表达式归根结底是一个定值,所以不能用于表达式-_-

发表于 2017-05-03 09:45:26 回复(0)
感觉这种题目没有任何意义,谁会这样去写?纯粹是牛角尖
发表于 2016-10-03 11:29:51 回复(2)
先说结论: 因为a++返回的是右值(rvalue),而我们不能对一个右值进行自增操作。所以++(a++)会报错。
后置a++相当于做了三件事情:
1. tmp = a;
2. ++a
3. return tmp;
事实上,如果这里a是一个对象,而非一个基本类型数据的话,我们重载其后置自增运算符就分成上述三个步骤(参考《C++Primer 第五版》p503 “区分前置和后置运算符”小节)

再简单的说说什么是右值吧,所谓右值,可以理解为是即将结束生命周期的对象。
在这里,(a++)返回的是a在+1之前的值,这个值是一个临时的对象(在上面的对于a++的行为解释中对应于返回值tmp)。这个临时的对象在(a++)这条语句结束后,马上就结束了生命周期,即右值

关于左值,右值,以及C++11中引入的新的分类在StackOverFlow上有精彩的论述,感兴趣的同学可以自行搜索或者点击下面的链接。
http://stackoverflow.com/questions/3601602/what-are-rvalues-lvalues-xvalues-glvalues-and-prvalues
编辑于 2017-05-03 22:30:40 回复(3)
其他答案的意思是有了的
但是回答用词用意还未到点子上
首先
我们需要理解左右值
表达式分为左值表达式和右值表达式
简单来说 左值可以置于赋值运算符左边或右边 右值则只能置于赋值运算符右边
也就是说左值是可被赋值的 右值则不可以
左值代表着一个较长的生命周期的对象 而右值往往只是一个瞬时值 临时量 马上要被销毁
所以左值一般包含了对象的引用
从汇编的角度理解
左值是那些在内存中的对象 而右值往往只是存储于一个寄存器中的值
并不具备内存地址 所以无法被引用
可以说右值的地址是未定义的(undefined) 而左值的地址是明确的(well-defined)
有了内存地址 就可以根据指针来修改内存中的值
常见的需要左值操作数的运算有:赋值、取地址、前后置递增减
常见的可返回左值的运算有:赋值、解引用、下标、前置递增减(后置返回右值)

有了上面的前情提要 我们来看看各楼答主的发言
小菜鸟上校回答中所说的“表达式”指的事实上是“右值表达式”
其所谓的“变量”指的事实上是“左值表达式”
牛客4614492号所说的“变量”也是“左值表达式”
其所谓的“数字”就是指“右值表达式”

我们用C++写一个重载了前置后置递增运算符的类模板
看看相关运算符的内部实现演示:

template<typename T>
struct Incrementable {
    //Pre-increment
    Incrementable& operator++() {
        selfIncrement();
        return *this;
    }
    
    //Post-increment
    Incrementable operator++(int) {
        Incrementable o = *this;
        selfIncrement();
        return o;
    }
private:
    void selfIncrement() {
        //TODO value increment
        //...
    }
    //Actual internal representation
    T value;
};


C++规定:调用一个返回引用的函数得到左值,其他返回类型得到右值
由示例代码可知
前置递增运算符返回当前对象的引用,是一个左值
而后置递增运算符返回对象的拷贝对象,是一个右值
所以 题目中++(a++)这个复合表达式中
a++返回一个右值拷贝对象
我们知道右值是临时变量
内存地址未定义
是不能赋值的
operator++()会修改自身的value
所以不能调用他的operator++()函数

当然
以上是以面向对象范式做的形式化推演
用来帮助情景代入和理解
让我们深入汇编来看看纯有理数的前置和后置递增的本质
考虑如下C代码
int main() {
    int
    i = 0,
    j = ++i,
    k = i++;
}
使用编译器将其加工为中间语言——汇编代码
此处汇编语言采用的CPU架构是X86_64
语法使用AT&T
每一句都做了详细注释
望能为大家理解汇编减轻负担
由于代码块高亮功能对这段汇编支持不佳
所以直接贴为正文 请见谅

# rbp是栈底指针寄存器

# ecx/edx是通用寄存器

# i,j,k为整型变量,占4字节内存

# i的内存地址是(rbp-4

# j的内存地址为(rbp-8)

# k的内存地址为(rbp-12)



## 以下内容将完成"i=0"

# i初始化为0

movl $0, -4(%rbp)



## 以下内容将完成"j=++i"

# 将(rbp-4)的内存单元i的值存入ecx寄存器

movl -4(%rbp), %ecx

# ecx寄存器中存储的值递增1后存回ecx寄存器

addl $1, %ecx

# ecx寄存器中的值存入i对象的内存单元

movl %ecx, -4(%rbp)

# ecx寄存器中的值存入j对象的内存单元

movl %ecx, -8(%rbp)



## 以下内容将完成"k=i++"

# 将(rbp-4)的内存单元i的值存入ecx寄存器

movl -4(%rbp), %ecx

# ecx寄存器中的值存入edx寄存器

movl %ecx, %edx

# edx寄存器中存储的值递增1后存回edx寄存器

addl $1, %edx

# edx寄存器中的值存入i对象的内存单元

movl %edx, -4(%rbp)

# ecx寄存器中的值存入k对象的内存单元

movl %ecx, -12(%rbp)



## 以下为编译器隐式添加的逻辑

# main函数的主调函数的栈底指针出栈

popq %rbp

# main函数返回

retq


这段汇编饶有趣味地揭露了前后递增运算符的真面目
可以看到
前置递增运算符只需要一枚寄存器ecx
因为前置递增只需要用到递增后的值
所以ecx+1之后分别赋予了i和j
而后置递增运算符需要两枚寄存器ecx和edx
因为递增前后的值都需要用到
两枚寄存器分别获取i的原值后
只对edx+=1
并且立刻把edx中的结果写回i的内存单元
到了这个时候内存中的i的原值已经荡然无存(i已被重写)
只剩下ecx这枚寄存器保存着 最后将ecx中的旧值写入k
可见 这就是右值表达式的本质:
修改源内存对象后,存在一个中间状态,在这个状态下右值是一个仅由寄存器保存的值
左值则不存在这样的状态
因为修改源变量后,目标值(递增后的i)就已经写回内存了

用该汇编码反向验证我们之前写的C++模板
可以看出代码逻辑是符合内部前后递增时序的:
先完成递增 再返回左右值

编辑于 2017-09-25 16:40:58 回复(1)
编译出错:
++(a++)先计算的是括号里的(a++),返回的结果是一个表达式,其值是5,不能对表达式进行赋值
error: expression is not assignable        
printf("%d\n", ++(a++));
               ^ ~~~~~
1 error generated.


发表于 2016-08-18 20:13:22 回复(0)
不能对常量 进行自增运算
发表于 2016-08-18 23:46:03 回复(0)
 :++(++a),(++a)++这样是可以的,而++(a++)却是不可以的;
原因:我们在模拟实现一些类的时候,有对++进行重载,
前置++:++a,返回的引用,所以对++a进行链接式的使用是可以的,
但后置++:a++我们是先对a加一个1;然后返回一个临时变量,当我们在++时,就是对临时变量的操作,临时变量具有常属性,所以就会出错
发表于 2017-08-06 08:52:18 回复(1)
++(a++)
(a++)运算后产生两个值,一个是a本身自加1,另外返回一个临时的值(a未自加的值)
这个临时的值是常量,不能对它的值进行操作
发表于 2018-08-05 08:57:06 回复(0)
++操作符只能作用于变量
发表于 2022-03-12 15:46:54 回复(0)
前置++返回的是左值,后置++返回的是右值。而++是作用于左值的。
发表于 2021-08-01 13:37:42 回复(0)
a++是一个表达式,运算出错是因为这是一个临时常量5,不能对一个常量做自增运算。如果是++++a就可以正常运行,因为++a返回的就是增加1后的a本身,这是一个变量可以继续运算 。
发表于 2017-02-08 11:07:21 回复(0)
因为a++是右值,而前置版本的++没有定义移动语义。
编辑于 2016-09-01 10:58:48 回复(0)
a++返回的是右值。
发表于 2016-08-20 10:42:30 回复(0)
a++ 和 ++a都隐含着a = a + 1只不过是执行顺序的差别而已,返回值为一个数,++a返回加之后的数,a++返回加之前的数。
发表于 2020-07-26 10:11:06 回复(0)
本题考查的是左值与右值的区别。++的对象必须是一个左值,不能是右值。
左值是有内存实体对应的,++需要先加然后再写到内存,右值没有内存实体可以写。


编辑于 2017-10-05 08:48:32 回复(0)
我有一个疑问,为什么不加括号也会报错?

++a++,这个是先执行后置++吗?
编辑于 2023-12-19 22:22:30 回复(0)
不能对常量进行自增运算
编辑于 2023-12-07 13:01:30 回复(0)