이 소스 코드는 C에서 문자열을 켜고 있습니다. 어떻게 그렇게합니까?


106

나는 일부 에뮬레이터 코드를 읽고 있으며 정말 이상한 것을 반박했습니다.

switch (reg){
    case 'eax':
    /* and so on*/
}

이것이 어떻게 가능한지? 나는 당신이 switch정수형 에서만 할 수 있다고 생각했습니다 . 매크로 속임수가 진행되고 있습니까?


29
그것은 문자열이 아니고 'eax'상수 정수 값을 열거합니다
P__J__

12
큰 따옴표가 아닌 작은 따옴표. 문자 상수는로 승격 int되므로 합법적입니다. 그러나 다중 문자 상수의 값은 구현에 따라 정의되므로 코드가 다른 컴파일러에서 예상대로 작동하지 않을 수 있습니다. 예를 들어, eax수도 0x65, 0x656178, 0x65617800, 0x786165, 0x6165, 다른 또는 뭔가.
Davislor 2017-08-08

2
@Davislor : 변수 "reg"의 이름과 eax가 x86 레지스터라는 사실을 감안할 때 구현 정의 동작이 코드에서 사용되는 모든 곳에서 동일하기 때문에 괜찮을 것이라고 생각합니다. 그냥만큼 'eax' != 'ebx'그것은 단지 하나 또는 두 개의 귀하의 예제에 실패 물론, 그래서. 실제로 가정하는 코드가 어딘가에있을 수 *(int*)("eax") == 'eax'있으므로 대부분의 예제가 실패합니다.
Steve Jessop

2
@SteveJessop 나는 당신이 말하는 것에 동의하지 않지만 누군가가 같은 아키텍처에서도 다른 컴파일러에서 코드를 컴파일하려고 시도하고 다른 동작을 얻을 수있는 실제 위험이 있습니다. 예를 들어, 'eax'같음 'ebx'또는 과 비교 될 수 'ax'있으며 switch 문이 의도 한대로 작동하지 않습니다.
Davislor

1
당신이 reg의 데이터 유형을 찾아 보거나 보여 주었 더라면 그 모든 미스터리는 빨리 풀렸을 것입니다.
ths aug

답변:


146

(더 많은 코드를 붙여 넣지 않는 한 "매크로 속임수"부분에 대해서만 답할 수 있습니다.하지만 여기에는 매크로가 작동 할 것이 많지 않습니다. 공식적으로는 키워드 를 재정의 할 수 없습니다. . 그 동작은 정의되지 않았습니다.)

프로그램 가독성을 얻기 위해 재치있는 개발자는 구현 정의 동작을 이용 합니다. 'eax'입니다 하지 문자열하지만, 멀티 문자 상수 . 주위의 작은 따옴표 문자를 매우주의하여주의하십시오 eax. 대부분 int의 경우 해당 문자 조합에 고유 한 케이스를 제공합니다 . (매우 자주 각 문자는 32 비트에서 8 비트를 차지합니다 int.) 그리고 모두는 당신 switchint !

마지막으로 표준 참조 :

C99 표준은 다음과 같이 말합니다.

6.4.4.4p10 : "두 개 이상의 문자 (예 : 'ab')를 포함하거나 단일 바이트 실행 문자에 매핑되지 않는 문자 또는 이스케이프 시퀀스를 포함하는 정수 문자 상수의 값은 구현에서 정의됩니다. "


55
누군가가 그것을보고 패닉을 당할 경우를 대비하여 "구현 정의"가 작동하고 적절한 방식으로 컴파일러에 의해 문서화되어야합니다 (표준은 동작이 직관적이거나 문서가 좋은 것을 요구하지는 않지만 ...). 이것은 "정의되지 않음"이 아니라 자신이 작성하는 내용을 완전히 이해하는 코더에게 "안전"합니다.
Leushenko 2017-08-07

7
@Justin 그럴 수는 있지만 꽤 비뚤어 질 것입니다. 대답이 제안한대로 수행하지 않으면 다음 가능성은 아마도 첫 번째 문자 만 사용하고 나머지는 무시하는 것입니다.
Barmar

