2009-03-16 4 views
0

Итак, у меня есть несколько функций, которые работают с созданным мной типом string. Один из них создает динамически распределенное жало. Другой принимает указанную строку и расширяет ее. И последний освобождает строку. Примечание. Имена функций изменяются, но все меня настраивают.C вопрос распределения памяти

string new = make("Hello, "); 
adds(new, "everyone"); 
free(new); 

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

string new = make("Hello, "); 
adds(new, "everyone!"); 
free(new); 

Разница между кодом является то, что функция adds() добавляет еще 1 символ (!). Персонаж, который он добавляет, не имеет значения - просто длина. Просто для полноты картины, следующий код не работает:

string new = make("Hello, "); 
adds(new, "everyone"); 
adds(new, "!"); 
free(new); 

Как ни странно, следующий код, который использует другую функцию, addc() (который добавляет 1 символ вместо строки) работает:

string new = make("Hello, "); 
adds(new, "everyone"); 
addc(new, '!'); 
free(new); 

Далее, что также делает то же самое, работает:

string new = make("Hello, everyone!"); 
free(new); 

ошибка, что все те, которые не работают податливость это:

test(526) malloc: *** error for object 0x100130: double free 
*** set a breakpoint in malloc_error_break to debug 

(test крайне описательное название программы у меня есть это.)

Что касается функции внутренних органов, мой make() призыва к strlen() и два вызова malloc() и вызов memcpy() , мой adds() является вызовом strlen(), звоните по номеру realloc() и позвоните по номеру memcpy(), а мой free() - это два вызова стандартной библиотеки free().

Итак, есть идеи, почему я получаю это, или мне нужно сломать и использовать отладчик? Я получаю его только с adds() es на определенную длину, а не с addc() s.

Ломая и размещения кода для функций:

typedef struct _str { 
    int _len; 
    char *_str; 
} *string; 

string make(char *c) 
{ 
    string s = malloc(sizeof(string)); 
    if(s == NULL) return NULL; 
    s->_len = strlen(c); 
    s->_str = malloc(s->_len + 1); 
    if(s->_str == NULL) 
     {  
     free(s); 
     return NULL; 
     } 
    memcpy(s->_str, c, s->_len); 
    return s; 
} 

int adds(string s, char *c) 
{ 
    int l = strlen(c); 
    char *tmp; 
    if(l <= 0) return -1; 
    tmp = realloc(s->_str, s->_len + l + 1); 
    if(!tmp) return 0; 
    memcpy(s->_str + s->_len, c, l); 
    s->_len += l; 
    s->_str[s->_len] = 0; 
    return s->_len; 
} 

void myfree(string s) 
{ 
    if(s->_str) free(s->_str); 
    free(s); 
    s = NULL; 
    return; 
} 
+0

Нам понадобится код функций. Очевидно, что есть ошибка –

+0

, вам нужно будет опубликовать код для добавлений(); –

+0

в целом в стороне, лучше всего не называть что-то «новое» в C ... если вы когда-либо хотели скомпилировать его как C++, вам придется его изменить. Компиляция как C++ - хорошая идея, так как вы можете получать больше сообщений компилятора, которые по очереди делают для лучшего кода (при условии, что вы исправляете сообщения :-) – TofuBeer

ответ

4

ряд потенциальных проблем, которые я бы исправить:

1/Ваш make() опасно, так как это не копирование через нуль-терминатор для строки.

2/Он также имеет мало смысла устанавливать s к NULL в myfree(), так как это передаваемый параметр и не будет иметь никакого влияния на фактический параметр передается в

3/Я не уверен, почему вы возвращаете. - 1 от adds(), если добавленная длина строки равна 0 или меньше. Во-первых, это не может быть отрицательным. Во-вторых, кажется довольно правдоподобным, что вы могли бы добавить пустую строку, что должно привести к тому, что она не изменит строку и не вернет текущую длину строки. Я бы только вернул длину -1, если она не удалась (т. Е. realloc() не сработала) и убедитесь, что старая строка сохраняется, если это произойдет.

4/Вы не храните переменную tmp в s->_str, хотя она может меняться - она ​​редко перераспределяет память на месте, если вы увеличиваете размер, хотя это возможно, если увеличение достаточно мало, чтобы соответствовать в пределах любого дополнительного места, выделенного malloc(). Сокращение размера почти наверняка будет переопределяться на месте, если ваша реализация malloc() не использует разные пулы буферов для блоков памяти различного размера. Но это просто в стороне, так как вы никогда не уменьшаете использование памяти с помощью этого кода.

5/Я думаю, что ваша проблема заключается в том, что вы выделяете пространство для строки, которая является указателем на структуру, а не на структуру. Это означает, что когда вы вставляете строку, вы развращаете арену памяти.

Это код, который я бы написал (включая более описательные имена переменных, но это только мои предпочтения).

Я изменился:

  • возвращаемые значения от adds() лучше отражать длину и ошибок условий. Теперь он возвращает только -1, если он не может расширяться (и исходная строка не затронута) - любое другое возвращаемое значение - это новая длина строки.
  • возвращение от myfree(), если вы действительно хотите установить строку в NULL с чем-то вроде «s = myfree (s)».
  • чеки в myfree() для NULL строка, так как теперь у вас нет ни одного выделенного string без выделенного string->strChars.

