How does printf know the address of CString character data?

Given this piece of code:

struct My { operator const char*()const{ return "my"; } } my; CStringA s( "aha" ); printf("%s %s", s, my ); // another variadic function to get rid of comments about printf :) void foo( int i, ... ) { va_list vars; va_start(vars, i); for( const char* p = va_arg(vars,const char*) ; p != NULL ; p=va_arg(vars,const char*) ) { std::cout << p << std::endl; } va_end(vars); } foo( 1, s, my ); 

This fragment leads to the "intuitive" conclusion of "aha." But I do not know how this can work:

  • if the call of the variational function is converted to pressing the argument pointers, printf will get CStringA* , which is interpreted as const char*
  • if calling a function variable calls operator (const char*) on it, why shouldn't it do it for my own class?

Can someone explain this?

EDIT: A dummy variational function has been added that processes it as const char* s. Here - it even crashes when it reaches the argument my ...

+7
source share
7 answers

The corresponding text of the C ++ 98 standard §5.2.2 / 7:

The standard conversion lvalue-to-rvalue (4.1), the standard conversion using the pointer array (4.2) and the standard conversion (4.2) are performed in the argument expression. After these conversions, if the argument does not have arithmetic, an enumeration, a pointer, a pointer to a member, or a class type, the program is poorly formed. If the argument is of a non-POD class type (section 9), the behavior is undefined.

So, formally, the behavior is undefined .

However, this compiler can provide any number of language extensions, and Visual C ++. The MSDN library describes the behavior of Visual C ++ as follows, with respect to passing arguments to ... :

  • If the actual argument is of type float, he is prompted to enter double before calling the function.
  • Any signed or unsigned char, short, enumerated type, or bit-bit is converted to a signature or unsigned int using an entire advertising campaign.
  • Any class type argument is passed by value as a data structure; the copy is created by binary copying, and not by calling the class copy constructor (if one exists).

This does not mean that Visual C ++ uses user-defined conversions.

It is possible that CString::Format , which is the function you are really interested in, depends on the last point above.

Cheers and hth.,

+7
source

What you do is this behavior is undefined and is either a non-standard extension provided by your compiler, or works by pure luck. I assume that CString saves the string data as the first element in the structure, and therefore reading from CString , as if it were a char * , gave a valid string with zero termination.

+6
source

You cannot insert Non-POD data into variable functions. Additional Information

+4
source

if calling variadic-function calls the operator (const char *) on it, why shouldn't it do it for my own class?

Yes, but you must explicitly specify it in your code: printf("%s", (LPCSTR)s, ...); .

+1
source

If a call to a variational function is converted to a click on pointers to arguments, ...

This is not how variational functions work. Values ​​of arguments, not pointers to arguments, are passed after special conversion rules for built-in types (for example, char to int).

C ++ 03 §5.2.2p7:

If there is no parameter for this argument, the argument is passed in such a way that the receiving function can get the value of the argument by calling va_arg (18.7). The standard conversions lvalue-to-rvalue (4.1), array-to-pointer (4.2) and the standard conversions of the function-to-pointer (4.3) are performed in the argument expression. After these conversions, if the argument does not have arithmetic, an enumeration, a pointer, a pointer to a member, or a class type, the program is poorly formed. If the argument is of a non-POD class type (section 9), the behavior is undefined. If the argument has an integer or enumerated type that is subject to integral promotions (4.5) or a floating point type that is subject to promotion with a floating point (4.6), the value of the argument before the call is converted to an advanced type. These promotions are called default promotions.

In particular, from the above:

If the argument is of a non-POD class type (section 9), the behavior is undefined.

C ++ points to C for the definition of va_arg, while C99 TC3 §7.15.1.2p2 says:

... if the type is incompatible with the type of the actual next argument (according to the default promotion), the behavior is undefined, except for the following cases: [list of cases that do not apply here]

Thus, if you pass a class type, it must be a POD, and the receive function must apply the correct type, otherwise the behavior is undefined. This means that in the worst case, it may work exactly as you expect.

Printf will not apply the correct type for any user-defined class type, because it does not know about them, so you cannot pass the UDT class type to printf. Your foo does the same, using a char pointer instead of the correct class type.

+1
source

This is not true. It does not even call operator const char* . Visual C ++ simply passes the class data to printf , as if to memcpy . It works because of the layout of the CString class: it contains only one member variable, which is a pointer to the character data.

+1
source

Your printf statement is incorrect:

 printf("%s", s, my ); 

Must be:

 printf("%s %s", s, my); 

What will print "yeah my."

CString has a conversion operator for const char* (its actually for LPCTSTR , which has const TCHAR* - CStringA has a conversion function for LPCSTR ).

A call to printf does not convert your CStringA object to a CStringA* pointer. He basically sees it as a void* . In the case of CString, it’s just luck (or perhaps the development of Microsoft developers using something that doesn’t conform to the standard) that it will give you a NULL terminating string. If you used _bstr_t instead (which has the size of the string first), despite having a conversion function, this would fail.

It is good practice (and in many cases required) to explicitly point your objects / pointers to what you want them to be when you call printf (or any variational function, for that matter).

0
source

All Articles