C 문자열은 항상 null로 종료됩니까, 아니면 플랫폼에 따라 달라 집니까?


13

지금은 임베디드 시스템으로 작업하고 운영 체제가없는 마이크로 프로세서에서 문자열을 구현하는 방법을 모색하고 있습니다. 지금까지 내가하고있는 일은 NULL로 끝나는 문자 포인터를 가지고 NULL을 끝으로 나타내는 문자열로 취급한다는 아이디어를 사용하는 것입니다. 나는 이것이 매우 일반적이라는 것을 알고 있지만, 항상 이것을 믿을 수 있습니까?

내가 묻는 이유는 언젠가는 실시간 운영 체제를 사용하려고 생각했기 때문에 가능한 한 현재 코드를 최대한 재사용하고 싶습니다. 그래서 거기에있는 다양한 선택에 대해 문자열이 똑같이 작동 할 것으로 기대할 수 있습니까?

내 경우에는 좀 더 구체적으로 설명하겠습니다. 직렬 포트를 통해 명령을 받아서 처리하는 시스템을 구현하고 있습니다. 명령 처리 코드를 동일하게 유지 한 다음 RTOS (명령 포함)에서 생성 된 문자열 개체가 모두 NULL로 종료 될 것으로 예상 할 수 있습니까? 아니면 OS에 따라 다릅니 까?

최신 정보

이 질문을 살펴 보라고 권고 한 후에는 내가 요구하는 것에 정확하게 대답하지 못한다고 결정했습니다. 질문 자체는 항상 내가 요구하는 것과 완전히 다른 문자열의 길이를 전달 해야하는지 묻고 있으며, 일부 답변에는 유용한 정보가 있었지만 정확히 내가 원하는 것이 아닙니다. 대답은 또는 왜 이유 줄 듯이 없는 널 문자로 문자열을 종료합니다. 내가 묻는 것과의 차이점은 다른 플랫폼의 선천적 인 문자열이 나가서 모든 단일 플랫폼을 시도하지 않고도 null로 자체 문자열을 종료 할 것으로 기대할 수 있는지 여부입니다.


3
오랫동안 C를 사용하지는 않았지만 NULL로 끝나는 문자열을 사용하지 않는 구현에 빠진 시간을 생각할 수 없습니다. 그것은 내가 정확하게 기억한다면 표준 C의 일부입니다 (내가 말했듯이, 그것은
오래

1
나는 C의 전문가가 아니지만 C의 모든 문자열이 null로 끝나는 char 배열이라는 것을 알고 있습니다. 그래도 고유 한 문자열 유형을 만들 수 있지만 모든 문자열 조작 함수를 직접 구현해야합니다.
Machado


1
@MetalMikester이 정보는 표준 C 사양에서 찾을 수 있다고 생각하십니까?
Snoop

3
@ 스누피 가장 가능성이 높습니다. 그러나 실제로 C에서 문자열에 대해 이야기 할 때 NULL로 끝나는 문자 배열 일뿐입니다. 비표준 문자열 라이브러리를 사용하지 않는 한 우리가 여기서 말하는 것은 아닙니다. 특히 C의 강점 중 하나가 이식성이라는 점에서이를 존중하지 않는 플랫폼을 찾을 것입니다.
MetalMikester

답변:


42

"C 문자열"이라고하는 것은 모든 플랫폼에서 널 종료됩니다. 이것이 표준 C 라이브러리 함수가 문자열의 끝을 결정하는 방법입니다.

C 언어 내에서 널로 끝나지 않는 문자 배열을 막을 수있는 것은 없습니다. 그러나 문자열 끝에서 벗어나지 않도록 다른 방법을 사용해야합니다.


4
추가하는 것; 일반적으로 문자열 길이를 추적 할 수있는 정수가 있고 Qt
Rudolf Olah

8
적절한 예 : C 프로그램과 I 작업이 사용하는 다른 문자열 형식 최소 5 : null로 끝나는 char배열, char(일반적으로 "파스칼 문자열"로 알려진) 첫 번째 바이트로 인코딩 길이와 배열 wchar_t의 모두의 기반 버전 위의 char메서드와 첫 번째 바이트로 인코딩 된 길이 및 문자열을 종료하는 null 문자를 모두 결합하는 배열입니다.
Mark

4
@Mark 많은 타사 구성 요소 / 응용 프로그램 또는 레거시 코드 엉망과의 인터페이스?
Dan은 Filightling에 의해

2
@ DanNeely, 위의 모든 것. 고전적인 MacOS와의 인터페이스를위한 파스칼 문자열, 내부 용 및 Windows 용 C 문자열, 유니 코드 지원을 추가하기위한 넓은 문자열 및 영리한 사람이 MacOS 및 Windows와 동시에 인터페이스 할 수있는 문자열을 만들었 기 때문에 멍청한 문자열.
Mark

1
@Mark ... 그리고 물론 고전적인 MacOS는 오래 전에 죽었 기 때문에 기술 부채를 갚기 위해 기꺼이 돈을 쓰려고하지 않습니다. 동정.
Dan은 Filightling에 의해

22

종료 문자의 결정은 리터럴의 컴파일러 및 일반적으로 문자열의 표준 라이브러리 구현에 달려 있습니다. 운영 체제에 의해 결정되지 않습니다.

NUL종료 협약은 사전 표준 C로 거슬러 올라가며 30 년 이상이 지난 후에도 다른 일을하는 환경에 빠졌다고 말할 수는 없습니다. 이 동작은 C89에서 체계화되었으며 C 언어 표준의 일부로 계속 유지됩니다 (링크는 C99의 초안에 연결됨).

  • 6.4.5 절 은 문자열 리터럴에 추가를 NUL요구하여 종료 문자열 의 단계를 설정합니다 NUL.
  • 7.1.1 절은 문자열 을 "첫 번째 널 문자로 끝나고 포함하는 연속 된 문자 시퀀스"로 문자열 을 정의하여 표준 라이브러리의 함수에 제공합니다 .

누군가 다른 문자로 끝나는 문자열을 처리하는 함수를 작성할 수없는 이유는 없지만 프로그래머가 목표를 달성하지 않는 한 대부분의 경우 설정된 표준을 버릴 이유도 없습니다. :-)


