주목할만한 C 확장자에는 동작이 기계어 크기와 무관 한 정수 유형이 포함됩니다.


12

다른 언어와 비교하여 C의 흥미로운 특징은 많은 데이터 유형이 절대 용어로 지정되지 않고 대상 아키텍처의 단어 크기를 기반으로한다는 것입니다. 이렇게하면 특정 유형에 문제가있는 컴퓨터 에서 언어 를 사용하여 코드를 작성할 수 있지만 다른 아키텍처에서 일관되게 실행되는 코드를 설계하는 것은 매우 어렵습니다. 코드를 고려하십시오 :

uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;

int16 비트 (여전히 작은 마이크로 컨트롤러에서도 마찬가지) 인 아키텍처 에서이 코드는 잘 정의 된 동작을 사용하여 1의 값을 할당합니다. int64 비트 인 컴퓨터에서는 잘 정의 된 동작을 사용하여 4294836225라는 값을 할당합니다. int32 비트 인 컴퓨터에서는 -131071의 값을 할당 할 수 있습니다 (구현 정의 또는 정의되지 않은 동작인지 알 수 없음). 코드가 명목상 "고정 크기"유형으로 가정 된 것을 제외하고는 아무것도 사용하지 않더라도, 표준은 오늘날 사용중인 두 가지 다른 종류의 컴파일러가 두 가지 다른 결과를 산출하고 오늘날 많은 인기있는 컴파일러가 1/3을 산출 할 것을 요구합니다.

이 특정 예제는 실제 코드에서 두 개의 16 비트 값의 곱을 64 비트 값에 직접 할당 할 것으로 기대하지는 않지만 세 가지 방법으로 정수를 보여주는 간단한 예로 선택되었습니다. 프로모션은 수정 된 크기의 부호없는 유형과 상호 작용할 수 있습니다. 부호없는 유형의 수학을 수학 정수 산술 규칙에 따라 수행해야하는 실제 상황과 모듈 식 산술 규칙에 따라 수행해야하는 상황, 실제로는 그렇지 않은 상황이 있습니다. 상관 없습니다. 체크섬과 같은 많은 실제 코드는 uint32_t산술 래핑 모드 2³² 및 임의의 수행 능력에 의존합니다uint16_t (정의되지 않은 동작을 트리거하는 것과는 대조적으로) 정확한 모드 65536으로 정의 된 최소한의 산술 및 결과를 얻습니다.

이 상황은 분명히 바람직하지 않은 것처럼 보이지만 (64 비트 처리가 많은 목적을 위해 표준이됨에 따라 더 커질 것임), 내가 관찰 한 것으로부터 C 표준위원회는 이미 주목할만한 제작에 사용 된 언어 기능을 도입하는 것을 선호합니다 환경을 "처음부터"발명하는 것이 아니라 C 언어에 대한 주목할만한 확장이있어 코드가 유형을 저장하는 방법뿐만 아니라 승격 가능성이있는 시나리오에서 어떻게 동작해야 하는지를 지정할 수 있습니까? 컴파일러 확장이 이러한 문제를 해결할 수있는 적어도 세 가지 방법을 볼 수 있습니다.

  1. 컴파일러에게 특정 "기본"정수 유형을 특정 크기로 설정하도록 지시하는 지시문을 추가합니다.

  2. 대상 아키텍처의 실제 유형 크기에 관계없이 머신 유형에 특정 크기가있는 것처럼 다양한 승격 시나리오를 평가하도록 컴파일러에 지시하는 지시문을 추가합니다.

  3. 추가 (특정 특성을 갖는 유형에 관계없이 근본적인 워드 크기 중, 개조-65536 포장 대수 링으로 동작해야하고, 다른 유형의 암시 적으로 변환해야하는 것이 아닌 예 선언을 유형을 선언하는 수단을 허용함으로써 wrap32내지 An은 int을 수득한다 16 비트보다 큰지 wrap32여부에 관계없이 유형의 결과는 직접에 int추가하는 것이 불법이어야합니다 (둘 다 다른 것으로 변환 할 수 없으므로).wrap32wrap16

내가 선호하는 것은 세 번째 대안이 될 수있다. 특이한 단어 크기를 가진 기계조차도 변수가 2의 거듭 제곱처럼 "포장"될 것으로 예상하는 많은 코드에서 작동 할 수 있기 때문이다. 컴파일러는 형식을 적절하게 작동시키기 위해 비트 마스킹 명령어를 추가해야 할 수도 있지만, 코드가 mod 65536을 감싸는 형식이 필요한 경우 소스 코드를 복잡하게 만드는 것보다 필요한 마스킹을 기계에서 생성하는 것이 좋습니다. 또는 단순히 그러한 마스킹이 필요한 기계에서 사용할 수없는 코드를 사용하십시오. 그러나 위의 수단 중 하나를 통해 또는 내가 생각하지 못한 수단을 통해 휴대용 행동을 달성하는 일반적인 확장이 있는지 궁금합니다.

내가 찾고있는 것을 명확히하기 위해 몇 가지가 있습니다. 가장 주목할만한 것 :

  1. 원하는 의미를 보장하기 위해 코드를 작성하는 방법에는 여러 가지가 있지만 (예 : 특정 크기의 부호없는 피연산자에 대해 수학을 수행하여 매크로를 정의하여 랩핑하거나 랩핑하지 않는 결과를 생성하거나) 원하지 않는 것을 방지하는 방법 의미론 (예 : 타입 이 승격되지 않는 컴파일러에서 유형 wrap32_t을 조건부로 정의하고 해당 유형 이 승격 된 머신에서 컴파일을 실패 해야하는 코드가 실행되어 가짜 동작을 생성하는 것보다 낫다는 것을 파악), 미래의 언어 확장에 가장 유리한 코드를 작성하는 방법이 있다면, 그것을 사용하는 것이 내 자신의 접근 방식을 고안하는 것보다 낫습니다.uint32_tuint32_twrap32_t

  2. 많은 정수 크기 문제를 해결하기 위해 언어를 확장하는 방법에 대한 확실한 아이디어가 있습니다. 코드가 다른 단어 크기의 컴퓨터에서 동일한 의미를 생성 할 수는 있지만 작성하는 데 상당한 시간을 소비하기 전에 그 방향으로 어떤 노력이 이미 수행되었는지 알기 위해.

나는 어떤 식 으로든 C 표준위원회 나 그들이 생산 한 일을 비방하는 것으로 여겨지기를 원하지 않는다. 그러나 몇 년 안에 "자연"승격 유형이 32 비트 인 시스템과 64 비트 인 시스템에서 코드가 올바르게 작동해야 할 것으로 예상됩니다. 나는 언어에 대한 약간의 확장 (C99 nnd C14 사이의 다른 많은 변경보다 더 겸손한)을 통해 64 비트 아키텍처를 효율적으로 사용하는 깔끔한 방법을 제공 할뿐만 아니라 거래와의 상호 작용을 촉진 할 수 있다고 생각합니다 표준을 지원하기 위해 역사적으로 뒤로 구부러진 "비정상적인 단어 크기"기계 (예 : 12 비트 char를 가진 기계가 코드를 실행할 수있게하는 것)uint32_tmod 2³²를 감싸기 위하여]. 미래의 확장이 취하는 방향에 따라, 오늘 작성된 코드가 기본 정수 유형이 "예상 된"동작을하는 오늘날의 컴파일러에서 사용할 수 있고 정수가있는 미래의 컴파일러에서도 사용할 수있는 매크로를 정의 할 수 있어야합니다. 유형은 기본적으로 다르게 동작하지만 필요한 동작을 제공 할 수있는 위치입니다.


