문자형 배열을 문자열로 어떻게 사용해야합니까?


10

C의 문자열은 문자 배열이라는 것을 알고 있습니다. 그래서 다음 코드를 시도했지만 가비지 출력 또는 프로그램 충돌과 같은 이상한 결과가 나타납니다.

#include <stdio.h>

int main (void)
{
  char str [5] = "hello";
  puts(str);
}

왜 작동하지 않습니까?

로 깔끔하게 컴파일됩니다 gcc -std=c17 -pedantic-errors -Wall -Wextra.


참고 : 이 게시물은 문자열을 선언 할 때 NUL 종료자를위한 공간을 할당하지 못한 경우 발생하는 문제에 대한 표준 FAQ로 사용됩니다.

답변:


12

AC 문자열은 null 종결 자로 끝나는 문자 배열입니다 .

모든 문자에는 기호 테이블 값이 있습니다. 널 종료자는 기호 값입니다.0 (0)입니다. 문자열의 끝을 표시하는 데 사용됩니다. 문자열의 크기는 어디에도 저장되지 않기 때문에 필요합니다.

따라서 문자열을위한 공간을 할당 할 때마다 널 종료 문자를위한 충분한 공간을 포함해야합니다. 귀하의 예는이 작업을 수행하지 않으며의 5 자에 대해서만 공간을 할당합니다 "hello". 올바른 코드는 다음과 같아야합니다.

char str[6] = "hello";

또는 5 자에 1 개의 null 종료자를위한 자체 문서화 코드를 작성할 수 있습니다.

char str[5+1] = "hello";

런타임에 문자열에 대한 메모리를 동적으로 할당 할 때 널 종료자를위한 공간도 할당해야합니다.

char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);

문자열 끝에 null 종결자를 추가하지 않으면 문자열을 기대하는 라이브러리 함수가 제대로 작동하지 않으며 가비지 출력 또는 프로그램 충돌과 같은 "정의되지 않은 동작"버그가 발생합니다.

C에서 널 종료 문자를 작성하는 가장 일반적인 방법은 다음과 같이 소위 "8 진 이스케이프 시퀀스"를 사용하는 것 '\0'입니다. 이것은 writing과 100 % 동일 0하지만 \0은 명시 적으로 null 종결 자임을 의미하는 자체 문서화 코드 역할을합니다. 와 같은 코드if(str[i] == '\0')특정 문자가 널 (null) 종료 자인지 확인하는 입니다.

null 터미네이터라는 용어는 null 포인터 나 NULL매크로 와 아무 관련이 없습니다 . 혼동 될 수 있습니다. 이름은 비슷하지만 의미가 다릅니다. 이것이 널 터미네이터가 때때로 NUL하나의 L 로 언급 NULL되거나, 널 포인터 와 혼동되지 않는 이유 입니다. 이 SO 질문에 대한 답변보기 대한 하십시오.

"hello"코드에서이 호출 문자열 리터럴 . 이것은 읽기 전용 문자열로 간주됩니다. 이 ""구문은 컴파일러가 문자열 리터럴 끝에 자동으로 null 종료자를 추가 함을 의미합니다. 따라서 인쇄 sizeof("hello")하면 null 종결자를 포함하여 배열의 크기를 얻으므로 5가 아닌 6이됩니다.


gcc로 깔끔하게 컴파일됩니다.

실제로 경고조차도 아닙니다. 이것은 C 언어의 미묘한 세부 사항 / 결함으로 인해 배열에 공간이있는만큼 정확하게 문자가 포함 된 문자열 리터럴로 문자 배열을 초기화 한 다음 null 종료자를 자동으로 버릴 수 있습니다 (C17 6.7.9 / 15). 이 언어는 역사적 이유로 의도적으로 이와 같이 작동 합니다. 자세한 내용 은 문자열 초기화에 대한 일관성없는 gcc 진단을 참조 하십시오. 또한 C ++은 여기가 다르 므로이 트릭 / 결함을 사용할 수 없습니다.


1
char str[] = "hello";사건을 언급해야합니다 .
Jabberwocky

@Jabberwocky 커뮤니티 위키입니다. 자유롭게 편집하고 기여하십시오.
Lundin

1
... 그리고 아마도 char *str = "hello";... str[0] = foo;문제.
Jabberwocky

아마도 sizeof배열로 정의 될 때 함수 매개 변수에 대한 사용 의 의미를 확장 할 수 있습니다.
Weather Vane

@WeatherVane 여기에 또 다른 FAQ가 있습니다 : stackoverflow.com/questions/492384/…
Lundin

4

C 표준 (7.1.1 용어 정의)에서

1 문자열은 첫 번째 널 문자로 끝나고 포함 된 연속 된 문자 시퀀스입니다. 멀티 바이트 문자열이라는 용어는 때로는 문자열에 포함 된 멀티 바이트 문자에 대한 특수 처리를 강조하거나 넓은 문자열과 혼동을 피하기 위해 사용됩니다. 문자열에 대한 포인터는 초기 (가장 낮은 주소 지정) 문자에 대한 포인터입니다. 문자열의 길이는 널 문자 앞에 오는 바이트 수이고 문자열의 값은 포함 된 문자 값의 순서입니다.

