Does any of the C Standards describe any kind of a check to ensure that const
is used and accessed appropriately with variable argument lists?
If I have the following source for a function with a variable argument list:
int func (char *pBuffer, int nLen, ...)
{
va_list ArgList;
va_start(ArgList, nLen);
char *pName = va_arg(ArgList, char*);
// do things with the string such as copy it to an output buffer.
strcpy (pBuffer, pName); // works. source string argument declared const char *, not modified by strcpy()
*pName = 'x'; // error here running in Debug mode, write access violation.
va_end(ArgList);
return 0;
}
And I want to call this function with a const
argument as in:
int func2(char *pBuff, int nLen, const char *pName)
{
return func(pBuff, nLen, pName);
}
I have tried this with Visual Studio 2019 Community Edition and it compiles with no warnings on Warning level 3 using the C11 standard.
When I try to run a Debug compile, I see a write violation exception thrown. When I try a Release compile it runs but the exit code is wrong probably because something important was trampled.
It appears in an AI created source example that the argument pName
in the above function, func()
could be accessed with const char *p = va_arg(ArgList, const char*);
to enforce const
however not doing so does not appear to make any difference to the compiler.
So it appears from my testing that using a const
variable as a parameter to a function with a variable argument list depends on the called function to be conservative and treat variables as const
unless the argument is designated to be non-const by the function documentation.
If the called function does not specify the const
modifier in the va_arg()
call and the variable definition used with the va_arg()
and the called function then tries to modify the variable which may be const
then is it Undefined Behavior?
I suppose that vsprintf()
takes such a conservative stance with strings and only modifies the output buffer treating the variables in the list of arguments to print as being const
?
So it appears that when using variable argument lists a coding policy would be to use const char *p = va_arg(ArgList, const char*);
by default unless the argument is explicitly designed to have a modifiable value.
And it doesn't hurt to specify const
in other function interfaces even if the leaf function called is using variable arguments. I suppose in many cases function higher up in the call tree may never know the leaf function and whether it uses variable argument list or not.
And I want to call this function with a
const
argument as in:…
Your pName
argument is not a const
argument. It is a (non-const
) pointer to a const char
. That distinction is important when dealing with const
.
It is defined behavior for a const char *
argument to be fetched with va_arg
using char *
per C 2024 7.16.2.2:
… If type [the type passed to
va_arg
] is not compatible with the type of the actual next argument…, the behavior is undefined, except for the following cases:— both types are pointers to qualified or unqualified versions of compatible types;…
char
is compatible with char
, so both const char *
and char *
are pointers to qualified or unqualified versions of compatible types. (Further, 6.2.5 tells us “…pointers to qualified or unqualified versions of compatible types shall have the same representation…”, and a footnote tells us this is meant to imply interchangeability.)
If the called function does not specify the
const
modifier in theva_arg()
call and the variable definition used with theva_arg()
and the called function then tries to modify the variable which may beconst
then is it Undefined Behavior?
With regard to const
, what determines whether attempting to modify an object has undefined behavior or not is how the object was defined. That is, it does not matter whether the argument passed to the function pointed to a const
type. It matters whether the actual object pointed to was defined with const
. 6.7.4.1 says:
… If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined…
For example, if we have:
char Buffer[1024];
func2(whatever, whatever, Buffer);
then func2
receives Buffer
for pName
and passes this pName
of type const char *
to func
, which then takes it as a char *
and attempts to modify it. That modification is allowed, because Buffer
was not defined with const
. The fact that pName
was const char *
is irrelevant.
I suppose that
vsprintf()
takes such a conservative stance with strings and only modifies the output buffer treating the variables in the list of arguments to print as beingconst
?
vsprintf
attempts to do what you tell it to do. It attempts to write to the buffer you pass it, and it attempts to write to an object you pass it for the n
conversion (which reports how many characters have been written by vsprintf
so far), and it does not attempt to write to anything else except its own workspace (its own automatic variables and such).