C 문자 리터럴이 문자 대신 정수인 이유는 무엇입니까?


103

C ++에서 sizeof('a') == sizeof(char) == 1. 이것은 'a'문자 리터럴이고 sizeof(char) == 1표준에 정의 된대로 직관적으로 이해 됩니다.

그러나 C에서는 sizeof('a') == sizeof(int). 즉, C 문자 리터럴은 실제로 정수인 것처럼 보입니다. 이유를 아는 사람이 있습니까? 이 C 특성에 대한 많은 언급을 찾을 수 있지만 왜 존재하는지에 대한 설명은 없습니다.


sizeof는 바이트 크기를 반환하지 않습니까? char 및 int 크기가 같지 않습니까?
Josh Smeaton

1
이것은 아마도 컴파일러 (및 아키텍처)에 따라 다릅니다. 무엇을 사용하고 있는지 말씀해 주시겠습니까? 표준 (적어도 89 년까지)은 매우 느슨했습니다.
dmckee --- 전 중재자 새끼 고양이

2
아니. 숯불은, 항상 sizeof 연산자 1 바이트 크다 ( 'A') == 1 항상 (C ++)에서의 int이 할 수있는 동안 이론적으로 는 sizeof 1 수 있지만 즉 적어도 16 비트 갖는 바이트를 필요로 매우 가능성을 : ) 그래서 sizeof ( 'a')! = sizeof (int)는 대부분의 구현에서 C ++에서 매우 가능성이 높습니다
Johannes Schaub-litb

2
... C에서는 항상 잘못되었습니다.
Johannes Schaub-litb

22
'a'는 C 기간의 정수입니다. C가 먼저 도착했습니다. C가 규칙을 만들었습니다. C ++는 규칙을 변경했습니다. C ++ 규칙이 더 합리적이라고 주장 할 수 있지만 C 규칙을 변경하면 좋은 것보다 더 많은 피해를 입힐 수 있으므로 C 표준위원회는 현명하게 이것을 건드리지 않았습니다.
Jonathan Leffler

답변:


36

같은 주제에 대한 토론

"보다 구체적으로 통합 프로모션입니다. K & R C에서는 문자 값을 int로 먼저 승격하지 않고는 문자 값을 사용하는 것이 사실상 (?) 불가능했기 때문에 처음에 문자를 상수 int로 만들면 해당 단계가 제거되었습니다. 여전히 여러 문자가 있습니다. 'abcd'와 같은 상수 또는 많은 수가 int에 적합합니다. "


다중 문자 상수는 단일 시스템의 컴파일러 간에도 이식 가능하지 않습니다 (GCC는 플랫폼간에 일관된 것처럼 보임). 참조 : stackoverflow.com/questions/328215
Jonathan Leffler

8
나는 a)이 인용문은 귀속되지 않는다는 것에 주목할 것입니다. 인용문에는 "문제의 문제를 논의하는 과거 스레드에 게시 된이 의견에 동의하지 않겠습니까?"라고만 표시됩니다. ... 그리고 b) 그것은 우스꽝 스럽습니다 . 왜냐하면 char변수는 정수가 아니기 때문입니다. 그래서 문자 상수를 1로 만드는 것은 특별한 경우입니다. 그리고 문자 값을 승격하지 않고도 쉽게 사용할 수 있습니다 : c1 = c2;. OTOH c1 = 'x'는 하향 전환입니다. 가장 중요한 것은, sizeof(char) != sizeof('x')이것은 진지한 언어입니다. 멀티 바이트 문자 상수에 관해서는 그 이유가 있지만 쓸모가 없습니다.
Jim Balter

27

원래 질문은 "왜?"입니다.

그 이유는 리터럴 문자의 정의가 진화하고 변경되면서 기존 코드와 하위 호환성을 유지하기 때문입니다.

초기 C의 어두운 날에는 유형이 전혀 없었습니다. C로 프로그래밍하는 법을 처음 배웠을 때 유형이 도입되었지만 함수에는 호출자에게 인수 유형이 무엇인지 알려주는 프로토 타입이 없었습니다. 대신 매개 변수로 전달되는 모든 것이 int의 크기 (모든 포인터를 포함)이거나 double이되도록 표준화되었습니다.

즉, 함수를 작성할 때 두 배가 아닌 모든 매개 변수는 선언 된 방식에 관계없이 스택에 int로 저장되었고 컴파일러는이를 처리하기 위해 함수에 코드를 넣었습니다.

