Как правильно восстановить структуру?

Вот способ сделать это без Javascript, и он также совместим с любым браузером.


EDIT: в Safari input отключается, когда скрывается с помощью display: none. Лучшим подходом было бы использовать position: fixed; top: -100em.


<label>
  Open file dialog
  <input type="file" style="position: fixed; top: -100em">
</label>

Кроме того, если вы предпочитаете, вы можете пойти «правильный путь» , используя for в label, указывающий на id входа следующим образом:

<label for="inputId">file dialog</label>
<input id="inputId" type="file" style="position: fixed; top: -100em">
0
задан iBug 18 January 2019 в 09:17
поделиться

1 ответ

Так что является обычной практикой определять параметр метода struct reclamation как указатель на const struct, чтобы мы могли избежать приведения к неконстантному в любое время и выполнить это грязное приведение один раз в реализации метода reclamation?

Нет. Чаще всего не использовать const с динамически распределенными структурами или со структурами, содержащими указатели на динамически распределенную память.

Вы отмечаете только const вещи, которые не собираетесь изменять; и освобождение его или данных, на которые ссылаются его члены, является изменением. Просто посмотрите, как объявлено free() : void free(void *), а не void free(const void *).

Это основная проблема в коде OP, и использование struct test_struct_t *test_struct_ptr = create(10); без квалификатора const является правильным решением.


Здесь есть интересный вопрос, который я хочу немного обсудить, потому что формулировка этого вопроса такова, что те, кто ищет ответы на него, столкнутся с этим вопросом через веб-поиск. [ 1139]

Как правильно вернуть struct?

Давайте рассмотрим реальный случай: динамически размещаемый строковый буфер. Есть два основных подхода:

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);

, хотя я лично реализую соответствующие функции, чтобы вы могли выполнять

    sbuffer2 *my2 = NULL;
[ 1147] как эквивалент 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);

Функции, которые только получают доступ к данным, но не изменяют их, также различаются:

int  sbuffer1_copy(sbuffer1 *dst, const sbuffer1 *src);

int  sbuffer2_copy(sbuffer2 **dst, const sbuffer2 *src);
[1151 ] Обратите внимание, что 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 означает, что если разыменовать указатель, программа должна завершиться с ошибкой сегментации . Это намного легче отладить с помощью отладчика; по крайней мере, вы можете легко найти точно, где и как произошла авария.

Это не так важно в автономном коде, но для библиотечного кода или кода, используемого другими программистами, это может повлиять на общее качество объединенного кода. Это зависит; Я всегда оцениваю это в каждом конкретном случае, хотя я склонен использовать версию-указатель-член-отравление для примеров.

В этом ответе я больше и меньше обращал внимание на элементы указателя по сравнению с элементами гибкого массива . Это может быть интересно тем, кто интересуется, как вернуть / освободить структуры и как выбрать, какой тип (элемент указателя или элемент гибкого массива) использовать в различных случаях.

0
ответ дан Nominal Animal 18 January 2019 в 09:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: