Intereting Posts
Условное использование побитовых операторов c встроенная assembly, получающая «несоответствие размера операнда» при использовании cmpxchg Преобразование простых C # define в константы Rust В Objective-C / C вы можете написать функцию, которая объединяет 2 блока? Спецификатор форматирования формата в printf Количество секунд и минут с таймером / прерыванием MCU? Автоматическая перезагрузка программы при сбое в OSX Чтение пакетов RTCP с IP-камеры с использованием FFMPEG C стандартный способ доступа к нулевому адресу указателя? Как я могу вернуть более одного значения через функцию в C? Почему разрешено перезаписывать переменную const с помощью указателя на нее с помощью memcpy? Какой способ упорядочения полей в структуре лучше? Realloc выходит из строя после 10-й итерации внутри цикла Нажмите «Ввод», чтобы продолжить в C `bash: ./a.out: нет такого файла или каталога` при запуске исполняемого файла, созданного` ld`

Лучший способ обработки выделения памяти в C?

Я думаю, что я хорошо разбираюсь в том, как обрабатывать память на C ++, но делать это на C – это совсем другое. Я немного не понимаю.

В C ++ у меня есть конструкторы и деструкторы, у меня есть довольно простой новый и удалить, и я знаю, как инкапсулировать его с помощью RAII, используя интеллектуальные указатели и внутри classов.

Однако в CI нельзя обрабатывать malloc и освобождать то же самое. Я не знаю, как скрыть их и как автоматизировать вещи. Все, что я могу понять, это использовать функции для инициирования и уничтожения моих указателей. Но как мне настроить структуру памяти?

При написании этого я понял, что это больше вопрос о том, как я понимаю stream С, чем что-либо другое, но один вопрос за раз.

Редактировать : Спасибо за ответы, но мне нужно перефразировать себя.

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

Например, в моих classах я динамически добавляю и уничтожаю память, которую использует мой class. Таким образом, я могу добиться своего рода инкапсуляции, мне не нужно знать, когда / как / почему class обрабатывает ее память, это просто так. Это означает, что я могу «скрыть» управление нижней памятью и просто сосредоточиться на нескольких «больших» classах.

Что я хочу знать, так это то, что лучше всего подходит для обработки памяти в C? Для меня нет classов с конструкторами / деструкторами. Хорошо ли выделять память в начале функции или использовать функцию, которая создает ее для меня? И как я должен освободить их снова?

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

Часть путаницы заключается в том, что в C. malloc своей природе сложнее и free , похожие на new и delete : malloc выделяет новую память и возвращает указатель на эту память. free делает эту память доступной снова, пока это память, которая была выделена с помощью malloc . В противном случае, это просто делает hash некоторой части памяти. Это все равно.

Важная вещь с malloc / free – это принять решение и последовательно поддерживать дисциплинированное использование. Вот несколько советов:

