Не удалось выделить память, полученную указателем linux

У меня есть функция, которая получает указатель на место, где будет храниться. Это место может иметь другие подобные структуры. Функция должна прочитать файл. В этом файле хранится структура, которую мне нужно прочитать.

typedef struct user_manage_t{
    short int user_id;
    char permission;
    long int other_id;
    long int check;
}user_manage_t;


typedef struct holder_t{
    user_manage_t *user_manage;
    user_manage_t *user_manage_backup;
    //(...)and a lot of stuff
}holder_t;

holder_t  holder;


int db_read_from_file(user_manage_t *prt){

    DEBUG_PRINT("READ_FROM file started");
    FILE *fd_read;
    char buffer[480];
    int read, bytesRead=0; 
    int num;
    const  struct user_manage_t *header;
    fd_read = fopen("/home/user/user_list","r+b");   
    if (fd_read == NULL) 
    {
        printf("Error");
    }
    else
    {
        DEBUG_PRINT("Its open!!!");
    }
    do 
    {
        read=fread(buffer, 2, 90, fd_read);
        bytesRead=bytesRead+read;   
        DEBUG_PRINT("Number of bytes lidos read=%d",bytesRead);
    }while(read!=0); //(bytesRead < 480);
    header = (struct  user_manage_t *) (buffer);
    fclose(fd_read);
    if ( NULL == ( prt = calloc( 10, sizeof(user_manage_t))))//aloca
    {
        DEBUG_PRINT("MAJOR_ERROR: couldnt allocate mem to users");
        return -1;
    }
    else
    {
        memcpy( (struct user_manage_t *) &prt, &buffer, 90); 
        DEBUG_PRINT("Users copied to main list");
        for ( short int i=0;i<4 ; i++ )
        {
            DEBUG_PRINT("i= %hd",i);
            DEBUG_PRINT("User id: %d",holder.user_manage[i].user_id );
            DEBUG_PRINT("Permission: %d",holder.user_manage[i].permission);
            DEBUG_PRINT("other_ID:%ld",holder.user_manage[i].other_id);
            DEBUG_PRINT("Check_value:%ld", holder.user_manage[i].check);
        }
        return 1;
    }
}
main(){

        db_read_from_file((struct user_manage_t *) &holer.user_manage); 
        db_read_from_file((struct user_manage_t *) &holder.user_manage_backup);
}

Когда я запускаю код, я получаю SEGFAULT, и valgrind сообщает мне об этом:

Тема 2:
==2746== Неверное чтение размера 2
==2746== по адресу 0x80523B4: db_read_from_file (code.c:3069)
==2746== по адресу 0x20303333: ???< br> == 2746 == Адрес 0x0 не стек, malloc или (недавно) освобожден

Это строка "DEBUG_PRINT("Идентификатор пользователя: %d",holder.user_manage[i].user_id);" Очевидно, что я храню его не в том месте. Вы можете мне помочь?


person Kxp    schedule 19.06.2014    source источник
comment
Не уверен, что это причина вашей точной проблемы, но в memcpy( (struct user_manage_t *) &prt, &buffer, 90); вы копируете 90 байтов в место, где хранится переменная-указатель prt, а не в то место, на которое она указывает.   -  person sth    schedule 19.06.2014
comment
Я считаю, что в чем-то есть смысл, который выглядит ужасно неправильно.   -  person Marco A.    schedule 19.06.2014
comment
sth прав, что memcpy должно портить память, если только ваши указатели не имеют размер 90 байт... и, возможно, также рассмотреть возможность проверки prt на наличие NULL указателей?   -  person Elias Van Ootegem    schedule 19.06.2014
comment
поправьте меня, если я ошибаюсь, но я думаю, что выделяю 10x sizeof(user_manage_t), который имеет 12 байтов, поэтому всего 120 байтов   -  person Kxp    schedule 19.06.2014
comment
@Kxp: &prt означает адрес prt, prt уже является указателем, поэтому вы передаете указатель на указатель на memcpy, поэтому memcpy перезапишет все, на что указывает этот указатель на указатель, и он указывает на указатель, который содержит адрес памяти фактической структуры. Этот адрес памяти - это то, что вы перезаписываете. И еще немного памяти, даже!   -  person Elias Van Ootegem    schedule 19.06.2014
comment
@sth: у вас также не возникает неприятного ощущения, что все эти приведения предназначены для подавления предупреждений компилятора?   -  person Elias Van Ootegem    schedule 19.06.2014
comment
@EliasVanOotegem: Судя по тому, что вы видите здесь, в Stack Overflow, это, кажется, самый распространенный вариант использования приведения;)   -  person sth    schedule 20.06.2014