2
한 가지 이유는 같은 문자열의 끝을 계속 찾아야하는 것을 피하는 것입니다.
Paŭlo Ebermann

@ PaŭloEbermann 맞습니다. 하나가 아닌 두 개의 값을 전달해야하는 대가로 에서처럼 문자열 리터럴을 전달하면 약간 어색합니다 printf("string: \"%s\"\n", "my cool string"). 이 경우 (종종의 종료 바이트 이외의) 네 개의 매개 변수를 전달하는 유일한 방법은 문자열 std::string에 C ++에서 와 같은 것으로 정의하는 것 입니다. 이는 자체 문제와 제한이 있습니다.
cmaster-monica reinstate

1
6.4.5 절 은 문자열 리터럴 이 널 문자로 끝나는 것을 요구하지 않습니다 . " 문자열 리터럴은 문자열 일 필요는 없습니다 (7.1.1 참조). 널 문자는 \ 0 이스케이프 시퀀스에 의해 포함될 수 있기 때문입니다. "
bzeaman

1
@bzeaman 각주에 따르면 문자열에 대한 7.1.1의 정의를 충족하지 않는 문자열 리터럴을 구성 할 수 있지만이를 참조하는 문장은 호환되는 컴파일러라고 말합니다 NUL. "번역 단계 7에서 바이트 또는 코드 문자열 리터럴 또는 리터럴의 결과 인 각 멀티 바이트 문자 시퀀스에 값 0이 추가됩니다. " 7.1.1의 정의를 사용하는 라이브러리 함수는 처음 NUL발견 할 때 멈추고 추가 문자가 그 이상으로 존재하는지 알거나 신경 쓰지 않습니다.
Blrfl

나는 정정되었다. 'null'과 같은 다양한 용어를 검색했지만 'value zero'를 언급하는 6.4.5.5가 누락되었습니다.
bzeaman

3

나는 임베디드 시스템으로 작업하고 있습니다 ... 운영 체제가 없습니다 ... 나는 NULL로 끝나는 문자 포인터를 가지고 NULL을 끝으로 나타내는 문자열로 취급한다는 아이디어를 사용하고 있습니다. 나는 이것이 매우 일반적이라는 것을 알고 있지만, 항상 이것을 믿을 수 있습니까?

C 언어에는 문자열 데이터 유형이 없지만 문자열 리터럴이 있습니다.

