Почему в C / C ++ есть проблемы с памятью? - PullRequest
8 голосов
/ 05 июня 2010

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

Ответы [ 9 ]

16 голосов
/ 05 июня 2010

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

Например, просто невозможно освободить выделенную память. Например, это сделает «двойное освобождение», пытаясь освободить a дважды, и не может освободить b:

char *a = malloc(128*sizeof(char));
char *b = malloc(128*sizeof(char));
b = a;
free(a);
free(b); // will not free the pointer to the original allocated memory.

Ниже приведен пример переполнения буфера, который повреждает произвольную память. Это переполнение буфера, потому что вы не знаете, как долго str. Если он длиннее 256 байт, он запишет эти байты где-нибудь в память, возможно, перезаписывая ваш код, возможно, нет.

void somefunc(char *str) {
    char buff[256];
    strcpy(buff, str);
}
7 голосов
/ 05 июня 2010

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

Ручное управление памятью может привести к ряду ошибок:

  • Если вы забыли освободить память, у вас есть утечка памяти
  • Если вы используете больше памяти, чем запрашивали для данного указателя, у вас переполнение буфера.
  • Если вы освобождаете память и продолжаете использовать «висячий указатель» на нее, у вас будет неопределенное поведение (обычно программа вылетает)
  • Если вы неправильно рассчитали арифметику указателя, у вас произошел сбой или повреждены данные

И многие из этих проблем очень трудно диагностировать и отлаживать.

5 голосов
/ 05 июня 2010

Я могу честно сказать, что у меня нет "проблем" с распределением памяти при программировании на C ++. В последний раз у меня была утечка памяти более 10 лет назад, и это было связано со слепой глупостью с моей стороны. Если вы пишете код с использованием RAII, стандартных библиотечных контейнеров и небольшого количества здравого смысла, проблема действительно не существует.

5 голосов
/ 05 июня 2010

Я планирую научиться программировать на C / C ++

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

С точки зрения пользователя, управление памятью в C ++ намного проще, чем в C, потому что большая часть его инкапсулирована классами, например std::vector<T>. С концептуальной точки зрения, управление памятью C является спорно намного проще. В основном, есть только malloc и free:)

3 голосов
/ 05 июня 2010

Одна из распространенных проблем управления памятью в C и C ++ связана с отсутствием проверки границ для массивов. В отличие от Java (например), C и C ++ не проверяют, чтобы убедиться, что индекс массива находится в пределах фактических границ массива. Из-за этого легко случайно перезаписать память. Например (C ++):

char *a = new char[10];
a[12] = 'x';

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

3 голосов
/ 05 июня 2010

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

2 голосов
/ 09 июня 2010

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

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

struct student
{
 char name[20];
 int roll;
 float marks;
}s[100];

Здесь я полагаю, 100 учеников в классе. Учащемуся может быть больше 100 или меньше 100. Если больше 100, то ваша программа потеряет информацию или меньше 100, тогда программа будет работать, но потеря памяти может быть большой.

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

struct student *s;

s=(struct student *)malloc(sizeof(struct student));

scanf("%s %d %f",s->name,s->roll,s->marks);

если он не используется, то удалить его из пробела.

free(s);

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

2 голосов
/ 05 июня 2010

Когда new используется для получения блока памяти, размер, зарезервированный операционной системой, может быть больше, чем ваш запрос, но никогда не меньше. Из-за этого и того факта, что удаление не сразу возвращает память операционной системе, при проверке всей памяти, используемой вашей программой, вы можете быть уверены, что у вашего приложения серьезные утечки памяти. Поэтому проверка количества байтов, используемых всей программой, не должна использоваться как способ обнаружения ошибок памяти. Вы можете подозревать утечки памяти только в том случае, если диспетчер памяти указывает на большой и непрерывный рост используемой памяти.

1 голос
/ 05 июня 2010

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

Итак, почему вы хотите вручную управлять памятью, по нескольким причинам: - Чтобы понять, как это работает / - Для реализации сборки мусора / автоматического управления памятью нужно вручную - С некоторыми вещами более низкого уровня, такими как ядро, вам нужна гибкость, которую может дать ручное управление памятью. - Самое главное, если вы сделаете правильное ручное управление памятью, вы можете получить большое ускорение / меньшие накладные расходы памяти (лучшую производительность), связанную проблему со сборкой мусора (хотя она становится лучше, когда пишутся лучшие сборщики мусора, такие как hotspot jvm one) в том, что вы не можете контролировать управление памятью, поэтому сложно делать что-то с вещами в реальном времени (гарантируя сроки для определенных задач, таких как автомобильные тормоза и кардиостимуляторы, попробуйте специальный gc в реальном времени), и программы, которые взаимодействуют с пользователями, могут немного зависнуть или отставание (это будет отстой для игры).

Многие "Современные C ++" (как уже говорилось, C ++ можно рассматривать как несколько языков в зависимости от того, как вы их используете), в отличие от c с классами (или функцией x и y C ++), часто используют компромисс использование простого необязательного gc / автоматического управления памятью (обратите внимание, что необязательный gc может хуже работать с управлением памятью, чем обязательный, потому что, когда он обязателен, это более простая система), а также и некоторое ручное управление памятью. В зависимости от того, как вы это делаете, он может иметь некоторые преимущества и недостатки использования gc и ручного управления памятью. Дополнительный GC также доступен с некоторыми библиотеками C, но он реже встречается с c, чем c ++.

...