Форум программистов, компьютерный форум, киберфорум
C для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.80/5: Рейтинг темы: голосов - 5, средняя оценка - 4.80
0 / 0 / 0
Регистрация: 05.11.2023
Сообщений: 4

Почему происходит утечка памяти?

05.11.2023, 18:56. Показов 1262. Ответов 11

Студворк — интернет-сервис помощи студентам
Я пытался написать функцию для конкатенации 2 строк. При этом к одной строке можно было бы повторно присоединять еще строки и при это не было утечки памяти. В итоге добился того что утечки памяти нет, но не совсем понимаю почему в 1 варианте её нет, но во 2 она появляется? Проверял с помощью valgrind --leak-check=full.

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void con2(char **str1, const char *str2)
{
    char *tmp = realloc(*str1, (strlen(*str1) + strlen(str2) + 1));
 
    if (!tmp) // Не удалось выделить память
    {
        printf("malloc() failed: insufficient memory!\n");
    }
 
    memcpy(tmp + strlen(tmp), str2, strlen(str2) + 1);
    *str1 = tmp;
}
 
int main()
{
    char *str1 = malloc(6 * sizeof(char));
    scanf("%s", str1);
    char *str2 = malloc(6 * sizeof(char));
    scanf("%s", str2);
    puts(str1);
    puts(str2);
 
    con2(&str1, str2);
    puts(str1);
    con2(&str1, str2);
    con2(&str1, str2);
    puts(str1);
 
    free(str1);
    free(str2);
}
All heap blocks were freed -- no leaks are possible

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 void con2(char *str1, const char *str2)
{
    str1 = realloc(str1, (strlen(str1) + strlen(str2) + 1));
 
    if (!str1) // Не удалось выделить память
    {
        printf("malloc() failed: insufficient memory!\n");
    }
 
    memcpy(str1 + strlen(str1), str2, strlen(str2) + 1);
}
 
int main()
{
    char *str1 = malloc(6 * sizeof(char));
    scanf("%s", str1);
    char *str2 = malloc(6 * sizeof(char));
    scanf("%s", str2);
    puts(str1);
    puts(str2);
 
    con2(str1, str2);
    puts(str1);
    con2(str1, str2);
    con2(str1, str2);
    puts(str1);
 
    free(str1);
    free(str2);
}
}
LEAK SUMMARY:
==23495== definitely lost: 9 bytes in 1 blocks
==23495== indirectly lost: 0 bytes in 0 blocks
==23495== possibly lost: 0 bytes in 0 blocks
==23495== still reachable: 2,054 bytes in 3 blocks
==23495== suppressed: 0 bytes in 0 blocks
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
05.11.2023, 18:56
Ответы с готовыми решениями:

Почему возникла утечка памяти
#include <stdio.h> #include <stdlib.h> #include <string.h> #define LEN 100 int main(void) { char **words=NULL; int...

Вектор, утечка памяти, функция создания и выделение памяти
Здравствуйте. Есть проблема. функция malloc выделяет память лишь в функции CreateVector(), и при выходе из нее указатель теряется. Или не...