이로 인해 다소 일관성이 없었기 때문에 K & R이 유명한 책을 썼을 때 문자 리터럴은 함수 매개 변수뿐만 아니라 모든 표현식에서 항상 int로 승격된다는 규칙을 적용했습니다.

ANSI위원회가 C를 처음 표준화했을 때 문자 리터럴이 단순히 int가되도록이 규칙을 변경했습니다. 이는 동일한 작업을 수행하는 더 간단한 방법으로 보였기 때문입니다.

C ++를 설계 할 때 모든 함수는 완전한 프로토 타입을 가져야했습니다 (일반적으로 좋은 방법으로 받아 들여지지 만 C에서는 여전히 필요하지 않습니다). 이 때문에 문자 리터럴을 문자에 저장할 수있는 것으로 결정되었습니다. C ++에서 이것의 장점은 char 매개 변수가있는 함수와 int 매개 변수가있는 함수가 다른 서명을 갖는다는 것입니다. 이 장점은 C의 경우가 아닙니다.

이것이 그들이 다른 이유입니다. 진화...


2
실제로 '왜?'라고 답한 것에 대한 +1. 하지만 마지막 문장에 동의하지 않습니다. "C ++에서이 기능의 장점은 char 매개 변수가있는 함수와 int 매개 변수가있는 함수가 서로 다른 서명을 갖는다는 것입니다."-C ++에서 두 함수가 다음과 같은 매개 변수를 가질 수 있습니다. 같은 크기의 다른 서명, 예를 들면 void f(unsigned char)void f(signed char).
Peter K

3
@PeterK John은 그것을 더 잘 표현할 수 있었지만 그가 말하는 것은 본질적으로 정확합니다. C ++에서 변경 한 동기는을 작성 하는 경우 . 대신 해당 호출에 대해 f('a')오버로드 해결을 선택 f(char)하기를 원할 것 f(int)입니다. 당신이 말했듯 이 int및 의 상대적 크기는 char관련이 없습니다.
zwol

21

C의 문자 리터럴이 int 유형 인 구체적인 이유를 모르겠습니다. 그러나 C ++에서는 그렇게하지 않는 좋은 이유가 있습니다. 이걸 고려하세요:

void print(int);
void print(char);

print('a');

print 호출이 문자를 사용하는 두 번째 버전을 선택한다고 예상 할 수 있습니다. 문자 리터럴이 int라는 것은 불가능합니다. C ++에서 문자가 두 개 이상인 리터럴에는 값이 구현이 정의되어 있지만 int 유형이 여전히 있습니다. 그래서, 'ab'유형이 int있지만, 'a'유형이 있습니다 char.


예, "C ++의 설계 및 발전"은 과부하 된 입력 / 출력 루틴이 C ++에서 규칙을 변경 한 주된 이유라고 말합니다.
Max Lybbert

5
맥스, 그래 내가 속였 어. 내가 :) 호환성 섹션에서 표준으로 보았다
litb - 요하네스 SCHAUB

18

내 MacBook에서 gcc를 사용하여 다음을 시도합니다.

#include <stdio.h>
#define test(A) do{printf(#A":\t%i\n",sizeof(A));}while(0)
int main(void){
  test('a');
  test("a");
  test("");
  test(char);
  test(short);
  test(int);
  test(long);
  test((char)0x0);
  test((short)0x0);
  test((int)0x0);
  test((long)0x0);
  return 0;
};

실행하면 다음이 제공됩니다.

'a':    4
"a":    2
"":     1
char:   1
short:  2
int:    4
long:   4
(char)0x0:      1
(short)0x0:     2
(int)0x0:       4
(long)0x0:      4

이것은 당신이 의심하는 것처럼 문자가 8 비트라는 것을 암시하지만 문자 리터럴은 정수입니다.


7
흥미로워 서 +1. 사람들은 종종 sizeof ( "a")와 sizeof ( "")가 char *이고 4 (또는 8)를 주어야한다고 생각합니다. 그러나 사실 그들은 그 시점에서 char []입니다 (sizeof (char [11])는 11을줍니다). 초보자를위한 함정.
paxdiablo

3
문자 리터럴은 int로 승격되지 않으며 이미 int입니다. 객체가 sizeof 연산자의 피연산자이면 어떤 승격도 진행되지 않습니다. 만약 있다면 이것은 sizeof의 목적을 무너 뜨릴 것입니다.
Chris Young

@Chris Young : 예. 검사. 감사.
dmckee --- 전 중재자 새끼 고양이

8

