Почему я могу указать указатель const char на изменяемый массив символов? - PullRequest
2 голосов
/ 21 февраля 2020

Почему это хорошо:

char a[2];
a[0]='a';
const char* b;
b=a;
printf("%s\n",b);
a[0]='b';
printf("%s",b);

Почему указатель на постоянную строку может указывать на непостоянную строку? Также как работают строковые константы при назначении их переменным? Почему вы можете сделать это:

const char* b="hello";
b="test";

или

char* b="hello";
b="test";

, но вы можете сделать только первую строку, если это массив? постоянная или нет?

Ответы [ 2 ]

3 голосов
/ 21 февраля 2020

Почему я могу указать const char * на изменяемый массив char?

const здесь с b накладывает ограничение на его использование. Это не позволяет какое-то новое использование, но потенциально меньше. Так что здесь нет Ящика Пандоры .

char a[2];
const char* b = a;

b не имеет доступа для чтения к массиву. Массив a[] все еще может меняться с помощью a.

a[0]='y'; // OK
b[0]='z'; // leads to error like "error: assignment of read-only location "

Кроме того, как работают строковые константы при назначении их переменным? константа или нет?

"hello" - это строковый литерал типа char [6]. При объявлении и инициализации const char* b1 = "hello" b1 назначается указатель типа char *. Это могло быть const char *, но по историческим причинам 1 это char *. Поскольку это char*, char* b2 = "hello"; тоже в порядке.

const char* b1 = "hello";
char* b2 = "hello";

Кроме того, как работают строковые константы при назначении их переменным?

b="test";

Как часть назначения, строковый литерал , массив char, преобразуется в адрес и тип его первого элемента, a char * , Этот указатель присваивается b.


1 const не было доступно в начале C, поэтому, чтобы не сломать существующие базы кода, строковые литералы остался без const.

0 голосов
/ 21 февраля 2020

Вы всегда можете добавить константу к типу. Это хорошая вещь, потому что она позволяет вам писать функции, которые дают некоторые гарантии.

//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
...