Keith Thompson
2023-06-02 04:41:03 UTC
The latest draft of the upcoming C23 standard is:
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
It introduces several type-generic functions in <string.h>, replacing
normal functions of the same names: memchr, strchr, strpbrk, strrchr,
strstr.
I'll use strchr() as an example; the same applies to the other str*()
generic functions (but not to memchr()).
The problem this solves is that calling strchr() with a const char*
argument yields a non-const char* result that points into the array.
For example:
#include <stdio.h>
#include <string.h>
int main(void) {
const char s[] = "hello";
char *p = strchr(s, 'h');
*p = 'J'; // Undefined behavior
printf("%s\n", s); // Likely to print "Jello"
}
This makes it possible to obtain a non-const pointer to a const object
without a pointer cast.
The C23 strchr() generic function returns a char* if the first argument
is a char*, or a const char* if the first argument is a const char*.
The stateless search functions in this section (memchr, strchr,
strpbrk, strrchr, strstr) are *generic functions*. These functions
are generic in the qualification of the array to be searched and
will return a result pointer to an element with the same
qualification as the passed array. If the array to be searched is
const-qualified, the result pointer will be to a const-qualified
element. If the array to be searched is not const-qualified, the
result pointer will be to an unqualified element.
So far so good, and I definitely approve of this change. It does break
code that calls strchr() with a const char* argument and assigns the
result to a (non-const) char* object. That's IMHO a minor issue, and
arguably breaking such code is part of the point of the change. (Making
string literals const would be similar, but I suppose that's still a
bridge too far.)
But I've thought of away in which this could break some existing valid
code, namely code that passes a void* or const void* argument to
strchr().
Currently, since void* can be implicitly converted to char* and vice
versa, such a call is valid. (I can't think of a *good* reason to write
such a call, but my imagination is not unlimited.)
Question: Is this a valid call in C23? (It's valid in C17.)
char hello[] = "hello";
void *p = strchr((void*)hello, 'h');
An implementation of the generic strchr() will presumably use a generic
selection in a macro definition. If the generic selection covers only
types char* and const char*, the call will violate a constraint. If it
also covers void* and const void*, the call will be valid.
The current wording in N3096 suggests that only char* and const char*
are covered, implying that a call with a void* or const void* argument
is a constraint violation.
I suggest that the C23 standard should specify whether void* arguments
are valid or not. I have a slight preference for making them valid. If
so, the simplest approach would be for strchr() to return a char* given
a char* or void* argument, or a const char* given a const char* or const
void* argument.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
It introduces several type-generic functions in <string.h>, replacing
normal functions of the same names: memchr, strchr, strpbrk, strrchr,
strstr.
I'll use strchr() as an example; the same applies to the other str*()
generic functions (but not to memchr()).
The problem this solves is that calling strchr() with a const char*
argument yields a non-const char* result that points into the array.
For example:
#include <stdio.h>
#include <string.h>
int main(void) {
const char s[] = "hello";
char *p = strchr(s, 'h');
*p = 'J'; // Undefined behavior
printf("%s\n", s); // Likely to print "Jello"
}
This makes it possible to obtain a non-const pointer to a const object
without a pointer cast.
The C23 strchr() generic function returns a char* if the first argument
is a char*, or a const char* if the first argument is a const char*.
The stateless search functions in this section (memchr, strchr,
strpbrk, strrchr, strstr) are *generic functions*. These functions
are generic in the qualification of the array to be searched and
will return a result pointer to an element with the same
qualification as the passed array. If the array to be searched is
const-qualified, the result pointer will be to a const-qualified
element. If the array to be searched is not const-qualified, the
result pointer will be to an unqualified element.
So far so good, and I definitely approve of this change. It does break
code that calls strchr() with a const char* argument and assigns the
result to a (non-const) char* object. That's IMHO a minor issue, and
arguably breaking such code is part of the point of the change. (Making
string literals const would be similar, but I suppose that's still a
bridge too far.)
But I've thought of away in which this could break some existing valid
code, namely code that passes a void* or const void* argument to
strchr().
Currently, since void* can be implicitly converted to char* and vice
versa, such a call is valid. (I can't think of a *good* reason to write
such a call, but my imagination is not unlimited.)
Question: Is this a valid call in C23? (It's valid in C17.)
char hello[] = "hello";
void *p = strchr((void*)hello, 'h');
An implementation of the generic strchr() will presumably use a generic
selection in a macro definition. If the generic selection covers only
types char* and const char*, the call will violate a constraint. If it
also covers void* and const void*, the call will be valid.
The current wording in N3096 suggests that only char* and const char*
are covered, implying that a call with a void* or const void* argument
is a constraint violation.
I suggest that the C23 standard should specify whether void* arguments
are valid or not. I have a slight preference for making them valid. If
so, the simplest approach would be for strchr() to return a char* given
a char* or void* argument, or a const char* given a const char* or const
void* argument.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */