Malloc calloc не может выделить структуру

Я получаю нулевую память, используя следующий код malloc/calloc. Иногда не удается выделить память для name1, а затем происходит сбой strcpy. Пожалуйста, направляйте.

struct testMalloc
{
    char name1[90];
    char name2[90]; 
    struct testMalloc* ptr;
};
int main(int argc, char* argv[])
{
    struct testMalloc* test = 0 ;   
    int size = 0;
    size = sizeof(struct testMalloc);

    printf("Size of struct is %d", size);

    test = (struct testMalloc*) calloc(sizeof(struct testMalloc));  
    
    strcpy((test->name1), "hdshdssdsdfsfffffffffffffffffffffffffffffh");

    return 0;

}

person Rohan Kothari    schedule 06.08.2020    source источник
comment
Указатель не может быть инициализирован с 0, но с (void*)0 в случае, если вы хотите получить указатель NULL в качестве значения. В противном случае это неопределенное поведение.   -  person alinsoar    schedule 06.08.2020
comment
@alinsoar Ты уверен? У вас есть источник?   -  person klutt    schedule 06.08.2020
comment
@alinsoar Комитет по стандартам C действительно наконец сдался всем тем заблудшим программистам, которые считали, что 0 IS NULL. На самом деле это не NULL сейчас, но 0 является константой нулевого указателя в соответствии со стандартом C.   -  person Andrew Henle    schedule 06.08.2020
comment
@AndrewHenle В некоторых системах я слышал, что нулевой указатель не имеет всех битов 0, но никогда не видел такой системы.   -  person alinsoar    schedule 06.08.2020
comment
@alinsoar 0 при преобразовании в тип указателя всегда равен == NULL. Он не обязательно должен быть представлен всеми битами 0. Не имеет значения, ставите ли вы явное приведение (void *) или просто разрешаете его неявное преобразование.   -  person trentcl    schedule 06.08.2020
comment
@alinsoar Что сказал Тренкл. NULL == 0 гарантированно оценивается как true.   -  person klutt    schedule 06.08.2020
comment
@alinsoar Такие системы существуют. Но в C также есть концепция константы нулевого указателя, одной из которых является NULL. NULL не обязательно должно быть равно нулю, но числовое значение 0 определено как константа нулевого указателя. И все нулевые указатели определены как равные. По сути, это взлом стандартов, позволяющий работать тому, что в противном случае было бы плохим кодом, который предполагает, что NULL на самом деле 0.   -  person Andrew Henle    schedule 06.08.2020
comment
Вероятно, для сравнения с NULL будет интерпретироваться что-то вроде define NULL? (x) x<SOME/CONSTANT? Взять SOME/CONSTANT небольшое число, например 1024? В этом случае NULL можно интерпретировать как любое число, целочисленное значение которого будет небольшим...   -  person alinsoar    schedule 06.08.2020
comment
@alinsoar Нет, сравнение указателей с числами недопустимо C, если только число точно не равно 0. Преобразование целого числа в указатель не гарантирует отрисовки указателя с тем же битовым представлением, что и целое число. Если в конкретной архитектуре NULL является значением указателя 0x8675309, то (void *)0 представлено в машине как 0x8675309. Но в вашем коде C это все еще нулевой указатель.   -  person trentcl    schedule 06.08.2020
comment
См. C FAQ 5.5, 5.9 и 5.10 для дальнейшего объяснения. Причина использования NULL вместо 0 не связана с внутренним представлением нулевых указателей.   -  person trentcl    schedule 06.08.2020


Ответы (5)


Вы не включаете <stdlib.h>, чтобы компилятор знал сигнатуру calloc, и в этом случае он использует соглашение о вызовах K&R.

Если вы включите <stdlib.h>, код не скомпилируется до того, как правильно вызовет calloc.

person alinsoar    schedule 06.08.2020

calloc принимает 2 аргумента: количество элементов и размер каждого элемента. Это обнулит выделенную память. То, что вы ищете, это malloc, которое принимает только 1 аргумент: общий размер выделенного фрагмента памяти, и он не обнуляет выделенную память.

