Почему выделение памяти для структуры в C работает с любым значением, заданным для mallo c? - PullRequest
0 голосов
/ 03 мая 2020

Я пытался найти правильный способ динамического выделения памяти для структуры, которая выглядит следующим образом:

typedef struct myThread {
    unsigned int threadId;
    char threadPriority;
    unsigned int timeSlice;
    sem_t threadSem;
} myThread;

Я помню, но я не уверен, что в какой-то школьной газете я видел что правильный способ выделить память для этого случая таков:

myThread *node = (myThread *)malloc(sizeof(myThread *));

Я попробовал это, и это сработало, но я не понял почему. Указатель sizeof для моей архитектуры составляет 8 байтов, поэтому, написав приведенную выше инструкцию, я выделяю 8 байтов непрерывной памяти, что недостаточно для хранения информации, необходимой в моей структуре. Поэтому я попытался выделить 1 байт памяти следующим образом:

myThread *node = (myThread *)malloc(1);

И он все еще работает. Я пытался найти ответ для этого поведения, но мне не удалось. Почему это работает? Кроме того, у меня есть еще несколько вопросов:

  • Какой правильный способ динамически распределять память для структуры?
  • Нужно ли это приведение?
  • Как это сделать? структура хранится в памяти? Я знаю, что (* node) .threadId эквивалентен node-> threadId, и это немного смущает меня, потому что, разыменовав указатель на структуру, я получаю всю структуру, а затем мне нужно получить доступ к указанному c полю. Я ожидал получить доступ к полям, зная адрес структуры следующим образом: * (node) это значение для первого элемента, * (node ​​+ sizeof (firstElement)) это значение для второго и так далее. Я думал, что доступ к полям структуры это похоже на доступ к значениям массива.

Спасибо

Позже Редактировать: Спасибо за ваши ответы, но я понял, что не объяснил себя должным образом. Говоря, что это работает, я имею в виду, что оно работало для хранения значений в указанных полях c структуры и использования их позже. Я проверил это, заполнив поля и напечатав их потом. Интересно, почему это работает, почему я могу заполнять и работать с полями структуры, для которой я выделил только один байт памяти.

Ответы [ 4 ]

3 голосов
/ 03 мая 2020

Ниже работает то, что они выделяют память - но неправильный размер.

myThread *node = (myThread *)malloc(sizeof(myThread *));// wrong size,s/b sizeof(myThread) 
myThread *node = (myThread *)malloc(1);                 // wrong size 

Почему это работает?

Когда код пытается сохранить данные в этот адрес, неправильный размер может стать или не стать очевидным. Это неопределенное поведение (UB).

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

myThread *node = (myThread *)malloc(1);  // too small
node->timeSlice = 42;  // undefined behavior

Какой правильный способ динамически выделить память для структуры? @ MM

Приведенный ниже код легко корректируется, просматривается и поддерживается.

p = malloc(sizeof *p);  //no cast, no type involved.
// or
number_of_elements = 1;
p = malloc(sizeof *p * number_of_elements);

// Robust code does error checking looking for out-of-memory
if (p == NULL) {
  Handle_error();
}

Нужно ли это приведение?

Нет. Должен ли я привести результат mallo c?

Как структура сохраняется в памяти?

Каждый элемент сопровождается потенциальным заполнением. Это зависит от реализации.

unsigned int
maybe some padding
char
maybe some padding
unsigned int
maybe some padding
sem_t
maybe some padding

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

OP ищет причину, по которой он работает.

Возможно, выделение памяти выполняется кусками по 64 байта или чем-то большим, чем sizeof *p, поэтому выделение 1 имеет тот же эффект, что и sizeof *p .

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

Возможно, распределитель - злобный зверь, играющий с OP, только чтобы уничтожить жесткий диск следующим 1 апреля . (Нечестивый код часто использует UB для заражения систем - это не так уж надумано)

Все это UB. Все может случиться.

1 голос
/ 03 мая 2020

Поскольку распределение памяти в C весьма подвержено ошибкам, я всегда определяю макрофункции NEW и NEW_ARRAY , как в примере ниже. Это делает распределение памяти более безопасным и лаконичным.

#include <semaphore.h> /*POSIX*/
#include <stdio.h>
#include <stdlib.h>

#define NEW_ARRAY(ptr, n) \
    { \
        (ptr) = malloc((sizeof (ptr)[0]) * (n)); \
        if ((ptr) == NULL) { \
            fprintf(stderr, "error: Memory exhausted\n"); \
            exit(EXIT_FAILURE); \
        } \
    }

#define NEW(ptr) NEW_ARRAY((ptr), 1)

typedef struct myThread {
    unsigned int threadId;
    char threadPriority;
    unsigned int timeSlice;
    sem_t threadSem;
} myThread;

int main(void)
{
    myThread *node;
    myThread **nodes;
    int nodesLen = 100;

    NEW(node);
    NEW_ARRAY(nodes, nodesLen);
    /*...*/
    free(nodes);
    free(node);
    return 0;
}
0 голосов
/ 03 мая 2020

malloc резервирует память для использования.

Когда вы пытаетесь использовать больше памяти, чем запрошено, возможны несколько результатов, включая:

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

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

Какой правильный способ динамического выделения памяти для структура?

Хороший код: myThread *node = malloc(sizeof *node);.

Нужно ли это приведение?

Нет , не в C.

Как структура хранится в памяти? Я знаю, что (* node) .threadId эквивалентен node-> threadId, и это немного смущает меня, потому что, разыменовав указатель на структуру, я получаю всю структуру, а затем мне нужно получить доступ к указанному полю c. Я ожидал получить доступ к полям, зная адрес структуры следующим образом: * (node) это значение для первого элемента, * (node ​​+ sizeof (firstElement)) это значение для второго и так далее. Я думал, что доступ к полям структуры похож на доступ к значениям массива.

Структура хранится в памяти в виде последовательности байтов, как и все объекты в C. Вам не нужно делать какие-либо вычисления в байтах или указателях, потому что компилятор сделает это за вас. Например, когда вы пишете node->timeSlice, компилятор берет указатель node, добавляет смещение к элементу timeSlice и использует результат для доступа к памяти, в которой хранится элемент timeSlice.

0 голосов
/ 03 мая 2020

Вы не выделяете правильный размер, делая

myThread *node = (myThread *)malloc(sizeof(myThread *));

, например, правильный путь может быть

myThread *node = (myThread *)malloc(sizeof(myThread));

, и бросок бесполезен, так что, наконец,

myThread *node = malloc(sizeof(myThread));

или, как сказано в комментариях к вашему вопросу

myThread *node = malloc(sizeof(*node));

Причина в том, что вы выделяете myThread , а не указатель на, поэтому размер для выделения размер myThread

Если вы выделите sizeof(myThread *), это означает, что вы хотите myThread **, а не myThread *

Я знаю, что (* узел ) .threadId эквивалентен node-> threadI

да, -> разыменование, в то время как . не

Имеет myThread node; для доступа к полю threadId вы делаете node.threadId, но, имея указатель на вас, вы должны уважать его любым способом


Позднее редактировать: ...

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

...