C가 작성되었을 때 PDP-11의 MACRO-11 어셈블리 언어는 다음과 같습니다.

MOV #'A, R0      // 8-bit character encoding for 'A' into 16 bit register

이런 종류의 것은 어셈블리 언어에서 매우 일반적입니다. 하위 8 비트는 문자 코드를 보유하고 다른 비트는 0으로 지워집니다. PDP-11에는 다음과 같은 기능이 있습니다.

MOV #"AB, R0     // 16-bit character encoding for 'A' (low byte) and 'B'

이는 16 비트 레지스터의 하위 및 상위 바이트에 두 문자를로드하는 편리한 방법을 제공했습니다. 그런 다음 텍스트 데이터 또는 화면 메모리를 업데이트하여 다른 곳에 쓸 수 있습니다.

따라서 문자가 크기를 등록하도록 승격되는 아이디어는 매우 정상적이고 바람직합니다. 그러나 하드 코딩 된 opcode의 일부가 아니라 다음을 포함하는 주 메모리의 어딘가에서 레지스터에 'A'를 가져와야한다고 가정 해 보겠습니다.

address: value
20: 'X'
21: 'A'
22: 'A'
23: 'X'
24: 0
25: 'A'
26: 'A'
27: 0
28: 'A'

이 메인 메모리에서 레지스터로 'A'만 읽으려면 어떤 것을 읽으시겠습니까?

  • 일부 CPU는 16 비트 값을 16 비트 레지스터로 직접 읽기만 지원할 수 있습니다. 즉, 20 또는 22에서 읽기를 수행하려면 'X'의 비트를 지워야하며 CPU의 엔디안 상태에 따라 하위 바이트로 이동해야합니다.

  • 일부 CPU에는 메모리 정렬 읽기가 필요할 수 있습니다. 즉, 관련된 최하위 주소는 데이터 크기의 배수 여야합니다. 주소 24와 25에서는 읽을 수 있지만 27과 28에서는 읽을 수 없습니다.

따라서 레지스터에 'A'를 가져 오는 코드를 생성하는 컴파일러는 약간의 추가 메모리를 낭비하고 엔디안에 따라 값을 0 'A'또는 'A'0으로 인코딩하고 올바르게 정렬되도록하는 것을 선호 할 수 있습니다 ( 즉, 이상한 메모리 주소가 아닙니다.)

내 생각에 C는 단순히 메모리의 레지스터 크기를 차지하는 문자 상수를 생각하고 "고수준 어셈블러"로서 C의 공통 평가를지지하면서 CPU 중심의이 수준의 동작을 수행했다는 것입니다.

