Нужно некоторое объяснение о malloc - PullRequest
4 голосов
/ 13 апреля 2019

Я не понимаю, распечатывает OneExample, когда я выделил память на 5. Я знаю, что это можно сделать с помощью strncpy Но есть ли способ сделать это без использования strncpy

Он работает с strncpy, но есть ли способ сделать это без strncpy?

void main()
{

    char *a;

    a=(char*)malloc(5);

    a="OneExample";
    printf("%s",a);

    free(a);

}

Распечатывает OneExample

Не следует ли печатать OneE ?? Может кто-нибудь объяснить?

void main()
{

    char *a;

    a=(char*)malloc(5);

    a="OneExample";
    printf("%s",a);

    free(a);

}

Ответы [ 7 ]

4 голосов
/ 13 апреля 2019

Вы не используете выделенную память. После выделения памяти вы переопределяете указатель a указателем на строковый литерал, вызывая утечку памяти. Последующий вызов free может также вызвать сбой в вашем приложении, поскольку вы вызываете free по указателю, который не был выделен с помощью malloc.

Если вы хотите использовать выделенное пространство, вы можете скопировать в него (например, используя strncpy).

2 голосов
/ 15 апреля 2019
  • Чтобы использовать malloc, вам нужно #include <stdlib.h>, который вы не показываете в своем коде.Причина в том, что у malloc есть прототип, возвращающий void *, и без этого прототипа ваш компилятор предполагает, что он возвращает int.В 64-битных архитектурах это проблема, поскольку void * - это 64-битный тип, а int - 32-битный.Это делает код, который извлекает значение, возвращаемое malloc(), чтобы взять только 32 менее значащих бита результата, а затем преобразовать их в указатель (согласно приведенному вами приведению, который вы не должны --- видетьмного комментариев об этом --- и вы избежите ошибки, которую вы должны были получить, пытаясь преобразовать значение int в char * без приведения)
  • Посленазначая указатель, заданный malloc(3) в a, вы перезаписываете этот указатель адресом строкового литерала "OneExample".Это делает две вещи:

    • Во-первых, вы теряете значение указателя, заданное malloc(3) и которое было сохранено в a, поэтому у вас его больше нет, и вы никогда не сможетедо free(3) этого.Это называется утечка памяти , и вам следует избегать их, поскольку они являются ошибками программирования.
    • Это приведет к некоторому неопределенному поведению при вызове free(3), который принимает тольков качестве параметра значение указателя, ранее возвращаемое malloc (и это не фактический адрес, сохраненный в a). Возможно, вы получили некоторое прерывание SIGSEGV, и ваша программа аварийно завершилась из-за этого вызова.

    Когда вы делаете присвоение a, вы просто изменяете значение указателя, которое вы там сохранили, а не содержимое отложенной памяти, так что именно это имеет смысл при вызове strcpy(3), потому что этоозначает только скопировать строку символов вокруг.Или вы можете копировать символы один за другим, как в:

    char *a = malloc(5);  /* this makes a memory buffer of 5 char available through a */
    int i;
    for(i = 0; i < 4 /* see below */; i++)
        a[i] = "OneExample"[i]; /* this is the copy of the char at pos i */
    a[i] = '\0'; /* we must terminate the string if we want to print it */
    

    последний шаг - это то, что делает необходимым запуск цикла for в то время как i < 4, а не в то время как i < 5, так какмы запросили у malloc() пять символов, и это должно включать символ конца строки.

    Существует одна стандартная альтернатива этой библиотеке:

    char *a = strdup("OneExample");
    

    , что эквивалентно:

    #define S "OneExample"
    char *a = malloc(strlen(S) + 1); /* see the +1 to allow for the null terminator */
    strcpy(a, S);
    

    но если вы хотите решить свой пример с усечением строки в 5, вы можете сделать следующее:

    char *dup_truncated_at(const char *s, int at)
    {
        char *result = malloc(at + 1); /* the size we need */
        memcpy(result, s, at); /* copy the first at chars to the position returned by malloc() */
        result[at] = '\0';  /* and put the string terminator */
        return result; /* return the pointer, that must be freed with free() */
    }
    

    и вы сможете вызвать егокак:

    char *a = dup_truncated_at("OneExample", 5);
    printf("truncated %s\n", a);
    free(a);  /* remember, the value returned from dup_truncated_at has been obtained with a call to malloc() */
    
2 голосов
/ 13 апреля 2019

Я не понимаю, что он распечатывает OneExample, когда я выделил память для 5. Я знаю, что это можно сделать с помощью strncpy, но есть ли способ сделать это без использования strncpy

Он работает с strncpy, но есть ли способ сделать это без strncpy?

Вы можете использовать memcpy() в качестве альтернативы.
Прочитайте о memcpy () и проверьте это также.

