在C++ 03中,表达式是rvalue或lvalue.
在C++ 11中,表达式可以是:
两类已成为五大类.
在您看来,您遇到的最令人惊讶,奇怪,奇怪或真正的"WTF"语言功能是什么?
每个答案只能有一个功能.
C和C++中未定义,未指定和实现定义的行为有什么区别?
c c++ undefined-behavior unspecified-behavior implementation-defined-behavior
是否有比x >= start && x <= end
C或C++ 更快的方法来测试整数是否在两个整数之间?
更新:我的特定平台是iOS.这是盒子模糊功能的一部分,它将像素限制为给定方块中的圆圈.
更新:在尝试接受的答案后,我在一行代码上以正常x >= start && x <= end
方式执行了一个数量级的加速.
更新:这是来自XCode的汇编程序的after和before代码:
新方法
// diff = (end - start) + 1
#define POINT_IN_RANGE_AND_INCREMENT(p, range) ((p++ - range.start) < range.diff)
Ltmp1313:
ldr r0, [sp, #176] @ 4-byte Reload
ldr r1, [sp, #164] @ 4-byte Reload
ldr r0, [r0]
ldr r1, [r1]
sub.w r0, r9, r0
cmp r0, r1
blo LBB44_30
Run Code Online (Sandbox Code Playgroud)
老路
#define POINT_IN_RANGE_AND_INCREMENT(p, range) (p <= range.end …
Run Code Online (Sandbox Code Playgroud) 我正在阅读有关评估违规的顺序,他们给出了一个令我困惑的例子.
1)如果标量对象的副作用相对于同一标量对象的另一个副作用未按顺序排列,则行为未定义.
Run Code Online (Sandbox Code Playgroud)// snip f(i = -1, i = -1); // undefined behavior
在这种情况下,i
是一个标量对象,显然意味着
算术类型(3.9.1),枚举类型,指针类型,指向成员类型的指针(3.9.2),std :: nullptr_t和这些类型的cv限定版本(3.9.3)统称为标量类型.
在这种情况下,我不明白该陈述是如何含糊不清的.在我看来,无论第一个或第二个参数是否首先被评估,i
最终都是-1
,并且两个参数也是-1
.
有人可以澄清一下吗?
我非常感谢所有的讨论.到目前为止,我非常喜欢@ harmic的答案,因为它暴露了定义这个陈述的陷阱和错综复杂,尽管它看起来有多么简单.@ acheong87指出了使用引用时出现的一些问题,但我认为这与这个问题的未测序副作用方面是正交的.
由于这个问题得到了很多关注,我将总结一下主要观点/答案.首先,请允许我进行一个小小的题外话,指出"为什么"可以具有密切相关但又略有不同的含义,即"为什么原因 ","为什么原因 "和"为了什么目的 ".我将根据他们所解决的"为什么"的含义分组答案.
这里的主要答案来自Paul Draper,Martin J提供了类似但不那么广泛的答案.Paul Draper的回答归结为
它是未定义的行为,因为它没有定义行为是什么.
答案在解释C++标准所说的内容方面总体上非常好.它还解决了UB的一些相关案例,如f(++i, ++i);
和f(i=1, i=-1);
.在第一个相关案例中,不清楚第一个论点是否应该是i+1
第二个i+2
,反之亦然; 在第二个中,不清楚i
函数调用后是否应为1或-1.这两种情况都是UB,因为它们属于以下规则:
如果相对于同一标量对象的另一个副作用,标量对象的副作用未被排序,则行为未定义.
因此,f(i=-1, i=-1)
也是UB,因为它属于同一规则,尽管程序员的意图是(恕我直言)显而易见且毫不含糊.
Paul Draper在他的结论中也明确表示
可以定义行为吗?是.它被定义了吗?没有.
这让我们想到"为什么原因/目的是 …
在阅读有关未定义行为和序列点的答案后,我写了一个小程序:
#include <stdio.h>
int main(void) {
int i = 5;
i = (i, ++i, 1) + 1;
printf("%d\n", i);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是2
.天啊,我没有看到减量来了!这里发生了什么?
另外,在编译上面的代码时,我收到一条警告:
px.c:5:8:警告:逗号表达式的左侧操作数无效
Run Code Online (Sandbox Code Playgroud)[-Wunused-value] i = (i, ++i, 1) + 1; ^
为什么?但可能会通过我的第一个问题的答案自动回答.
我想为SO准备一些教育工具,这应该有助于初学者(和中级)程序员识别和挑战他们在C,C++及其平台中的无根据的假设.
例子:
我认为可以在各种平台上运行一个小型测试程序,它运行"合理的"假设,根据我们在SO中的经验,通常是由许多缺乏经验/半经验的主流开发人员制作的,并记录他们在各种机器上打破的方式.
这样做的目的不是要证明做某事是"安全的"(这是不可能做到的,测试只有在他们破坏的情况下证明了什么),而是向最难以理解的个体展示最不起眼的表达方式如果它具有未定义或实现定义的行为,则在另一台机器上中断..
为此,我想问你:
这是测试玩具的当前版本:
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <stddef.h>
int count=0;
int total=0;
void expect(const char *info, const char *expr)
{
printf("..%s\n but '%s' is false.\n",info,expr);
fflush(stdout);
count++;
}
#define EXPECT(INFO,EXPR) if (total++,!(EXPR)) expect(INFO,#EXPR)
/* stack check..How can I do this better? */
ptrdiff_t check_grow(int k, int *p)
{
if (p==0) p=&k;
if (k==0) return &k-p;
else return check_grow(k-1,p);
}
#define BITS_PER_INT (sizeof(int)*CHAR_BIT)
int bits_per_int=BITS_PER_INT;
int int_max=INT_MAX; …
Run Code Online (Sandbox Code Playgroud) 如果我通过我的GCC 4.7快照传递以下代码,它会尝试将unique_ptr
s 复制到向量中.
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
Run Code Online (Sandbox Code Playgroud)
显然,因为std::unique_ptr
不可复制而无法工作:
错误:使用已删除的函数'std :: unique_ptr <_Tp,_Dp> :: unique_ptr(const std :: unique_ptr <_Tp,_Dp>&)[with _Tp = int; _Dp = std :: default_delete; std :: unique_ptr <_Tp,_Dp> = std :: unique_ptr]'
GCC是否正确尝试从初始化列表中复制指针?
我需要一个函数(就像来自WinAPI的SecureZeroMemory)总是将内存归零并且不会被优化掉,即使编译器认为在此之后永远不会再访问内存.似乎是挥发性的完美候选者.但是我实际上遇到了一些与GCC合作的问题.这是一个示例函数:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Run Code Online (Sandbox Code Playgroud)
很简单.但是,如果你调用它,GCC实际生成的代码会因编译器版本和你实际尝试为零的字节数而有很大不同.https://godbolt.org/g/cMaQm2
我测试过的任何其他编译器(clang,icc,vc)都会生成一个人们期望的存储,包括任何编译器版本和任何数组大小.所以在这一点上我想知道,这是一个(非常古老而严重的?)GCC编译器错误,或者是标准中volatile的定义,它不确定这实际上是符合行为的,这使得编写便携式基本上是不可能的" SecureZeroMemory"功能?
编辑:一些有趣的观察.
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <atomic>
void callMeMaybe(char* buf);
void volatileZeroMemory(volatile void* ptr, std::size_t size)
{
for (auto bytePtr = static_cast<volatile std::uint8_t*>(ptr); size-- > 0; )
{
*bytePtr++ = 0;
}
//std::atomic_thread_fence(std::memory_order_release);
}
std::size_t foo()
{
char …
Run Code Online (Sandbox Code Playgroud) 我一直都这么问,但我从来没有得到过一个非常好的答案; 我认为,在写第一个"Hello World"之前,几乎所有程序员都遇到过"宏不应该使用宏","宏是邪恶的"这样的短语等等,我的问题是:为什么?有了新的C++ 11,这么多年后还有一个真正的选择吗?
简单的部分是关于宏#pragma
,特定于平台和特定于编译器,并且大多数时候它们具有严重的缺陷,例如#pragma once
在至少两种重要情况下容易出错:不同路径中的相同名称以及一些网络设置和文件系统.
但总的来说,宏的用法和替代品呢?
c++ ×8
c ×5
c++11 ×3
expression ×2
c++-faq ×1
gcc ×1
implementation-defined-behavior ×1
math ×1
operators ×1
performance ×1
portability ×1
standards ×1