C和C++的用户使用未指定的行为术语。它被定义为C11标准的Annex-J。让我们深入了解该术语。
类型1。函数参数的求值顺序:
当标准提供两种或多种可能性,但不强加要求编译器编写者选择的要求时,则该程序具有未指定的行为。例如,未指定在以下表达式中调用函数fun1和fun2的顺序!
x = fun1() + fun2();
编译器可以选择从左到右或从右到左实现它,从而首先调用fun1或fun2 。该标准不会检测呼叫顺序。
未定义的行为会导致整个程序的意外行为。但是,在未指定行为的情况下,程序会在特定的连接处进行选择,并像原始函数一样照常继续执行。
下面是说明未指定行为的程序:
C
// C program to illustrate Unspecified
// Behaviour
#include
// Declaring x as a global variable
// defining fun1
int x;
// Defining function fun1()
int fun1()
{
x = 5;
return 2;
}
// Defining function fun2()
int fun2()
{
x = 10;
return 3;
}
// Driver Code
int main()
{
// Function call
int sum = fun1() + fun2();
// Printing the value of x
printf("%d", x);
return 0;
}
C++
// C++ program to illustrate Unspecified
// Behaviour
#include
using namespace std;
// Declaring x as a global variable
// defining fun1
int x;
// Defining function fun1()
int fun1()
{
x = 5;
return 2;
}
// Defining function fun2()
int fun2()
{
x = 10;
return 3;
}
// Driver Code
int main()
{
// Function call
int sum = fun1() + fun2();
// Print the value of x
cout << x;
return 0;
}
C++
// C++ program to illustrate the value
// of an out-of-range enum:
#include
using namespace std;
// enum Structure
enum Geeks {
ONE = 1,
TWO = 2,
FOUR = 4,
};
// Driver Code
int main()
{
Geeks s = static_cast(3);
// Printing the value of s.
cout << s;
return 0;
}
C++
// C++ program to illustrate the result
// of reinterpret_cast conversions
#include
using namespace std;
// Driver Code
int main()
{
int x = 42;
char* p = reinterpret_cast(&x);
// Print the value of p
cout << p;
return 0;
}
C++
// C++ program to illustrate that the
// pointers point into different arrays
#include
using namespace std;
// Driver Code
int main()
{
int x;
int y;
// unspecified
const bool b1 = &x < &y;
int a[10];
// Given True
const bool b2 = &a[0] < &a[1];
// unspecified
const bool b3 = &a[0] < &x;
// a + 10 points past the end of array
const bool b4 = (a + 9) < (a + 10);
cout << b1 << "\n"
<< b2
<< "\n"
<< b3 << "\n"
<< b4;
return 0;
}
C++
// C++ program to illustrate that the
// pointers point into the same object,
// but to members with different
// access control
#include
using namespace std;
// Class A
class A {
public:
int x;
int y;
// Function returns true if
// x comes before y
bool f1() { return &x < &y; }
// unspecified
bool f2() { return &x < &z; }
private:
int z;
};
// Driver Code
int main()
{
// Object of class A
A a;
// Function Call
cout << a.f1() << "\n"
<< a.f2();
return 0;
}
10
解释:
最初在上述程序中,x为0。fun1()将x更改为5并返回2。fun2()将x更改为10并返回3。sum的值肯定为5, x是全局变量,并且所有这三个函数都访问相同的全局x。如果函数正在更改x,则它正在更改与其他访问相同的x副本。
这两个函数调用是+(add)运算符符的操作数,如果plus运算符从左到右评估其操作数,则将首先调用fun1来设置x-5的值,然后会调用fun2来设置x的最终值像10。
同样,如果求值顺序是从右到左,则x的最终值将为10,如fun1中的那样将被最后调用,而fun1中的x的值为5。
类型2。超出范围的枚举的值:
如果将作用域枚举转换为太小而无法容纳其值的整数类型,则结果值将不确定。同样,如果将整数转换为枚举,并且该整数的值超出枚举值的范围,则未指定结果值。
例子:
enum Geeks {
ONE = 1,
TWO = 2,
THREE = 3,
};
Geeks s = static_cast(4);
但是,在下一个示例中,行为不是未指定的,因为源值在枚举的范围内,尽管它与所有枚举器都不相等:
enum Geeks {
ONE = 1,
TWO = 2,
FOUR = 4,
};
Geeks s = static_cast(3);
此处s的值为3 ,并且不等于ONE,TWO和FOUR 。
下面是相同的插图:
C++
// C++ program to illustrate the value
// of an out-of-range enum:
#include
using namespace std;
// enum Structure
enum Geeks {
ONE = 1,
TWO = 2,
FOUR = 4,
};
// Driver Code
int main()
{
Geeks s = static_cast(3);
// Printing the value of s.
cout << s;
return 0;
}
3
类型3.从void *值强制转换的静态值:
如果将void *值转换为指向对象类型A *的指针,但未正确地与A对齐,则未指定结果指针值。
例子:
// Suppose that alignof(int) is 4
int x = 20;
void* p1 = &x;
// Perform some pointer arithmetic...
void* p2 = static_cast(p1) + 2;
int* p3 = static_cast(p2);
由于p2不能指向int类型的对象,因此未指定p3的值。它的值不是正确对齐的地址。
类型4。reinterpret_cast转换的结果:
未指定从一种对象指针类型到另一种对象指针或从一种对象引用类型到另一种对象的reinterpret_cast的结果。
例子:
int x = 42;
char* p = reinterpret_cast(&x);
但是,对于大多数编译器,这等效于static_cast
C++
// C++ program to illustrate the result
// of reinterpret_cast conversions
#include
using namespace std;
// Driver Code
int main()
{
int x = 42;
char* p = reinterpret_cast(&x);
// Print the value of p
cout << p;
return 0;
}
*
类型5。一些指针比较的结果:
如果使用<,>,<=或≥比较两个指针,则在以下情况下未指定结果:
- 指针指向不同的数组,即,非数组对象被视为大小为(1)的数组。下面是相同的插图:
C++
// C++ program to illustrate that the // pointers point into different arrays #include
using namespace std; // Driver Code int main() { int x; int y; // unspecified const bool b1 = &x < &y; int a[10]; // Given True const bool b2 = &a[0] < &a[1]; // unspecified const bool b3 = &a[0] < &x; // a + 10 points past the end of array const bool b4 = (a + 9) < (a + 10); cout << b1 << "\n" << b2 << "\n" << b3 << "\n" << b4; return 0; } 输出:1 1 0 1
- 指针指向同一对象,但指向具有不同访问控制的成员。下面是相同的实现:
C++
// C++ program to illustrate that the // pointers point into the same object, // but to members with different // access control #include
using namespace std; // Class A class A { public: int x; int y; // Function returns true if // x comes before y bool f1() { return &x < &y; } // unspecified bool f2() { return &x < &z; } private: int z; }; // Driver Code int main() { // Object of class A A a; // Function Call cout << a.f1() << "\n" << a.f2(); return 0; } 输出:1 1
C语言有许多未指定的行为,其中一些是:
- 静态初始化的方式和时间。
- 如果main的返回类型与int不兼容,则终止状态返回到托管环境。
- 在对结构或联合中的值进行排序时,填充字节的值。
- 除了为函数调用(),&&,||,?:和逗号运算符指定的子表达式的计算顺序和副作用发生的顺序。
- 函数参数在堆栈框架上的存储布局。
- 值超出范围时的舍入结果。