person Aplet123    schedule 06.08.2020

  1. Всегда проверяйте результат malloc.
  2. Используйте объекты вместо типов в sizeof
  3. Попробуйте использовать более безопасные версии строковых функций (в этом примере переданная строка длиннее массива.
  4. Вам не нужно приводить результат malloc, и это считается плохой практикой (в настоящее время довольно устаревшей, но в любом случае)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct testMalloc
{
    char name1[20];
    char name2[90]; 
    struct testMalloc* ptr;
};

struct testMalloc *allocAndCopy(const char *str)
{
    struct testMalloc *ptr = malloc(sizeof(*ptr));
    if(ptr)
    {
        strncpy(ptr -> name1, str, sizeof(ptr -> name1));
        ptr -> name1[sizeof(ptr -> name1) - 1] = 0;
    }
    return ptr;
}


int main(int argc, char* argv[])
{
    struct testMalloc* test = allocAndCopy("hdshdssdsdfsfffffffffffffffffffffffffffffh");

    if(test) printf("the string is: %s\n", test -> name1);
}

https://godbolt.org/z/zjvvYW

person 0___________    schedule 06.08.2020
comment
вы забыли оператор return в allocAndCopy() :-) - person Ingo Leonhardt; 06.08.2020

Есть несколько синтаксических ошибок:

  1. struct testMalloc* test = NULL; Так инициализируется указатель NULL
  2. calloc(sizeof(struct testMalloc)); в calloc передано слишком мало аргументов. Правильная форма calloc(no_of_elements, sizeof(type));

Вот правильная реализация вашего кода:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct testMalloc
{
    char name1[90];
    char name2[90]; 
    struct testMalloc* ptr;
};

int main(int argc, char* argv[])
{
    struct testMalloc* test = NULL;
    size_t size = sizeof(struct testMalloc);

    printf("Size of struct is %ld\n", size);

    if((test = (struct testMalloc*)calloc(1, size)) == NULL){
        return -1; //Failed to allocate memory
    }
    else {
        strcpy((test->name1),"hdshdssdsdfsfffffffffffffffffffffffffffffh");
        printf("%s\n",test->name1);
    }
    
    return 0;
}
person Sourabh Choure    schedule 06.08.2020
comment
Я бы 1. использовал вместо этого sizeof(*test), что более гибко, 2. никогда не обращался к test, пока не проверил, что это не NULL. Распространенной ошибкой является предположение, что выделение всегда выполняется успешно, но на самом деле это не так. - person Iharob Al Asimi; 06.08.2020
comment
@IharobAlAsimi malloc() и calloc() используют sbrk()/brk(), которые могут изменить размер кучи. Проблема в основном возникает, когда мы используем mmap(), так как там мы выделяем почти кучу памяти размером со страницу. - person Sourabh Choure; 06.08.2020
comment
Придерживайтесь руководств и стандартного разрешения. Как реализовано malloc() не имеет значения. Если по какой-либо причине (которая может зависеть от реализации) произойдет сбой, он вернет NULL. НИКОГДА НЕ ПРЕДПОЛАГАЕМ. - person Iharob Al Asimi; 06.08.2020
comment
Хорошо, я думаю, я, возможно, узнал что-то новое от @IharobAlAsimi от вас, чтобы никогда не предполагать при использовании функций, сбой которых может быть неизбежен. Я обновлю ответ. - person Sourabh Choure; 06.08.2020
comment
Это не вопрос неизбежности. Это закон Мерфи! - person Iharob Al Asimi; 06.08.2020

Сверху донизу:

1.

Вы забыли #include stdlib.h использовать calloc() и malloc(). Неявные объявления запрещены начиная с C99.

2.

int main(int argc, char* argv[])

Вашей программе не нужно вводить в нее аргументы.

Этот:

int main (void)

было бы более уместно.

3.

int size = 0;

size никогда не должно иметь отрицательное значение. Так что было бы правильнее объявить его как unsigned int или даже лучше size_t.

4.

struct testMalloc* test = 0 ; 

Вы можете использовать 0 для инициализации указателя. Это совершенно правильно, так как 0 является константой нулевого указателя. Но лучше использовать NULL при работе с указателями, а не 0, чтобы показать назначение указателя и улучшить читаемость.

struct testMalloc* test = NULL; 

5.

calloc(sizeof(struct testMalloc)); 

calloc требует два аргумента по сравнению с malloc. Первое должно быть количеством элементов, а второе - размером одного элемента.

calloc(sizeof(1,struct testMalloc)); 

6.

test = (struct testMalloc*) calloc(sizeof(struct testMalloc)); 

Вам не нужно приводить возвращаемое значение malloc() или calloc().

7.

Вы забыли проверить возвращаемый указатель из calloc() на наличие нулевого указателя, если выделение не удалось. Всегда проверяйте возвращаемое значение функций управления памятью.

test = calloc(1, sizeof(struct testMalloc)); 
if (test == NULL)
{
    fputs("Allocation failed!", stderr);
    // error routine.
}
person RobertS supports Monica Cellio    schedule 06.08.2020