我经常看到人们不鼓励其他人使用scanf并说有更好的选择。但是,我最终看到的只是“不要使用scanf”或“这里是正确的格式字符串”,并且从来没有提到“更好的替代方案”的任何示例。
例如,让我们看一下这段代码:
scanf("%c", &c);
Run Code Online (Sandbox Code Playgroud)
这将读取最后一次转换后留在输入流中的空白。通常建议的解决方案是使用:
scanf(" %c", &c);
Run Code Online (Sandbox Code Playgroud)
还是不使用scanf。
由于scanf不好,用于转换scanf通常无需使用即可处理的输入格式(例如整数,浮点数和字符串)的ANSI C选项有哪些scanf?
在我正在阅读的一本书中,写printf了一个单一的参数(没有转换说明符)被弃用了.它建议替代
printf("Hello World!");
Run Code Online (Sandbox Code Playgroud)
同
puts("Hello World!");
Run Code Online (Sandbox Code Playgroud)
要么
printf("%s", "Hello World!");
Run Code Online (Sandbox Code Playgroud)
谁能告诉我为什么printf("Hello World!");是错的?书中写道它包含漏洞.这些漏洞是什么?
情况1:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0.0)<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它编译时没有任何警告和打印inf.好的,C++可以处理除零,(实时查看).
但,
案例2:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0)<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
编译器发出以下警告(请参见实时):
warning: division by zero [-Wdiv-by-zero]
std::cout<<(d/0)<<std::endl;
Run Code Online (Sandbox Code Playgroud)
为什么编译器会在第二种情况下发出警告?
是0 != 0.0吗?
编辑:
#include <iostream>
int main()
{
if(0 == 0.0)
std::cout<<"Same"<<std::endl;
else
std::cout<<"Not same"<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出:
Same
Run Code Online (Sandbox Code Playgroud) 编辑:这个问题并不是一个讨论未定义行为的(de)优点的论坛,但这就是它的变化.在任何情况下,这个关于假设的C编译器没有未定义行为的线程可能对那些认为这是一个重要主题的人更感兴趣.
当然,"未定义行为"的经典伪装例子是"鼻子恶魔" - 物理上是不可能的,无论C和C++标准允许什么.
因为C和C++社区倾向于强调未定义行为的不可预测性以及允许编译器在遇到未定义行为时使程序完全做任何事情的想法,所以我假设标准没有任何限制关于行为,以及未定义的行为.
[C++14: defns.undefined]:[..]允许的未定义行为包括完全忽略不可预测的结果,在转换或程序执行期间以环境特征(有或没有发出诊断消息)的文档方式执行,终止转换或执行(发布诊断信息).[..]
这实际上指定了一小组可能的选项:
我假设在大多数情况下,编译器选择忽略未定义的行为; 例如,当读取未初始化的内存时,可能是插入任何代码以确保一致行为的反优化.我认为陌生人类型的未定义行为(例如" 时间旅行 ")将属于第二类 - 但这需要记录这些行为并"环境特征"(所以我猜鼻腔恶魔只能由地狱计算机?).
我误解了这个定义吗?这些仅仅是可能构成未定义行为的例子,而不是一个全面的选项列表吗?"任何可能发生的事情"的说法仅仅意味着忽视这种情况的意外副作用吗?
编辑:两个小问题澄清:
我正在准备 C++ 入门课程的期末考试。我们的教授给了我们这个问题来练习:
解释为什么代码会产生以下输出:
120 200 16 0
using namespace std;
int main()
{
int x[] = {120, 200, 16};
for (int i = 0; i < 4; i++)
cout << x[i] << " ";
}
Run Code Online (Sandbox Code Playgroud)
该问题的示例答案是:
cout 语句只是循环遍历其下标由 for 循环的增量定义的数组元素。元素大小不是由数组初始化定义的。for 循环定义了数组的大小,该大小恰好超出了初始化元素的数量,因此最后一个元素默认为零。第一个 for 循环打印元素 0 (120),第二个循环打印元素 1 (200),第三个循环打印元素 2 (16),第四个循环打印默认数组值零,因为元素 3 没有任何初始化。现在 i 点超出了条件并且 for 循环终止。
我有点困惑为什么数组之外的最后一个元素总是“默认”为零。为了进行实验,我将问题中的代码粘贴到我的 IDE 中,但将 for 循环更改为for (int i = 0; i < 8; i++). 然后输出更改为120 200 16 0 4196320 0 …
c++ arrays initialization undefined-behavior zero-initialization
我可以'some'在MSVC生成的汇编代码中看到两个文字,但只有一个有clang和gcc.这导致完全不同的代码执行结果.
static const char *A = "some";
static const char *B = "some";
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释这些编译输出之间的差异和相似之处吗?为什么即使没有请求优化,clang/gcc也会优化某些内容?这是某种未定义的行为吗?
我还注意到,如果我将声明更改为下面显示的声明,则clang/gcc/msvc根本不会"some"在汇编代码中留下任何声明.为什么行为不同?
static const char A[] = "some";
static const char B[] = "some";
Run Code Online (Sandbox Code Playgroud) 根据cppreference.com size_t定义的几个标题,即
<cstddef>
<cstdio>
<cstring>
<ctime>
Run Code Online (Sandbox Code Playgroud)
而且,从C++ 11开始,也在
<cstdlib>
<cwchar>
Run Code Online (Sandbox Code Playgroud)
首先,我想知道为什么会这样.这与DRY原则是否相矛盾?但是,我的问题是:
我应该使用上面哪个标题来使用size_t?它有关系吗?
我不太明白为什么我不能除以零例外:
int d = 0;
d /= d;
Run Code Online (Sandbox Code Playgroud)
我本来希望得到除以零的除法运算,但是反而d == 1。
为什么在什么时候不d /= d将被零除d == 0?
c++ division divide-by-zero compiler-optimization undefined-behavior
来自http://en.cppreference.com/w/cpp/string/byte/memcpy:
如果对象不是TriviallyCopyable(例如标量,数组,C兼容结构),则行为未定义.
在我的工作中,我们使用std::memcpy了很长时间来按比例交换不是TriviallyCopyable的对象:
void swapMemory(Entity* ePtr1, Entity* ePtr2)
{
static const int size = sizeof(Entity);
char swapBuffer[size];
memcpy(swapBuffer, ePtr1, size);
memcpy(ePtr1, ePtr2, size);
memcpy(ePtr2, swapBuffer, size);
}
Run Code Online (Sandbox Code Playgroud)
从来没有任何问题.
我理解滥用std::memcpy非TriviallyCopyable对象并导致下游的未定义行为是微不足道的.但是,我的问题是:
std::memcpy当与非TriviallyCopyable对象一起使用时,为什么它本身的行为是未定义的?为什么标准认为有必要指定?
UPDATE
http://en.cppreference.com/w/cpp/string/byte/memcpy的内容已经过修改,以回应这篇文章和帖子的答案.目前的描述说:
如果对象不是TriviallyCopyable(例如标量,数组,C兼容结构),则行为是未定义的,除非程序不依赖于目标对象(不运行
memcpy)的析构函数的效果和生命周期目标对象(已结束,但未开始memcpy)由其他一些方法启动,例如placement-new.
PS
@Cubbi的评论:
@RSahu如果有东西保证UB下游,它会使整个程序不确定.但我同意在这种情况下似乎可以绕过UB并相应地修改cppreference.
我编写了一个简单的多线程程序,如下所示:
static bool finished = false;
int func()
{
size_t i = 0;
while (!finished)
++i;
return i;
}
int main()
{
auto result=std::async(std::launch::async, func);
std::this_thread::sleep_for(std::chrono::seconds(1));
finished=true;
std::cout<<"result ="<<result.get();
std::cout<<"\nmain thread id="<<std::this_thread::get_id()<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它通常表现在调试模式下在Visual Studio中或-O0在GC c和后打印出的结果1秒钟。但是它卡住了,在“ 释放”模式或中不打印任何内容-O1 -O2 -O3。