프로그램에 문자열 리터럴을 넣으면 일반적으로 NUL로 종료됩니다 (그러나 아래의 주석에서 논의 된 특수 사례 참조). 즉 "foobar", const char *값이 예상되는 곳에 넣으면 컴파일러가 방출합니다 foobar⊘프로그램의 const / code 세그먼트 / 섹션에 대한 표현식의 값은 f문자를 저장 한 주소의 포인터가됩니다 . (참고 : NUL 바이트를 나타내는 데 사용 하고 있습니다.)

C 언어에 문자열이있는 유일한 다른 의미는 NUL 종료 문자 시퀀스에서 작동하는 표준 라이브러리 루틴이 있다는 것입니다. 이러한 라이브러리 루틴은 사용자가 직접 이식하지 않으면 베어 메탈 환경에 존재하지 않습니다.

그것들은 코드 일뿐입니다. 여러분이 직접 작성한 코드와 다르지 않습니다. 당신이 그들을 포팅 할 때 그들을 끊지 않으면, 그들은 항상 그들이하는 일을 할 것입니다 (예를 들어, NUL에서 멈추십시오).


2
Re : "프로그램에 문자열 리터럴을 넣으면 항상 NUL로 종료됩니다": 확실합니까? 나는 (예를 들어) char foo[4] = "abcd";가 null로 끝나지 않은 4 문자 배열을 만드는 유효한 방법 이라고 확신합니다 .
ruakh

2
@ruakh, 죄송합니다! 내가 고려하지 않은 경우입니다. char const * 식이 예상되는 곳에 나타나는 문자열 리터럴에 대해 생각하고있었습니다 . C 이니셜 라이저 가 때때로 다른 규칙을 따를 수 있다는 것을 잊었습니다 .
Solomon Slow

@ruakh 문자열 리터럴은 NUL로 종료됩니다. 배열이 아닙니다.
jamesdlin

2
@ruakh 당신은 있습니다 char[4]. 그건 아닌 문자열,하지만 한 초기화 하나
Caleth

2
@Caleth, "하나에서 초기화"는 런타임에 발생해야하는 것이 아닙니다. 우리는 키워드를 추가하는 경우 staticRuakh의 예에, 다음 컴파일러 방출 NUL 변수가 프로그램 로더에 의해 초기화되도록 초기화 된 데이터 세그먼트에 "ABCD"를 종료했습니다. Ruakh는 옳았습니다. 프로그램에서 문자열 리터럴이 나타나기 위해 컴파일러에서 NUL 종료 문자열을 생성하지 않아도되는 경우가 적어도 하나 있습니다. (ps, 실제로 gcc 5.4.0으로 예제를 컴파일했고 컴파일러는 NUL을 방출하지 않았습니다.)
Solomon Slow

2

다른 사람들이 언급했듯이 문자열의 null 종료는 C 표준 라이브러리의 규칙입니다. 표준 라이브러리를 사용하지 않을 경우 원하는 방식으로 문자열을 처리 할 수 ​​있습니다.

이는 'C'컴파일러가있는 모든 운영 체제에 해당되며, 실제 운영 체제에서 실행되지 않는 'C'프로그램을 작성할 수 있습니다. 한 번 디자인 한 잉크젯 프린터 용 컨트롤러를 예로들 수 있습니다. 임베디드 시스템에서는 운영 체제의 메모리 오버 헤드가 필요하지 않을 수 있습니다.

메모리가 부족한 상황에서는 예를 들어 프로세서의 명령어 세트에 대한 컴파일러의 특성을 살펴볼 것입니다. 문자열이 많이 처리되는 응용 프로그램에서는 문자열 길이와 같은 설명자를 사용하는 것이 좋습니다. CPU가 주소 레지스터를 사용하여 짧은 오프셋 및 / 또는 상대 오프셋으로 작업하는 데 특히 효율적인 경우를 생각하고 있습니다.

따라서 응용 프로그램에서 코드 크기 및 효율성 또는 OS 또는 라이브러리와의 호환성 중 더 중요한 것은 무엇입니까? 또 다른 고려 사항은 유지 보수성입니다. 컨벤션에서 멀어 질수록 다른 사람이 유지하기가 더 어려워집니다.


1