Утечка памяти
Как полностью освободить память? Утекает 39 байт int ft_badend(char ***arr, int k) { int i; int j; i = 0; j = 0; ...

11
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
05.11.2023, 19:52
Лучший ответ Сообщение было отмечено MidnightUser как решение

Решение

Цитата Сообщение от MidnightUser Посмотреть сообщение
но во 2 она появляется?
Распространенные ошибки
1
Заблокирован
05.11.2023, 21:34
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void con2(char **str1, const char *str2)
{
    *str1 = realloc(*str1, (strlen(*str1) + strlen(str2) + 1));
 
    if (!*str1) // Не удалось выделить память
    {
        printf("malloc() failed: insufficient memory!\n");
    }
 
    memcpy(*str1 + strlen(*str1), str2, strlen(str2) + 1);
}
 
int main()
{
    char *str1 = malloc(6 * sizeof(char));
    scanf("%s", str1);
    char *str2 = malloc(6 * sizeof(char));
    scanf("%s", str2);
    puts(str1);
    puts(str2);
 
    con2(&str1, str2);
    puts(str1);
    con2(&str1, str2);
    con2(&str1, str2);
    puts(str1);
 
    free(str1);
    free(str2);
}
0
2487 / 1151 / 709
Регистрация: 25.04.2016
Сообщений: 3,310
05.11.2023, 22:31
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
char *s1 = NULL;
char *s2 = NULL;
 
void done (void);
char * concat (char *, char *);
 
int main (void) {
    if (atexit(done) > 0) {
        puts("Error: registering the atexit() function!");
        return 3;
    }
 
    if ((s1 = (char *) malloc(sizeof(char) * 6)) == NULL)
        return 1;
    if ((s2 = (char *) malloc(sizeof(char) * 6)) == NULL)
        return 1;
 
    if (printf("s1: ") && scanf("%5s", s1) != 1)
        return 2;
    if (printf("s2: ") && scanf("%5s", s2) != 1)
        return 2;
 
    s1 = concat(s1, s2);
    if (s1 == NULL)
        return 1;
 
    printf("s1s2: %s\n", s1);
    return 0;
}
// -------------------------------------------------------------
void done (void) {
    if (s1 != NULL)
        free(s1);
    if (s2 != NULL)
        free(s2);
}
// -------------------------------------------------------------
char * concat (char *str1, char *str2) {
    size_t size1 = strlen(str1);
    size_t size2 = strlen(str2);
    size_t new_size = size1 + size2 + 1;
 
    char *temp = (char *) realloc(str1, sizeof(char) * new_size);
    if (temp == NULL)
        return NULL;
 
    for (size_t i = 0; i < size2; i++)
        temp[size1 + i] = str2[i];
    temp[new_size - 1] = '\0';
 
    return temp;
}
// -------------------------------------------------------------
Добавлено через 16 минут
ну и если у нас копирование происходит несколько раз, мы каждый раз пропускаем его через проверку:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
char *s1 = NULL;
char *s2 = NULL;
 
void done (void);
char * concat (char *, char *);
 
int main (void) {
    if (atexit(done) > 0) {
        puts("Error: registering the atexit() function!");
        return 3;
    }
 
    if ((s1 = (char *) malloc(sizeof(char) * 6)) == NULL)
        return 1;
    if ((s2 = (char *) malloc(sizeof(char) * 6)) == NULL)
        return 1;
 
    if (printf("s1: ") && scanf("%5s", s1) != 1)
        return 2;
    if (printf("s2: ") && scanf("%5s", s2) != 1)
        return 2;
 
    if ((s1 = concat(s1, s2)) == NULL)      // первое копирование
        return 1;
    printf("s1s2: %s\n", s1);
 
    if ((s1 = concat(s1, s2)) == NULL)      // второе
        return 1;
    printf("s1s2s2: %s\n", s1);
 
    if ((s1 = concat(s1, s2)) == NULL)      // третье
        return 1;
    printf("s1s2s2s2: %s\n", s1);
    return 0;                               // и т.д.
}
// -------------------------------------------------------------
void done (void) {
    if (s1 != NULL)
        free(s1);
    if (s2 != NULL)
        free(s2);
}
// -------------------------------------------------------------
char * concat (char *str1, char *str2) {
    size_t size1 = strlen(str1);
    size_t size2 = strlen(str2);
    size_t new_size = size1 + size2 + 1;
 
    char *temp = (char *) realloc(str1, sizeof(char) * new_size);
    if (temp == NULL)
        return NULL;
 
    for (size_t i = 0; i < size2; i++)
        temp[size1 + i] = str2[i];
    temp[new_size - 1] = '\0';
 
    return temp;
}
// -------------------------------------------------------------
Добавлено через 13 минут
URUSOFF, if (!*str1) означает if(!str1[0]) вы уверены, что вам именно это нужно?

ошибка кроется, во-первых, в проверке выделения памяти realloc'ом, поскольку программа просто сообщает, что память не была выделена, а дальше как ни в чем ни бывало копирует данные, даже если там нулевой указатель.

Во-вторых memcpy тоже возвращает указатель и его тоже надо проверять.

И в-третьих, запрос памяти происходит внутри функции, а realloc может просто выделить новый блок памяти и вернуть указатель уже на этот новый блок памяти, и в этом случае адрес str1, переданный в функцию, становится бесполезен, т.е. нам нужно не только записать результат realloc в temp, но потом еще и вернуть этот temp в main, чтобы мы не потеряли адрес выделенного realloc'ом блока памяти.

И это еще без учета возможных ошибок malloc'а в main и возможных ошибок ввода, проверок которых в коде просто нет.

Сюда подкинем еще пару ошибок размера данных, ведь и realloc, и memcpy ждут количество байт в качестве размера, а char далеко не всегда и не везде равен одному байту. И только после всего этого мы получаем примерную картину происходящего.

Тут ошибки практически в каждой второй строчке, просто удивительно как оно вообще запускалось.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
05.11.2023, 22:35
Цитата Сообщение от stake-k26 Посмотреть сообщение
Во-вторых memcpy тоже возвращает указатель и его тоже надо проверять.
Зачем? Спецификация memcpy четко говорит, что возвращаемый указатель всегда равен переданому. Никогда нет смысл проверять результат memcpy. Проверять надо до.

Цитата Сообщение от stake-k26 Посмотреть сообщение
Сюда подкинем еще пару ошибок размера данных, ведь и realloc, и memcpy ждут количество байт в качестве размера, а char далеко не всегда и не везде равен одному байту.
В С и С++ "байт" - это char по определению. Поэтому char всегда и везде равен одному байту. Никаких ошибок размера данных из-за этого в этом коде нет. realloc и memcpy ждут количество char в качестве размера.

По этой причине умножение на sizeof(char) при выделении памяти - бесполезное и ненужное действие. Это не ошибка, но и смысла в этом нет, ибо sizeof(char) всегда и везде гарантированно по определению равно 1.

Странно, что эта разобранная вдоль и поперек тема по-прежнему вызывает затруднения.
0
2487 / 1151 / 709
Регистрация: 25.04.2016
Сообщений: 3,310
05.11.2023, 23:13
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
возвращаемый указатель всегда равен переданому
А что произойдет в случае, скажем, пересекающихся блоков памяти? Мы просто получим UB. Хотя вроде как в glibc 3.14 memcpy является макросом и вызывает memmove, т.е. такой проблемы не должно возникать, однако это никак не гарантирует, что у всех стоит 3.14 и выше.

Как по мне, лучше уж перебдить. Хуже точно не будет.

На счет того, что char всегда равен одному байту, нет не всегда, мне пару раз попадались двухбайтные чары. Гарантируется что char <= short, однако не припоминаю, чтобы гарантировалось, что он всегда равен одному байту. Так что, на мой взгляд смысл есть.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
05.11.2023, 23:42
Цитата Сообщение от stake-k26 Посмотреть сообщение
что произойдет в случае, скажем, пересекающихся блоков памяти? Мы просто получим UB.
Да, именно UB. Но к проверке возвращаемого значения memcpy это никак не относится. К чему вы это упомянули?

Цитата Сообщение от stake-k26 Посмотреть сообщение
На счет того, что char всегда равен одному байту, нет не всегда, мне пару раз попадались двухбайтные чары.
Еще раз повторяю: с точки зрения "байтов - восьмибитных октетов" char может состоять и из двух, и из четырех и из сколько угодно "байтов". Однако в языках С и С++ минимальной атомарной единицей измерения памяти является char. В С и С++ байтом является именно char. sizeof, malloc, realloc и все остальное работают именно в единицах char, а не в ваших "байтах". То есть sizeof(char) всегда гарантированно равно 1. И о том, сколько там у вас в реальности "байтов" прячется в char никогда беспокоиться не нужно.

Если у вас char состоит из 100 "байтов", то malloc(1) будет выделять именно 100 таких "байтов".
0
 Аватар для Наталья8
518 / 368 / 65
Регистрация: 09.03.2016
Сообщений: 3,870
06.11.2023, 00:54
Можно вставлю?
А кто за вас будет возвращать указатель
на новый блок памяти из функции.?
Молодые люди....
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning(disable : 4996)
 
void con2(char **str1, const char *str2)
{
    *str1 = (char *)realloc(*str1, (strlen(*str1) + strlen(str2) + 1));
 
    if (!*str1) // Не удалось выделить память
    {
        printf("malloc() failed: insufficient memory!\n");
    }
 
    memcpy(*str1 + strlen(*str1), str2, strlen(str2) + 1);
}
 
int main()
{
    char *str1 = (char *)malloc(6 * sizeof(char));
    scanf("%s", str1);
    char *str2 = (char *)malloc(6 * sizeof(char));
    scanf("%s", str2);
    puts(str1);
    puts(str2);
 
    con2(&str1, str2);
    puts(str1);
    con2(&str1, str2);
    con2(&str1, str2);
    puts(str1);
 
    free(str1);
    free(str2);
    
    
    getchar(); getchar();
    return 0;
}
realloc делает массив в другом месте памяти.
и возвращает на него указатель....
0
2487 / 1151 / 709
Регистрация: 25.04.2016
Сообщений: 3,310
06.11.2023, 02:56
TheCalligrapher, я понял о чем вы, но я имел в виду немного другое.

Посмотрите на код ТС. В функции main, он запрашивает память под массивы:
Цитата Сообщение от MidnightUser Посмотреть сообщение
char *str1 = malloc(6 * sizeof(char));
Все отлично. Функция ждет объем памяти в байтах, и он передает ей объем памяти в байтах. А теперь смотрите что происходит в функции con2:
Цитата Сообщение от MidnightUser Посмотреть сообщение
char *tmp = realloc(*str1, (strlen(*str1) + strlen(str2) + 1));
Почему? Что вдруг случилось, что ТС взял и поменял передаваемое в функцию значение? realloc ведь тоже ждет объем памяти в байтах, а ТС передает что? Просто число элементов массива. Почему?

Он вдруг вспомнил, что единицей измерения является char? Да ничего подобного. Он элементарно не учел, что и realloc, и memcpy ждут размер в байтах. И то, что это все заработало - это чистой воды везение, поскольку конкретно в этой программе используется массив чаров. Если бы использовался массив любого другого типа, уже была бы ошибка.

По поводу возвращаемого значения memcpy я имел в виду вот это:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
char * concat (char *str1, char *str2) {
    size_t size1 = strlen(str1);
    size_t size2 = strlen(str2);
    size_t new_size = size1 + size2 + 1;
 
    char *temp = (char *) realloc(str1, sizeof(char) * new_size);
    if (temp == NULL)
        return NULL;
 
    memcpy(temp + size1, str2, sizeof(char) * size2);
    temp[new_size - 1] = '\0';
    return temp;
}
а затем в main мы смотрим что нам вернула функция:
C
1
2
if ((s1 = concat(s1, s2)) == NULL)
    return 1;
если у нас realloc накрылся, то мы получим NULL и понятно, что возникли проблемы с памятью и надо что-то делать. У ТС же в коде просто сообщается, что память не выдали, и на этом все.

ablex, оставьте свои фантазии при себе. У вас богатое воображение, не спорю, однако здесь оно как-то вообще не к месту.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
06.11.2023, 05:15
Цитата Сообщение от stake-k26 Посмотреть сообщение
Почему? Что вдруг случилось, что ТС взял и поменял передаваемое в функцию значение? realloc ведь тоже ждет объем памяти в байтах, а ТС передает что? Просто число элементов массива. Почему?
Ну, скажем так, да - вы правы. Действительно имеет место необъяснимая разница между вызовами malloc и realloc. Как будто два разных человека писало. В одном месте умножено на sizeof(char), а в другом - нет. Это действиельно странно.

Но разница эта - чисто холиворно-стилистическая: sizeof(char) везде и всегда равно 1, поэтому от того, умножите вы на него или нет, ничего не изменится. Кто-то предпочитает умножать - чтобы везде было единообразно, независимо от конкретного sizeof. Кто-то наоборот, старательно избегает умножения на заведомую 1.

(Я лично принадлежу к первой группе, то есть везде единообразно умножаю на размер типа. Но при этом я всегда стремлюсь делать p = malloc(n * sizeof *p), то есть мой код абстрагирован от имен конкретных типов.)

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

Цитата Сообщение от stake-k26 Посмотреть сообщение
если у нас realloc накрылся, то мы получим NULL и понятно, что возникли проблемы с памятью и надо что-то делать. У ТС же в коде просто сообщается, что память не выдали, и на этом все.
Это верно. Но это совсем не про возвращаемое значение memcpy.
0
0 / 0 / 0
Регистрация: 05.11.2023
Сообщений: 4
06.11.2023, 10:44  [ТС]
По поводу "качества" своего кода, скажу, что я пытался сделать так, чтобы это работало и найти рабочую логику решения, проверки на выделенную память в моем коде остались от другого кода из интернета, как и использование realloc, тоже оттуда же, я гуглил про размер char, поэтому не стал исправлять. С памятью работаю совсем недавно, обрабатывать ее особо не умею, да. Если есть гайд, то скиньте пожалуйста. Опять же топик в "C для начинающих" , с чего такая агрессия... За помощь, спасибо.
0
2487 / 1151 / 709
Регистрация: 25.04.2016
Сообщений: 3,310
06.11.2023, 16:05
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Это верно. Но это совсем не про возвращаемое значение memcpy.
Да, простите, более раннюю версию функции скинул:

C
1
2
3
4
5
6
7
8
9
10
11
12
char * concat (char *str1, char *str2) {
    size_t size1 = strlen(str1);
    size_t size2 = strlen(str2);
    size_t new_size = size1 + size2 + 1;
 
    char *temp = (char *) realloc(str1, sizeof(char) * new_size);
    if (temp == NULL)
        return NULL;
 
    temp[new_size - 1] = '\0';
    return (char *) memcpy(temp + size1, str2, sizeof(char) * size2) - size1;
}
В случае, если realloc отработал успешно, в main мы по сути проверяем значение, которое вернула memcpy.

Цитата Сообщение от MidnightUser Посмотреть сообщение
с чего такая агрессия
Никакой агрессии, вы о чем? Обычное дело. Порой тут оппоненты о каждой запятой в коде спорят. К новичкам это никакого отношения не имеет. А я и правда выразился обобщенно и из-за этого не совсем корректно, так что возникшие у TheCalligrapher вопросы вполне закономерны.

Ну а что касается вашего кода
Цитата Сообщение от MidnightUser Посмотреть сообщение
проверки на выделенную память в моем коде остались от другого кода
это хорошо, так и нужно делать, только у вас проверка есть, но не везде, где она нужна, да и сама проверка ничего не исправляет, просто сообщает об ошибке, что не хорошо.

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

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

Кроме того, malloc, calloc, realloc, memcpy, memmove и прочие функции, работающие с памятью, ждут не количество элементов массива, а либо объема в байтах, либо числа элементов массива и объема отдельного элемента в байтах, и это тоже нужно учитывать.

Ну и если вы что-то делаете с размером массива в функции, то всегда стоит возвращать указатель на получившийся массив, поскольку внутри функции собственное пространство имен.

Добавлено через 1 час 57 минут
MidnightUser, допустим, вы в main создаете массив:
C
1
2
3
4
5
6
7
8
int main (void) {
    int *a = (int *) malloc(sizeof(int) * 5);
 
    //....
 
    free(a);
    return 0;
}
в этом случае вы всегда должны проверить, что вернул вам malloc:
C
1
2
3
4
5
6
7
8
9
10
11
12
int main (void) {
    int *a = (int *) malloc(sizeof(int) * 5);
    if (a == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    //....
 
    free(a);
    return 0;
}
а что если это уже не первый массив, который вы создаете, и до этого вы уже создали какой-то другой массив? Тогда в случае, если malloc вернул NULL, вам необходимо не только сообщить об ошибке и прервать выполнение программы, но еще и освободить память, которая к этому моменту уже была занята другим массивом:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main (void) {
    int *a = (int *) malloc(sizeof(int) * 5);
    if (a == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    int *b = (int *) malloc(sizeof(int) * 7);
    if (b == NULL) {
        puts("сообщение об ошибке");
        free(a);    // освобождаем память, занятую ранее созданным массивом a
        return код_ошибки;
    }
 
    //....
 
    free(a);
    free(b);
    return 0;
}
а что если у вас 3 массива? То вам каждый раз при создании массива надо проверять что вернул malloc и каждый раз освобождать занятую ранее другими массивами память:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int main (void) {
    int *a = (int *) malloc(sizeof(int) * 5);
    if (a == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    int *b = (int *) malloc(sizeof(int) * 7);
    if (b == NULL) {
        puts("сообщение об ошибке");
        free(a);    // освобождаем память, занятую ранее созданным массивом a
        return код_ошибки;
    }
 
    int *с = (int *) malloc(sizeof(int) * 9);
    if (с == NULL) {
        puts("сообщение об ошибке");
        free(a);    // освобождаем память, занятую ранее созданным массивом a
        free(b);    // освобождаем память, занятую ранее созданным массивом b
        return код_ошибки;
    }
 
    // ....
 
    free(a);
    free(b);
    free(c);
    return 0;
}
Хорошо. А что если у вас 5 массивов?..

К счастью, существует функция atexit(), которая регистрирует некоторую другую функцию, например my_function, которая вызывается при завершении программы. Эта функция всегда объявляется как void name_function (void) и вызывается она вне зависимости от того как именно была завершена программа, был ли это return в main или exit в другой функции, вызванной из main:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
int *a = NULL;
int *b = NULL;
int *c = NULL;
 
void my_function (void) {
    if (a != NULL)
        free(a);
    if (b != NULL)
        free(b);
    if (c != NULL)
        free(c);
}
 
int main (void) {
    if (atexit(my_function) > 0) {
        puts("Error: registering the atexit() function!");
        return код_ошибки;
    }
 
    a = (int *) malloc(sizeof(int) * 5);
    if (a == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    b = (int *) malloc(sizeof(int) * 7);
    if (b == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    с = (int *) malloc(sizeof(int) * 9);
    if (с == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    //....
 
    return 0;
}
Однако это вовсе не панацея и возможны случаи, когда мы обрабатываем ошибку в функции, вызванной внутри другой функции, вызванной внутри третьей функции.. и так далее, и в каждой из этих функций мы что-то делали с массивом, и тут уже лучше проверять каждую ошибку отдельно.

Это один момент. Другой момент касается realloc внутри другой функции, например:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void my_function (int * a, size_t new_size) {
    int *temp = (int *) realloc(a, sizeof(int) * new_size);
    if (temp != NULL)
        a = temp;
}
 
int main (void) {
    int *a = (int *) malloc(sizeof(int) * 5);
    if (a == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    my_function(a, 8);
 
    //....
 
    free(a);
    return 0;
}
Так делать нельзя. Почему? Да потому, что int *a в main - это одна переменная, а вот int *a в my_function - это уже другая переменная, которая существует только пока работает функция my_function. И если мы каким-то образом изменим значение a внутри my_function, то оно изменится только внутри my_function, а в main значение a так и останется прежним, поэтому мы делаем следующее:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int * my_function (int * a, size_t new_size) {
    int *temp = (int *) realloc(a, sizeof(int) * new_size);
    if (temp == NULL) {
        free(a);
        return NULL;
    }
    return temp;
}
 
int main (void) {
    int *a = (int *) malloc(sizeof(int) * 5);
    if (a == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    a = my_function(a, 8);
    if (a == NULL) {
        puts("сообщение об ошибке");
        return код_ошибки;
    }
 
    //....
 
    free(a);
    return 0;
}
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
06.11.2023, 16:05
Помогаю со студенческими работами здесь

Утечка памяти
Препод сказал, что из-за пустого вектора утечка памяти, но я пока что плохо понимаю, как исправить, а время поджимает vector*...

Утечка памяти в free()
Приветствую. Стоит задача вывести заданное количество символов из последовательности Фибоначчи. Функция free() вызывает утечку памяти....

Будет ли утечка памяти?
Есть такая функция: struct Item* addItem(struct Item *,int*); В main я ее вызываю следующим образом: table=addItem(table,&amp;n); ...

Утечка памяти в функции
Сама суть Есть fcgi-приложение которое парсит полученные get-данные и потом производит выборку по MySQL. В приложении есть функция,...

Генератор случайного пароля. Утечка памяти
Написал программу для генерации случайного пароля/ключа. #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #define...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru