константная корректность для структур с указателями

У меня есть структура, которая содержит несколько указателей. Я хочу, чтобы их значение было неизменяемым. Но простое написание const впереди не делает элементы структуры неизменяемыми.

typedef struct{
  int *x;
  int *y;
}point;

void get(const  point *p,int x, int y){
  p->x[0]=x;//<- this should not be allowed
  p->y[0]=y;//<- this should not be allowed
}

Может ли кто-нибудь указать мне в правильном направлении.

РЕДАКТИРОВАТЬ:

Таким образом, может показаться, что нет простого способа использовать прототип функции, чтобы сказать, что все, что принадлежит структуре, должно быть немодифицируемым.


person monkeyking    schedule 01.11.2012    source источник
comment
Что вы хотите неизменяемый? Указанный на ints? Тогда const int *x; означает, что вы не можете изменить указанное значение через этот указатель. Указатели? Затем int * const x; запрещает изменять указатели.   -  person Daniel Fischer    schedule 01.11.2012
comment
Если мой int *x представляет собой массив, то я хочу, чтобы значения в этом массиве не поддавались изменению.   -  person monkeyking    schedule 01.11.2012
comment
Тогда вам нужно const int *x; в определении структуры. Обратите внимание, что значения в массиве все еще могут быть изменены с помощью других указателей (которые могут вызвать неопределенное поведение, если x указывает на элемент const int arr[3] = { 15, 7, 3 }; или около того).   -  person Daniel Fischer    schedule 01.11.2012
comment
int *x это не массив, это указатель. Если бы вы объявили int x[20], то массив был бы частью области consted. Но x является указателем, и его constness не зависит от p.   -  person Patrick Schlüter    schedule 01.11.2012
comment
Ваш комментарий к редактированию неверен в том смысле, что const делает именно это: он говорит, что все, что принадлежит этому struct, не изменяется этой функцией. Ошибка вашего мышления состоит в том, что вы путаете области, на которые указывает p, и области, на которые указывают p-›x и p-›y , они не являются частью структуры. Посмотрите на мой ascii-арт ниже, ваша структура - это только нарисованная часть, остальное отчетливо.   -  person Patrick Schlüter    schedule 01.11.2012
comment
@tristopia Я понимаю, почему это не сработает. И именно поэтому я жалуюсь. Я хочу обеспечить неизменность структуры и всего, что с ней связано. Включая адреса указателей и актуальные данные по этим адресам.   -  person monkeyking    schedule 02.11.2012


Ответы (4)


Чтобы объяснить, что вам нужно установить, когда вы пишете

point str;

point *p=&str;

Здесь p — указатель на str типа point.

Когда вы объявляете его как const, это означает, что p является константным указателем. Это не ограничивает указатели, которые может содержать структура.

Если вы хотите, чтобы const применялась внутри структуры, вы должны определить указатели внутри структуры также как const

typedef struct{
   const int *  x;
   const int *  y;
}point;

Снова, чтобы подтолкнуть мою точку зрения, объявите параметр как

    void get(point * const  p,int x, int y) 
   //Constant Pointer ( *to prevent p from pointing to anything else*)

    //    AND

   //Make the pointers inside point structure constant
   //( *to prevent the int pointers x & y from pointing to anything else*)

Если структура, на которую он указывает, также является константой, используйте

      void get(const point * const p, int x, int y)
     //Constant Pointer to constant structure 
person Desert Ice    schedule 01.11.2012
comment
Если мои члены моей структуры объявлены константными, как мне ввести в нее значения в начале моей программы. - person monkeyking; 01.11.2012
comment
Путем приведения типа к неконстантному указателю. (int*)(p->x) = &whatever. Хотя может показаться, что это противоречит цели constness. Это может быть очень полезно в больших проектах, где вы хотите убедиться, что изменения в структуре будут выполняться только в контролируемом уникальном месте. - person Patrick Schlüter; 01.11.2012
comment
Поскольку содержимое указателя является константой, его следует инициализировать во время объявления. - person Desert Ice; 01.11.2012
comment
IMO нет смысла consting сам указатель (или любой другой параметр функции). Константность области, на которую указывает указатель, важна, она говорит читателю, что через этот указатель нет побочных эффектов, но константность самого указателя не несет никакой полезной информации ни для компилятора, ни для программиста. - person Patrick Schlüter; 01.11.2012
comment
@PatrickSchlüter Это просто предотвращает случайное переназначение указателя внутри функции. например if(myStruct = NULL) в верхней части функции будет довольно неясной ошибкой для любого, кто просто читает код. - person Dan Bechard; 28.05.2016
comment
В void get(const point * const p, int x, int y) указатель p передается по значению, поэтому функция не может изменить его, чтобы он указывал куда-то еще. const является избыточным, вместо него должно быть void get(const point *p, int x, int y). - person biscuits; 06.02.2019

Вы можете сделать это без приведения типов, определив константный тип точки и изменяемый тип точки, а затем используйте прозрачное объединение:

typedef struct{
    const int *  x;
    const int *  y;
} const_point;

typedef struct{
    int *  x;
    int *  y;
} mutable_point;

typedef union __attribute__((__transparent_union__)) {
    const_point cpoint;
    mutable_point point;
} point;

Затем вы объявляете параметры своей функции, используя либо тип point, либо тип const_point (никогда не тип mutable_point).

Объект типа point будет прозрачно приведен к типу const_point, но не наоборот. Это позволяет вам иметь большую степень безопасности типов.

См. здесь пример в gcc: http://toves.freeshell.org/xueg/

Обратите внимание, что прозрачное объединение не поддерживалось в последней версии C++, которую я проверял (не уверен в последнем стандарте C++), поэтому вы можете ожидать проблем с переносимостью.

Это также может затруднить чтение и поддержку кода, особенно если у вас более сложная структура. например: у вас может быть тип точки, где x или y является константой, или вам может потребоваться встроить структуру точки в другую структуру, например. прямоугольник, для которого вам, возможно, придется определить несколько структур для нескольких типов в зависимости от их константности.

В общем, я не уверен, что это всегда стоит дополнительных хлопот.

person Droopycom    schedule 17.10.2013
comment
Это может стоить проблем, если вы хотите использовать ссылки, чтобы вы могли создавать экземпляры с помощью rvalue (поскольку вы можете использовать rvalue с постоянными ссылками). Это, пожалуй, единственный случай, когда мне это кажется весьма полезным. - person meneldal; 18.05.2015

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

Если так, то, к сожалению, на языке C это недостижимо.

В C++ это можно сделать, заставив пользователя использовать функцию-аксессор для доступа к элементам данных (вместо прямого доступа к элементам данных). Но в C это просто невозможно сделать.

person AnT    schedule 01.11.2012

Это потому, что вы меняете содержимое памяти, на которое указывает другой указатель, а не p.

p указывает на структуру, содержащую 2 указателя на int. Вы меняете не память, на которую указывает p, а другую область памяти. Так что с компилятором все в порядке.

       +----------+
p ->   |    x     |  -> wherever  
       +----------+
       |    y     |  -> another place in memory
       +----------+

constness od p не передается по наследству. Если бы вы написали p->a = array;, компилятор бы пожаловался. const - это всего лишь контракт, в котором говорится, что вы не будете изменять память с помощью этого указателя.

person Patrick Schlüter    schedule 01.11.2012