다른 사람들은 C에서 문자열이 주로 당신이 만드는 문제를 해결했습니다. 그러나 귀하의 질문에 터미네이터 자체에 대한 혼란이있는 것으로 보이며 한 관점에서 이것은 귀하의 위치에있는 누군가가 걱정하는 것일 수 있습니다.

C 문자열은 null로 종료됩니다. 즉, 널 문자로 종료됩니다 NUL. 그것들은 NULL완전히 다른 목적을 가진 완전히 다른 종류의 값인 null 포인터로 끝나지 않습니다 .

NUL정수 값 0을 보장합니다. 문자열 내에서 기본 문자 유형의 크기도 가지며 일반적으로 1입니다.

NULL정수 유형을 가질 수는 없습니다. NULL는 포인터 컨텍스트에서 사용하기위한 것이며 일반적으로 포인터 유형을 가질 것으로 예상되며, 컴파일러가 좋은 경우 문자 또는 정수로 변환해서는 안됩니다. 의 정의에 NULLglyph 가 포함되어 있지만 0실제로는 그 값을 갖는 것이 보장되지 않습니다 [1]. 컴파일러가 상수를 한 문자로 구현하지 않는 한 #define(많은하지 않습니다. 왜냐하면 NULL 실제로 는 포인터 컨텍스트)에 따라 확장 된 코드는 실제로 0 값을 포함한다고 보장하지 않습니다 (혼란스럽게 0 글리프가 포함되어 있음에도 불구하고).

경우 NULL입력 할 때, 또한 하나의 크기 (또는 다른 문자 크기)가 않을 것입니다. 실제 문자 상수는 대부분 문자 크기를 갖지 않지만 추가 문제가 발생할 수 있습니다.

이제 대부분의 사람들은 이것을보고 "제로 비트 이외의 다른 것으로 널 포인터? 어떤 말도 안 돼요"라고 생각하지만 x86과 같은 일반적인 플랫폼에서만 안전합니다. 다른 플랫폼을 대상으로하는 것에 대한 관심을 명시 적으로 언급 했으므로 포인터와 정수의 관계 특성에 대한 가정과 코드를 명시 적으로 분리 했으므로이 문제를 고려해야합니다.

C 문자열은 널 종료하는 동안 따라서, 그들은 종료되지 NULL하지만 의해 NUL(일반적으로 작성 '\0'). 명시 적으로 NULL문자열 종결 자로 사용 되는 코드 는 간단한 주소 구조를 가진 플랫폼에서 작동하며 많은 컴파일러로 컴파일 될 수도 있지만 C에는 맞지 않습니다.


[1] 실제 널 포인터 값은 포인터 유형으로 변환 될 컨텍스트에서 0 토큰 을 읽을 때 컴파일러에 의해 삽입됩니다 . 이것은 정수 0 으로부터의 변환 이 아니며 0변수 자체의 동적 값과 같이 토큰 자체 이외의 것이 사용되는 경우에는 유지되지 않습니다 . 변환도 되돌릴 수 없으며, 정수로 변환 될 때 널 포인터는 값 0을 생성 할 필요가 없습니다.


좋은 지적입니다. 이 문제를 해결하기 위해 수정 사항을 제출했습니다.
Monty Harder

" NUL는 정수 값 0을 보장합니다." -> C는 정의하지 않습니다 NUL. 대신 C는 문자열에 최종 널 chracter 가 있고 모든 비트가 0으로 설정된 바이트를 갖도록 정의 합니다.
chux-Reinstate Monica

1

C에서 문자열을 사용하고 있습니다. 널 종료가있는 문자를 문자열이라고합니다.

베어 메탈 또는 Windows, Linux, RTOS : (FreeRTO, OSE)와 같은 운영 체제에서 사용할 때는 아무런 문제가 없습니다.

임베디드 월드에서 null 종료는 실제로 문자를 문자열로 토큰 화하는 데 더 도움이됩니다.

많은 안전 핵심 시스템에서와 같이 C에서 문자열을 사용했습니다.

C에서 실제로 문자열이 무엇인지 궁금 할 것입니다.

배열 인 C 스타일 문자열에는 "this"와 같은 문자열 리터럴도 있습니다. 실제로이 두 문자열 유형은 단순히 메모리에서 서로 옆에 앉아있는 문자 모음 일뿐입니다.

큰 따옴표로 묶은 문자열을 작성할 때마다 C는 해당 문자열을 포함하여 \ 0 문자로 끝나는 문자 배열을 자동으로 만듭니다.

