programing

C 문자열의 '\0' 뒤에 있는 메모리는 어떻게 됩니까?

kingscode 2022. 7. 11. 23:41
반응형

C 문자열의 '\0' 뒤에 있는 메모리는 어떻게 됩니까?

의외로 간단한 질문/진지한 질문/기본적인 질문이지만, 저는 잘 모르겠습니다.함수 사용자에게 함수 시작 시 길이를 알 수 없는 C 문자열을 반환한다고 가정합니다.처음에는 길이에 상한만 넣을 수 있으며, 가공에 따라 사이즈가 줄어들 수 있습니다.

문제는 충분한 힙 공간(상한)을 할당한 후 처리 중에 문자열에 훨씬 못 미치는 부분(즉, 할당된 메모리 중간에 "\0"을 삽입하면 해당 문자열이 종료되는 경우)이 잘못된 것이냐는 것입니다(a).free()(b) '0' 뒤의 공간은 중요하지 않은가?'\0'이 추가되면 메모리가 반환될까요?아니면 다음 시간까지 메모리를 계속 사용할까요?free()이름이 뭐죠?malloc을 호출하기 전에 필요한 공간을 계산하는 선행 프로그래밍 시간을 절약하기 위해 이 행 공간을 그대로 두는 것은 일반적으로 잘못된 프로그래밍 스타일입니까?

예를 들어 다음과 같이 연속되는 중복을 삭제합니다.

입력 "Hello oOOOO !!" --> 출력 "Hello oO!"

...그리고, 이하에 나타내는 코드도 있습니다.작업에 따른 사이즈를 사전에 계산하고, 히프 사이즈를 올바르게 하기 위해서 2회 처리를 효과적으로 실행하고 있습니다.

char* RemoveChains(const char* str)
{
    if (str == NULL) {
        return NULL;
    }
    if (strlen(str) == 0) {
        char* outstr = (char*)malloc(1);
        *outstr = '\0';
        return outstr;
    }
    const char* original = str; // for reuse
    char prev = *str++;       // [prev][str][str+1]...
    unsigned int outlen = 1;  // first char auto-counted

    // Determine length necessary by mimicking processing
    while (*str) {
        if (*str != prev) { // new char encountered
            ++outlen;
            prev = *str; // restart chain
        }
        ++str; // step pointer along input
    }

    // Declare new string to be perfect size
    char* outstr = (char*)malloc(outlen + 1);
    outstr[outlen] = '\0';
    outstr[0] = original[0];
    outlen = 1;

    // Construct output
    prev = *original++;
    while (*original) {
        if (*original != prev) {
            outstr[outlen++] = *original;
            prev = *original;
        }
        ++original;
    }
    return outstr;
}

할당된 메모리 중간에 '0'을 붙이면,

(a.) free()는 정상적으로 동작합니다.

네.

(b.) '\0' 뒤의 공백이 중요하지 않은가?일단 '\0'이 추가되면 메모리는 그냥 반환됩니까?아니면 free()가 호출될 때까지 메모리를 계속 사용할 수 있습니까?

경우에 따라 다르지요.많은 경우 대량의 힙스페이스를 할당하면 먼저 가상 주소 공간이 할당됩니다.페이지에 쓸 때 실제 물리 메모리가 할당됩니다(또한 나중에 OS가 가상 메모리를 지원하면 이 메모리가 디스크로 스왑될 수 있습니다).가상 주소 공간의 낭비적인 할당과 실제 물리/스왑 메모리의 구별을 통해 이러한 OS에서 스퍼스 어레이의 메모리 효율을 합리적으로 높일 수 있습니다.

이 가상 어드레싱과 페이징의 세밀도는 메모리 페이지 사이즈(4k, 8k, 16k...)로 되어 있습니다.대부분의 OS에는 호출하여 페이지 크기를 확인할 수 있는 기능이 있습니다.따라서 많은 양의 작은 할당을 하는 경우 페이지 크기를 반올림하는 것은 낭비입니다.또, 실제로 사용하는 메모리 용량에 비해 주소 공간이 한정되어 있는 경우는, 상기의 방법으로 가상 주소 지정에 의존하는 것(예를 들면, 32비트 주소 지정의 4 GB RAM)은 확장되지 않습니다.한편, 예를 들어 32GB의 RAM으로 64비트 프로세스를 실행하고 있고, 비교적 적은 수의 문자열을 할당하고 있는 경우, 플레이할 수 있는 가상 주소 공간이 매우 많아 페이지 크기로 반올림하는 것은 그다지 중요하지 않습니다.

단, 버퍼 전체에 기입한 후 이전 시점에서 종료하는 것과 첫 번째 비트에 기입한 후에 종료하는 큰 버퍼의 차이(이 경우 백업 메모리는 사용된 SP에만 할당됨)에 주의해 주십시오.페이지 크기로 반올림합니다).

또, 많은 operating system에서는, 프로세스가 종료할 때까지 operating system에 히프 메모리가 반환되지 않는 경우가 있습니다.대신 malloc/free 라이브러리는 히프를 확장할 필요가 있는 경우(예를 들어 를 사용하여) OS에 통지합니다.sbrk()UNIX 또는VirtualAlloc()를 참조해 주세요).그런 의미에서free()메모리는 프로세스에서 재사용할 수 있지만 다른 프로세스에서는 사용할 수 없습니다.일부 운영체제는 이를 최적화하고 있습니다.예를 들어 매우 큰 할당에 대해 개별적으로 해제 가능한 메모리 영역을 사용합니다.

malloc을 호출하기 전에 필요한 공간을 계산하는 선행 프로그래밍 시간을 절약하기 위해 이 행 공간을 그대로 두는 것은 일반적으로 잘못된 프로그래밍 스타일입니까?

다시 말씀드리지만, 이러한 할당량을 얼마나 처리하느냐에 따라 다릅니다.가상 주소 공간/RAM과 관련하여 많은 메모리가 있는 경우 메모리 라이브러리에 대해 처음에 요청된 모든 메모리가 실제로 필요한 것은 아님을 명시적으로 알릴 필요가 있습니다.realloc()또는strdup()실제 요구에 따라 새로운 블록을 보다 촘촘하게 할당한다(그렇다면).free()원래) - malloc/free 라이브러리 구현에 따라 개선되거나 악화될 수 있지만, 그 차이에 크게 영향을 받는 애플리케이션은 거의 없습니다.

경우에 따라서는, 발신측 애플리케이션이 관리하는 문자열 인스턴스의 수를 추측할 수 없는 라이브러리의 코드가 있는 경우가 있습니다.이러한 경우, 동작이 느려지는 일은 없습니다.따라서 메모리 블록을 문자열 데이터에 맞게 축소하는 데 기울일 수 있습니다(일정한 수의 추가 연산이 빅 O 효율에 영향을 미치지 않음). 원래 문자열 버퍼의 비율이 낭비되는 대신(병리적인 경우 0 또는 임의로 큰 할당 후에 사용되는 문자 중 하나).성능 최적화를 위해 특이한 공간이 >=인 경우에만 메모리를 반환할 수 있습니다. 즉, 취향에 맞게 조정하거나 호출자가 메모리를 호출할 수 있도록 합니다.

다른 답변에 대해 코멘트합니다.

즉, 재할당이 더 오래 걸릴지, 아니면 전처리 크기를 결정할지를 판단해야 합니다.

퍼포먼스가 최우선이라면 프로파일링을 해야 합니다.CPU에 얽매이지 않는 경우는, 일반적으로 「전처리」의 타격을 받아 적절한 사이즈의 할당을 실시합니다.그냥 단편화나 혼란이 적습니다.이에 대응하여, 일부 함수에 대해 특별한 전처리 모드를 작성해야 하는 경우, 이는 오류와 코드를 유지하기 위한 추가 "표면"입니다.(이 트레이드오프 결정은 일반적으로 고객 고유의 실장 시에 필요합니다.asprintf()부터snprintf()하지만 적어도 신뢰할 수 있는 것은snprintf()문서화된 대로 행동하고 개인적으로 관리할 필요가 없습니다.)

일단 '\0'이 추가되면 메모리는 그냥 반환됩니까?아니면 free()가 호출될 때까지 메모리를 계속 사용할 수 있습니까?

마법 같은 건 없어\0전화하셔야 합니다.realloc할당된 메모리를 "확장"하는 경우.그렇지 않으면 전화할 때까지 메모리가 그대로 남아 있습니다.free.

