Использование fscanf с динамически распределяемым буфером - PullRequest
1 голос
/ 27 марта 2010

Я получил следующий код:

char buffer[2047];
int charsRead;

do {
    if(fscanf(file, "%2047[^\n]%n%*c", buffer, &charsRead) == 1) {
        // Do something
    }
} while (charsRead == 2047);

Я хотел преобразовать этот код для использования динамически размещаемых переменных, чтобы при частом вызове этого кода я не получал сильной утечки памяти. Таким образом, я попробовал это:

char *buffer = malloc(sizeof(char) * 2047);
int *charsRead = malloc(sizeof(int));

do {
    if(fscanf(file, "%2047[^\n]%n%*c", *buffer, charsRead) == 1) {
        // Do something
    }
} while (*charsRead == 2047);

К сожалению, это не работает. Я всегда получаю ошибки «EXC_BAD_ACCESS», перед оператором if с вызовом fscanf. Что я делаю не так?

Спасибо за любую помощь!

- Ry

Ответы [ 5 ]

5 голосов
/ 27 марта 2010

Исходный код имеет гораздо меньшую вероятность утечки, чем новый, так как компилятор управляет памятью для вас, но, если вы чувствуете необходимость, измените на:

if(fscanf(file, "%2047[^\n]%n%*c", buffer, charsRead) == 1) {

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

1 голос
/ 27 марта 2010

Я вижу три проблемы с вашим кодом. Во-первых, чтобы ответить на поставленный вопрос, вы должны передать fscanf вместо *buffer в качестве третьего аргумента *1003*. Ответ Нейла хорошо объясняет почему.

Во-вторых, у вас переполнение буфера. fscanf автоматически добавляет нулевой завершающий символ к отсканированному вводу. В буфере, который вы даете fscanf, должно быть достаточно места для отсканированного ввода и нулевого завершающего символа. Если вы хотите отсканировать 2047 символов, ваш буфер должен иметь длину 2048 символов.

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

Случай only , где новая версия может быть предпочтительнее, - это если вы ориентируетесь на систему с очень ограниченным пространством стека (как в случае некоторых встроенных систем). В таких системах выделение большого буфера в стеке может быть плохой идеей. В таком случае может быть предпочтительнее выделить буфер из кучи, используя malloc, чтобы избежать возможных переполнений стека. Если это так, тогда вы должны быть осторожны, чтобы избежать утечек памяти, вызвав free для освобождения памяти .

1 голос
/ 27 марта 2010

Чтобы ответить «что я делаю не так», вы разыменовываете указатель buffer. Так как он объявлен как char *, когда вы пишете *buffer, вы получаете первый символ в буфере, вы хотите всего этого, поэтому просто удалите * спереди, например:

if(fscanf(file, "%2047[^\n]%n%*c", buffer, charsRead) == 1) {

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

1 голос
/ 27 марта 2010

Хорошей идеей будет сделать буфер как минимум на один байт длиннее, чем число символов, которые вы ожидаете прочитать, чтобы вы могли также содержать завершающий NUL. Кроме того, оригинал не будет течь; как только функция, содержащая код, возвращается, эти переменные исчезают. Если есть утечка, это должно быть что-то, что вы нам не показываете.

0 голосов
/ 27 марта 2010

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

...