2009-09-23 4 views
6

다음 코드에서는 strncpy()을 사용하여 10 자 길이의 char * str에 문자열을 복사합니다.문자열이 NULL로 끝나지는 않지만 여전히 정상적으로 작동합니다. 이유가 무엇입니까?

이제 strncpy() 설명서에 따르면 "경고 : src의 첫 번째 n 바이트 중 null 바이트가 없으면 dest에 배치 된 문자열은 null이 종료되지 않습니다."이것은 정확히 여기서 발생합니다.

소스 문자열의 길이는 26 charcters이고 10 문자를 복사 했으므로 null 문자는 str의 끝에 배치됩니다.

그러나 str의 내용을 출력 할 때 '\ 0'이 될 때까지 0부터 시작하여 정상적으로 작동합니다.

왜? 끝 부분에 '\ 0'이없는 경우 루프가 올바른 위치에 멈춰있는 이유는 무엇입니까?

내가 알기로는 "Segmentation fault"를 제공해야하며, 적어도 멈추지 말고 일부 쓰레기 값을 계속 인쇄해서는 안됩니다. 어떤 도움을 이해할 것이다

 
str[ 0 ] has got : a 
str[ 1 ] has got : b 
str[ 2 ] has got : c 
str[ 3 ] has got : d 
str[ 4 ] has got : e 
str[ 5 ] has got : f 
str[ 6 ] has got : g 
str[ 7 ] has got : h 
str[ 8 ] has got : i 
str[ 9 ] has got : j 

: 여기

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIZE 10 

int main() 
{ 
    char *str ; 
    str = malloc(sizeof(char) * SIZE); 
    if(str == NULL) 
     exit(1); 
    memset(str, 0, sizeof(char) * SIZE); 

    strncpy(str, "abcdefghijklmnopqrstuvwxyz", sizeof(char) * SIZE); 

    unsigned int index; 
    for(index = 0; str[ index ] != '\0' ; index++) { 
     printf("str[ %u ] has got : %c \n ", index, str[ index ]); 
    } 

    return 0; 
} 

가 출력된다.

편집

확인하는 적절한 방법이 있나요 문자열은 '\ 0'여부에 종료 여부? 나는 항상 위의 루프가 궁극적 인 테스트라고 생각했지만 지금은 그렇지 않다.

다른 프로그래머가 개발 한 함수에서 문자열을 얻을 수 있습니다. 이제 '\ 0'으로 올바른 위치에서 끝나는 것을 어떻게 알 수 있습니까? 5 월이되지 않으면 실제 크기를 넘어서서 '\ 0'을 얻을 것입니다. 문자열의 실제 크기를 결코 알 수 없습니다.

그런 상황을 어떻게 해결할 수 있습니까?

의견이 있으십니까?

+2

sizeof (char) is * always * 1 !! – paxdiablo

+1

@Pax : 사실,하지만 여기에 대한 설명이 있습니다. http://stackoverflow.com/questions/1011806/is-it-necessary-to-multiply-by-sizeof-char-when-manipulating-memory – sharptooth

+0

strncmp는 문자열의 크기가 아니라 문자의 수이기 때문에 거기서 사용하는 것은 잘못입니다. –

답변

6

을 편집 한, 나는 현학적 인 것은 몇 가지 문제를 명료하게 도움이 될 것입니다 생각합니다.

C에서는 문자열과 같은 것이 없습니다. C 표준 라이브러리가 작동하는 "C 문자열"개념은 NUL 종료 문자 시퀀스로 정의되어 있으므로 실제로는 "null이 아닌 종료 문자열"과 같은 것은 없습니다 C에서. "그렇다면 질문은"임의의 문자 버퍼가 유효한 C 문자열인지 어떻게 판단 할 수 있습니까? " 또는 "내가 찾은 문자열이 의도 한 문자열인지 어떻게 판단 할 수 있습니까?"

첫 번째 질문에 대한 대답은 아쉽게도 NUL 바이트가 발생할 때까지 선형 적으로 버퍼를 스캔하는 것입니다. 이렇게하면 C 문자열의 길이를 알 수 있습니다.

