Be careful when assigning to std::vector elements!
-
Today I have debugged my Big Program for an hour, trying to understand why the simplest assignment does not work. When I understood, I made the following demo code (Compile VS 2008 / Release default settings, but /Ob1):
//Disable automatic function inlining before compiling this code! (/Ob1) // Copyright (C) 2008 Sukhinov Anton. Soukhinov@gmail.com #include <vector> #include <iostream> std::vector<int> V( 10 ); //At first vector has 10 ints int F(void) { V.resize( 20 ); //Resize vector to 20 ints return 7; //Return number 7 } int main(int argc, char **argv) { V[1] = F(); //F return 7, so V[1] must be 7. Instead, heap corruption here. std::cout << V[1] << std::endl; //CRASH. The simplest std::cout << std::endl also crashes. return EXIT_SUCCESS; }
To fix example above you either should make the function
F
inline:inline int F(void)
, or, preferably, make an assignment using temporary variable:int x = F(); V[1] = x;
-
compiler bug?
-
That is pretty easy to explain. The operations are evaluated in the following order:
operator [] of V -> return reference to object in V
call F
resize V, we will probably need to allocate new memory. So all previous pointers, references and iterators are invalid.
return 7 from F
write the result of F to the invalid reference -> Undefined behaviorGrüssli
-
Hmm, I thought the right side of the assignment operator would be evaluated first (at least
operator=
is right-associative)...
-
Yes. The problem is that compiler first evaluates the adress of the left part of assignment operator:
void *adr = V.internal_buffer + sizeof(int)*1
Then the compiler executes the function in the right side. The function changes the internal_buffer using realloc.
Then the return value 7 is copied to the calculated adress:
*((int *)adr) = 7;
But calculated adress is invalid now. So, heap corryption occurs.
-
Nexus schrieb:
Hmm, I thought the right side of the assignment operator would be evaluated first (at least
operator=
is right-associative)...Associativity has almost nothing to do with evaluation order, which is unspecified in general.
-
Other case I have thinked up:
#include <vector> #include <iostream> int AskNumber(void) { std::cout << "Enter number: "; int x; std::cin >> x; return x; } char *Minus(int a, int b) { std::cout << "The result is " << (a-b) << std::endl; return "Done."; } int main(int argc, char **argv) { std::cout << "Hello! This is my program for numbers substraction!" << std::endl << //Display greeting Minus( AskNumber(), AskNumber() ) << std::endl; //Minus() display the result and return "Done", which is displayed at the end return EXIT_SUCCESS; }
Program output:
Enter number: 10 Enter number: 5 The result is -5 Hello! This is my program for numbers substraction! Done.
-
It's like bashar said. The evaluation order is unspecified. Another simple example:
int i=1; std::cout<<(++i)*(++i)+i;
The result is undefined.
so: don't write code which depends on evaluation order!
-
The evaluation order is unspecified in most cases, but not "in general", if I'm not mistaken.
-
hustbaer schrieb:
The evaluation order is unspecified in most cases, but not "in general", if I'm not mistaken.
That's what "in general" means :p
-
Bashar schrieb:
hustbaer schrieb:
The evaluation order is unspecified in most cases, but not "in general", if I'm not mistaken.
That's what "in general" means :p
The standard is in this question not really accurate.
5/4 schrieb:
Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual
expressions, and the order in which side effects take place, is unspecified.53)- (footnote)
The precedence of operators is not directly specified, but it can be derived from the syntax.
So, the order depends on the case. But generally I would say it's better praxis not program that the order makes a difference.
- (footnote)
-
drakon schrieb:
But generally I would say it's better praxis not program that the order makes a difference.
I think for the most use cases this is not really relevant. For example, I can't imagine me coding SAn's example; I'd rather split such statements up (also the operators
++
,--
,+=
, ...).But nevertheless it is good to keep it in mind.
-
drakon schrieb:
The standard is in this question not really accurate.
It says in your quote "Except where noted", which implies that the cases for which the evaluation order is specified have been listed explicitly. It can't get any more accurate.
So, the order depends on the case. But generally I would say it's better praxis not program that the order makes a difference.
When in doubt, yes. But IMO it's better to know where the order is guaranteed and where it's not. For example, I don't want to see code from someone who thinks it's a good practice to assume that the arguments of the logical && and || operators can be evaluated in any order.
-
Bashar schrieb:
drakon schrieb:
The standard is in this question not really accurate.
It says in your quote "Except where noted", which implies that the cases for which the evaluation order is specified have been listed explicitly. It can't get any more accurate.
Yes, you'r right, but you have to go through all cases, to know where it is specified.
-
Hey, people!
May be this is really a compiler bug?!
I tested the code
A() = B();
, andB()
evaluated first!
And bug in the first code is not reproducible in other compilers...
-
Quote from C++ primer:
5.10.3: Order of Evaluation schrieb:
The only other operands [other than && and ||] that guarantee the order in which operands are evaluated are the conditional (?
and comma operators. In all other cases, the order is unspecified.
If that is true, it is no compiler bug.
-
mammamia;
-
mammamia;