5
@ZanLynx 긍정적 인 것은 아니지만이 기능이 유니 코드 및 기타 MBCS 표준보다 오래 앞서 있다고 생각합니다. 메모리 덤프와 RIFF 스타일의 파일 형식 청크 ID의 텍스트처럼 보이는 "매직 넘버"는 제가 아는 첫 번째 애플리케이션이었습니다.
Russell Borogove

16
@ jpmc26 이것은 정의되지 않은 동작이 아니라 구현에 따라 정의됩니다. 따라서 컴파일러 문서에서 악마를 언급하지 않는 한 코는 안전합니다.
Barmar

7
@ZanLynx : 원래 의도가 유니 코드, UTF-8 및 모든 멀티 바이트 문자 인코딩보다 거의 20 년 앞선 것 같습니다. 다중 문자 상수 는 2, 3 또는 4 바이트 그룹을 나타내는 정수를 표현하는 편리한 방법 일뿐입니다 (바이트 및 정수 크기에 따라 다름). 구현 및 아키텍처 간의 불일치로 인해위원회는이를 구현 정의 된 것으로 선언했습니다. 즉, 'ab'from 'a'및 의 값을 계산하는 이식 가능한 방법이 없음을 의미 'b'합니다.
chqrlie

45

C 표준 (6.8.4.2 스위치 문)에 따름

3 각 케이스 라벨의 표현은 정수 상수 표현이어야합니다 .

및 (6.6 상수 표현식)

6 정수 상수 표현식 은 정수 유형을 가져야하며 정수 상수 인 피연산자, 열거 상수, 문자 상수 , 결과가 정수 상수 인 표현식의 크기 및 캐스트의 직접적인 피연산자 인 부동 상수 만 있어야합니다. 정수 상수 표현식의 캐스트 연산자는 sizeof 연산자에 대한 피연산자의 일부를 제외하고 산술 유형을 정수 유형으로 만 변환해야합니다.

이제 무엇입니까 'eax' 입니까?

C 표준 (6.4.4.4 문자 상수)

2 정수 문자 상수는 'x'에서와 같이 작은 따옴표로 묶인 하나 이상의 멀티 바이트 문자 시퀀스입니다 .

따라서 'eax'같은 섹션의 단락 10에 따른 정수 문자 상수입니다.

  1. ... 두 개 이상의 문자 (예 : 'ab')를 포함하거나 단일 바이트 실행 문자에 매핑되지 않는 문자 또는 이스케이프 시퀀스를 포함하는 정수 문자 상수의 값은 구현에서 정의됩니다.

따라서 처음 언급 한 인용문에 따르면 케이스 레이블로 사용할 수있는 정수 상수 표현식의 피연산자가 될 수 있습니다.

문자 상수 (작은 따옴표로 묶인)에는 int유형이 있고 문자 배열 유형을 갖는 문자열 리터럴 (큰 따옴표로 묶인 일련의 문자)과 동일하지 않습니다.


12

다른 사람들이 말했듯이 이것은 int 상수이며 실제 값은 구현에서 정의됩니다.

나머지 코드는 다음과 같다고 가정합니다.

if (SOMETHING)
    reg='eax';
...
switch (reg){
    case 'eax':
    /* and so on*/
}

첫 번째 부분의 'eax'가 두 번째 부분의 'eax'와 같은 값을 가지고 있음을 확신 할 수 있으므로 모든 것이 잘 작동합니다. ... 잘못된.

주석에서 @Davislor는 'eax'에 대해 가능한 값을 나열합니다.

... 0x65, 0x656178, 0x65617800, 0x786165, 0x6165, 또는 뭔가 다른

첫 번째 잠재적 가치를 아십니까? 그것은 단지 'e'다른 두 문자를 무시하는 것입니다. 문제는 프로그램이 'eax', 'ebx'등을 사용한다는 것 입니다. 이 모든 상수가 'e'당신 과 같은 값을 가지면

switch (reg){
    case 'e':
       ...
    case 'e':
       ...
    ...
}

이건 너무 좋아 보이지 않습니까?

