Так что является обычной практикой определять параметр метода struct reclamation как указатель на const struct, чтобы мы могли избежать приведения к неконстантному в любое время и выполнить это грязное приведение один раз в реализации метода reclamation?
blockquote>Нет. Чаще всего не использовать
const
с динамически распределенными структурами или со структурами, содержащими указатели на динамически распределенную память.Вы отмечаете только
const
вещи, которые не собираетесь изменять; и освобождение его или данных, на которые ссылаются его члены, является изменением. Просто посмотрите, как объявленоfree()
:void free(void *)
, а неvoid free(const void *)
.Это основная проблема в коде OP, и использование
struct test_struct_t *test_struct_ptr = create(10);
без квалификатораconst
является правильным решением.Здесь есть интересный вопрос, который я хочу немного обсудить, потому что формулировка этого вопроса такова, что те, кто ищет ответы на него, столкнутся с этим вопросом через веб-поиск. [ 1139]
Как правильно вернуть struct?
blockquote>Давайте рассмотрим реальный случай: динамически размещаемый строковый буфер. Есть два основных подхода:
typedef struct { size_t size; /* Number of chars allocated for data */ size_t used; /* Number of chars in data */ unsigned char *data; } sbuffer1; #define SBUFFER1_INITIALIZER { 0, 0, NULL } typedef struct { size_t size; /* Number of chars allocated for data */ size_t used; /* Number of chars in data */ unsigned char data[]; } sbuffer2;
Можно объявить и инициализировать первую версию с помощью макроса инициализатора препроцессора:
sbuffer1 my1 = SBUFFER1_INITIALIZER;
Это используется, например, в. POSIX.1
pthread_mutex_t
мьютексы иpthread_cond_t
условные переменные.Однако, поскольку второй имеет элемент гибкого массива, он не может быть объявлен статически; Вы можете только объявить указатели на него. Итак, вам нужна функция конструктора:
sbuffer2 *sbuffer2_init(const size_t initial_size) { sbuffer2 *sb; sb = malloc(sizeof (sbuffer2) + initial_size); if (!sb) return NULL; /* Out of memory */ sb->size = initial_size; sb->used = 0; return sb; }
, которую вы используете следующим образом:
sbuffer2 *my2 = sbuffer2_init(0);
, хотя я лично реализую соответствующие функции, чтобы вы могли выполнять
[ 1147] как эквивалентsbuffer2 *my2 = NULL;
sbuffer1 my1 = SBUFFER1_INITIALIZER;
.
Функция, которая может увеличивать или уменьшать объем памяти, выделенной для данных, нуждается только в указателе на первую структуру; но либо указатель на указатель на вторую структуру, либо возвращаемый возможно измененный указатель, чтобы изменения были видны вызывающей стороне.
Например, если мы хотим установить содержимое буфера из какого-либо источника, возможно,
int sbuffer1_set(sbuffer1 *sb, const char *const source, const size_t length); int sbuffer2_set(sbuffer2 **sb, const char *const source, const size_t length);
Функции, которые только получают доступ к данным, но не изменяют их, также различаются:
[1151 ] Обратите внимание, чтоint sbuffer1_copy(sbuffer1 *dst, const sbuffer1 *src); int sbuffer2_copy(sbuffer2 **dst, const sbuffer2 *src);
const sbuffer2 *src
не является опечаткой. Поскольку функция не будет изменять указательsrc
(мы могли бы сделать егоconst sbuffer2 *const src
!), Ей не нужен указатель на указатель на данные, только указатель на данные.
Действительно интересной частью являются функции возврата / освобождения.
Функции для освобождения такой динамически выделяемой памяти различаются в одной важной части: первая версия может тривиально отравить поля, чтобы помочь обнаружить ошибки, возникающие при использовании после освобождения:
void sbuffer1_free(sbuffer1 *sb) { free(sb->data); sb->size = 0; sb->used = 0; sb->data = NULL; }
Вторая сложная задача , Если мы будем следовать приведенной выше логике, мы напишем функцию восстановления / освобождения от отравления как
void sbuffer2_free1(sbuffer2 **sb) { free(*sb); *sb = NULL; }
, но поскольку программисты привыкли к шаблону
void *v = malloc(10); free(v);
(в отличие отfree(&v);
!), Они обычно ожидают вместо этого должна бытьvoid sbuffer2_free2(sbuffer2 *sb) { free(sb); }
; и этот не может отравить указатель. Если пользователь не выполнит эквивалент
sbuffer2_free2(sb); sb = NULL;
, существует риск повторного использования содержимогоsb
впоследствии.Библиотеки C обычно не сразу возвращают память в ОС, а просто добавляют ее в свой собственный внутренний свободный список, чтобы использовать его при последующих вызовах
malloc()
,calloc()
илиrealloc()
. Это означает, что в большинстве ситуаций указатель может быть разыменован послеfree()
без ошибки времени выполнения, но данные, на которые он указывает, будут чем-то совершенно другим. Это то, что делает эти ошибки настолько неприятными для воспроизведения и отладки.Отравление просто устанавливает недопустимые значения для элементов структуры, так что использование после освобождения легко обнаруживается во время выполнения благодаря легко видимым значениям. Установка указателя, используемого для доступа к динамически распределенной памяти, на
NULL
означает, что если разыменовать указатель, программа должна завершиться с ошибкой сегментации . Это намного легче отладить с помощью отладчика; по крайней мере, вы можете легко найти точно, где и как произошла авария.Это не так важно в автономном коде, но для библиотечного кода или кода, используемого другими программистами, это может повлиять на общее качество объединенного кода. Это зависит; Я всегда оцениваю это в каждом конкретном случае, хотя я склонен использовать версию-указатель-член-отравление для примеров.
В этом ответе я больше и меньше обращал внимание на элементы указателя по сравнению с элементами гибкого массива . Это может быть интересно тем, кто интересуется, как вернуть / освободить структуры и как выбрать, какой тип (элемент указателя или элемент гибкого массива) использовать в различных случаях.
Править: согласно предложению Halr9000
$foo = "300-";
$bar = 0;
$numberStyles = [System.Globalization.NumberStyles];
$cultureInfo = [System.Globalization.CultureInfo];
[int]::TryParse($foo, $numberStyles::AllowTrailingSign, $cultureInfo::CurrentCulture, [ref]$bar);
[System.Globalization.NumberStyles]::AllowTrailingSign
Я должен также указать, что, когда я имею дело с перечислениями в целом, иногда я могу добраться путем ввода строки. Например, в этом случае, просто помещенный
"AllowTrailingSign"
Заключительное примечание, опрашивая Перечисление для всех возможных значений, использует строку:
[System.Globalization.NumberStyles] | gm -static
Вот лучший способ получить перечислимые значения:
$type = [System.Globalization.NumberStyles]
[enum]::GetValues($type)
Если Вы уверены, что знаки могли быть - или +, Строка. Замена могла помочь.
Если Вы подразумеваете, что 323-должен возвратиться-323, проверение на знак и умножение его-1 помогли бы.