Почему c99 const arg позволяет изменять дополнительные значения без предупреждений?

#c #c99

#c #c99

Вопрос:

Я не занимался C или C уже более десяти лет, и я работаю над небольшим проектом, чтобы снова освоиться с ним, и пытаюсь определить хорошие и плохие привычки, где я могу. Одна вещь, с которой я столкнулся, это const аргументы, которые не только должны быть сигналом для потребителей о том, что значения не изменятся. Я большой поклонник сигналов неизменности, поэтому я подумал, что это было бы здорово.

Пока я не начал играть с этим и не написал следующий код:

 #include <stdio.h>
#include <stdlib.h>

struct A {
    int value;
};

struct B {
    struct A* a;
};

void DoStuff(const struct B* b) {
    b->a->value = 5;
}

int main() {
    struct B* b = malloc(sizeof(struct B));
    b->a = malloc(sizeof(struct A));
    DoStuff(b);

    printf("Value: %in", b->a->value);
}
 

К моему большому удивлению, когда я скомпилировал via gcc -Wall -Wextra -std=c99 test.c -o test.exe , я не получил никаких предупреждений, ошибок или чего-либо еще, и все работало так, как ожидалось.

Итак, что const должно сигнализировать? Я не уверен, что считаю правильным указывать const там, где это позволяет компилятор, потому что, если я использую его в подобных ситуациях, потребители могут не ожидать изменения внутренних значений, и я беспокоюсь о том, чтобы использовать функции, которые являются чистыми прямо сейчас, но я добавляю побочные эффекты позже, не забывая удалять const .

Ответ №1:

const Защищает элементы самого struct B себя. Таким образом, вы не можете переназначить указатель, хранящийся в a , но поскольку этот указатель не указывает на a const struct A , вы все равно можете изменять члены структуры, на которую он указывает.

Ответ №2:

Итак, что const должно сигнализировать?

При void DoStuff(const struct B* b) этом данные b , на которые указывают, не должны быть изменены. При b->a->value = 5; этом данные b , на которые указывают, не меняются. b->a остается неизменным.

value изменения, но это не данные, на которые указывает a .

Хорошие компиляторы предупреждают, когда код пытается напрямую изменить const данные.


Изменение помеченных данных const в отдельных случаях допустимо.

Было бы нормально, если бы код изменился b->a так, (struct B*)b->a = 0 как struct B* b если бы изначально была назначена точка, не являющаяся const данными. Если бы данные b указывали на been const , то (struct B*)b->a = 0 это было бы неопределенное поведение (UB).