Undefined behaviour or broken compiler?

D

duckfreezone

Hi,

I've got a small test program which behaves correctly (IMHO) on all
compilers that I have acccess to, with the exception of the gcc on
Macintosh OSX "gcc version 4.0.0 (Apple Computer, Inc. build 5026)"

Looks to be that the order of evaluation of the assignment on OSX is
different to other systems. I *think* it is wrong on OSX, or am I
simply relying on undefined behaviour?

On a "correct" system, I get this output:
before:
0 members
after:
0 : 'something'='something extra'
1 members

On OSX, I get this output:
before:
0 members
after:
0 : 'something'=''
1 members

I appreciate that this newsgroup doesn't cover platform-specific
issues, but my question is more one of trying to understand if this
program is relying on undefined behaviour.

Source follows:

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

static std::map<std::string,std::string> strings;

static void dump (const std::string& prefix)
{
std::cout << prefix << ":" << std::endl;
unsigned int count = 0;
for (std::map<std::string,std::string>::const_iterator si =
strings.begin (); si != strings.end (); ++si)
{
const std::string s1 (si -> first);
const std::string s2 (si -> second);
std::cout << " " << count++ << " : '" << s1 << "'='" << s2 << "'"
<< std::endl;
}
std::cout << count << " members" << std::endl;
}


static std::string findString (const std::string& which)
{
if (strings.find (which) != strings.end ()) return strings [which];
return which + " extra";
}


int main (int argc, char *argv[])
{
const std::string key ("something");
dump ("before");
strings [key] = findString (key);
dump ("after");
return EXIT_SUCCESS;
}
 
A

Alipha

Hi,

I've got a small test program which behaves correctly (IMHO) on all
compilers that I have acccess to, with the exception of the gcc on
Macintosh OSX "gcc version 4.0.0 (Apple Computer, Inc. build 5026)"

Looks to be that the order of evaluation of the assignment on OSX is
different to other systems. I *think* it is wrong on OSX, or am I
simply relying on undefined behaviour?

On a "correct" system, I get this output:
before:
0 members
after:
0 : 'something'='something extra'
1 members

On OSX, I get this output:
before:
0 members
after:
0 : 'something'=''
1 members

Both are correct outputs. See below.
I appreciate that this newsgroup doesn't cover platform-specific
issues, but my question is more one of trying to understand if this
program is relying on undefined behaviour.

Source follows:

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

static std::map<std::string,std::string> strings;

static void dump (const std::string& prefix)
{
std::cout << prefix << ":" << std::endl;
unsigned int count = 0;
for (std::map<std::string,std::string>::const_iterator si =
strings.begin (); si != strings.end (); ++si)
{
const std::string s1 (si -> first);
const std::string s2 (si -> second);
std::cout << " " << count++ << " : '" << s1 << "'='" << s2 << "'"
<< std::endl;
}
std::cout << count << " members" << std::endl;
}


static std::string findString (const std::string& which)
{
if (strings.find (which) != strings.end ()) return strings [which];
return which + " extra";
}