두 번째 질문에는 쉬운 대답이 없습니다.사실 C에는 길이 메타 데이터가있는 실제 문자열 유형이 없기 때문에 (또는 함수 호출을 통해 배열 크기를 전달할 수있는 능력이 있기 때문에) 위에서 결정한 문자열 길이가 길이인지 여부를 결정할 실제 방법이 없습니다. 의도 한 캐릭터 라인 우리가 프로그램에서 segfault를 보거나 출력에서 ​​"garbage"를 보게 될지는 알 수 있지만, 일반적으로 첫 번째 NUL 바이트까지 스캔하여 문자열 연산을 수행해야합니다 (일반적으로 문자열 길이에 상한이 있으므로 지저분한 버퍼 오버런 오류)

15

할당 된 블록의 끝을 넘어서 바로 null 바이트가 발생합니다.

는 대부분의 경우 malloc() 더 많은 메모리를 할당하고 널 바이트를 포함하는 일이 소위 가드 값을 넣습니다 또는 나중에 free()가 사용하는 몇 가지 메타 데이터를두고이 메타 데이터는 그 위치에서 널 바이트 권리를 포함하는 발생합니다.

어쨌든이 동작에 의존해서는 안됩니다. null 문자 위치가 합법적으로 할당되도록 널 문자에 대해 하나 더 많은 바이트 (malloc())를 요청해야합니다.

문자열이 Null로 올바르게 끝나면 테스트 할 수있는 휴대용 방법이 없습니다. 할당 된 블록이 끝나면 프로그램이 중단됩니다. 또는 블록의 끝 부분에 널 문자가 있고 나중에 오정렬 된 문자열을 조작 할 때 블록 끝을 넘어 메모리를 덮어 쓸 수 있습니다.

주어진 주소가 할당 된 주소 인 이 다른 주소 (블록의 시작 부분)와 동일한 할당에 속하는지 확인하는 것이 이상적입니다. 이것은 느리고 가치가 없으며이를 수행하는 표준 방법이 없습니다.

즉, null로 끝나는 문자열이지만 실제로는 그렇지 않은 경우 - 프로그램이 정의되지 않은 동작으로 실행됩니다.

+0

아니요, 없습니다. –

+0

예 문자열의 끝에 null 바이트가 발생합니다. 다른 크기를 시도하면 * bad * 출력이 나옵니다. –

+0

따라서 문자열이 널 종료인지 여부를 확인하는 표준 방법은 없습니다. 그것은 나쁜 소식입니다. 저는 응용 프로그램에서 작업하는 모든 프로그래머가 일부 표준에 동의해야한다고 생각합니다. 포인터의 처음 세 문자와 마찬가지로 크기를 알려주고 4에서 실제 문자열이 시작됩니다. –

4

왜 작동합니까?

할당 할 메모리의 크기는 '\0' 바이트입니다. 예를 들어, 디버그 모드에서 Visual C++를 사용하는 경우 힙 관리자는 메모리를 할당하여 프로그램에 넘겨 주지만 순수 운이 될 수도 있습니다.)

문자열이 '\0'에서 끝나는 지 확인하는 올바른 방법은 무엇입니까?

아니요. 문자열이 필요 없으며 (C std lib 문자열 처리 함수에서 기대하는 것입니다) 또는 추가 변수에서 길이를 유지해야합니다. 둘 중 하나도 없으면 버그가 있습니다.

이제 우리는 어떻게 다른 프로그래머에 의해 개발 된 일부 기능에서 일부 문자열이 '\0'와 함께 올바른 장소에서 끝나는 것을 알 수 있습니다. 5 월이 될 때까지 실제 크기를 넘어 설 것입니다. '\0'. 문자열의 실제 크기를 결코 알 수 없습니다.

그런 상황을 어떻게 해결할 수 있습니까?

수 없습니다. 다른 기능이 나쁘면 나사를 조이면 나 빠지게됩니다.

+0

