看起来
if (x=y) { .... }
代替
if (x==y) { ... }
是许多邪恶的根源.
为什么并非所有编译器都将其标记为错误而不是可配置警告?
我有兴趣找出构造if (x=y)
有用的案例.
一个有用的构造是例如:
char *pBuffer; if (pBuffer = malloc(100)) { //continue to work here }
编辑:
如前所述,现在已经多次投票,我可能会补充说这不是特别好的风格,但经常看到它说它很有用.我也见过这个new
,但它让我的胸部更加疼痛.
另一个例子,争议较少,可能是:
while (pointer = getNextElement(context)) { //go for it, use the pointer to the new segment of data }
这意味着当没有下一个元素时函数getNextElement()
返回NULL
,以便退出循环.
大多数时候,编译器都非常努力地保持向后兼容.
改变他们在这个问题上的行为以抛出错误将破坏现有的合法代码,甚至开始抛出警告将导致自动系统出现问题,通过自动编译并检查错误和警告来跟踪代码.
这是一个邪恶,我们几乎坚持atm,但有办法规避和减少它的危险.
例:
void *ptr = calloc(1, sizeof(array)); if (NULL = ptr) { // some error }
这会导致编译错误.
简单答案:赋值操作(如x = y)具有一个值,该值与x中新分配的值相同.您可以直接在比较中使用它,而不是
x = y; if (x) ...
你可以写
if (x = y) ...
写入(和读取)的代码较少,这有时是一件好事,但现在大多数人都认为应该以其他方式编写它以提高可读性.例如这样:
if ((x = y) != 0) ...
这是一个现实的例子.假设你想用malloc分配一些内存,看它是否有效.它可以像这样逐步编写:
p = malloc(4711); if (p != NULL) printf("Ok!");
与NULL的比较是多余的,因此您可以像这样重写它:
p = malloc(4711); if (p) printf("Ok!");
但由于赋值操作具有可以使用的值,因此可以将整个赋值放在if条件中:
if (p = malloc(4711)) printf("Ok!");
这也是一样的,但更简洁.
因为它不是非法的(无论如何使用C或C++)并且有时候很有用......
if ( (x = read(blah)) > 0) { // now you know how many bits/bytes/whatever were read // and can use that info. Esp. if you know, say 30 bytes // are coming but only got 10 }
大多数编译器如果你没有在括号内放置括号,我会喜欢这个问题.
问题是你正在把问题颠倒过来."if"表示法不是像在某些其他语言中那样比较两个值.
C/C++"if"指令等待任何将计算为布尔值或null /非null值的表达式.该表达式可以包括两个值比较,和/或可以更复杂.
例如,您可以:
if(i >> 3) { std::cout << "i is less than 8" << std::endl }
这证明,在C/C++中,if表达式不限于==和=.任何事都可以,只要它可以被评估为真或假(C++),或零非零(C/C++).
另一个C++有效用途:
if(MyObject * pObject = dynamic_cast(pInterface)) { pObject->doSomething() ; }
这些是if表达式的简单用法(请注意,这也可以在for循环声明行中使用).确实存在更复杂的用途.
在发现此问题的副本后 在哪种情况下,(a = b)是个好主意?我决定用一个额外的奖励来完成这个答案,也就是说,可变注入一个范围,这在C++中是可能的,因为if
它将评估它的表达式,包括一个变量声明,而不是限制自己来比较两个操作数,就像它在其他语言:
所以,引用自己:
另一种用途是使用所谓的C++ Variable Injection.在Java中,有这个很酷的关键字:
synchronized(p) { // Now, the Java code is synchronized using p as a mutex }
在C++中,您也可以这样做.我没有确切的代码(也没有我发现它的DDJ的确切文章),但是这个简单的定义应该足以用于演示目的:
#define synchronized(lock) \ if (auto_lock lock_##__LINE__(lock)) synchronized(p) { // Now, the C++ code is synchronized using p as a mutex }
这是相同的方式,混合注入if和for声明,你可以声明一个原始的foreach宏(如果你想要一个工业级的foreach,使用boost).
请参阅以下文章,了解不那么天真,更完整和更强大的实现:
http://www.drdobbs.com/184401723?pgno=6
http://www.drdobbs.com/184401728
http://www.drdobbs.com/184401752
很少.事实上,我还没有记住一个,我8年来一直很专业.我猜它发生了,但是在8年后,我确实产生了相当数量的错误.只是这种错误并没有发生,让我在挫折中记住它们.
在C中,由于缓冲区溢出会导致更多错误,例如:
void doSomething(char * p) { strcpy(p, "Hello World, how are you \?\n") ; } void doSomethingElse() { char buffer[16] ; doSomething(buffer) ; }
事实上,微软被烧得如此之难,因为他们在Visual C++ 2008中添加了一个警告,因为他们不赞成使用strcpy!
对此错误的第一个"保护"是"转向"表达式:由于无法为常量赋值,因此:
if(0 = p) // ERROR : It should have been if(0 == p). WON'T COMPILE !
不会编译.
但我觉得这个解决方案很糟糕,因为它试图隐藏在风格背后应该是一般的编程实践,即:任何不应该改变的变量应该是不变的.
例如,而不是:
void doSomething(char * p) { if(p == NULL) // POSSIBLE TYPO ERROR return ; size_t length = strlen(p) ; if(length == 0) // POSSIBLE TYPO ERROR printf("\"%s\" length is %i\n", p, length) ; else printf("the string is empty\n") ; }
尝试尽可能多地"变量"变量将使您避免大多数拼写错误,包括那些不在"if"表达式中的错误:
void doSomething(const char * const p) // CONST ADDED HERE { if(p == NULL) // NO TYPO POSSIBLE return ; const size_t length = strlen(p) ; // CONST ADDED HERE if(length == 0) // NO TYPO POSSIBLE printf("\"%s\" length is %i\n", p, length) ; else printf("the string is empty\n") ; }
当然,它并不总是可能的(因为一些变量需要改变),但我发现我使用的大多数变量都是常量(我一直初始化它们,然后只读它们).
通常,我看到代码使用if(0 == p)表示法,但没有const-notation.
对我来说,就像有一个垃圾桶用于可回收物品,另一个用于不可回收物品,然后在最后,将它们放在同一个容器中.
因此,不要嘲笑一种简单的习惯习惯,希望它能让你的代码变得更好.它不会.尽可能使用语言结构,这意味着,在这种情况下,在可用时使用if(0 == p)表示法,并尽可能使用const关键字.
'if(0 = x)'成语旁边是无用的,因为当双方都是变量('if(x = y)')和大多数(全部?)的时候你应该使用常量变量时它没有帮助而不是魔术数字.
另外两个原因我从不使用这个成语,恕我直言,它使代码可读性降低,说实话,我发现单个'='是非常小的罪恶的根源.如果你彻底测试你的代码(显然我们都这样做),这种语法错误很快就会出现.
许多编译器会检测到这一点并发出警告,但前提是警报级别设置得足够高.
例如:
~> gcc -c -Wall foo.c foo.c: In function ‘foo’: foo.c:5: warning: suggest parentheses around assignment used as truth value
用于迭代的标准C语言:
list_elem* curr; while ( (curr = next_item(list)) != null ) { /* ... */ }