문제

그래서 나는 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);

아마도 코드를 게시해야하지만 이중 무료는 동일한 포인터에서 두 번 무료로 호출하는 것을 의미합니다.

  1. 끝에 0 바이트에 1에 1을 추가하고 있습니까?
  2. 포인터를 풀면 회원 변수를 NULL로 설정하여 다시 자유롭게 해제하지 않도록하십시오 (또는 0xffffffff와 같은 알려진 나쁜 포인터로).

"내 무료 ()는 왜 표준 라이브러리 무료 ()에 대한 두 번의 호출입니다." 왜 두 번 무료로 전화합니까? 한 번만 전화하면됩니다.

adds ()를 게시하십시오. 및 free () 함수.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top