할당된 메모리 중간에 "\0"을 붙여도 (a.) free()는 정상적으로 동작합니까?

기억 속에서 무엇을 하든 free에서 반환된 포인터와 동일한 포인터를 전달하면 항상 정상적으로 동작합니다.malloc물론 밖에서 글을 쓴다면 모든 베팅은 무효가 된다.

\0에서 한 글자만 더하면 된다.malloc그리고.free어떤 데이터를 메모리에 저장해도 상관없습니다.그렇게free를 추가해도 동작합니다.\0중간에 있거나 추가하지 말 것\0조금도.할당된 추가 공간은 그대로 유지되며 추가되는 즉시 프로세스로 반환되지 않습니다.\0기억에 남습니다.개인적으로는 상한으로 할당하는 것이 아니라 필요한 양의 메모리만 할당하는 것이 좋습니다.그렇게 하면 리소스가 낭비되기 때문입니다.

malloc()를 호출하여 힙에서 메모리를 가져오면 바로 사용할 수 있습니다.\0을 삽입하는 것은 다른 문자를 삽입하는 것과 같습니다.이 메모리는, 유저가 메모리를 해방할 때까지, 또는 OS가 메모리를 회수할 때까지 보관 유지됩니다.

\0는 문자 배열을 따끔따끔한 것으로 해석하는 순수한 규약입니다.메모리 관리와는 무관합니다.즉, 돈을 돌려받고 싶으면realloc. 이 문자열은 메모리(많은 보안 문제의 원인)에는 관심이 없습니다.

malloc은 메모리 청크를 할당합니다.초기 포인터 위치에서 호출하지 않고 원하는 대로 사용할 수 있습니다.중간에 '\0'을 삽입해도 아무 문제가 없습니다.

특정 malloc은 원하는 메모리 유형을 알 수 없습니다(void 포인터를 반환합니다).

예를 들어 0x10부터0x19까지 10바이트의 메모리를 할당한다고 가정합니다.

char * ptr = (char *)malloc(sizeof(char) * 10);

5번째 위치(0x14)에 Null을 삽입해도 메모리 0x15 이후로는 해방되지 않습니다.

단, 0x10에서 프리하면 10바이트의 청크 전체가 해방됩니다.

  1. free()메모리 내의 NUL 바이트로 동작합니다.

  2. 그 공간은 계속 낭비될 것이다.free()호출되거나 나중에 할당을 축소하지 않는 한

일반적으로 메모리는 메모리입니다.네가 뭘 쓰든 상관없다.단, 레이스가 있거나 플레이버(malloc, new, VirtualAlloc, HeapAlloc 등)를 원하는 경우.즉, 메모리를 할당하는 측도 메모리를 할당 해제하는 수단을 제공해야 합니다.API가 DLL에 포함되어 있는 경우 어떤 종류의 무료 함수를 제공합니다.이게 당연히 발신자에게 부담이 되는 거죠?그럼 왜 전화 건 사람에게 모든 부담을 지우지 않는 거죠?동적으로 할당된 메모리를 처리하는 가장 좋은 방법은 메모리를 직접 할당하지 않는 것입니다.발신자에게 그것을 할당해, 전달하도록 지시합니다.그는 자신이 할당한 맛을 알고 있으며, 그것을 다 사용할 때마다 그것을 해방시킬 책임이 있다.

발신자는 할당량을 어떻게 알 수 있습니까?많은 Windows API가 NULL 포인터를 사용하여 호출했을 때 필요한 바이트 수를 반환하고 NULL 이외의 포인터가 있으면 작업을 수행합니다(접근성을 재확인하는 것이 적절한 경우 IsBadWritePtr 사용).

이것은 또한 훨씬 더 효율적일 수 있다.메모리 할당에는 비용이 많이 듭니다.메모리 할당이 너무 많으면 힙 조각화가 발생하고 할당 비용이 더 많이 듭니다.그렇기 때문에 커널 모드에서는 이른바 "Look-aside lists"를 사용합니다.메모리 할당의 수를 최소화하기 위해서, NT 커널이 드라이버 라이터에 제공하는 서비스를 사용해, 이미 할당하고 「자유」한 블록을 재이용합니다.메모리 할당에 대한 책임을 발신자에게 전가하면, 발신자가 스택으로부터 값싼 메모리(_alloca)를 건네주거나, 추가 할당 없이 같은 메모리를 몇 번이고 건네주거나 하는 경우가 있습니다.물론 신경 쓰지 않지만, 최적의 메모리 처리를 담당할 수 있도록 전화 걸기를 허용해야 합니다.