Похоже, у вас есть какое-то недопонимание относительно указателей в C.
Посмотрите на эту часть вашего кода:

    a=(char*)malloc(5);

    a="OneExample";
    printf("%s",a);

    free(a);

Вы выделяете память для char указателя a и затем сразу указываете его на строковый литерал OneExample.

Это утечка памяти в вашем коде, потому что вы потеряли ссылку на выделенную память.

Вы должны знать, что free() освобождает пространство, ранее выделенное malloc(), calloc() или realloc(), в противном случае поведение не определено.
В вашей программе вы пытаетесь освободить память строкового литерала "OneExample", что приведет к неопределенному поведению.

Используя memcpy(), вы можете сделать:

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

int main (void) {
    char *a = malloc(5);

    if (a == NULL) {
        fprintf (stderr, "Failed to allocate memory\n");
        exit(EXIT_FAILURE);
    }

    memcpy (a, "OneExample", 4);   // copy 4 characters to a, leaving space for null terminating character

    a[4] = '\0';      // make sure to add null terminating character at the end of string

    printf("%s\n",a);

    free(a);

    return 0;
}

Дополнительно:

  • Использование void в качестве типа возврата функции main не соответствует стандартам. Возвращаемый тип основной функции должен быть int.

  • Следуйте хорошей практике программирования, всегда проверяйте возврат malloc.

  • Не разыгрывать возврат malloc .

0 голосов
/ 13 апреля 2019

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

#define MYTEXT "This string will be copied"

char *a = malloc(strlen(MYTEXT) + 1);  //+1 because you need to store the trailing zero
strcpy(a,MYTEXT);

, затем вы можете освободить a, когда больше не нужны

0 голосов
/ 13 апреля 2019

Оператор a="OneExample"; не означает «Копировать строку "OneExample" в a

В C строковый литерал, такой как "OneExample", представляет собой массив char, который компилятор выделяет для вас. При использовании в выражении массив автоматически становится указателем на его первый элемент. 1

То есть a="OneExample" означает «Установить указатель a, чтобы он указывал на первый char в "OneExample"».

Теперь a указывает на строку, а printf, конечно, печатает ее.

Тогда free(a) неверно, потому что он пытается освободить память "OneExample" вместо предоставленной памяти malloc.

Сноска

1 Это автоматическое преобразование не происходит, если строковый литерал является операндом sizeof, операндом унарного & или используется для инициализации массива.

0 голосов
/ 13 апреля 2019

Вы должны четко понимать, на что указывает ваш указатель.Сначала вы объявляете char *a;.Это указатель на символ.

В строке a=(char*)malloc(5); вы выделяете бит памяти с помощью malloc и назначаете расположение этой памяти указателю a.(немного разговорный, но вы понимаете, что я имею в виду).Так что теперь a указывает на 5 байт свеже выделенной памяти.

Далее, a="OneExample";.a получает новое значение, а именно расположение строки OneExample;Больше нет указателя, доступного для пяти выделенных байтов.Теперь они сироты.a указывает на строку в статическом контексте.В зависимости от архитектуры, компилятора и многих других вещей, это может быть статическое пространство данных, пространство программы или что-то в этом роде.По крайней мере, не часть памяти, которая используется для переменных.

Так что, когда вы делаете printf("%s",a);, a указывает на строку, а printf выводит строку (полную строку).

Когда вы делаете free(a);, вы пытаетесь освободить часть программы, данные и т. Д. Пространство.То есть вообще не допускается.Вы должны получить сообщение об ошибке для этого;в Linux это будет примерно так:

*** Error in `./try': free(): invalid pointer: 0x000000000040081c ***
======= Backtrace: =========
/lib64/libc.so.6(+0x776f4)[0x7f915c1ca6f4]
/lib64/libc.so.6(+0x7ff4a)[0x7f915c1d2f4a]
/lib64/libc.so.6(cfree+0x4c)[0x7f915c1d6c1c]
./try[0x400740]
/lib64/libc.so.6(__libc_start_main+0xf0)[0x7f915c1737d0]
./try[0x4005c9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 21763273                           /home/ljm/src/try
00600000-00601000 r--p 00000000 08:01 21763273                           /home/ljm/src/try
00601000-00602000 rw-p 00001000 08:01 21763273                           /home/ljm/src/try
[- more lines -]
0 голосов
/ 13 апреля 2019

Сначала здесь

a=(char*)malloc(5);

Вы выделили 5 байтов памяти из кучи и сразу

a="OneExample";

переопределяет предыдущую динамическую память строковым литералом "OneExample" базовый адрес, так как a больше не указывает на какую-либо динамическую память, это вызывает утечка памяти (так как ни один объект не указывает на эту динамическую память, он потерял ) здесь и даже после этого

free(a);

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

примечание, прочитайте это Почему malloc выделяет больше памяти, чем я запрашиваю & Malloc против пользовательского распределителя: у Malloc много накладных расходов. Почему? , чтобы получить больше информации о malloc().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...