Вы всегда можете добавить константу к типу. Это хорошая вещь, потому что она позволяет вам писать функции, которые дают некоторые гарантии.
//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));
}
Обратите внимание, что отбрасывание константности ведет к неопределенному поведению:
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.
}
Что касается вашего второго вопроса, ваш первый пример верен, потому что тип строкового литерала в C (т.е. любая последовательность символов между двойными кавычками) имеет тип const char*
. Это действительно так:
const char *b = "hello"; //"hello" is of type const char*
b = "text"; //"text" is of type const char*
Поскольку отбрасывание const приводит к неопределенному поведению, этот код недопустим:
char *b = "hello"; //undefined behavior; "hello" is const char*
b = "text"; //undefined behavior; "text" is const char*
Случай с массивами немного сложнее. При использовании в выражениях массивы действуют как указатели, но массивы имеют принципиально иной тип, чем указатели:
char a[10];
a = "hello"; //does not compile - "hello" is a const char*; a is a char[10]
Однако при использовании в операторе инициализации правила утверждают, что const char*
может использоваться для инициализировать массив символов:
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}
Кроме того, не забывайте, что вы можете присвоить строковый литерал массиву с помощью 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