4
@RobertHarvey 확실합니까? 내가 알고있는 것처럼 홍보 정수 경우, int보다 큰 uint16_t, 곱셈의 피연산자로 승격 될 수 int및 곱셈은 다음과 같이 수행 될 수 int곱셈, 결과 int값으로 변환 될 것이다 int64_t의 초기화 who_knows.

3
@RobertHarvey 어떻게? OP의 코드에는에 대한 언급이 없지만 int여전히 몰래 들어옵니다. (C 표준에 대한 나의 이해가 정확하다고 가정하면)

2
@RobertHarvey 물론 그것은 나쁘게 들리지만, 그런 식으로 지적 할 수 없다면 "아무것도 잘못하고있다"고 말함으로써 아무것도 기여하지 않습니다. 가장 중요한 질문 은 정수 승격을 피하거나 그 효과를 피하는 방법입니다!

3
@RobertHarvey : C 표준위원회의 역사적 목표 중 하나는 거의 모든 기계가 "C 컴파일러"를 가질 수있게하고, 특정 대상 기계를 위해 독립적으로 개발 된 C 컴파일러가 규칙을 구체적으로 정립하는 것입니다. 대부분 상호 교환 가능합니다. 사람들이 표준 초안을 작성하기 전에 많은 기계에 대해 C 컴파일러를 작성하기 시작했고 , 표준위원회는 컴파일러가 기존 코드가 의존 할 수있는 작업을 수행하는 것을 금지하고 싶지 않았기 때문에 복잡했습니다 . 표준의 다소 근본적인 측면 ...
supercat

3
... 누군가가 "합리적인"규칙을 만들려고했던 것이 아니라,위원회가 이미 존재했던 독립적으로 작성된 컴파일러 공통으로 가지고있는 모든 것을 없애려고했기 때문 입니다. 불행하게도,이 접근 방식은 프로그래머가 수행해야 할 작업을 지정하기에는 너무 모호한 표준을 가져 왔지만 컴파일러가 "그냥 수행"하기에는 너무 구체적이었습니다.
supercat

답변:


4

이와 같은 코드의 일반적인 의도로

uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;

64 비트 (결과가 저장되는 변수의 크기)로 곱셈을 수행하는 것입니다 (플랫폼 독립적) 올바른 결과를 얻는 일반적인 방법은 피연산자 중 하나를 캐스팅하여 64 비트 곱셈을 강제하는 것입니다.

uint16_t ffff16 = 0xFFFF;
int64_t i_know = (int64_t)ffff16 * ffff16;

이 프로세스를 자동으로 만드는 C 확장을 본 적이 없습니다.


1
내 질문은 하나의 특정 산술 표현에 대한 올바른 평가를 강제로 수행하는 방법이 아니라 (어떤 종류의 결과에 따라 피연산자를 캐스팅 uint32_t하거나 #define UMUL1616to16(x,y)((uint16_t)((uint16_t)(x)*(uint16_t)(y)))또는 로 정의 된 매크로를 사용 하거나 #define UMUL1616to16(x,y)((uint16_t)((uint32_t)(x)*(uint16_t)(y)))크기에 따라) 매크로가 int아닌 내 매크로를 정의하는 대신 이러한 것들을 유용하게 처리하는 방법에 대한 새로운 표준.
supercat

또한 해싱 및 체크섬 계산과 같은 경우 결과를 가져 와서 피연산자 크기로 자르는 것이 목적이라고 종종 언급했습니다. 식의 일반적인 의도는 (ushort1*ushort2) & 65535u모든 피연산자 값에 대해 mod-65536 산술을 수행하는 것입니다. C89의 이론적 근거를 읽은 결과, 저자가 결과가 2147483647을 초과하면 일부 구현에서는 이러한 코드가 실패 할 수 있음을 인식했지만 그러한 구현은 점점 드물어 질 것으로 예상했습니다. 그러나 이러한 코드는 때로는 현대 gcc에서 실패합니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.