문제
그래서 나는 string
유형 I가 만들었습니다. 그들 중 하나는 동적으로 할당 된 찌르기를 만듭니다. 다른 하나는 말한 문자열을 가져 와서 확장합니다. 그리고 마지막은 끈을 해방시킵니다. 참고 : 함수 이름은 변경되었지만 모두 사용자 지정 정의됩니다.
string new = make("Hello, ");
adds(new, "everyone");
free(new);
위의 코드는 작동합니다. 컴파일하고 잘 실행됩니다. 아래 코드는 작동하지 않습니다. 컴파일, 실행 및
string new = make("Hello, ");
adds(new, "everyone!");
free(new);
코드의 차이점은 adds()
함수는 1 문자를 더 추가합니다 (a !
). 그것이 추가하는 캐릭터는 길이 만 차이를 만듭니다. 완전성을 위해서는 다음 코드가 작동하지 않습니다.
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()
에스.
기능에 대한 코드를 세분화하고 게시 :
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;
}
해결책
내가 고칠 수있는 여러 가지 잠재적 문제 :
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()
이 경우 기능이 큰 버퍼와 작은 문자열이있는 문자열을 줄입니다.
다른 팁
첫 번째 malloc make
해야한다:
malloc (sizeof (struct _str));
그렇지 않으면 당신은 충분한 공간을 할당하고 있습니다 바늘 에게 struct _str
.
tmp = realloc(s->_str, s->_len + l + 1);
Realloc은 요청 된 블록에 새 포인터를 반환 할 수 있습니다. 다음 코드 줄을 추가해야합니다.
s->_str = tmp;
그것이 하나의 경우에 충돌하지 않지만 하나를 더 추가 한 후에는 메모리가 할당되는 방식 때문일 것입니다. 아마도 최소 할당 델타가있을 수 있습니다 (이 경우 16의 경우). 따라서 Hello의 첫 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;
}
기능에서 adds
, 당신은 그것을 가정합니다 realloc
재 할당 해야하는 메모리 블록의 주소를 변경하지 않습니다.
tmp = realloc(s->_str, s->_len + l + 1);
if(!tmp) return 0;
memcpy(s->_str + s->_len, c, l);
이것은 작은 재 할당에 해당 될 수 있지만 (당신이 얻는 메모리 블록의 크기는 일반적으로 할당을 최적화하기 위해 반올림되기 때문에) 일반적으로 사실이 아닙니다. Realloc이 새로운 포인터를 반환하면 프로그램이 여전히 이전 정보를 사용하여 문제를 일으 킵니다.
memcpy(s->_str + s->_len, c, l);
아마도 코드를 게시해야하지만 이중 무료는 동일한 포인터에서 두 번 무료로 호출하는 것을 의미합니다.
- 끝에 0 바이트에 1에 1을 추가하고 있습니까?
- 포인터를 풀면 회원 변수를 NULL로 설정하여 다시 자유롭게 해제하지 않도록하십시오 (또는 0xffffffff와 같은 알려진 나쁜 포인터로).
"내 무료 ()는 왜 표준 라이브러리 무료 ()에 대한 두 번의 호출입니다." 왜 두 번 무료로 전화합니까? 한 번만 전화하면됩니다.
adds ()를 게시하십시오. 및 free () 함수.