힙 관리자 제로 메모리 정보 : Microsoft 컴파일러는 메모리를 제로 (n 개의 디버그 또는 릴리스 빌드)하지 않습니다. 디버그 힙을 사용할 때 MSVC 런타임은 0이 아닌 0xCD 바이트로 할당 된 메모리를 채 웁니다. 기억을 없애기보다는 '쓰레기'로 채우는 것이 일반적으로 문제를 찾는 데 더 효과적입니다. 또한, 얼 로이 전후의 메모리 일부는 0xFD 값으로 채워질 것입니다. http://stackoverflow.com/questions/370195/when-and-why-will-an-os-initialise-memory-to-0xcd-0xdd-etc-on-malloc-free-new/370362#370362 –

+0

@ Micheal : 나는 네가 옳을 수도 있다는 것을 알고있다. 그러나 여전히 ISTR은 디버그 버전이 VC에서 작동하는 동안 릴리스 버전이 손상되는 일반적인 원인입니다. '' – sbi

0

Sharptooth가 동작의 가능한 원인을 설명 했으므로이를 반복하지는 않습니다.

버퍼를 할당, 난 항상 다음과 같이 바이트를 초과 할당 :로

#define SIZE 10 
char* buf = malloc(sizeof(char)*(SIZE+1)); 
/* error-check the malloc call here */ 
buf[SIZE] = '\0'; 
+0

어, "sizeof (char) - (SIZE + 1)"? 마이너스? –

+0

우리는 이것을 할 수도 있습니다 memet (dest, 0, SIZE); strncpy (dest, source, SIZE -1); 이렇게하면 마지막 바이트는 0이됩니다. –

+0

* - 번이어야합니다. 새 키보드 :) – gnud

0

할당 된 공간 영역을 초과하여 0이 생길 수있어서 다행입니다.

다른 모든 플랫폼에서이 코드를 사용하면 동일한 방식으로 작동하지 않을 수 있습니다.

0

나는 sharptooth의 대답이 옳다고 생각한다. 할당 된 공간이 더 있습니다. 나는 다음과 같이 프로그램을 수정

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIZE 10 

int main() 
{ 
    char *str ; 
    int *p; 
    int actual_length; 
    str = malloc(sizeof(char) * SIZE); 
    if(str == NULL) 
     exit(1); 

    actual_length = (int)*(str - 4) - 1 - 4; 
    printf("actual length of str is %d\n", actual_length); 
    p = (int*) malloc(sizeof(int)); 
    if (p == NULL) exit(1); 
    *p = -1; 
    char* pc = (char*)(p - 1); 
    pc [0] = 'z'; 
    pc [1] = 'z'; 
    pc [2] = 'z'; 
    pc [3] = 'z'; 

    memset(str, 0, sizeof(char) * SIZE); 

    memcpy(str, "abcdefghijklmnopqrstuvwxyz", sizeof(char) * SIZE); 

    int i; 
    for (i = SIZE; i < actual_length; i++) 
    str[i] = 'y'; 

    unsigned int index; 
    for(index = 0; str[ index ] != '\0' ; index++) { 
     printf("str[ %u ] has got : %c \n ", index, str[ index ]); 
    } 

    return 0; 
} 

출력은

actual length of str is 12 
str[ 0 ] has got : a 
str[ 1 ] has got : b 
str[ 2 ] has got : c 
str[ 3 ] has got : d 
str[ 4 ] has got : e 
str[ 5 ] has got : f 
str[ 6 ] has got : g 
str[ 7 ] has got : h 
str[ 8 ] has got : i 
str[ 9 ] has got : j 
str[ 10 ] has got : y 
str[ 11 ] has got : y 
str[ 12 ] has got : z 
str[ 13 ] has got : z 
str[ 14 ] has got : z 
str[ 15 ] has got : z 
str[ 16 ] has got : \377 
str[ 17 ] has got : \377 
str[ 18 ] has got : \377 
str[ 19 ] has got : \377 

내 OS는 데비안 짜기/SID입니다.

관련 문제