C에서의 NULL 터미네이터 사용에 대한 자세한 내용은 "C 문자열"을 할당할 수 없습니다.캐릭 배열을 할당하고 문자열을 저장할 수 있지만 malloc 및 free는 요청된 길이의 배열로 볼 수 있습니다.

C 문자열은 데이터 유형이 아니라 null 문자 '\0'이 문자열 종단자로 처리되는 char 배열을 사용하기 위한 규칙입니다.이는 별도의 인수로 길이 값을 전달할 필요 없이 문자열을 전달하는 방법입니다.일부 다른 프로그래밍 언어에는 문자 데이터와 함께 길이를 저장하여 단일 매개 변수에서 문자열을 전달할 수 있는 명시적 문자열 유형이 있습니다.

인수를 "C 문자열"로 문서화하는 함수는 char 배열이 전달되지만 null 터미네이터가 없으면 배열 크기를 알 수 없기 때문에 문제가 발생합니다.

반드시 문자열로 처리되지 않는 char 배열이 예상되는 함수는 항상 버퍼 길이 파라미터를 전달해야 합니다.예를 들어 0바이트가 유효한 값인 문자 데이터를 처리하려면 '\0'을 종단 문자로 사용할 수 없습니다.

MS Windows API 의 일부에서, (발신자가) 포인터를 건네주고, 할당한 메모리의 사이즈를 조작할 수 있습니다.크기가 충분하지 않은 경우 할당해야 할 바이트 수가 표시됩니다.충분한 경우 메모리가 사용되고 결과는 사용된 바이트 수입니다.

따라서 메모리를 효율적으로 사용하는 방법에 대한 결정은 발신자에게 맡겨집니다.고정 255바이트(Windows에서 패스를 사용할 때 공통)를 할당하고 함수 호출 결과를 사용하여 더 많은 바이트가 필요한지(Win32 API를 바이패스하지 않고 MAX_PATH가 255인 경로가 아닌 경우) 또는 대부분의 바이트를 무시할 수 있는지 여부를 알 수 있습니다.또, 발신자는 메모리 사이즈로서 제로를 건네고, 할당이 필요한 양을 정확하게 알 수 있습니다.처리 효율은 그다지 높지 않지만, 공간 효율이 향상될 수 있습니다.

상한에 미리 할당하여 전체 또는 그 이하를 사용할 수 있습니다.전부 또는 더 적은 것을 실제로 사용했는지 확인하세요.

두 번 패스하는 것도 괜찮다.

단점에 대한 올바른 질문을 하셨습니다.

어떻게 결정해요?

처음에 두 개의 패스를 사용하는 이유는 다음과 같습니다.

1. you'll know you aren't wasting memory.
2. you're going to profile to find out where
   you need to optimize for speed anyway.
3. upperbounds are hard to get right before
   you've written and tested and modified and
   used and updated the code in response to new
   requirements for a while.
4. simplest thing that could possibly work.

암호를 좀 더 엄격히 하는 게 좋을 것 같아요.보통 짧은 게 더 좋아요.그리고 코드가 알려진 진실을 더 많이 이용할수록, 나는 그것이 말하는 대로 하는 것이 더 편해진다.

char* copyWithoutDuplicateChains(const char* str)
    {
    if (str == NULL) return NULL;

    const char* s = str;
    char prev = *s;               // [prev][s+1]...
    unsigned int outlen = 1;      // first character counted

    // Determine length necessary by mimicking processing

    while (*s)
        { while (*++s == prev);  // skip duplicates
          ++outlen;              // new character encountered
          prev = *s;             // restart chain
        }

    // Construct output

    char* outstr = (char*)malloc(outlen);
    s = str;
    *outstr++ = *s;               // first character copied
    while (*s)
        { while (*++s == prev);   // skip duplicates
          *outstr++ = *s;         // copy new character
        }

    // done

    return outstr;
    }

언급URL : https://stackoverflow.com/questions/10170808/what-happens-to-memory-after-0-in-a-c-string

반응형