Вот она, использовать (или не :-), как вы считаете нужным:

/*================================*/ 
/* Structure for storing strings. */ 

typedef struct _string { 
    int strLen;  /* Length of string */ 
    char *strChars; /* Pointer to null-terminated chars */ 
} *string; 

/*=========================================*/ 
/* Make a string, based on a char pointer. */ 

string make (char *srcChars) { 
    /* Get the structure memory. */ 

    string newStr = malloc (sizeof (struct _string)); 
    if (newStr == NULL) 
     return NULL; 

    /* Get the character array memory based on length, free the 
     structure if this cannot be done. */ 

    newStr->strLen = strlen (srcChars); 
    newStr->strChars = malloc (newStr->strLen + 1); 
    if(newStr->strChars == NULL) {  
     free(newStr); 
     return NULL; 
    } 

    /* Copy in string and return the address. */ 

    strcpy (newStr->strChars, srcChars); 
    return newStr; 
} 

/*======================================================*/ 
/* Add a char pointer to the end of an existing string. */ 

int adds (string curStr, char *addChars) { 
    char *tmpChars; 

    /* If adding nothing, leave it alone and return current length. */ 

    int addLen = strlen (addChars); 
    if (addLen == 0) 
     return curStr->strLen; 

    /* Allocate space for new string, return error if cannot be done, 
     but leave current string alone in that case. */ 

    tmpChars = malloc (curStr->strLen + addLen + 1); 
    if (tmpChars == NULL) 
     return -1; 

    /* Copy in old string, append new string. */ 

    strcpy (tmpChars, curStr->strChars); 
    strcat (tmpChars, addChars); 

    /* Free old string, use new string, adjust length. */ 

    free (curStr->strChars); 
    curStr->strLen = strlen (tmpChars); 
    curStr->strChars = tmpChars; 

    /* Return new length. */ 

    return curStr->strLen; 
} 

/*================*/ 
/* Free a string. */ 

string myfree (string curStr) { 
    /* Don't mess up if string is already NULL. */ 

    if (curStr != NULL) { 
     /* Free chars and the string structure. */ 

     free (curStr->strChars); 
     free (curStr); 
    } 

    /* Return NULL so user can store that in string, such as 
     <s = myfree (s);> */ 

    return NULL; 
} 

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

Для этого потребуется как длина буфера, так и длина строки и изменение кода, чтобы выделять больше места, если длина объединенной строки и длина новых символов больше длины буфера.

Все это будет инкапсулировано в функцию, чтобы API не изменился вообще. И, если вы когда-нибудь обойдетесь с функциями, чтобы уменьшить размер строки, им также не придется перераспределять память, они просто уменьшат использование буфера. В этом случае вам, вероятно, понадобится функция compress(), чтобы уменьшить строки, имеющие большой буфер и небольшую строку.

+0

# 4 был верным, но я выбрал это как правильное для всех других пунктов, которые вы сделали. Спасибо.У меня есть функция trunc(), чтобы обрезать строку, но в настоящее время она выполняет realloc(), а затем просто устанавливает последний символ в '\ 0' - это работает, но мне нравится ваше предложение о пред-malloc() ed space , Спасибо! –

0

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

  1. Вы добавляете 1 к strlen для байта \ 0 в конце?
  2. После того, как вы освободите указатель, вы настройка вашего переменный члена в NULL, так что вы не свободен снова (или известный плохой указатель как 0xFFFFFFFF)
+0

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

0

Почему «мой свободное() есть два звонки в стандартную библиотеку free(). " Почему вы звоните бесплатно дважды? Вам нужно только позвонить один раз.

Пожалуйста, разместите свои добавленные(); и free().

+0

Структура имеет указатель в ней, а функция make() malloc() - это структура AND указатель внутри структуры. Функция free(), следовательно, free() - указатель внутри структуры, а также сама структура. Отправленный код. –

4

Первый таНос в make должно быть:

malloc (sizeof (struct _str)); 

В противном случае вы только выделить достаточно места для указателя к struct _str.

2
tmp = realloc(s->_str, s->_len + l + 1); 

realloc может возвращать новый указатель на запрошенный блок. Вам нужно добавить следующую строку кода:

s->_str = tmp; 

Причина не врезаться в одном случае но после добавления еще одного символа, вероятно, просто из-за того, как выделяется память. Вероятно, существует дельта минимального распределения (в данном случае 16). Поэтому, когда вы выделяете первые 8 символов для приветствия, он фактически выделяет 16. Когда вы добавляете всех, это не превышает 16, поэтому вы возвращаете исходный блок. Но для 17 символов realloc возвращает новый буфер памяти.

Попробуйте изменить добавить следующие

tmp = realloc(s->_str, s->_len + l + 1); 
if (!tmp) return 0; 
if (tmp != s->_str) { 
    printf("Block moved!\n"); // for debugging 
    s->_str = tmp; 
} 
1

В функции adds, вы предполагаете, что realloc не меняет адрес блока памяти, который должен быть перераспределены:

tmp = realloc(s->_str, s->_len + l + 1); 
if(!tmp) return 0; 
memcpy(s->_str + s->_len, c, l); 

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

memcpy(s->_str + s->_len, c, l); 
Смежные вопросы