Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why/how does this compile?

C++ in MS Visual Studio 2008. Warning level 4 plus a load of extra warnings enabled as well. I'd expect this to give a warning at least, but more likely a compiler error?

Function declaration is as follows:

int printfLikeFunction(
   const int               bufferLength,
   char * const            buffer,
   const char * const      format,
   ... );

Code usage - there's a typo: although the ARRAY_SIZE of outputBuffer is passed in, outputBuffer itself isn't - surely this should not compile:

printfLikeFunction( ARRAY_SIZE( outputBuffer ), "Format: %s, %s", arg1, arg2 );

Clearly this is wrong and a mistake has been made. However the compiler should have caught it! The buffer parameter should be a char-pointer, and it's being passed a string literal which is a const char-pointer. This must be an error. (arg1 and arg2 are (possibly const) char pointers as well, so coincidentally the declaration is matched even without outputBuffer being in the correct place).

At run time, this code crashes as it attempts to write into the string literal. No surprise there, I just don't understand how it was allowed to compile.

(Though, incidentally, this is presumably why sprintf_s has the buffer and size parameters in a different order to this function - it makes such errors unequivocally fail).

like image 882
Andy Patrick Avatar asked Oct 07 '09 08:10

Andy Patrick


3 Answers

C++ has a special loophole for string literals for compatibility with pre-const C-style code. Although string literals are arrays of const char, they can be converted to a pointer to non-const char.

Paraphrasing 4.2/2 [conv.array]: a 'narrow' string literal can be converted to an rvalue of type pointer to non-const char. The conversion is only considered when there is an explicit target type (e.g. a function parameter) and not when a general lvalue to rvalue conversion is required.

This conversion is deprecated, but still available. Note that while the conversion allows the literal to be converted to a pointer to non-const char type, it would still invoke undefined behaviour to try to modify any of the characters in the string literal through this pointer.

like image 64
CB Bailey Avatar answered Sep 28 '22 23:09

CB Bailey


I seem to recall that the compiler has an option that controls how string literals are treated. By default they are treated as char * in order to not break a whole lot of existing non-const-safe code, but you can change it to treat them as const char *. This may help trigger the error you are looking for.

(later) I don't have a Microsoft compiler handy at the moment, but looking throught the reference on MSDN I can't seem to find such an option. I may be thinking of GCC, which does have such an option.

like image 27
Greg Hewgill Avatar answered Sep 29 '22 01:09

Greg Hewgill


int printfLikeFunction( const int bufferLength, char * const buffer, const char * const format, ... );

the buffer parameter is specified as char* const, so it only protects the buffer address to be modified, not the buffer content.

to avoid buffer to be written into, you need to declare it as const char* const like the format.

the compiler allows writing into buffer since you declare it as char* . the const postfix modifier only prevents to reassign buffer value in your function.

see http://jriddell.org/const-in-cpp.html to view const modifier impact on variables

like image 30
dweeves Avatar answered Sep 29 '22 01:09

dweeves