( http://www.dmv.net/dec/pdf/macro.pdf의 6-25 페이지에있는 6.3.3 참조 )


5

K & R을 읽고 EOF에 도달 할 때까지 한 번에 문자를 읽는 코드 스 니펫을 본 기억이 있습니다. 모든 문자는 파일 / 입력 스트림에있는 유효한 문자이므로 EOF는 char 값이 될 수 없습니다. 코드는 읽은 문자를 int에 넣은 다음 EOF를 테스트 한 다음 그렇지 않은 경우 char로 변환했습니다.

나는 이것이 당신의 질문에 정확히 대답하지 않는다는 것을 알고 있지만 EOF 리터럴이 있다면 나머지 문자 리터럴이 sizeof (int) 일 것입니다.

int r;
char buffer[1024], *p; // don't use in production - buffer overflow likely
p = buffer;

while ((r = getc(file)) != EOF)
{
  *(p++) = (char) r;
}

그래도 0이 유효한 문자라고 생각하지 않습니다.
gbjbaanb

3
@gbjbaanb : 물론입니다. 널 문자입니다. 생각해보세요. 파일이 0 바이트를 포함해서는 안된다고 생각하십니까?
P Daddy

1
위키피디아 읽기- "EOF의 실제 값은 시스템에 따라 다른 음수이며 일반적으로 -1이며 유효한 문자 코드와 동일하지 않음을 보장합니다."
Malx

2
Malx가 말했듯이-EOF는 char 유형이 아닙니다-int 유형입니다. getchar () 및 friends는 충돌없이 EOF뿐만 아니라 모든 문자를 보유 할 수있는 int를 리턴합니다. 이것은 실제로 리터럴 문자가 int 유형을 가질 필요가 없습니다.
Michael Burr

2
EOF == -1은 C의 문자 상수 이후 오래되었으므로 이것은 대답이 아니며 관련성이 없습니다.
Jim Balter 2011 년

5

나는 그것에 대한 근거를 보지 못했지만 (C char 리터럴은 int 유형 임) Stroustrup이 그것에 대해 말해야했던 것이 있습니다 (Design and Evolution 11.2.1-Fine-Grain Resolution).

C에서와 같은 문자 리터럴의 유형은 'a'입니다 int. 놀랍게도 C ++에서 'a'유형 char을 지정해 도 호환성 문제가 발생하지 않습니다. 병리학 적 예를 제외하고 sizeof('a')C와 C ++로 표현할 수있는 모든 구조는 동일한 결과를 제공합니다.

따라서 대부분의 경우 문제가 발생하지 않습니다.


흥미 롭군요! 일종의 다른 사람들이 모순은 "현명하게"결정 C 표준위원회는 C.에서이 특질을 제거하지 않도록하는 방법에 대해 말하고 있었다
j_random_hacker

2

이것에 대한 역사적인 이유는 C와 그 이전 모델 B가 원래 8 비트 ASCII를 지원하지만 레지스터에서만 산술을 수행 할 수있는 다양한 단어 크기의 DEC PDP 미니 컴퓨터의 다양한 모델에서 개발 되었기 때문입니다. (그러나 PDP-11은 아닙니다. 나중에 출시되었습니다.) C의 초기 버전은 int컴퓨터의 기본 단어 크기로 정의 되었으며 함수로 또는 함수에서 전달하기 위해 int확장해야하는 것보다 작은 값 int으로 정의되었습니다. , 또는 비트, 논리 또는 산술 표현식에서 사용됩니다. 기본 하드웨어가 작동하는 방식 이었기 때문입니다.

이것이 정수 승격 규칙이 여전히 an보다 작은 데이터 유형 intint. C 구현은 비슷한 역사적 이유로 2의 보수 대신 1의 보수 수학을 사용할 수도 있습니다. 8 진수 문자 이스케이프와 8 진수 상수가 16 진수에 비해 일류 시민 인 이유는 초기 DEC 미니 컴퓨터가 3 바이트 청크로 나눌 수있는 단어 크기를 가지고 있었지만 4 바이트 니블은 아니기 때문입니다.


... 그리고 char3 진수 자리 정확하게이었다
안티 Haapala

1

이것은 "통합 승격"이라고하는 올바른 동작입니다. 다른 경우에도 발생할 수 있습니다 (올바르게 기억하는 경우 주로 이항 연산자).

편집 : 확실히하기 위해 Expert C Programming : Deep Secrets의 사본을 확인하고 char 리터럴이 int 유형으로 시작 하지 않는다는 것을 확인했습니다 . 처음에는 char 유형 이지만 표현식 에서 사용 되면 int승격 됩니다 . 이 책에서 인용 한 내용은 다음과 같습니다.

문자 리터럴은 int 유형을 가지며 char 유형에서 승격 규칙을 따라 도달합니다. 이것은 K & R 1, 39 페이지에서 너무 짧게 다루고 있습니다.

표현식의 모든 문자는 int로 변환됩니다 .... 표현식의 모든 float는 double로 변환됩니다 .... 함수 인수가 표현식이므로 유형 변환은 인수가 함수에 전달 될 때도 발생합니다. 특히 char와 short는 int가되고 float는 double이됩니다.


다른 주석을 믿어야하는 경우 'a'표현식은 int 유형으로 시작합니다 . sizeof () 내부에서는 유형 승격이 수행되지 않습니다. 'a'가 int 유형을 갖는 것은 C의 특이한 것 같습니다.
j_random_hacker 09-01-12

2
문자 리터럴 에는 int 유형 있습니다. ANSI / ISO 99 표준은이를 '정수 문자 상수'(wchar_t 유형을 갖는 '와이드 문자 상수'와 구별하기 위해)라고 부르며 구체적으로 "정수 문자 상수에는 유형 int가 있습니다."라고 말합니다.
Michael Burr

내가 의미하는 것은 int 유형으로 시작 하지 않고 char (답변 편집)에서 int로 변환된다는 것입니다. 물론 이것은 변환이 항상 수행되기 때문에 컴파일러 작성자를 제외하고는 누구에게도 해당되지 않을 것입니다.
PolyThinker

3
아니! ANSI / ISO 99 C 표준읽으면 C에서 'a'표현식이 int 유형으로 시작 한다는 것을 알 수 있습니다. void f (int) 함수와 char c 변수가 있으면 f (c) 적분 승격 수행하지만 'a'유형이 이미 int 이므로 f ( 'a')는 수행하지 않습니다 . 이상하지만 사실입니다.
j_random_hacker

2
"그냥 확실하게"- "문자 리터럴에는 int 유형이 있습니다"라는 문을 실제로 읽어 보면 더 확실 할 수 있습니다. "나는 그것이 침묵의 변화 중 하나 일 뿐이라고 생각할 수있다"-당신은 잘못 생각한다. C의 문자 리터럴은 항상 int 유형이었습니다.
짐 발터

0

잘 모르겠지만 그렇게 구현하는 것이 더 쉬웠고 그다지 중요하지 않았던 것 같아요. 유형이 어떤 함수가 호출 될지 결정할 수있는 것은 C ++가 되어서야 수정이 필요했습니다.


0

나는 이것을 정말로 몰랐다. 프로토 타입이 존재하기 전에 int보다 좁은 것은 함수 인수로 사용할 때 int로 변환되었습니다. 그것은 설명의 일부일 수 있습니다.


1
또 다른 "답변". 자동 변환 char으로는 int꽤 만들 것 불필요한 문자 상수는 int 치의 될하기. 관련이있는 것은 언어가 문자 상수를 char변수와 다르게 (다른 유형을 제공하여) 취급한다는 것이며 , 필요한 것은 그 차이에 대한 설명입니다.
Jim Balter 2011 년

아래에 설명해 주셔서 감사합니다. 답변에 대한 설명을 더 자세히 설명 할 수 있습니다. 답변이 속한 곳은 찬성 투표가 가능하며 방문자가 쉽게 볼 수 있습니다. 또한 여기에 좋은 대답이 있다고 말한 적이 없습니다. 그러므로 당신의 가치 판단은 도움이되지 않습니다.
Blaisorblade 2011 년

0

이것은 언어 사양에 접선 일 뿐이지 만 하드웨어에서 CPU는 일반적으로 하나의 레지스터 크기 (32 비트) 만 가지고 있으므로 실제로 문자 (추가, 빼기 또는 비교)에서 작동 할 때마다 레지스터에로드 될 때 int 로의 암시 적 변환. 컴파일러는 각 작업 후에 숫자를 적절하게 마스킹하고 이동하므로 2를 (unsigned char) 254에 더하면 256 대신 0으로 래핑되지만 실리콘 내부에서는 실제로 int입니다. 메모리에 다시 저장할 때까지.

어쨌든 언어가 8 비트 리터럴 유형을 지정할 수 있었기 때문에 일종의 학문적 요점이지만이 경우 언어 사양은 CPU가 실제로 수행하는 작업을 더 가깝게 반영합니다.

(x86 wonks는 예를 들어 짧은 범위의 레지스터를 한 단계로 추가하는 기본 addh 작업 있지만 RISC 코어 내부에서는 두 단계로 변환됩니다. 숫자를 추가 한 다음 추가 / 확장 쌍처럼 PowerPC)


1
또 다른 잘못된 대답. 여기서 문제는 문자 리터럴과 char변수의 유형이 다른 이유 입니다. 하드웨어를 반영하는 자동 프로모션은 관련이 없습니다. char변수가 자동으로 프로모션되므로 문자 리터럴이 유형이 아닐 이유가 없기 때문에 실제로는 관련이 없습니다 char. 진짜 이유는 이제 쓸모없는 멀티 바이트 리터럴입니다.
Jim Balter 2011 년

@Jim Balter 멀티 바이트 리터럴은 전혀 사용되지 않습니다. 멀티 바이트 유니 코드와 UTF 문자가 있습니다.
Crashworks 2011 년

@Crashworks 우리는 멀티 바이트 문자열 리터럴이 아니라 멀티 바이트 문자 리터럴 에 대해 이야기하고 있습니다. 주의를 기울이십시오.
Jim Balter 2011 년

4
Chrashworks는 문자를 작성했습니다 . 와이드 문자 리터럴 (예 : L' à ')은 더 많은 바이트를 사용하지만 멀티 바이트 문자 리터럴이라고 부르지 않는다고 작성해야합니다 . 덜 오만하면 자신이 더 정확 해지는 데 도움이됩니다.
Blaisorblade 2011 년

@Blaisorblade 와이드 문자 리터럴은 여기서 관련이 없습니다. 내가 쓴 것과는 아무 관련이 없습니다. 나는 정확했고 당신은 이해력이 부족하고 나를 시정하려는 당신의 가짜 시도는 오만합니다.
Jim Balter 2011 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.