In a C function, an argument of type char *a
can be passed as a pointer or as an array.
In the function below I'm using that fact to execute function func
. This function replaces the first character of an array.
#include <stdio.h>
void func(char* var) {
var[0] = 'X';
}
int main() {
char a[] = "HELLO";
func(a); // OK
char *b = "HELLO";
func(b); // ERROR
}
As an author of such a function, how can I guard against someone passing a pointer? In my function I want only arrays to be passed (so, a pointer to an actual array).
CodePudding user response:
Using a question I asked earlier, How do I force a warning from using an array of wrong size when passed to function? here is a way:
void foo(char (*arg)[]) {
char *p = *arg;
p[0]='X';
}
int main(void) {
char s[1];
char *p;
// These three will throw a warning
foo(p);
foo(&p);
foo(s);
// But this will not
foo(&s);
}
So the drawback is that you need to use the &
operator for this approach to work. Solving it without this requirement would be tricky and ugly if it is at all possible. The reason is that an array will automatically decay to a pointer in most situations.
CodePudding user response:
The actual issue here is whether the function is passed a pointer to data that may be changed or not, not whether the function is passed a string or not.
Per C 2018 7.1.1 1, “A string is a contiguous sequence of characters terminated by and including the first null character.” So, with char a[] = "HELLO";
, the array contains a string.
With char *b = "HELLO";
, b
points to the first character of a string literal1. The data in a string literal ought to be treated as constant—a program should not attempt to modify it, and the C implementation may put it in read-only memory so that attempting to modify it results in a program exception.
If we were designing the C language from scratch, string literals would be qualified with const
. However, const
did not exist when string literals were created. Programmers simply had to know when data was part of a string literal, and it was, and still is, their responsibility to avoid attempting to change it.
To some extent, you can ameliorate this issue by adding const
manually, as with const char *b = "HELLO";
. Then attempting to pass b
to func
will result in a compiler diagnostic message about attempting to pass const char *
argument for a char *
parameter.
If it were deemed sufficiently important to guard against bugs, all string literals could be written as compound literals, such as (const char []) { "HELLO" }
, and a macro could be defined to do this:
#define StringLiteral(x) ((const char []) { (x) })
Footnote
1 Technically, a string literal is the characters in the source code, such as "HELLO"
, including the quotation marks. When a program is compiled, a string literal in source code results in creation of an array containing a string. Informally, we speak of this array as a string literal.