You can always add const-ness to a type. This is a good thing, because it allows you to write functions that make some guarantees.
//Here we know *str is const, so the function will not change what's being pointed to.
int strlength(const char *str) {
int length = 0;
while (*str++)
++length;
return length;
}
int main(void) {
char a[2] = "a"; //a[0] and a[1] are mutable in main(), but not in strlength().
printf("%d", strlength(a));
}
Note that casting away const-ness leads to undefined behavior:
void capitalize(char *str) {
if (isalpha(*str))
*str = toupper(*str);
}
int main(void) {
const char *b = "hello";
capitalize((char*) b); //undefined behavior. Note: without the cast, this may not compile.
}
As for your second question, your first example is correct because the type of a string literal in C (i.e. any sequence of characters between double quotes) is of type const char*
. This is valid:
const char *b = "hello"; //"hello" is of type const char*
b = "text"; //"text" is of type const char*
Because casting away const leads to undefined behavior, this code is invalid:
char *b = "hello"; //undefined behavior; "hello" is const char*
b = "text"; //undefined behavior; "text" is const char*
The case for arrays is a little more involved. When used in expressions, arrays act as pointers, but arrays are a fundamentally different type than pointers:
char a[10];
a = "hello"; //does not compile - "hello" is a const char*; a is a char[10]
However, when used in an initialization statement, the rules state that a const char*
can be used to initialize a character array:
char a[10] = "hello"; //initialization - a is a char[10], "hello" is a const char*
//a will contain {'h','e','l','l','o',0,0,0,0,0}
Also, don't forget that you can assign a string literal to an array with strcpy:
char a[10];
strcpy(a, "hello");
assert(strcmp(a, "hello") == 0);
//a will contain {'h','e','l','l','o',0,x,x,x,x}
//here x's mean uninitialized