我们先来看下面的代码,想一想它的输出
#include <iostream>
using namespace std;
int g = 0;
template<typename T>
int foo()
{
int value = ++g;
return value;
}
int main()
{
//Qs1
int a = 1;
//cout << a << " " << ++a << " " << a++ << endl;
cout << a << " " << a++ << " " << ++a << endl;
//Qs2
int one = 1;
int *b = &one;
cout << *b << " " << ++(*b) << " " << (*b)++ << endl;
//cout << *b << " " << (*b)++ << " " << ++(*b) << endl;
//Qs3
cout << foo<int>() << " " << foo<char>() << " " << foo<float>() << " " << foo<double>() << endl;
return 0;
}
输出结果:
ubuntu18.04 下的结果:
root@lui-pc:~/CLionProjects/test01# g++ -std=c++11 main.cpp
root@lui-pc:~/CLionProjects/test01# ./a.out
1 1 3
root@lui-pc:~/CLionProjects/test01# cat main.cpp
#include <iostream>
using namespace std;
int main()
{
int a = 1;
cout << a << " " << a++ << " " << ++a << endl;
}
我们注意这条语句:cout << a << " " << ++a << " " << a++ << endl;(求值顺序)
呈现的规律(注意项):
- 计算顺序:自右至左
- 输出顺序:自左至右
- std::cout<< a++:输出的是a++的值,而不是a的值
- 一条表达式语句中对同一个变量使用多次副作用语法(题目中的前缀自增和后缀自增)是未定义行为,输出什么都不算错。关于表达式的求值顺序请参考:求值顺序
原因解释:
- 很多的编译器在函数调用的时候,会在内存中分配一个函数调用栈,函数的参数总是从右往左的顺序计算并进栈。
- 关于更多未定义行为的错误或者未定义行为的危险查看John Regehr的博客
John在文章中总结了要遵循的规则:
- 启用并注意编译器警告,最好使用多个编译器
- 使用静态分析仪(如Clang,Coverity等)可以获得更多警告
- 使用编译器支持的动态检查; 例如,gcc的-ftrapv标志生成用于捕获有符号整数溢出的代码
- 使用像Valgrind这样的工具来获得额外的动态检查
- 当函数是上面分类的“类型2”时,记录它们的前置条件和后置条件
- 使用断言来验证函数的前置条件是否实际上是后置条件
- 特别是在C ++中,使用高质量的数据结构库