C ++ teaser:这是一个编译器错误,还是这个预期的行为? [英] C++ teaser: Is this a compiler bug, or is this expected behavior?

查看:87
本文介绍了C ++ teaser:这是一个编译器错误,还是这个预期的行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编译以下代码片段并运行它。如果程序吐出蝙蝠:蝙蝠而不是蝙蝠:zat,你会说什么?你会说编译器有问题吗?或者你会把责任归咎于未定义

执行函数参数在C / C ++标准和序列

积分中?


///////代码片段开始///////


#include< iostream>

char foo [10] =" cat";

char * writestring()

{

foo [0] =''b'';

返回foo;

}


char * write2()

{

foo [0] =''z'';

return foo;

}

int main(无效)

{std :: cout<< writestring()<< ":" << write2()<<的std :: ENDL; }


///////代码片段结束///////


谢谢,

Bhat

[认为这个NG意味着讨论编译器中立的纯粹主义者,

标准C ++问题只能超出这一点; - )]




对于那些非常倾向的人来说,这里有一些

背景...... ......

我偶然发现了一个虫子。在我的C ++编译器(g ++ 3.3.1)中,我很快就向Bugzilla报告了
。上面的代码片段实际上是由GCC志愿者社区的某个人提供的
。他们将

的意外行为归因于

函数参数和序列点的执行未定义行为。在我的原始代码段中,

我在IP地址之间维护STL映射,例如105.52.20.33,

5000和47.32.68.95,6000。


当我在地图中显示条目时,第二个IP地址是

显示不正确。因此,而不是映射:


105.52.20.33,5000>> - >> 47.32.68.95,6000

我得到了


105.52.20.33,5000>> - >> 105.52.20.33,6000

当使用本机编译代码时,错误不会显现

Solaris C ++

107311-17"


这是我的原始代码片段


///////代码片段开始///////

#include< sys / socket.h>

#包括< netinet / in.h>

#include< arpa / inet.h>


#include< string>

#include< map>

#include< iostream>


using namespace std;

struct addrLessThan: public binary_function< const struct sockaddr_in,

const

struct sockaddr_in,bool>

{

bool operator() (const struct sockaddr_in addr1,const struct

sockaddr_in

addr2)const

{

bool retVal = true;


string addrStr1 = inet_ntoa(addr1.sin_addr);

string addrStr2 = inet_ntoa(addr2.sin_addr);


if(addrStr1> addrStr2)

retVal = false;

else if(addrStr1 == addrStr2)

retVal =(addr1.sin_port< addr2.sin_port) ;


返回retVal;

}

};


typedef map< struct sockaddr_in,struct sockaddr_in,addrLessThan>

IpV4AddrMap;


main()

{

struct sockaddr_in actualAddress,mappedAddress;


actualAddress.sin_port = 5000;

actualAddress.sin_addr.s_addr = inet_addr(" 105.52.20.33");


mappedAddress.sin_port = 6000;

mappedAddress.sin_addr.s_addr = inet_addr(" 47.32.68.95");


IpV4AddrMap map;


map [actualAddress] = mappedAddress;


IpV4AddrMap :: iterator itor = map.find(actualAddress) ;


if(itor!= map.end())

{

cout<< 关键: << inet_ntoa(itor-> first.sin_addr)

<< , << itor-> first.sin_port<< endl

<< 价值: << inet_ntoa(itor-> second.sin_addr)

<< , << itor-> second.sin_port<< endl

<<结束;

}

返回0;

}


///////代码片段结束///////

有关详情,请访问
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=22265

解决方案

Generic Usenet Account写道:

编译以下代码片段并运行它。如果程序吐出蝙蝠:蝙蝠而不是蝙蝠:zat,你会说什么?你会说编译器有问题吗,或者你会把责任归咎于未定义的
执行函数参数吗?在C / C ++标准和序列
点?

///////代码片段开始///////

#include< iostream>
char foo [10] =" cat" ;;
char * writestring()
{
foo [0] =''b'';
返回foo;
}
char * write2()
{
foo [0] =''z'';
返回foo;
}

int main(void)
{std :: cout<< writestring()<< ":" << write2()<<的std :: ENDL; }
[..]




是的,后者,正确的术语是

的评估顺序函数参数未指定。一个更简单的表达是


cout<< writestring()<< write2();





(cout.operator<<(writestring()))相同。运营商LT;< (write2());


其中''write2()''允许在''writestring()'之前评估

作为我明白它。第二个点右边的部分是

函数及其参数。左边的部分是对象,也需要评估
...


V


< blockquote> Generic Usenet Account写道:

编译以下代码片段并运行它。如果程序
吐出bat:bat而不是bat:zat,你会说什么?你会说编译器有问题,或者你会指责未定义的函数参数执行吗?在C / C ++
标准和序列点中?
#include< iostream>
char foo [10] =" cat";
char * writestring()
{
foo [0] =''b'';
返回foo;
}