ВСЕГДА проверяем возвращенный указатель из malloc для NULL

 if((p = (char *) malloc(BUFSIZ)) == NULL { /* then malloc failed do some error processing. */ } 

Для безопасности ремней и подтяжек установите указатель на NULL после его освобождения.

 free(p); p = NULL ; 

попробуйте malloc и освободите кусок памяти в том же объеме, если это возможно:

  { char * p ; if((p = malloc(BUFSIZ)) == NULL { /* then malloc failed do some error processing. */ } /* do your work. */ /* now you're done, free the memory */ free(p); p = NULL ; /* belt-and suspenders */ } 

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

  /* foo: do something good, returning ptr to malloc memory */ char * foo(int bar) { return (char *) malloc(bar); } 

При написании этого я понял, что это больше вопрос о том, как я понимаю stream С, чем что-либо другое, но один вопрос за раз.

Я честно думаю, что вы должны читать K & R, если нет.

К сожалению, существуют ограниченные страtagsи для автоматизации распределения памяти и освобождения памяти в C. Компилятор C ++ генерирует много кода за кулисами для вас – он отслеживает каждую переменную в стеке и гарантирует, что соответствующий деструктор вызывается, когда стек очищается. На самом деле это довольно сложный тип генерации кода, особенно когда вы добавляете исключения в микс.

С другой стороны, гораздо проще, поэтому его иногда называют «языком ассемблера высокого уровня». C не имеет механизма, гарантирующего, что вызывается конкретный бит кода, когда функция выходит или переменная вылетает из стека, поэтому вам нужно отслеживать каждый бит памяти, которую вы выделяете, и каждый файл или сеть вы открываете и очищаете их в соответствующей точке. Нет практического способа создания автоматического интеллектуального указателя в C.

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

Вы должны заглянуть в проект Apache Portable Runtime. У них есть библиотека пула памяти (документы находятся по адресу http://apr.apache.org/docs/apr/1.3/group__apr__pools.html ). Если APR слишком много для вас, чтобы погрузиться в него, вы можете реализовать очень простой пул памяти, используя три функции и структуру связанных списков. Псевдокод будет примерно таким:

 struct Pool { void* memoryBlock; struct Pool *next; } struct Pool *createPool(void) { /* allocate a Pool and return it */ } void addToPool(struct Pool *pool, void *memoryBlock) { /* create a new Pool node and push it onto the list */ } void destroyPool(struct Pool *pool) { /* walk the list, free each memory block then free its node */ } 

Использование пула – вот что:

 int main(void) { struct Pool *pool = createPool(); /* pool is empty */ doSomething(pool); /* pool full of crap, clean it up and make a new one */ destroyPool(pool); pool = createPool(); /* new pool is empty */ doMoreStuff(pool); destroyPool(pool); return 0; } 

Печальная правда заключается в том, что C не предназначен для инкапсуляции всех этих проблем управления памятью.

Если вы посмотрите на довольно качественные API-интерфейсы, такие как POSIX, вы увидите, что общий шаблон заключается в том, что вы передаете указатель на указатель на функцию, которая затем выделяет память, и что вы позже передаете ее снова функции, которая разрушает Это.

Это не обязательно элегантный, но я не думаю, что есть много способов сделать его действительно элегантным без имитации ООП в C.

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

Один из способов «скрыть» выделение и выделение памяти – это передать его в пользовательские контейнеры. Пропустите контейнер нераспределенным объектом. Пусть это беспокоится о malloc, и когда я удаляю объект, пусть он беспокоится о бесплатном. Конечно, это работает, только если вы только сохраняете объект в одном контейнере. Если у меня есть ссылки на объекты во всем месте, я создам эквивалент методов конструктора и деструктора с синтаксисом c:

  glob* newGlob(); void freeGlob(glob* g); 

(по объекту я имею в виду все, на что вы указывали бы – не c ++-объекты).

Есть много возможностей сделать вашу жизнь проще. Кажется, вы уже столкнулись с идеей создания фабрик / конструкторов для ваших C-объектов. Это хорошее начало.

Некоторые другие идеи для рассмотрения.

  1. не соглашайтесь на стандартный malloc / free. пойдите в поисках лучшего, который был раскрыт или написал тот, который позволяет использовать память объектов, которые вы создаете. Кроме того, мы говорим C здесь, вы собираетесь перезаписывать свои объекты бесплатно больше и один раз и забыть их освобождать, поэтому создайте некоторую поддержку отладки в своем malloc. писать собственное не сложно, если вы не можете найти тот, который соответствует вашим потребностям.

  2. используйте более одной кучи. используйте один class для каждого classа, который вы создаете, используйте временные кучи, если знаете, что у вас будет большое количество связанных между собой временных объектов, это уменьшает fragmentацию памяти и позволяет вам управлять памятью в соответствии с использованием.

  3. взгляните на страtagsи, подобные пулам Objective-C

  4. если вы считаете, что понимаете, как работает C ++, то добавление поведения конструктора к распределению памяти на фабрике объектов не так сложно сделать, и использование настраиваемого бесплатного может затем предоставить вам возможность вызвать деструктор на объекте, свободном от предоставления вы вернули некоторые из поведения C ++, которое вам понравилось

Я не знаю, как скрыть их и как автоматизировать вещи.

C и C ++ – разные языки. Теперь скажи, что сто раз для себя. Будь громко.

Что вы имеете в виду, скрывая? Что вы подразумеваете под автоматизацией? Можете ли вы опубликовать несколько примеров? Зачем вам нужно скрывать и / или автоматизировать.

Хорошие места в сети, чтобы начать с выделения памяти C:

  • Часто задаваемые вопросы C (в частности, глава 7)
  • Десять заповедей для программистов C

Обычным способом является

 MyType *ptr = malloc(array_size * sizeof *ptr); 

Но если вы хотите быть совместимым с c ++, сделайте

 MyType *ptr = (MyType*) malloc(array_size * sizeof *ptr); 

Вы также можете сделать макрос

 #define MALLOC( NUMBER, TYPE ) ( TYPE * ) malloc( NUMBER * sizeof( TYPE ) ) MyType *ptr = MALLOC(10, MyType); 

Конечно, без RAII, убедитесь, что когда-нибудь вы

 free(ptr); 

Я не совсем уверен, что вы просите, но C довольно просто:

 struct Foo *f0 = malloc(sizeof(*f)); // alloc uninitialized Foo struct struct Foo *f1 = calloc(1,sizeof(*f)); // alloc Foo struct cleared to all zeroes //You usually either want to clear your structs using calloc on allocation, or memset. If // you need a constructor, just write a function: Foo *Foo_Create(int a, char *b) { Foo *r = calloc(1,sizeof(*r)); r->a = a; r->b = strdup(b); return r; } Here is a simple C workflow with arrays: struct Foo **foos = NULL; int n_foos = 0; ... for(i = 0; i < n_foos; ++i) { struct Foo *f = calloc(1,sizeof(*f)); foos = realloc(foos,sizeof(*foos)*++n_foos); // foos before and after may be different foos[n_foos-1] = f; } 

Если вам интересно, вы можете написать macros, чтобы помочь:

 #define MALLOCP(P) calloc(1,sizeof(*P)) // calloc inits alloc'd mem to zero 

Несколько баллов:

  • malloc, calloc, realloc и т. д. все используют free (), поэтому управлять этими вещами легко. Просто будьте последовательны.
  • производительность для mallocs может быть медленной. Кто-то разместил ссылку на это выше. В эти дни быстрые многопоточные распределения являются ключевыми, см. Tcmalloc et al. Вы, вероятно, не должны беспокоиться об этом.
  • на современной архитектуре виртуальной памяти malloc почти никогда не сбой, если вы не находитесь в виртуальном адресном пространстве. Если это произойдет, переключитесь на 64 бит;)
  • Убедитесь, что вы используете систему, которая имеет проверку границ, стирает бесплатные значения, отслеживание утечки и все эти полезные вещи (см. Valgrind, win32 debug heap и т. Д.).

Я знаю, что это старый пост, но на самом деле не было много полного ответа на передовую практику с точки зрения стиля , который, как мне кажется, действительно нужен оператору, поэтому вот мое выделение памяти в C. Note I Я больше человек C ++, поэтому мои мысли исходят из этого отношения.

Часто бывает полезно узнать, назначен ли ваш указатель, поэтому всегда указывайте NULL указателю при его объявлении. Вы также можете создать себе безопасную бесплатную функцию, которая освобождает память, а затем присваивает ей NULL, поэтому вам не нужно беспокоиться.

Если вы выделяете память в один файл C, вы должны освободить ее в том же файле. Это, пожалуй, более ограничительный, чем необходимо, однако, если вы пишете библиотеку, вы должны определенно освободить любую память в своей библиотеке, которая является malloc’d в вашей библиотеке. Это связано с тем, что на Windows dll есть другая куча для exe, поэтому mallocing memory в dll и освобождение ее в exe искажает вашу кучу.

По расширению и ради симметрии это означает, что если у вас есть функция, которая возвращает указатель на выделенную память, тогда у вас должна быть функция, которая освобождает эту память. Вот почему во многих библиотеках есть функция инициализации, которая возвращает указатель на некоторые данные (обычно передаваемые как void *), а затем функцию очистки, которая освобождает ресурсы библиотеки. Если вы можете malloc и освобождаться внутри одной и той же функции, это хорошо, так как вам легко следить за вещами.

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

Если у вас часто есть функции, которые выделяют много указателей, тогда рассмотрите создание и массив, который содержит указатели на все ваши указатели в начале функции, а затем функцию, которая освобождает их всех. Это избавит вас от неизбежного синдрома «Я вернусь и уберу утечку памяти позже», если вы хотите вернуть среднюю функцию.

Концепция фабрик полезна. Фабрика будет функцией, которая будет хранить память для структуры, назначает указатель на функцию struct, инициализирует ее переменные и затем возвращает указатель на нее. Если первый из них был деструктором или массивом определенных функций, то вы можете иметь общую функцию уничтожения, которая может вызвать деструктор любой структуры, а затем освободить память структуры. Вы также можете скрыть некоторые внутренние детали classа, имея различное определение структуры внутри и снаружи. COM построен на этих принципах.

Таким образом, это всего лишь то, как я смотрю на память в C. Это не так элегантно, как на C ++, но, поскольку вы полагаетесь на людей, чтобы справиться с этим, есть страtagsи, подобные описанным выше, которые могут сделать все как можно проще для их.

Обратите также внимание на то, что всегда есть исключения для каждого правила – это то, о чем я думаю, когда я использую C. Я уверен, что у других людей есть другие идеи.

Фил

Вы можете подумать, что то, что я говорю, странно, но нет большой разницы между C ++ и C. C имеет RAII. RAII не относится только к C ++.

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

Класс C ++:

 class foo { public: ofstream ff; int x,y; foo(int _x) : x(_x),y(_x){ ff.open("log.txt"); } void bar() { ff< f(new foo); f->bar(); } 

Объект C

 typedef struct FOO { FILE *ff; int x,y; } *foo_t; foo_t foo_init(int x) { foo_t p=NULL; p=malloc(sizeof(struct FOO)); // RAII if(!p) goto error_exit; p->x=x; p->y=x; p->ff=fopen("log.txt","w"); // RAII if(!p->ff) goto error_exit; return p; error_exit: // ON THROW if(p) free(p); return NULL; } void foo_close(foo_t p) { if(p) fclose(p->ff); free(p); } void foo_bar(foo_t p) { fprintf(p->ff,"%d\n",p->x+p->y); } int main() { foo_t f=foo_init(1); if(!f) return 1; foo_bar(f); foo_close(f); return 0; } и typedef struct FOO { FILE *ff; int x,y; } *foo_t; foo_t foo_init(int x) { foo_t p=NULL; p=malloc(sizeof(struct FOO)); // RAII if(!p) goto error_exit; p->x=x; p->y=x; p->ff=fopen("log.txt","w"); // RAII if(!p->ff) goto error_exit; return p; error_exit: // ON THROW if(p) free(p); return NULL; } void foo_close(foo_t p) { if(p) fclose(p->ff); free(p); } void foo_bar(foo_t p) { fprintf(p->ff,"%d\n",p->x+p->y); } int main() { foo_t f=foo_init(1); if(!f) return 1; foo_bar(f); foo_close(f); return 0; } и typedef struct FOO { FILE *ff; int x,y; } *foo_t; foo_t foo_init(int x) { foo_t p=NULL; p=malloc(sizeof(struct FOO)); // RAII if(!p) goto error_exit; p->x=x; p->y=x; p->ff=fopen("log.txt","w"); // RAII if(!p->ff) goto error_exit; return p; error_exit: // ON THROW if(p) free(p); return NULL; } void foo_close(foo_t p) { if(p) fclose(p->ff); free(p); } void foo_bar(foo_t p) { fprintf(p->ff,"%d\n",p->x+p->y); } int main() { foo_t f=foo_init(1); if(!f) return 1; foo_bar(f); foo_close(f); return 0; }