int main (int argc, char *argv[])
{
const std::string key ("something");
dump ("before");
strings [key] = findString (key);

Implementation specific behavior. strings[key] may be evaluated first
before the function call, in which case string[key] is created and
given the value "". See the link for more info:

http://www.eskimo.com/~scs/C-faq/q3.4.html

The correct way to write the above statement would be:

std::string value = findString(key);
strings[key] = value;
 
C

Clark S. Cox III

Hi,

I've got a small test program which behaves correctly (IMHO) on all
compilers that I have acccess to, with the exception of the gcc on
Macintosh OSX "gcc version 4.0.0 (Apple Computer, Inc. build 5026)"

Looks to be that the order of evaluation of the assignment on OSX is
different to other systems. I *think* it is wrong on OSX, or am I
simply relying on undefined behaviour?

Neither is wrong, and it's not undefined behaviour.
On a "correct" system, I get this output:
before:
0 members
after:
0 : 'something'='something extra'
1 members

On OSX, I get this output:
before:
0 members
after:
0 : 'something'=''
1 members

I appreciate that this newsgroup doesn't cover platform-specific
issues, but my question is more one of trying to understand if this
program is relying on undefined behaviour.

Source follows:

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

static std::map<std::string,std::string> strings;

static void dump (const std::string& prefix)
{
std::cout << prefix << ":" << std::endl;
unsigned int count = 0;
for (std::map<std::string,std::string>::const_iterator si =
strings.begin (); si != strings.end (); ++si)
{
const std::string s1 (si -> first);
const std::string s2 (si -> second);
std::cout << " " << count++ << " : '" << s1 << "'='" << s2 << "'"
<< std::endl;
}
std::cout << count << " members" << std::endl;
}


static std::string findString (const std::string& which)
{
if (strings.find (which) != strings.end ()) return strings [which];
return which + " extra";
}

As a style issue, I'd write that as (avoids searching the map twice):

static std::string findString (const std::string& which)
{
std::map<std::string,std::string>::const_iterator iter = strings.find(which);
if (iter != strings.end ()) return *iter;
return which + " extra";
}
int main (int argc, char *argv[])
{
const std::string key ("something");
dump ("before");
strings [key] = findString (key);

Either side of the '=' operator may be evaluated first, if the left
side is evaluated first, then an empty string is inserted into the map
with that key; that empty string is then found by findString, and
returned.
 
A

annamalai.gurusami

Alipha said:
Implementation specific behavior. strings[key] may be evaluated first
before the function call, in which case string[key] is created and
given the value "". See the link for more info:

http://www.eskimo.com/~scs/C-faq/q3.4.html

I am not sure whether this is true for assignment operators. If
I write

g() = f(); // g() returns an lvalue

Both g() and f() has to be called and then the assignment happens.
The above URL just says that both the following are correct.

case 1:

1. Evaluate g().
2. Evaluate f();
3. Carry out assignment.

case 2:

1. Evaluate f();
2. Evaluate g();
3. Carry out assignment.

In both cases, the result of f() is assigned to result of g(). So
the assignment should work fine.

Did I miss anything?

I tried the above piece of code with g++ 3.3.4 (on Slackware 10.0). The
optimization changes the behaviour of the program.

(begin-quote)

$ g++ foo.cc
$ ./a.out
before:
0 members
after:
0 : 'something'='something extra'
1 members
$ g++ -O foo.cc
$ ./a.out
before:
0 members
after:
0 : 'something'=''
1 members
$

(end-quote)

Rgds,
anna
 
M

msalters

(e-mail address removed) schreef:
Alipha said:
Implementation specific behavior. strings[key] may be evaluated first
before the function call, in which case string[key] is created and
given the value "". See the link for more info:

http://www.eskimo.com/~scs/C-faq/q3.4.html

I am not sure whether this is true for assignment operators. If
I write

g() = f(); // g() returns an lvalue

Both g() and f() has to be called and then the assignment happens.
The above URL just says that both the following are correct.

case 1:

1. Evaluate g().
2. Evaluate f();
3. Carry out assignment.

case 2:

1. Evaluate f();
2. Evaluate g();
3. Carry out assignment.

In both cases, the result of f() is assigned to result of g(). So
the assignment should work fine.

Fine, in the sense that it's not UB. However, assume the following:

std::list<int> L;
int& f() { L.push_back(1); return L.back(); }
int& g() { L.push_back(2); return L.back(); }
int main() { g() = f(); }

Clearly, it matters whether g() is called before or after f().
The assignment is legal in both cases, but L will differ.

HTH,
Michiel Salters
 
A

annamalai.gurusami

msalters said:
Fine, in the sense that it's not UB. However, assume the following:
UB?

std::list<int> L;
int& f() { L.push_back(1); return L.back(); }
int& g() { L.push_back(2); return L.back(); }
int main() { g() = f(); }

Clearly, it matters whether g() is called before or after f().
The assignment is legal in both cases, but L will differ.

Thanks for that good example. Now I understand.

Rgds,
anna
 
A

Andre Kostur

(e-mail address removed) wrote in @o13g2000cwo.googlegroups.com:

Undefined Behaviour. Behaviours defined by the Standard as
"compiler/implementation is free to do as it wishes, we make no guarantees
about anything".

As an example, attempting to call a method through a NULL pointer is
Undefined Behaviour:

Class * c = 0;

c->fn();


That call invokes UB because the NULL pointer is dereferenced. However, on
many implementations this will result in everything working properly
(assuming that fn() doesn't attempt to use any object member variables or
virtual functions). However this behaviour isn't guaranteed on all
platforms. It is perfectly acceptable (from a Standard point-of-view) for
your program to immediately crash, or an exception may be thrown, or your
hard drive may be formatted.
 
D

duckfreezone

Many thanks to all of those who replied; the information was exactly
what I was looking for. I'll now return to lurking :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top