Изменение неконстантного массива char, на который ссылается массив const char - PullRequest
3 голосов
/ 08 мая 2019

Рассмотрим следующий код:

struct ns_test{
    char *str;
};

struct ns_test *ns_test_alloc(char *str){
    struct ns_test *nt = malloc(sizeof(*nt));
    nt->str = str;
    return nt;
}

const char *ns_test_get_str(struct ns_test *tst){
    return tst->str;
}

void ns_test_release(struct ns_test* tst){
    free(tst);
}

void ns_test_set_char(struct ns_test *tst, size_t i, char c){
    tst->str[i] = c;
}

int main(void){
    char arr[] = "1234567890";
    struct ns_test *ns_test_ptr = ns_test_alloc(arr);
    const char *str = ns_test_get_str(ns_test_ptr);
    printf("%s\n", str); //1234567890
    ns_test_set_char(ns_test_ptr, 4, 'a');
    printf("%s\n", str); //1234a67890
}

Вопрос : поведение кода не определено?

Я думаю, что это так.

Стандарт указывает на 6.7.3(p6):

Если предпринята попытка изменить объект, определенный с константно-квалифицированный тип посредством использования lvalue с неконстантно-квалифицированным типом тип, поведение не определено.

Итак, чтобы избежать такого рода UB, нам нужно const char *ns_test_get_str(struct ns_test *tst), чтобы вместо этого скопировать char *str. Но главное было избежать копирования и ограничить модификацию единственной void ns_test_set_char(struct ns_test *tst, size_t i, char c), которая может выполнить некоторые проверки работоспособности или что-то еще до этого.

1 Ответ

2 голосов
/ 08 мая 2019

Ключом здесь является «объект, определенный с определением типа const». Важно то, как вы определяете (IOW, «выделяете память») объект, на который указывает. Если «объект» не был «создан» как const, то не имеет значения, сколько константных или неконстантных указателей и ссылок вы используете - вы все равно можете изменить его.

Итак,

const int i = 0;
int *p = (int*)&i;
*p = 1;

- это UB.

Пока

int i = 0;
const int *cp = &i;
int *p = (int*) cp;
*p = 1;

в порядке.

Я подозреваю, что это будет работать даже с new:

const int *cp = new const int(0);
int *p = (int*) cp;
*p = 1;

технически UB.

Он компилирует без предупреждения на cpp.sh, но это мало что значит.

Обновление: как указал Кристиан Гиббонс, язык в вопросе - C, поэтому часть об операторе new не применяется. malloc() а друзья никогда не const.

Чтобы немного это расширить - одна из возможных причин написания стандарта таким образом - дать компилятору свободу использовать постоянную память для значений const. В этом случае запись в такие места становится аварийной или нет. Другими словами, UB.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...