Ответы (2)


Приоритет № 1
Посмотрев еще немного на ваш код, я подозреваю, что вы выполняете приведение типов так же часто, как и раньше, потому что постоянно получаете предупреждения компилятора о "несовместимых типах [указателей]". " и тому подобное. Эти предупреждения существуют по какой-то причине: есть проблема, возможный источник ошибок. Не замалчивайте, не игнорируйте: ИСПРАВЬТЕ ЭТО!

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

Далее
В обоих вызовах db_read_from_file из main вы передаете указатель на нулевой указатель вашей функции. Это означает, что вам все еще нужно выделить память, чтобы фактически перейти и сохранить эти данные, или вам нужно переопределить holder на что-то вроде:

struct
{
    user_manage_t user_manage;//not pointers, actual structs
    user_manage_t user_manage_backup;
} holder;

Если вы сохраните их как указатели, просто выделите все члены holder в main:

holder.user_manage = malloc(sizeof *holder.user_manage);//and so on
//preferably, though:
if (NULL == (holder.user_manage_backup = malloc(sizeof *holder.user_manage_backup))
    exit (EXIT_FAILURE);//error

Большой
Как уже упоминалось:

memcpy( (struct user_manage_t *) &prt, &buffer, 90); 

Вы проезжаете мимо &prt, который гласит: адрес prt. Эта переменная сама по себе уже является указателем, адрес памяти указателя — это опять же указатель. Указатель на указатель (двойная косвенность, по возможности избегайте...). Теперь, как будто этого было недостаточно: посмотрите, что вы передаете своей функции:

db_read_from_file(&holder.user_manage);

Имейте в виду, что holder.user_manage уже является указателем, вы передаете указатель на указатель! Это двойная косвенность. Затем вы передаете указатель на этот указатель указателю на memcpy!! Да, возможно, вам придется прочитать это последнее предложение еще раз. Но вкратце: вы передаете указатель на указатель, на указатель на структуру, где этот последний бит (указатель на структуру) тоже может быть просто нулевым указателем!

Итак, что у вас есть:

memcpy(void ***, char *, 90);//where the prt is void ***, and **prt could be NULL

Думайте о memcpy как о функции, которая в основном делает следующее:

void * memcpy( void *target, const void *src, size_t nr_of_bytes)
{
    char *dest = target;
    char *from = src;//use char, as it is guaranteed to be 1 byte in size
    int i;
    while(nr_of_bytes--)
        *dest++ = *from++;//copy byte to destination, move pointer 1 byte
    return dest;//return destination
}

Обратите внимание, что место назначения разыменовано (*dest++). Если вы передадите указатель на указатель (&prt) и разыменуете его, вы получите указатель, верно? Это то, что вы пишете => *(&prt) == prt!

Приведение и то, как вы его используете, предполагает, что вы считаете, что пишете то, на что указывает prt, в то время как на самом деле вы пытаетесь записать 90 байтов на то, на что указывает указатель на prt. И он указывает на prt, который, в свою очередь, указывает на указатель. Только после этого третьего обращения мы находим структуру... это просто безумие.
В любом случае, размер указателя составляет 4 байта в 32-битной системе и 8 в 64-битной. Вы копируете 90 байт, так что вы, вероятно, окажетесь в памяти, с которой не стоит связываться.

Замените свой код на этот:

memcpy(*prt, buffer, sizeof *prt);//copy max the sizeof whatever prt is pointing to

И измените функцию db_read_from_file на это:

int db_read_from_file(user_manage_t **prt)//pointer to pointer!

И имейте в виду, что всякий раз, когда вы хотите изменить что-то в структуре, на которую указывает prt (2-й уровень), вы должны разыменовать ее, чтобы получить обычный указатель. Например, выделение памяти:

if ( NULL == ( prt = calloc( 10, sizeof(user_manage_t))))//aloca

Должен стать:

if ( NULL == ( *prt = calloc( 10, sizeof(user_manage_t))))//aloca

Тем не менее, это все еще неправильно во многих отношениях. Что вам действительно нужно, так это realloc, потому что prt может уже указывать на выделенную память:

*prt = realloc(*prt, 10*sizeof **prt);
if (*prt == NULL)
    //ERROR

Это чище и безопаснее.

Также проверяйте, не передается ли вашей функции нулевой указатель, избавляйтесь от ненужных приведений (они бесполезны) и всегда проверяйте возвращаемое значение функций!

person Elias Van Ootegem    schedule 19.06.2014
comment
Спасибо за помощь :) Но у меня все еще есть вопрос/проблема, В этом коде я указал размер как 10x12 байт, но это меняется при запуске программы, плюс я заранее не знаю размер, и есть другие потоки которые могут использовать эту функцию, и я бы предпочел сделать malloc внутри функции. Я уже изменил код для проверки nullprt - person Kxp; 19.06.2014
comment
@Kxp: Если вы хотите выделить память внутри функции, вам, вероятно, понадобится 2 уровня косвенности или возврат выделенной памяти в конце... - person Elias Van Ootegem; 19.06.2014
comment
Оооо... этого я и боялся. Вы знаете какой-нибудь веб-сайт, который может помочь мне понять, как это сделать? Большое спасибо:) - person Kxp; 19.06.2014
comment
@Kxp: Нипс, у тебя уже три! уровни косвенности... это просто зло! - person Elias Van Ootegem; 19.06.2014
comment
@Kxp: Просто любопытно: вы добавили эти приведения из-за предупреждений компилятора ?? -- Вы не знаете, как это работает, но вы используете несколько потоков?? Базовое управление памятью таково: оно простое. Просто погуглите пару статей об управлении памятью или выполните поиск по запросу узнайте C сложным путем, это бесплатная онлайн-книга. - person Elias Van Ootegem; 19.06.2014
comment
В какой-то момент это было из-за предупреждений. в программе это не должно было быть нужно, но сейчас это немного важно. Я знаю, как это работает с 1 уровнем, но когда добираюсь до 2 уровня у меня почти нет опыта, и это тоже был повод попробовать этот путь. Я не могу изменить часть user_manage_t user_manage; user_manage_t user_manage_backup; потому что размеры меняются каждую минуту, я постоянно освобождаю и выделяю этот мем, также это заставит меня изменить всю программу, что может занять несколько дней:\ Сейчас я собираюсь прочитать эту книгу и попытаться понять все, что ты сказал. Большое спасибо - person Kxp; 19.06.2014
comment
@Kxp: ну, на данный момент самым простым решением было бы изменить прототип функции, чтобы он принимал указатель на указатель в качестве аргумента (переменная `**prt), and add 1 line: user_manage_t *p = *prt;, then work with that p`, это простой указатель, тогда вы не не нужно иметь дело с 2 уровнями все время - person Elias Van Ootegem; 19.06.2014
comment
Я думаю, у вас это получилось. Похоже, это работает, мне еще нужно проверить часть, которая пишет в файл, но она делает то, что я хотел. Большое спасибо, Элиас, и всем, кто пытался мне помочь: D Я отправлю окончательный код всем, кому он может понадобиться. - person Kxp; 19.06.2014
comment
@Kxp: Пожалуйста. Пожалуйста, прочтите раздел помощь на этом сайте: пользователей просят не оставлять комментарии спасибо, а вместо этого помечать ответ как принятый - person Elias Van Ootegem; 19.06.2014

Вот окончательный код, на случай, если кто-то споткнется в подобной ситуации:

typedef struct user_manage_t{

short int user_id;
char permission;
long int other_id;
long int check;

}user_manage_t;

структура typedefholder_t{

user_manage_t *user_manage;
user_manage_t *user_manage_backup;
pthread_mutex_t check_mutex;
pthread_mutex_t backup_mutex;
//(...)and a lot of stuff

}holder_t;

держатель_t держатель;

int db_read_from_file(user_manage_t **prt, pthread_mutex_t mtx){

DEBUG_PRINT("READ_FROM file started");
FILE *fd_read;
char buffer[480];
int read, bytesRead=0; 
int num;
const  struct user_manage_t *header;
fd_read = fopen("/home/user/user_list","r+b");   
if (fd_read == NULL) 
{
    printf("Error");
}
else
{
    DEBUG_PRINT("Its open!!!");
}
do 
{
    read=fread(buffer, 1, 480, fd_read);
    bytesRead=bytesRead+read;   
    DEBUG_PRINT("Number of bytes lidos read=%d",read);
}while(read!=0); //(bytesRead < 480);
header = (struct  user_manage_t *) (buffer);
fclose(fd_read);
if ( NULL != prt )
{
   status = pthread_mutex_trylock (&mtx);
   if (status != 0)//compor isto
   {
         DEBUG_PRINT("ERROR with lock");
         return -1;
   }
   else
   {
        num = bytesRead / sizeof(user_manage_t);
        DEBUG_PRINT("prt is not null and num=%d",num);
        //should add an if to check if num >0
        //if ( NULL == ( *prt = calloc( num, sizeof(user_manage_t))))//aloca
        if ( NULL == ( *prt = malloc(bytesRead)))
        {    
             DEBUG_PRINT("MAJOR_ERROR: couldnt allocate mem to users");
             status = pthread_mutex_unlock(&mtx);
             return -1;
        }
        else
        {
             //memcpy( *prt, header, sizeof(**prt));    
             memcpy( *prt, header, bytesRead); 
             DEBUG_PRINT("Users copied to main list");
             status = pthread_mutex_unlock(&mtx);                 
             for ( short int i=0;i<4 ; i++ )
             {
                 DEBUG_PRINT("i= %hd",i);
                 DEBUG_PRINT("User id: %d",holder.user_manage[i].user_id );
                 DEBUG_PRINT("Permission: %d",holder.user_manage[i].permission);
                 DEBUG_PRINT("other_ID:%ld",holder.user_manage[i].other_id);
                 DEBUG_PRINT("Check_value:%ld", holder.user_manage[i].check);
             }
             return 1;
        }
    }
}
if ( NULL == prt )
{
    DEBUG_PRINT("Pointer is null!");
    return 0;
}

}

главный(){

    db_read_from_file(&holer.user_manage, holder.check_mutex);  
    db_read_from_file(&holder.user_manage_backup, holder.backup_mutex);

}

Я не уверен, хорошо ли я проверяю null prt, но остальное работает. Я внес небольшие изменения, чтобы быть «идеальным», отсутствует только одна вещь: это отправка имени файла в качестве входных данных функции. Похоже, мьютекс работает со 100% уверенностью.

person Kxp    schedule 20.06.2014
comment
memcpy(*prt, buffer, sizeof(*prt)); неправильно; sizeof(*prt) — это размер указателя на структуру, а не размер самой структуры; вместо этого используйте sizeof(**prt). - person Ruud Helderman; 20.06.2014
comment
Да, эта часть неверна, но sizeof(**prt) дает мне только 12 байтов, что соответствует размеру 1 структуры, когда num=1, но когда num=3 все равно дает=12, должно ли быть так? как я могу получить общее пространство выделенного пространства? - person Kxp; 20.06.2014
comment
Ваше последнее редактирование (с использованием bytesRead) — правильный подход. sizeof оценивается во время компиляции; он может дать вам размер одной структуры (поскольку она определяется во время компиляции), но не количество структур (поскольку это определяется во время выполнения). - person Ruud Helderman; 20.06.2014