char * write2()
{
foo [0] =''z'';
返回foo;
}

int main(void)
{ std :: cout<< writestring()<< ":" << write2()<<的std :: ENDL; }


行为未指定(未定义),并且

" bat:bat"," bat:zat"和" zat: ZAT"都是有效的输出。

(但是zat:bat不是。)


你必须记住,在任何时候都可以调用writestring()

在此语句执行开始之间,以及

之间需要返回值。 write2()也是如此。


序列点在这里不是问题,因为有没有 b没有 b的多个副作用的实例br />
干预序列点(一个函数调用有一个序列

点在它的参数被评估之后,另一个是

,因为它返回)。这个例子有一些相似之处:


foo(a(),b());


哪里没有理由怀疑a()将在b()之前调用




BTW,你为什么要发布到comp .sources.d?

上面的代码片段实际上是由GCC志愿者社区的某个人提供的。他们将意外的行为归因于执行函数
参数和序列点的未定义行为。


如果这是他们的确切措辞,那么他们就错了(或者说b $ b表示他们的意图不正确)。


如果你期望

错误的话,这种行为只会出乎意料:)

我偶然发现了一个bug。在我的C ++编译器(g ++ 3.3.1)中,我及时向Bugzilla报告。
cout<< 关键: << inet_ntoa(itor-> first.sin_addr)
<< , << itor-> first.sin_port<< endl
<< 价值: << inet_ntoa(itor-> second.sin_addr)
<< , << itor-> second.sin_port<< endl
<< endl;




不幸的是你浪费了Bugzilla人的时间。

你已经正确识别了问题的本质,

即inet_ntoa()返回指向静态缓冲区的指针。

实际上,在我的系统上,inet_ntoa手册页特别是

说:

字符串是

在静态分配的缓冲区中返回,

后续调用将覆盖。


如果您仍然认为这是一个错误,那么您认为

''修复'应该是什么?人们在comp.lang.c(或c ++)上制作

的最常见建议是强制从左到右评估

的函数参数。


之前已经讨论过死亡,但反对它的主要原因是它会迫使编译器产生更慢的代码。很多情况。例如,一些调用约定

将特征参数压入堆栈,最先推送最右边的
参数。具有这个调用约定的函数

需要编译器跳过一些箍,而不是

a几个简单的函数调用后跟一个堆栈推送

返回值。


Old Wolf写道:

行为未指定(未定义),并且蝙蝠:蝙蝠,蝙蝠:扎特和扎特:扎特都是有效的输出。
(但是zat:bat不是。)




尝试修改文字值是不确定的行为,当然?


Compile the following snippet of code and run it. If the program spits
out bat:bat instead of bat:zat, what would you say? Would you say that
the compiler has a problem, or would you lay the blame on "undefined
execution of function parameters" in the C/C++ standard and "sequence
points"?

/////// Code snippet begins ///////

#include <iostream>
char foo[10]="cat";
char* writestring()
{
foo[0]=''b'';
return foo;
}

char* write2()
{
foo[0]=''z'';
return foo;
}
int main(void)
{ std::cout << writestring() << ":" << write2() << std::endl; }

/////// Code snippet ends ///////

Thanks,
Bhat
[Purists who hold that this NG is meant to discuss compiler neutral,
standard C++ issues only may not proceed beyond this point;-)]




For those of you who are "trivially inclined", here''s some
background......
I stumbled upon a "bug" in my C++ compiler (g++ 3.3.1), which I
promptly reported to Bugzilla. The code snippet above was actually
provided by someone from the GCC volunteer community. They attributed
the unexpected behavior to the undefined behavior of execution of
function parameters and sequence points. In my original code snippet,
I was maintaining an STL map between IP addresses e.g. 105.52.20.33,
5000 and 47.32.68.95, 6000.

When I displayed the entries in the map, the second IP address was
displayed incorrectly. So instead of the mapping:

105.52.20.33, 5000 >>-->> 47.32.68.95, 6000
I got

105.52.20.33, 5000 >>-->> 105.52.20.33, 6000

The bug does not manifest when the code is compiled using native
Solaris C++
compiler version "WorkShop Compilers 5.0 02/04/10 C++ 5.0 Patch
107311-17"

Here''s my original code snippet

/////// Code snippet begins ///////
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <string>
#include <map>
#include <iostream>

using namespace std;
struct addrLessThan:public binary_function<const struct sockaddr_in,
const
struct sockaddr_in, bool>
{
bool operator()(const struct sockaddr_in addr1, const struct
sockaddr_in
addr2) const
{
bool retVal = true;

string addrStr1 = inet_ntoa(addr1.sin_addr);
string addrStr2 = inet_ntoa(addr2.sin_addr);

if(addrStr1 > addrStr2)
retVal = false;
else if(addrStr1 == addrStr2)
retVal = (addr1.sin_port < addr2.sin_port);

return retVal;
}
};