이 선언에서

char str [5] = "hello";

문자열 리터럴 "hello"은 내부 표현과 같습니다.

{ 'h', 'e', 'l', 'l', 'o', '\0' }

따라서 종료 0을 포함하여 6 개의 문자가 있습니다. 이것의 요소는 str5 문자의 공간을 예약 하는 문자 배열을 초기화하는 데 사용됩니다 .

C 표준 (C ++ 표준과 반대)은 문자열 리터럴의 종료 0을 초기화 자로 사용하지 않을 때 문자 배열을 초기화 할 수 있습니다.

그러나 결과적으로 문자 배열 str에는 문자열이 없습니다.

배열에 문자열이 포함되도록하려면 쓸 수 있습니다

char str [6] = "hello";

아니면 그냥

char str [] = "hello";

마지막 경우 문자 배열의 크기는 6과 같은 문자열 리터럴의 이니셜 라이저 수에서 결정됩니다.


0

모든 수 문자열 간주 문자의 배열 ( ), 모든 수 있습니다 문자 배열이 고려 될 문자열 ( 아니오 ).

왜 안돼? 그리고 왜 중요합니까?

문자열의 길이가 문자열의 일부로 저장되지 않으며 문자열이 정의 된 표준에 대한 참조로 설명하는 다른 답변 외에도, 반대쪽은 "C 라이브러리 함수가 문자열을 어떻게 처리합니까?"입니다.

문자 배열은 동일한 문자를 보유 할 수 있지만 마지막 문자 다음에 널 종료 문자 가 없으면 문자 배열입니다 . 그 널 종료 문자는 문자 배열을 문자열로 간주 할 수있게합니다.

문자열을 인수로 예상하는 C의 모든 함수는 문자 시퀀스가 Null끝나기 를 기대합니다 . 왜?

모든 문자열 함수가 작동하는 방식과 관련이 있습니다. 길이는 배열, 문자열 함수의 일부로 포함되지 않기 때문에 널 문자 (예 : '\0'10 진수와 동일 0)가 발견 될 때까지 배열에서 스캔합니다 . ASCII 테이블 및 설명을 참조하십시오 . 당신이 사용하는 여부에 관계없이 strcpy, strchr, strcspn, 등 모든 문자열 함수는 의지 NUL - 종료 해당 문자열의 끝이 어디 정의하는 문자 인 존재.

두 개의 유사한 기능을 비교 string.h하면 널 종료 문자 의 중요성이 강조 됩니다 . 예를 들어 보자.

    char *strcpy(char *dest, const char *src);

strcpy단순히 복사 기능에서 바이트 srcdest때까지 널 (null) 종료 문자가 발견 이야기 strcpy문자를 복사 중지. 이제 비슷한 기능을 수행하십시오 memcpy.

    void *memcpy(void *dest, const void *src, size_t n);

이 함수는 유사한 작업을 수행하지만 src매개 변수를 문자열 로 간주하거나 요구하지는 않습니다 . 이후 memcpy단순히 전진 스캔 할 수 src바이트 복사 dest까지 널 (null) 종결 자에 도달 할 때, 이는 세번째 파라미터로 복사 할 바이트의 수를 명시 적으로 요구한다. 이 세 번째 매개 변수는 memcpy동일한 크기 정보를 제공 strcpy하며 널 종료 문자를 찾을 때까지 앞으로 스캔하여 간단히 파생 할 수 있습니다.

( null로 끝나는 문자열을 strcpy함수에 제공하지 않으면 잘못 되거나 (또는 ​​문자열을 기대하는 모든 함수) 무엇이 잘못되는지 강조합니다. 여기서 메모리 세그먼트의 나머지 부분에서 어디에서 멈추고 행복하게 경쟁 할 것인지 전혀 알 수 없습니다 널 문자 가 메모리의 어딘가에서 발견되거나 세그먼트 오류가 발생할 때까지 정의되지 않은 동작 호출

기대 기능 NUL 종료 문자열이 전달되어야합니다 NUL 종료 문자열을하고 그 중요한 이유 .


0

직관적으로 ...

배열을 변수 (사물을 보유)로, 문자열을 값 (변수에 배치 할 수 있음)으로 생각하십시오.

그들은 확실히 같은 것이 아닙니다. 귀하의 경우 변수가 너무 작아 문자열을 보유 할 수 없으므로 문자열이 잘립니다. (C에서 "인용 된 문자열"은 끝에 암시 적 널 문자를 갖습니다.)

그러나 문자열 보다 훨씬 큰 배열에 문자열을 저장할 수 있습니다 .

일반적인 대입 및 비교 연산자 = == <등은 예상대로 작동하지 않습니다. 그러나 strxyz여러분이 무엇을하고 있는지 알고 나면 기능 군은 아주 가깝습니다. 문자열배열 에 대한 C FAQ 를 참조하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.