예를 들어, 문자 배열을 선언 및 정의하고 문자열 상수로 초기화 할 수 있습니다.

char string[] = "Hello cruel world!";

간단한 답변 : 널 종료 문자 사용에 대해 실제로 걱정할 필요가 없습니다. 이는 플랫폼과 독립적으로 작동합니다.


감사합니다, 큰 따옴표로 선언하면 NUL자동으로 추가됩니다 몰랐습니다 .
Snoop

1

다른 사람들이 말했듯이, 널 종료는 표준 C에 대해 거의 보편적입니다. 그러나 (다른 사람들도 지적했듯이) 100 %는 아닙니다. (또 다른) 예의 경우, VMS 운영 체제는 일반적으로 #include <descrip.h에 의해 C에서 액세스되는 "문자열 설명자" http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html을 사용 했습니다. >

응용 프로그램 수준의 항목은 null 종료를 사용할 수 있지만 개발자는 적합하다고 생각합니다. 그러나 저수준 VMS에는 절대적으로 널 종료를 사용하지 않는 설명자가 필요합니다 (자세한 내용은 위의 링크 참조). 이는 VMS 내부를 직접 사용하는 모든 언어 (C, 어셈블리 등)가 공통 인터페이스를 가질 수 있도록하기위한 것입니다.

따라서 어떤 종류의 유사한 상황을 예상하는 경우 "유니버설 널 종료"가 필요하다고 제안하는 것보다 다소주의를 기울여야 할 수 있습니다. 내가하고있는 일을하고 있다면 더 조심해야하지만 응용 프로그램 수준의 물건에는 null 종료를 가정하는 것이 안전합니다. 나는 당신에게 동일한 수준의 안전을 제안하지 않을 것입니다. 코드는 미래의 시점에서 어셈블리 및 / 또는 다른 언어 코드와 인터페이스해야 할 수도 있습니다. 이는 null 종료 문자열의 C 표준을 항상 준수하지는 않습니다.


오늘날 0 종료는 실제로 매우 이례적입니다. C ++ std :: string, Java 문자열, Objective-C NSString, Swift 문자열은 그렇지 않습니다-결과적으로 각 언어 라이브러리는 문자열 내부에 NUL 코드 있는 문자열을 지원 합니다 (C에서는 불가능합니다) 명백한 이유로 문자열).
gnasher729

@ gnasher729 나는 "... 꽤 많은 보편적"을 "표준 C의 아주 보편적"으로 바꿨는데, 이는 현재의 모호함을 없애고 오늘날에도 올바른 상태를 유지하기를 희망한다 (그리고 OP의 주제와 질문에 따라 내가 의미 한 바이다).
John Forkosh

0

임베디드, 안전 및 실시간 시스템에 대한 경험에서 C와 PASCAL 문자열 규칙을 모두 사용하는 것은 드문 일이 아닙니다. 즉, 문자열 길이를 첫 문자 (길이 255로 제한)로 제공하고 NUL사용 가능한 크기를 254로 줄이려면 하나 이상의 0x00 (( ))이 포함 된 문자열입니다 .

이에 대한 한 가지 이유는 첫 번째 바이트를 수신 한 후 예상되는 데이터 양을 알고 있고, 또 다른 이유는 이러한 시스템에서 가능한 경우 동적 버퍼 크기를 피할 수 있기 때문입니다. 고정 된 256 버퍼 크기를 할당하는 것이 더 빠르고 안전합니다. malloc실패 했는지 확인해야 함 ). 다른 하나는 통신중인 다른 시스템이 ANSI-C로 작성되지 않았을 수 있다는 것입니다.

임베드 된 작업에서 가능한 빨리 문자열 형식, 엔디안, 정수 크기 등을 포함하여 모든 통신 구조를 정의하는 IDC (Interface Control Document)를 설정하고 유지하는 것이 중요합니다 ( 이상적으로는 시작하기 전 ). 그것은 당신을, 그리고해야 모든 팀, 거룩한 책 때 시스템을 쓰기 - 누군가의 소원이 새로운 구조를 도입하거나 포맷 할 경우 합니다 이 문서화 첫째 , 정보 영향을받을 수있는 그 모든 사람들 가능성이 거부권의 변화에 대한 옵션 .

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