"구현 정의"의 좋은 점은 프로그래머가 컴파일러의 문서를 확인하고 이러한 상수로 적절한 작업을 수행하는지 확인할 수 있다는 것입니다. 그렇다면 집은 무료입니다.

나쁜 부분은 다른 가난한 사람이 코드를 가져와 다른 컴파일러를 사용하여 컴파일하려고 할 수 있다는 것입니다. 인스턴트 컴파일 오류. 이 프로그램은 이식성이 없습니다.

@zwol이 주석에서 지적했듯이 상황은 생각만큼 나쁘지 않으며 나쁜 경우 코드가 컴파일되지 않습니다. 이것은 적어도 문제에 대한 정확한 파일 이름과 줄 번호를 제공합니다. 그래도 작동하는 프로그램이 없습니다.


1
다른 형태의 다른 것 assert('eax' != 'ebx'); //if this fails you can't compile the code because...외에는 원래 작성자가 구조를 완전히 대체하지 않고 다른 컴파일러 실패를 방지하기 위해 할 수있는 일이
있습니까?

6
동일한 값을 가진 두 개의 케이스 레이블은 제약 위반입니다 (6.8.4.2p3 : "... 같은 switch 문에있는 두 개의 케이스 상수 표현식은 변환 후 동일한 값을 가질 수 없습니다"). 따라서 모든 코드가 이 상수의 값을 불투명하게 취급합니다. 이것은 작동하거나 컴파일에 실패합니다.
zwol 2017-08-08

더 나쁜 부분은 다른 컴파일러에서 컴파일하는 가난한 사람은 아마도 컴파일 타임 오류를 보지 못할 것입니다 (int로 전환해도 괜찮습니다). 대신 런타임 오류가 발생합니다 ...
tucuxi

1

코드 조각은 multi-chars 라고도하는 다중 문자 문자 상수 라는 역사적 특이성을 사용합니다 .

'eax' 값이 구현 정의 된 정수 상수입니다.

다음은 다중 문자에 대한 흥미로운 페이지이며 사용 방법은 있지만 사용해서는 안됩니다.

http://www.zipcon.net/~swhite/docs/computers/languages/c_multi-char_const.html


백미러를 더 멀리 들여다 보면, 예전에 Dennis Ritchie의 C 매뉴얼 ( https://www.bell-labs.com/usr/dmr/www/cman.pdf )이 문자 상수를 지정하는 방법이 있습니다. .

2.3.2 문자 상수

문자 상수는 작은 따옴표 '' '''로 묶인 1 개 또는 2 개의 문자 입니다. 문자 상수 내에서 작은 따옴표 앞에 백 슬래시 '' \'' 가 와야합니다 . 그래픽이 아닌 특정 문자 및 '' \''자체는 다음 표에 따라 이스케이프 될 수 있습니다.

    BS \b
    NL \n
    CR \r
    HT \t
    ddd \ddd
    \ \\

이스케이프 '' \ddd''는 원하는 문자의 값을 지정하는 데 사용되는 1, 2 또는 3 개의 8 진수가 뒤 따르는 백 슬래시로 구성됩니다. 이 구조의 특별한 경우는 ''\0 는 널 문자를 나타내는 ''(숫자가 뒤 따르지 않음)입니다.

문자 상수는 정수와 똑같이 작동합니다 (특히 문자 유형의 객체와 같지 않음). PDP-11의 주소 지정 구조에 따라 길이 1의 문자 상수는 하위 바이트에서 주어진 문자에 대한 코드를 갖고 상위 바이트에서 0을 갖습니다. 길이가 2 인 문자 상수는 하위 바이트의 첫 번째 문자에 대한 코드와 상위 바이트의 두 번째 문자에 대한 코드를 갖습니다. 둘 이상의 문자가있는 문자 상수는 본질적으로 기계에 따라 다르므로 피해야합니다.

마지막 문구는이 흥미로운 구성에 대해 기억해야 할 모든 것 입니다. 둘 이상의 문자가있는 문자 상수는 본질적으로 기계에 따라 다르므로 피해야합니다.

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