C++中的求值|副作用|序列点所导致的模糊语义

2024-01-18 04:38

本文主要是介绍C++中的求值|副作用|序列点所导致的模糊语义,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C++中的求值|副作用|序列点所导致的模糊语义

前一阵子一个偶然的机会,在soloist的blog上(http://blog.csdn.net/soloist)看到一篇关于C/C++中的一个十分历史悠久的问题的讨论,即表达式求值的问题。说实话这个问题着实不新鲜了,被所有论坛提出过无数次,无非就是表达式求值顺序不确定的问题嘛。所以我也就没太在意,soloist那里吵翻了天,说什么的都有,热闹非凡。

当时我就当复习一下,想看看标准对这一块到底有什么明确的说法,就随手翻开了[C++03],结果发现原来情况并非像很多人,乃至我所长期以来认为的那样,简而言之,有下面几个令人意外的结果,不但令我意外,当我把问题发到comp.std.c++新闻组上之后居然引起了一场不大不小的争吵,Andrew Koenig、Herb Sutter、David Abrahams、P.J Plauger...,于是我就看了几天的连台好戏,同时也把这个问题的答案在脑子里刷新了一次。

下面就是两个令人感到意外的现象:

1. 表达式求值并不一定意味着求值过程中的副作用会同步发生!这是一个违反直觉的地方,带来了非常晦涩的语义。

2. i = (i++); 这种极度简单的表达式的行为居然是未定义的(undefined behavior)『注意“未定义行为(undefined behavior)”跟“未指定行为(unspecified behavior)”之间的重大区别。前者是对于不正确的,有毛病的程序而言,未定义行为可能是任何行为,轻则出现意料之外的结果,重则程序崩溃(崩溃还算好的,糟的就是错了还一声不吭^_^)。后者则是对于well-formed程序而言,未指定(unspecified)行为的可能性一般是有限的(例如函数参数的求值顺序就是函数参数个数的全排列种),只不过具体的实现不用在文档里说明究竟在它的实现上的特定行为是怎样的。』 顺便提一下“由实现定义的行为(implementation defined)”,这一行为跟“unspecified behavior”比较类似,都是针对well-formed程序而言,只不过后者的具体行为需要特定实现注明在文档中,让用户知道。 我们一直以为i=(i++)的行为是unspecified,即以为它至少还是well-formed程序,只不过在不同编译器上有不同结果罢了,然后结果却大谬不然,其行为是undefined,可能产生任何结果(从概念上来说,甚至可能导致程序崩溃^_^)。

3. i = (i++)这种表达式如果i是用户自定义迭代器的话,其行为却又变成了 unspecified,甚至由于这里左端表达式并没有实际的side-effect,所以其结果甚至是 定 的!这就是说,在build-in operation跟user-defined operator之间某些情况下存在着不易察觉的隐晦差别。

多的就不说了,带着上面的看法,你可以去看看我发在comp.std.c++上的帖子,地址如下:

标题:Is this really unspecified behavior?

http://groups.google.com/group/comp.std.c++/browse_frm/thread/0174aa7b34b06a51/581d5219d5a75578#581d5219d5a75578

 




这篇关于C++中的求值|副作用|序列点所导致的模糊语义的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/618089

相关文章

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

Spring的RedisTemplate的json反序列泛型丢失问题解决

《Spring的RedisTemplate的json反序列泛型丢失问题解决》本文主要介绍了SpringRedisTemplate中使用JSON序列化时泛型信息丢失的问题及其提出三种解决方案,可以根据性... 目录背景解决方案方案一方案二方案三总结背景在使用RedisTemplate操作redis时我们针对

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat