Указатель на структуру const может быть изменен?
У меня есть структура, которую я хочу передать некоторому внешнему c-коду через некоторую функцию обратного вызова, которую они регистрируют в моей программе. Однако я хочу передать эту структуру как доступную только для чтения. Меня беспокоит то, что они все еще могут изменять структуры, которые у меня есть указатель внутри исходной структуры, которую я передаю. Разъяснение с небольшим примером ниже:
struct s1 {
int a;
int b;
};
struct s2 {
int x;
struct s1 *y;
};
void f(const struct s2 *o)
{
//o->x=10; //error
o->y->a=20; //no error
o->y->b=30; //no error
}
int main()
{
struct s1 o1 = {10, 20};
struct s2 o2 = {30, &o1};
f(&o2);
}
Итак, как мне улучшить дизайн кода, чтобы они ничего не могли изменить по структуре, которую я передал?
Ответы
Ответ 1
Чтобы правильно справиться с этой ситуацией, вы можете использовать только объявление вперед, чтобы скрыть участников вместе с функциями getter и setter.
Сосредоточьтесь на приведенном ниже коде и проверьте:
-
struct s1
имеет только объявление вперед, поэтому вы можете сделать указатель на него в struct s2
. - Фактическая реализация
struct s1
находится в mylib.c
поэтому все участники видны только вашей библиотеке, а не пользователю. - Getters и seters реализованы для установки/чтения значения для этих скрытых членов, поскольку только ваша библиотека имеет доступ к членам, что делает его полностью скрытым от пользователя.
- Это заставляет его использовать ваши функции.
mylib.h:
#ifndef __MYLIB_H
#define __MYLIB_H
//Create forward declaration only
//Implementation is in .c file
struct s1;
//Create user structure
struct s2 {
int x;
struct s1* y;
};
int get_a_from_s1(struct s2* s);
void set_a_to_s1(struct s2* s, int a);
#endif /* __MYLIB_H */
mylib.c:
#include "mylib.h"
//Now implement structure
struct s1 {
int a, b;
};
//Make getter
int
get_a_from_s1(struct s2* s) {
return s->y->a;
}
//Make setter
void
set_a_to_s1(struct s2* s, int a) {
s->y->a = a;
}
main.c:
#include <stdio.h>
#include "mylib.h"
int main(void) {
struct s2 s;
int a;
....
s.y->a = 5; //error
//Set s1.a value from s2 structure
set_a_to_s1(&s, 10); //OK
//To view members of s1 inside s2, create member functions
a = get_a_from_s1(&s); //OK
printf("a: %d\r\n", a);
return 0;
}
Конечно, убедитесь, что ->y
не является NULL
или у вас есть неопределенное поведение.
Ответ 2
Ты не можешь. Даже если вы передадите struct s2
по значению, вы получите в функции указатель на не const struct s1
, просто потому, что это то, что содержит s2
по его определению.
И как только у вас есть указатель на объект non const, вы можете изменить этот объект. То, что я имею в виду здесь и что другие ответы означают, заключается в том, что это не проблема языка - более точно, язык для вас здесь ничего не может, но проблема дизайна. Если по какой-либо причине неприемлемо, что struct s1
может быть изменена с f
вам нужно найти другой дизайн, в котором вы не передадите ему не const-указатель, будь он членом структуры const или нет. Здесь простой способ состоял бы в том, чтобы передать отдельные члены:
void f(int x, const struct s1 *y) {
y->a = 20; // error
}
Возможно, это не так, как вы ожидаете, но это лучшее, что я могу сказать для языка C.
Ответ 3
Вы можете изменить второе объявление структуры следующим образом:
struct s2 {
int x;
struct s1 const *y;
};
Добавленный const
гарантирует, что y доступен только для чтения.
Ответ 4
Я бы написал еще одну функцию, которая принимает в const struct s1 * для изменения значений s1.
Ответ 5
В f()
const struct s2 *o
означает, что в struct s2
указывается o
, значения ее членов, т.е. x
и y
не могут быть изменены.
С помощью o->y->a=20
вы не изменяете значение y
. Вы просто используете этот адрес для изменения члена структуры, на который указывают значения y
и y
, которые являются адресом, остаются неизменными.
Таким образом, эти 2 строки не будут давать никаких ошибок.
Я не вижу возможности избежать этого, если вы не можете сделать определение struct s2
struct s2 {
int x;
const struct s1 *y;
};
в этом случае y
является указателем на константу struct s1
.
См. Правило спирали и посетите веб-сайт cdecl.