typedef map<struct sockaddr_in, struct sockaddr_in, addrLessThan>
IpV4AddrMap;

main()
{
struct sockaddr_in actualAddress, mappedAddress;

actualAddress.sin_port=5000;
actualAddress.sin_addr.s_addr = inet_addr("105.52.20.33");

mappedAddress.sin_port=6000;
mappedAddress.sin_addr.s_addr = inet_addr("47.32.68.95");

IpV4AddrMap map;

map[actualAddress] = mappedAddress;

IpV4AddrMap::iterator itor = map.find(actualAddress);

if(itor != map.end())
{
cout << "Key: " << inet_ntoa(itor->first.sin_addr)
<< ", " << itor->first.sin_port << endl
<< "Value: " << inet_ntoa(itor->second.sin_addr)
<< ", " << itor->second.sin_port << endl
<< endl;
}
return 0;
}

/////// Code snippet ends ///////
For more details, you can go to
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=22265

解决方案

Generic Usenet Account wrote:

Compile the following snippet of code and run it. If the program spits
out bat:bat instead of bat:zat, what would you say? Would you say that
the compiler has a problem, or would you lay the blame on "undefined
execution of function parameters" in the C/C++ standard and "sequence
points"?

/////// Code snippet begins ///////

#include <iostream>
char foo[10]="cat";
char* writestring()
{
foo[0]=''b'';
return foo;
}

char* write2()
{
foo[0]=''z'';
return foo;
}
int main(void)
{ std::cout << writestring() << ":" << write2() << std::endl; }
[..]



Yes, the latter, the correct term is "the order of evaluation of the
function arguments is unspecified". A simpler expression is

cout << writestring() << write2();

which is the same as

( cout.operator<<( writestring() ) ) . operator<< ( write2() );

in which ''write2()'' is allowed to be evaluated before ''writestring()''
as I understand it. The part to the right of the second dot is the
function with its arguments. The left part is the object, which also
needs to be evaluated...

V


Generic Usenet Account wrote:

Compile the following snippet of code and run it. If the program
spits out bat:bat instead of bat:zat, what would you say? Would
you say that the compiler has a problem, or would you lay the blame
on "undefined execution of function parameters" in the C/C++
standard and "sequence points"?

#include <iostream>
char foo[10]="cat";
char* writestring()
{
foo[0]=''b'';
return foo;
}

char* write2()
{
foo[0]=''z'';
return foo;
}

int main(void)
{ std::cout << writestring() << ":" << write2() << std::endl; }
The behaviour is unspecified (NOT undefined), and
"bat:bat", "bat:zat", and "zat:zat" are all valid outputs.
(But "zat:bat" is not.)

You must remember that writestring() can be called at any point
between the start of this statement''s execution, and the point
where its return value is needed. The same goes for write2().

Sequence points are not an issue here, because there are
no instances of multiple side-effects occuring without an
intervening sequence point (a function call has a sequence
point after its arguments have been evaluated, and another one
as it returns).

The example has some similarities to:

foo( a(), b() );

where there is no reason to suspect that a() will be called
before b().

BTW, Why are you posting to comp.sources.d ?
The code snippet above was actually provided by someone from
the GCC volunteer community. They attributed the unexpected
behavior to the undefined behavior of execution of function
parameters and sequence points.
If that was their exact wording, then they are wrong (or
expressed their intention incorrectly).

The behaviour is only unexpected if you were expecting
the wrong thing :)
I stumbled upon a "bug" in my C++ compiler (g++ 3.3.1), which I
promptly reported to Bugzilla. cout << "Key: " << inet_ntoa(itor->first.sin_addr)
<< ", " << itor->first.sin_port << endl
<< "Value: " << inet_ntoa(itor->second.sin_addr)
<< ", " << itor->second.sin_port << endl
<< endl;



Unfortunately you have wasted the time of the Bugzilla people.
You have correctly identified the essence of the "problem",
namely that inet_ntoa() returns a pointer into a static buffer.
In fact, on my system, the inet_ntoa manpage specifically
says:
The string is
returned in a statically allocated buffer, which
subsequent calls will overwrite.

If you still think this is a bug, then what do you think the
''fix'' should be? The most common suggestion that people make
on comp.lang.c (or c++) is to force left-to-right evaluation
of function parameters.

This has been discussed to death before, but the main reason
for opposing it is that it would force compilers to produce
slower code in many cases. For example, some calling conventions
feature parameters being pushed onto a stack, with the right-most
parameters pushed first. A function with this calling convention
would need the compiler to jump through some hoops, instead of
a few simple function calls followed by a stack push of the
return value.


Old Wolf wrote:

The behaviour is unspecified (NOT undefined), and
"bat:bat", "bat:zat", and "zat:zat" are all valid outputs.
(But "zat:bat" is not.)



Attempting to modify a literal value is undefined behaviour, surely ?


这篇关于C ++ teaser:这是一个编译器错误,还是这个预期的行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