Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ unexecuted code changes function behaviour?

Tags:

c++

visual-c++

I use the following code (taken from https://codereview.stackexchange.com/questions/22901/reading-all-bytes-from-a-file) to efficiently read a file into an array in C++:

static std::vector<char> ReadAllBytes(char const* filename)
{
    std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
    std::ifstream::pos_type pos = ifs.tellg();

    std::vector<char> result(pos);

    ifs.seekg(0, std::ios::beg);
    ifs.read(&result[0], pos);

    for (unsigned int i = 0; i < result.size(); i++)
        if (result.at(i) != 0)
            return result; // [1]

    //if (!ifs.good()) // Commenting out changes contents of result
    //    return result; // [2] // Commenting out changes contents of result

    return result; // [3]
}

Everything works perfectly, a breakpoint at [1] fires and the function returns the data (the loop is just for debug, as I have been getting 0-filled returns which should hold data). However, as soon as I remove the If-Statement at [2], breakpoint [3] fires and the array is empty (the size is correct, but the array is filled with zeros).

How can code which is never executed actually change the behavior of the function? I figured it might have something to do with the stack layout and the fact that I hold the stream and the data as local variables but manually creating them on the heap leads to the exact same situation.

You see me completely baffled. I have never seen something quite like this before. How can this be possible?

PS: I should add that the file contents are binary and the file is about ~32 MB in size.

like image 239
Max Avatar asked Apr 19 '26 04:04

Max


1 Answers

Are you compiling with optimizations (release mode)? If so, then if I had to guess, I would say it's reordering your code. First off, notice that none of the code after the read actually matters. All it's doing is returning result in all cases and not changing anything in the array. Many of the functions could be inlined which means a good compiler could know this and remove all that code.

So that could easily explain the breakpoint behavior. But adding the if (!ifs.good()) and having the data be empty - that's tough to explain. Perhaps this tidbit about the read function provides insight:

Internally, the function accesses the input sequence by first constructing a sentry object (with noskipws set to true). Then (if good), it extracts characters from its associated stream buffer object as if calling its member functions sbumpc or sgetc, and finally destroys the sentry object before returning.

Notice that read contains a check on good(), which means that perhaps the compiler is combining the two checks of good() (the one in the read, after inlining, and the one in your code) and using that to skip the read entirely(?). That seems really unlikely, but perhaps it points you in a direction to debug.

I will say that I've seen this problem before with the optimizer. Not this specific problem with ifstreams but the more general problem that adding seemingly unused code changes the behavior. And the reason always ends up being that it's free to reorder code that doesn't change the results.

like image 106
JoshG79 Avatar answered Apr 20 '26 21:04

JoshG79