假设我有两个指针:
int *a = something;
int *b = something;
Run Code Online (Sandbox Code Playgroud)
如果我想比较它们并看它们是否指向同一个地方(a == b)工作?
我一直在愚弄一些代码,看到一些我不理解"为什么"的东西.
int i = 6;
int j;
int *ptr = &i;
int *ptr1 = &j
j = i++;
//now j == 6 and i == 7. Straightforward.
Run Code Online (Sandbox Code Playgroud)
如果您将操作员放在等号的左侧怎么办?
++ptr = ptr1;
Run Code Online (Sandbox Code Playgroud)
相当于
(ptr = ptr + 1) = ptr1;
Run Code Online (Sandbox Code Playgroud)
而
ptr++ = ptr1;
Run Code Online (Sandbox Code Playgroud)
相当于
ptr = ptr + 1 = ptr1;
Run Code Online (Sandbox Code Playgroud)
后缀运行编译错误,我得到它.你在赋值运算符的左侧有一个常量"ptr + 1".很公平.
一个编译的前缀和C++中的WORKS.是的,我理解它很乱,你正在处理未分配的内存,但它可以工作和编译.在C中,这不会编译,返回与后缀"左值作为赋值的左操作数所需的左值"相同的错误.无论如何编写,使用两个"="运算符或"++ ptr"语法扩展,都会发生这种情况.
C如何处理这样的赋值和C++如何处理它有什么区别?
我正在研究核心常量表达式*中允许的内容,这在C++标准草案的5.19 常量表达式第2段中有所描述:
条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2),但是未评估的逻辑AND(5.14),逻辑OR(5.15)和条件(5.16)操作的子表达式不考虑[注意:重载的运算符调用函数.-end note]:
并列出随后的子弹中的排除项并包括(强调我的):
- 具有未定义行为的操作 [注意:包括,例如,有符号整数溢出(第5条),某些指针算术(5.7),除零(5.6)或某些移位操作(5.8) - 结束注释];
嗯?为什么常量表达式需要此子句来涵盖未定义的行为?常量表达式是否有一些特殊的东西需要未定义的行为才能在排除中进行特殊划分?
拥有这个条款是否给了我们没有它的任何优势或工具?
作为参考,这看起来像广义常量表达式提案的最新修订版.
UndefinedBehaviorSanitizer(ubsan)是一个快速未定义的行为检测器,已添加并可通过-fsanitize = undefined启用.将检测各种计算以在运行时检测未定义的行为.UndefinedBehaviorSanitizer目前可用于C和C++语言.
我看了这个问题(一个检测未定义行为的C++实现?)但它似乎已经过时了.
这个链接(http://gcc.gnu.org/ml/gcc-patches/2013-06/msg00264.html)上有一些信息,但已有几个月了.
这是尝试将未定义的行为消毒剂添加到GCC.请注意,它是非常alpha版本; 到目前为止,它没有做那么多,目前它应该处理除零情况,INT_MIN/-1和各种移位情况(移位负值,当第二个操作数> = = TYPE_PRECISION(first_operand)时移位)这样的.(在整数类型上,到目前为止.)
从我读过它被移植到gcc从LLVM.
我试过它,(5 / 0)唯一的区别似乎是这个输出:
main.cpp:5:19: runtime error: division by zero
Run Code Online (Sandbox Code Playgroud)
有没有人有关于它的更多信息或它有什么功能?
我很抱歉,如果这是一个超级简单的概念,但我发现很难获得正确的心态,以便正确使用由提供的消毒剂clang.
float foo(float f) { return (f / 0); }
Run Code Online (Sandbox Code Playgroud)
我编译这个小片段
clang++ -fsanitize=float-divide-by-zero -std=c++11 -stdlib=libc++ -c source.cpp -o osan
Run Code Online (Sandbox Code Playgroud)
而且我还在不使用消毒剂的情况下编译了我的对象的"正常"版本
clang++ -std=c++11 -stdlib=libc++ -c source.cpp -o onorm
Run Code Online (Sandbox Code Playgroud)
我期待一些详细的输出,或者来自控制台的一些错误,但是在检查文件时nm我只发现了1个差异
nm o* --demangle
onorm:
0000000000000000 T foo(float)
osan:
U __ubsan_handle_divrem_overflow
0000000000000000 T foo(float)
Run Code Online (Sandbox Code Playgroud)
所以在清理版本中有一个未定义的符号,其名称类似于我在编译时使用的清洁剂; 但是一切都是"沉默的",而且从铿锵的前端根本没有输出.
我应该如何使用消毒剂以及什么是正确的工作流程?那个未定义的符号有什么意义?
我想CXXFLAG在我的构建系统中添加一个强制整个代码库的定义.因此,编译器应拒绝以静态方式展示未定义行为的每一段代码.
例如reinterpret_cast<A*>(someIntPtr)->aMember,没有任何运行时上下文未定义(a),而int i = bar(); i /= i;可能导致未定义的行为(b)取决于运行时评估bar()(可能返回零).
我只希望抓住(a)案件,而不一定是(b)案件.
C++ 不定义某些行为(例如更好的错误检查)的原因是什么?为什么不抛出一些错误并停止呢?
一些伪代码例如:
if (p == NULL && op == deref){
return "Invalid operation"
}
Run Code Online (Sandbox Code Playgroud)
对于整数溢出:
if(size > capacity){
return "Overflow"
}
Run Code Online (Sandbox Code Playgroud)
我知道这些都是非常简单的例子。但我很确定大多数 UB 都能被编译器捕获。那么为什么不实施它们呢?因为它确实很耗时并且不进行错误检查更快?一些 UB 可以通过单个 if 语句捕获。那么也许速度并不是唯一的问题?
我用g ++版本4.8.2编译并运行以下C++代码:
vector<int> ivec{0,1,2};
int& iref = ivec[1];
for (int i=3;i<100;++i)
ivec.push_back(i);
iref = 10;
cerr<<"After Error"<<'\n';
return 0;
Run Code Online (Sandbox Code Playgroud)
程序将按预期在行中崩溃iref = 10;,因为引用无效.但字符串"After Error"被打印出来了.为什么?
这个问题的答案对我来说很重要,因为大多数时候我使用cout或cerr找到导致运行时错误的行.
在下面的代码中,为什么指针保持的地址x在delete?之后改变?据我所知,delete调用应该从堆释放已分配的内存,但它不应该更改指针地址.
using namespace std;
#include <iostream>
#include <cstdlib>
int main()
{
int* x = new int;
*x = 2;
cout << x << endl << *x << endl ;
delete x;
cout << x << endl;
system("Pause");
return 0;
}
OUTPUT:
01103ED8
2
00008123
Run Code Online (Sandbox Code Playgroud)
观察:我使用的是Visual Studio 2013和Windows 8.据说这在其他编译器中不起作用.此外,我理解这是不好的做法,我应该在删除指针后重新指定NULL,我只是想了解是什么驱使这种奇怪的行为.
在编译阶段扩展UB的原因是什么?遇到UB代码时,不是编译和链接二进制文件,而是让二进制文件服从UB?(如果不可能生成二进制文件,那么只需打印一条关于它的错误消息.)
毕竟,我们期望从编译器的最确切的报告汇编即使在源代码中包含UB-代码(几乎每件的源代码可能包含一些UB-代码).
您能给这样的UB-代码一个具体的例子,这确实更有道理,让编译器表现出UB比让生成的二进制表现出UB?
这个问题源于这一点:"未定义的行为"是否扩展到编译时?