问题
I know its to do with compiler optimization, but I am looking for a real deep dive into how/why and how to ensure i am notified of this behaviour in real code.
I have this code
void swap(char *s)
{
strcpy(s, "nope!");
printf("Result: %s\n", s);
};
void main(){
...
swap("this should segfault");
...
}
obviously, it should segfault, but visual studio in release mode reduces it down to just inlining the printf.
This seems like the kind of thing that could really bite me in the ass later, so i would love it if you could shine some light on this for me.
for completeness sake here is the expected assembly
push offset s ; "this should segfault"
call j_?swap@@YAXPAD@Z ; swap(char *)
and here is the generated assembly
push offset s ; "this should segfault"
push offset Format ; "Result: %s\n"
call ds:__imp__printf
and here are the compiler options as requested in comments
/GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /Fd"Release\vc120.pdb" /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_LIB" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /Fa"Release\" /EHsc /nologo /Fo"Release\" /Fp"Release\scratchpad2.pch"
回答1:
Behavior confirmed here with VC++ 2013 and 2015. @IgorTandetnik's comment is correct. The code exhibits UB (undefined behavior), and not crashing is one such possible behavior.
That said, VC++ is also at fault for not issuing (at least) a warning when allowing the string-literal-to-non-const-char-pointer conversion which has been deprecated since (I think) C++0x - see Why can a string literal be implicitly converted to char* only in certain case? and Why is passing a string literal into a char* argument only sometimes a compiler error?. You may consider filing a bug report at https://connect.microsoft.com/visualstudio about that.
I know its to do with compiler optimization, but I am looking for a real deep dive into how/why and how to ensure i am notified of this behaviour in real code.
I don't have an answer to the latter part of the question.
As to how/why, it appears to be a matter of strcpy
behavior when optimized as an intrinsic
with a read-only destination. A rather odd behavior, indeed, since it ultimately results in the entire strcpy
being silently skipped.
Adding
#pragma function(strcpy)
beforeswap
will cause it to always crash.Changing the calling code to
char z[] = "this should segfault"; swap(z);
will remove the UB factor and make it always work.
[EDIT] Going back to the
how to ensure i am notified of this behaviour in real code
part, and until VC++ obliges with a compilation warning or error, the following could work around it by explicitly providing a const char *
overload. Confirmed again with both VC++ 2013 and 2015.
#include <string.h>
#include <stdio.h>
#pragma intrinsic(strcpy)
static void swap(char *s)
{
strcpy(s, "nope!");
printf("OK: %s\n", s);
};
static void swap(const char *s)
{
printf("No: %s?\n", s);
};
void main()
{
char z[] = "this should segfault";
swap(z); // prints 'OK: nope!'
swap("this should segfault"); // prints 'No: this should segfault?'
}
来源:https://stackoverflow.com/questions/38111158/ms-vc-compiler-optimizating-away-erroneous-code