C 데이터 유형은 어떻게 "대부분의 컴퓨터에서 직접 지원"됩니까?


114

저는 K & R의 “The C Programming Language” 를 읽고이 문장을 보았습니다. [Introduction, p. 삼]:

C에서 제공하는 데이터 유형과 제어 구조는 대부분의 컴퓨터 에서 직접 지원되기 때문에 자체 포함 프로그램을 구현하는 데 필요한 런타임 라이브러리는 매우 작습니다.

굵은 글씨는 무엇을 의미합니까? 컴퓨터에서 직접 지원 하지 않는 데이터 유형 또는 제어 구조의 예가 있습니까?


1
요즘 C 언어는 복잡한 산술을 지원하지만 원래는 컴퓨터가 데이터 유형으로 복소수를 직접 지원하지 않기 때문에 그렇지 않았습니다.
Jonathan Leffler 2015 년

12
사실, 역사적으로 그 반대였습니다. C는 당시 사용 가능한 하드웨어 운영 및 유형에서 설계되었습니다.
Basile Starynkevitch

2
대부분의 컴퓨터는 소수 부동 소수점에 대한 직접적인 하드웨어 지원이 없습니다
PlasmaHH

3
@MSalters : "컴퓨터에서 직접 지원하지 않는 데이터 유형이나 제어 구조의 예가 있습니까?"라는 질문에 대해 어떤 방향으로 힌트를 주려고했습니다. 이는 내가 K & R로 제한 해석하지 않았다
PlasmaHH

11
Stack Overflow가 출시 된 지 6 년이 지나도 중복되지 않는 이유는 무엇입니까?
피터 모텐슨

답변:


143

예, 직접 지원되지 않는 데이터 유형이 있습니다.

많은 임베디드 시스템에는 하드웨어 부동 소수점 단위가 없습니다. 따라서 다음과 같은 코드를 작성할 때 :

float x = 1.0f, y = 2.0f;
return x + y;

다음과 같이 번역됩니다.

unsigned x = 0x3f800000, y = 0x40000000;
return _float_add(x, y);

그런 다음 컴파일러 또는 표준 라이브러리는 _float_add()임베디드 시스템에서 메모리를 차지하는 의 구현을 제공 해야합니다. 정말 작은 시스템에서 바이트를 세는 경우이 값이 추가 될 수 있습니다.

또 다른 일반적인 예는 long long32 비트 시스템에서 직접 지원되지 않는 64 비트 정수 ( 1999 년 이후 C 표준)입니다. 구형 SPARC 시스템은 정수 곱셈을 지원하지 않았기 때문에 런타임에서 곱셈을 제공해야했습니다. 다른 예가 있습니다.

다른 언어

이에 비해 다른 언어에는 더 복잡한 기본 형식이 있습니다.

예를 들어 Lisp 기호는 Lua의 테이블, Python의 문자열, Fortran의 배열 등과 같이 많은 런타임 지원이 필요합니다. C의 동등한 유형은 일반적으로 표준 라이브러리의 일부가 아니거나 (표준 기호 또는 테이블 없음) 훨씬 더 간단하고 많은 런타임 지원이 필요하지 않습니다 (C의 배열은 기본적으로 포인터 일 뿐이며 nul로 끝나는 문자열은 거의 간단합니다).

제어 구조

C에서 누락 된 주목할만한 제어 구조는 예외 처리입니다. 비 로컬 종료는 프로세서 상태의 특정 부분을 저장하고 복원하는 setjmp()및 로 제한됩니다 longjmp(). 이에 비해 C ++ 런타임은 스택을 탐색하고 소멸자 및 예외 처리기를 호출해야합니다.


2
기본적으로 포인터 ... 오히려 기본적으로 원시 메모리 덩어리입니다. 비록 그것이 nit-picking이고 대답은 어쨌든 좋습니다.
Deduplicator

2
문자열 종결자가 대부분의 프로세서의 '0이면 점프'작업에 적합하므로 다른 가능한 문자열 구현보다 약간 빠르기 때문에 널로 종료 된 문자열에는 "하드웨어 지원"이 있다고 주장 할 수 있습니다.
Peteris 2015 년

1
C가 단순히 asm에 매핑되도록 설계되는 방법을 확장하기 위해 내 답변을 게시했습니다.
Peter Cordes

1
"배열은 기본적으로 포인터 일뿐"이라는 연어를 사용하지 마십시오. OP와 같은 초보자를 심각하게 오도 할 수 있습니다. "배열은 하드웨어 수준에서 포인터를 사용하여 직접 구현된다"는 IMO가 더 나을 것입니다.
상자성 크로아상

1
@TheParamagneticCroissant :이 맥락에서 그것은 적절하다고 생각합니다 ... 명확성은 정확성을 희생합니다.
Dietrich Epp

37

사실,이 소개의 내용은 Kernighan과 Ritchie가 책의 초판에 처음 썼던 1978 년 이후 크게 변하지 않았을 것입니다. 그리고 그들은 현대보다 그 당시 C의 역사와 진화를 언급합니다. 구현.

컴퓨터는 기본적으로 메모리 뱅크와 중앙 프로세서 일 뿐이며 각 프로세서는 기계 코드를 사용하여 작동합니다. 각 프로세서 설계의 일부는 어셈블리 언어 라고하는 명령어 세트 아키텍처로, 사람이 읽을 수있는 니모닉 세트에서 모든 숫자 인 기계어 코드로 일대일로 매핑됩니다.

C 언어의 작성자와 바로 앞의 B 및 BCPL 언어는 가능한 한 효율적으로 Assembly로 컴파일 된 언어로 구문을 정의하려고했습니다. 사실, 그들은 대상의 제한에 의해 강제되었습니다. 하드웨어. 다른 답변에서 지적했듯이 여기에는 분기 (GOTO 및 C의 기타 흐름 제어), 이동 (할당), 논리 연산 (& | ^), 기본 산술 (더하기, 빼기, 증가, 감소) 및 메모리 주소 지정 (포인터 ). 좋은 예는 C의 pre- / post-increment 및 decrement 연산자입니다.이 연산자는 컴파일 된 후 단일 opcode로 직접 변환 할 수 있었기 때문에 Ken Thompson이 B 언어에 추가 한 것으로 추정됩니다.

이것이 저자들이 "대부분의 컴퓨터에서 직접 지원한다"는 말의 의미입니다. 다른 언어에 직접 지원 되지 않는 유형과 구조가 포함되어 있다는 의미가 아니라 설계 상 C 구조 가 어셈블리로 가장 직접 (때로는 문자 그대로 직접) 번역 .

구조화 된 프로그래밍에 필요한 모든 요소를 ​​제공하면서도 기본 어셈블리와의 밀접한 관계는 C를 조기 채택하게했으며, 코드 컴파일의 효율성이 여전히 핵심 인 환경에서 오늘날 널리 사용되는 언어로 유지되었습니다.

언어의 역사에 대한 흥미로운 글은 The Development of the C Language-Dennis Ritchie를 참조하십시오 .


14

짧은 대답은 C에서 지원하는 대부분의 언어 구조는 대상 컴퓨터의 마이크로 프로세서에서도 지원되므로 컴파일 된 C 코드는 마이크로 프로세서의 어셈블리 언어로 매우 훌륭하고 효율적으로 변환되므로 코드와 설치 공간이 더 작아집니다.

더 긴 대답은 약간의 어셈블리 언어 지식이 필요합니다. C에서 다음과 같은 문 :

int myInt = 10;

어셈블리에서 다음과 같이 번역됩니다.

myInt dw 1
mov myInt,10

이것을 C ++와 같은 것과 비교하십시오.

MyClass myClass;
myClass.set_myInt(10);

결과 어셈블리 언어 코드 (MyClass () 크기에 따라 다름)는 최대 수백 개의 어셈블리 언어 라인을 추가 할 수 있습니다.

실제로 어셈블리 언어로 프로그램을 생성하지 않고 순수 C는 아마도 프로그램을 만들 수있는 "가장 얇고" "가장 엄격한"코드 일 것입니다.

편집하다

내 대답에 대한 의견을 감안할 때 나는 내 자신의 정신을 위해 테스트를 실행하기로 결정했습니다. 다음과 같은 "test.c"라는 프로그램을 만들었습니다.

#include <stdio.h>

void main()
{
    int myInt=10;

    printf("%d\n", myInt);
}

나는 이것을 gcc를 사용하여 어셈블리로 컴파일했습니다. 다음 명령 줄을 사용하여 컴파일했습니다.

gcc -S -O2 test.c

결과 어셈블리 언어는 다음과 같습니다.

    .file   "test.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB24:
    .cfi_startproc
    movl    $10, %edx
    movl    $.LC0, %esi
    movl    $1, %edi
    xorl    %eax, %eax
    jmp __printf_chk
    .cfi_endproc
.LFE24:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

그런 다음 클래스를 정의하고 "test.c"와 동일한 내용을 출력하는 "test.cpp"라는 파일을 만듭니다.

#include <iostream>
using namespace std;

class MyClass {
    int myVar;
public:
    void set_myVar(int);
    int get_myVar(void);
};

void MyClass::set_myVar(int val)
{
    myVar = val;
}

int MyClass::get_myVar(void)
{
    return myVar;
}

int main()
{
    MyClass myClass;
    myClass.set_myVar(10);

    cout << myClass.get_myVar() << endl;

    return 0;
}

이 명령을 사용하여 같은 방식으로 컴파일했습니다.

g++ -O2 -S test.cpp

결과 어셈블리 파일은 다음과 같습니다.

    .file   "test.cpp"
    .section    .text.unlikely,"ax",@progbits
    .align 2
.LCOLDB0:
    .text
.LHOTB0:
    .align 2
    .p2align 4,,15
    .globl  _ZN7MyClass9set_myVarEi
    .type   _ZN7MyClass9set_myVarEi, @function
_ZN7MyClass9set_myVarEi:
.LFB1047:
    .cfi_startproc
    movl    %esi, (%rdi)
    ret
    .cfi_endproc
.LFE1047:
    .size   _ZN7MyClass9set_myVarEi, .-_ZN7MyClass9set_myVarEi
    .section    .text.unlikely
.LCOLDE0:
    .text
.LHOTE0:
    .section    .text.unlikely
    .align 2
.LCOLDB1:
    .text
.LHOTB1:
    .align 2
    .p2align 4,,15
    .globl  _ZN7MyClass9get_myVarEv
    .type   _ZN7MyClass9get_myVarEv, @function
_ZN7MyClass9get_myVarEv:
.LFB1048:
    .cfi_startproc
    movl    (%rdi), %eax
    ret
    .cfi_endproc
.LFE1048:
    .size   _ZN7MyClass9get_myVarEv, .-_ZN7MyClass9get_myVarEv
    .section    .text.unlikely
.LCOLDE1:
    .text
.LHOTE1:
    .section    .text.unlikely
.LCOLDB2:
    .section    .text.startup,"ax",@progbits
.LHOTB2:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB1049:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $10, %esi
    movl    $_ZSt4cout, %edi
    call    _ZNSolsEi
    movq    %rax, %rdi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE1049:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE2:
    .section    .text.startup
.LHOTE2:
    .section    .text.unlikely
.LCOLDB3:
    .section    .text.startup
.LHOTB3:
    .p2align 4,,15
    .type   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, @function
_GLOBAL__sub_I__ZN7MyClass9set_myVarEi:
.LFB1056:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1056:
    .size   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, .-_GLOBAL__sub_I__ZN7MyClass9set_myVarEi
    .section    .text.unlikely
.LCOLDE3:
    .section    .text.startup
.LHOTE3:
    .section    .init_array,"aw"
    .align 8
    .quad   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .hidden __dso_handle
    .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

분명히 알 수 있듯이 결과 어셈블리 파일은 C ++ 파일에서 C 파일보다 훨씬 더 큽니다. 다른 모든 것을 잘라 내고 C "main"을 C ++ "main"과 비교하더라도 많은 추가 항목이 있습니다.


14
그 "C ++ 코드"는 C ++가 아닙니다. 그리고 MyClass myClass { 10 }C ++ 와 같은 실제 코드 는 정확히 동일한 어셈블리로 컴파일 될 가능성이 높습니다. 최신 C ++ 컴파일러는 추상화 패널티를 제거했습니다. 결과적으로 그들은 종종 C 컴파일러를 이길 수 있습니다. 예를 들어 C의 추상화 패널티 qsort는 실제이지만 C ++ std::sort는 기본 최적화 후에도 추상화 패널티가 없습니다.
MSalters

1
IDA Pro를 사용하면 대부분의 C ++ 구문이 C에서 수동으로 수행하는 것과 동일한 작업으로 컴파일되고 생성자와
dtor

7

K & R은 대부분의 C 표현식 (기술적 의미)이 지원 라이브러리에 대한 함수 호출이 아니라 하나 또는 몇 개의 어셈블리 명령에 매핑됨을 의미합니다. 일반적인 예외는 하드웨어 div 명령어가없는 아키텍처의 정수 나누기 또는 FPU가없는 시스템의 부동 소수점입니다.

인용문이 있습니다.

C는 어셈블리 언어의 유연성과 힘을 사용자 친화적 인 어셈블리 언어와 결합합니다.

( 여기에서 찾을 수 있습니다 . "어셈블리 언어의 편리함과 표현력을 갖춘 어셈블리 언어의 속도"와 같은 다른 변형을 기억한다고 생각했습니다.)

long int는 일반적으로 기본 머신 레지스터와 동일한 너비입니다.

일부 상위 수준 언어는 데이터 유형의 정확한 너비를 정의하며 모든 시스템의 구현은 동일하게 작동해야합니다. 하지만 C는 아닙니다.

x86-64에서 128 비트 정수로 작업하거나 일반적으로 임의 크기의 BigInteger로 작업하려면이를위한 함수 라이브러리가 필요합니다. 모든 CPU는 이제 음의 정수의 이진 표현으로 2의 보수를 사용하지만 C가 설계되었을 때는 그렇지 않았습니다. (이것이 2s가 아닌 시스템에서 다른 결과를 제공 할 수있는 일부 항목이 C 표준에서 기술적으로 정의되지 않은 이유입니다.)

데이터 또는 함수에 대한 C 포인터는 어셈블리 주소와 동일한 방식으로 작동합니다.

참조 횟수 참조를 원하면 직접 수행해야합니다. 포인터가 가리키는 개체의 종류에 따라 다른 함수를 호출하는 C ++ 가상 멤버 함수를 원하는 경우 C ++ 컴파일러는call 고정 주소를 가진 명령어 합니다.

문자열은 배열 일뿐입니다.

라이브러리 함수 외부에서 제공되는 유일한 문자열 작업은 문자 읽기 / 쓰기입니다. 연결 없음, 하위 문자열 없음, 검색 없음. (문자열은 nul로 끝나는 ('\0' 포인터 + 길이가 아닌 8 비트 정수의 ) 배열 되므로 부분 문자열을 얻으려면 원래 문자열에 nul을 써야합니다.)

CPU에는 때때로 문자열 검색 기능에서 사용하도록 설계된 명령어가 있지만 일반적으로 루프에서 실행되는 명령어 당 1 바이트를 처리합니다. (또는 x86 rep 접두사를 사용합니다. C가 x86에서 설계된 경우 문자열 검색 또는 비교는 라이브러리 함수 호출이 아닌 기본 작업 일 수 있습니다.)

다른 많은 답변은 예외 처리, 해시 테이블, 목록과 같이 기본적으로 지원되지 않는 것들의 예를 제공합니다. K & R의 디자인 철학은 C가 기본적으로 이러한 것을 갖지 않는 이유입니다.


"K & R은 대부분의 C 표현식 (기술적 의미)이 지원 라이브러리에 대한 함수 호출이 아니라 하나 또는 몇 개의 어셈블리 명령에 매핑된다는 것을 의미합니다." 이것은 매우 직관적 인 설명입니다. 감사.
gwg

1
방금 "von Neumann language"라는 용어를 발견했습니다 ( en.wikipedia.org/wiki/Von_Neumann_programming_languages ). C가 정확히 무엇인지입니다.
Peter Cordes 2015

1
이것이 바로 제가 C를 사용하는 이유입니다. 그러나 C를 배우면서 저를 놀라게 한 것은 광범위한 하드웨어에 대해 효율적으로 시도함으로써 대부분의 최신 하드웨어에서 때때로 부적절하고 비효율적이라는 것입니다. 예를 들어 integer-overflow-in-cmulti-word-addition-using-the-carry-flag를 감지하는 데 유용하고 신뢰할 수없는 방법을 의미 합니다.
Z boson

6

프로세스의 어셈블리 언어는 일반적으로 점프 (이동), 문, 이동 문, 이진 관절염 (XOR, NAND, AND OR 등), 메모리 필드 (또는 주소)를 다룹니다. 메모리를 명령어와 데이터의 두 가지 유형으로 분류합니다. 그것은 어셈블리 언어에 관한 모든 것입니다 (어셈블리 프로그래머는 그것보다 더 많은 것이 있다고 주장 할 것이지만 일반적으로 이것으로 요약됩니다). C는이 단순함과 매우 유사합니다.

C는 대수와 산술을 조합하는 것입니다.

C는 어셈블리의 기본 (프로세서 언어)을 캡슐화합니다. "C에서 제공하는 데이터 유형과 제어 구조는 대부분의 컴퓨터에서 직접 지원되기 때문에"보다 더 사실 일 것입니다.


5

오해의 소지가있는 비교주의

  1. 이 성명서 는 적어도 주류 고수준 언어의 경우 대부분 유행에서 벗어난 "런타임 라이브러리"개념에 의존 합니다. (이는 여전히 가장 작은 임베디드 시스템과 관련이 있습니다.) 런타임은 해당 언어에 내장 된 구성 만 사용할 때 해당 언어의 프로그램을 실행하는 데 필요한 최소한의 지원입니다 (라이브러리에서 제공하는 함수를 명시 적으로 호출하는 것과 반대). .
  2. 대조적으로, 현대 언어는 런타임과 표준 라이브러리구별 하지 않는 경향이 있으며 후자는 종종 상당히 광범위합니다.
  3. K & R 책 당시 C는 표준 라이브러리 조차 없었습니다. . 오히려, 사용 가능한 C 라이브러리는 Unix의 다른 종류간에 상당히 달랐습니다.
  4. 문을 이해하기 위해 표준 라이브러리가있는 언어 (예 : 다른 답변에서 언급 된 Lua 및 Python) 와 비교해서는 안되며 , 더 많은 기본 제공 구조가있는 언어 (예 : 다른 곳에서 언급 된 이전 LISP 및 이전 FORTRAN)와 비교해야합니다. 답변). 다른 예로는 BASIC (LISP와 같은 인터랙티브) 또는 PASCAL (FORTRAN과 같은 컴파일)이 있으며 둘 다 언어 자체에 내장 된 입출력 기능이 있습니다.
  5. 반대로 라이브러리가 아닌 런타임 만 사용하는 C 프로그램에서 계산 결과를 얻는 표준 방법은 없습니다.

반면에 대부분의 최신 언어는 가비지 수집과 같은 기능을 제공하는 전용 런타임 환경 내에서 실행됩니다.
Nate CK

5

컴퓨터에서 직접 지원하지 않는 데이터 유형 또는 제어 구조의 예가 있습니까?

C 언어의 모든 기본 데이터 유형과 해당 작업은 루프없이 하나 또는 몇 개의 기계 언어 명령으로 구현할 수 있습니다. 이는 (실제로 모든) CPU에서 직접 지원됩니다.

널리 사용되는 여러 데이터 유형 및 해당 작업에는 수십 개의 기계 언어 명령이 필요하거나 일부 런타임 루프를 반복하거나 둘 다 필요합니다.

많은 언어에는 이러한 유형 및 해당 작업에 대한 특수 축약 구문이 있습니다. C에서 이러한 데이터 유형을 사용하려면 일반적으로 더 많은 코드를 입력해야합니다.

이러한 데이터 유형 및 작업에는 다음이 포함됩니다.

  • 임의 길이 텍스트 문자열 조작-연결, 부분 문자열, 다른 문자열로 초기화 된 변수에 새 문자열 할당 등. ( 's = "Hello World!"; s = (s + s) [2 : -2] '파이썬에서)
  • 세트
  • C ++ 및 기타 모든 객체 지향 프로그래밍 언어에서와 같이 중첩 된 가상 소멸자가있는 객체
  • 2D 매트릭스 곱셈 및 나눗셈; 선형 시스템 풀기 ( "C = B / A; x = A \ b"(MATLAB 및 여러 배열 프로그래밍 언어))
  • 정규식
  • 가변 길이 배열-특히 배열의 끝에 항목을 추가하여 (때로는) 더 많은 메모리를 할당해야합니다.
  • 런타임에 유형을 변경하는 변수 값 읽기-때로는 부동 소수점이고 다른 때는 문자열입니다.
  • 연관 배열 (종종 "맵"또는 "사전"이라고 함)
  • 기울기
  • 비율 ( "(+ 1/3 2/7)"은 Lisp에서 "13/21" 나타냄 )
  • 임의 정밀도 산술 (종종 "bignums"라고 함)
  • 데이터를 인쇄 가능한 표현으로 변환 (JavaScript의 ".tostring"메소드)
  • 포화 고정 소수점 숫자 (임베디드 C 프로그램에서 자주 사용됨)
  • 런타임에 입력 된 문자열을 표현식 인 것처럼 평가합니다 (많은 프로그래밍 언어에서 "eval ()").

이러한 모든 작업에는 수십 개의 기계 언어 명령이 필요하거나 거의 모든 프로세서에서 일부 런타임 루프를 반복해야합니다.

수십 개의 기계 언어 명령 또는 루핑이 필요한 일부 인기있는 제어 구조는 다음과 같습니다.

  • 폐쇄
  • 연속
  • 예외
  • 게으른 평가

C로 작성 되든 다른 언어로 작성 되든, 프로그램이 이러한 데이터 유형을 조작 할 때 CPU는 결국 해당 데이터 유형을 조작하는 데 필요한 명령을 실행해야합니다. 이러한 지침은 종종 "라이브러리"에 포함됩니다. C를 포함한 모든 프로그래밍 언어에는 모든 실행 파일에 기본적으로 포함 된 각 플랫폼에 대한 "런타임 라이브러리"가 있습니다.

컴파일러를 작성하는 대부분의 사람들은 "언어에 내장 된"모든 데이터 유형을 조작하기위한 지침을 런타임 라이브러리에 넣습니다. C는 없기 때문에 어떤 런인보다 C 런타임 라이브러리 작은 수 -하여 언어, 그들 중 누구도이 C 런타임 라이브러리에 포함되지 않습니다에 내장 된 위의 데이터 유형과 운영 및 제어 구조를 위의 내용이 더 많이 내장 된 다른 프로그래밍 언어의 시간 라이브러리.

프로그래머가 "언어에 내장"되지 않은 다른 데이터 유형을 조작하기 위해 C 또는 자신이 선택한 다른 언어로 된 프로그램을 원할 때 해당 프로그래머는 일반적으로 해당 프로그램에 추가 라이브러리를 포함하도록 컴파일러에 지시하거나 때로는 ( "종속성을 피하기 위해") 프로그램에서 이러한 작업의 또 다른 구현을 직접 작성합니다.


3/21로 리스프 평가하여 (+ 1/3 2/7)의 구현은, 당신이 ... 특히 창조적 인 구현을 가지고 있어야합니다 생각한다면
RobertB

4

에 내장 된 데이터 유형은 C무엇입니까? 그들은 같은 것들이다 int, char, * int, float, 배열 등 ... 이러한 데이터 유형이 CPU에 의해 이해된다. CPU는 배열 작업 방법, 포인터 역 참조 방법 및 포인터, 정수 및 부동 소수점 숫자에 대한 산술을 수행하는 방법을 알고 있습니다.

그러나 더 높은 수준의 프로그래밍 언어로 이동하면 추상 데이터 유형과 더 복잡한 구조를 만들었습니다. 예를 들어 C ++ 프로그래밍 언어의 방대한 내장 클래스 배열을 살펴보십시오. CPU는 클래스, 객체 또는 추상 데이터 유형을 이해하지 못하므로 C ++ 런타임은 CPU와 언어 간의 간격을 연결합니다. 다음은 대부분의 컴퓨터에서 직접 지원되지 않는 데이터 유형의 예입니다.


2
x86은 일부 어레이에서 작동하는 것을 알고 있지만 전부는 아닙니다. 크거나 비정상적인 요소 크기의 경우 배열 인덱스를 포인터 오프셋으로 변환하기 위해 정수 산술을 수행해야합니다. 그리고 다른 플랫폼에서는 항상 필요합니다. 그리고 CPU가 C ++ 클래스를 이해하지 못한다는 생각은 우스꽝 스럽습니다. 그것은 C 구조체와 같은 포인터 오프셋입니다. 이를 위해 런타임이 필요하지 않습니다.
MSalters

@MSalters 예,하지만 iostreams 등과 같은 표준 라이브러리 클래스의 실제 메서드는 컴파일러에서 직접 지원하는 것이 아니라 라이브러리 함수입니다. 그러나 그들이 비교했을 가능성이 높은 상위 언어는 C ++가 아니라 FORTRAN 및 PL / I와 같은 현대 언어였습니다.
Random832

1
가상 멤버 함수가있는 C ++ 클래스는 단순한 오프셋 이상의 구조로 변환됩니다.
Peter Cordes

4

컴퓨터에 따라 다릅니다. C가 발명 된 PDP-11에서는 long제대로 지원되지 않았습니다 (32 비트 작업을 전부 지원하지는 않지만 일부를 지원하는 옵션 추가 모듈이 있습니다). 이는 원래 IBM PC를 포함하여 모든 16 비트 시스템에서 다양한 수준으로 적용됩니다. 마찬가지로 32 비트 컴퓨터 나 32 비트 프로그램에서 64 비트 작업을 수행 할 때도 마찬가지지만 K & R 책 당시 C 언어에는 64 비트 작업이 전혀 없었습니다. 물론 80 년대와 90 년대에 걸쳐 많은 시스템 (386 및 486 프로세서 포함)이 있었고 심지어는 부동 소수점 산술 ( float또는 double)을 직접 지원하지 않는 일부 임베디드 시스템도있었습니다 .

좀 더 이국적인 예를 들어, 일부 컴퓨터 아키텍처는 "단어 지향"포인터 (메모리에서 2 바이트 또는 4 바이트 정수를 가리킴) 만 지원 하며, 추가 오프셋 필드를 추가하여 바이트 포인터 ( char *또는 void *)를 구현해야했습니다. 이 질문 은 그러한 시스템에 대해 자세히 설명합니다.

참조하는 "런타임 라이브러리"함수는 매뉴얼에서 볼 수있는 함수 가 아니라 기계에서 지원 하지 않는 기본 유형 작업을 구현하는 데 사용되는 현대 컴파일러의 런타임 라이브러리에서 이와 같은 함수 입니다. . K & R 자체가 언급 한 런타임 라이브러리는 The Unix Heritage Society의 웹 사이트 에서 찾을 수 있습니다. 분할을 구현하는 데 사용되는 (동일한 이름의 C 함수와는 다른) 함수를 볼 수 있습니다. PDP-11이 추가 기능으로도 지원하지 않았던 32 비트 값 과 함수에서 호출 및 반환을 관리하기 위해 스택에 레지스터를 저장하고 복원하는 (및 csv.c에서도)ldivcsvcret

그들은 또한 CPU의 기본 포인터 지원에 잘 매핑되지 않는 배열 의미 체계를 가진 FORTRAN과 같은 다른 현대 언어와 달리 기본 컴퓨터에서 직접 지원하지 않는 많은 데이터 유형을 지원하지 않기로 선택한 것을 언급했을 가능성이 높습니다. C의 배열. C 배열은 항상 인덱스가 0이고 모든 순위에서 항상 알려진 크기이지만 첫 번째는 배열의 인덱스 범위 또는 크기를 저장할 필요가 없으며 액세스하기 위해 런타임 라이브러리 함수를 가질 필요가 없음을 의미합니다. 컴파일러는 필요한 포인터 산술을 간단히 하드 코딩 할 수 있습니다.


3

이 문장은 단순히 C의 데이터와 제어 구조가 기계 지향적이라는 것을 의미합니다.

여기서 고려해야 할 두 가지 측면이 있습니다. 하나는 C 언어에 데이터 유형이 정의되는 방식에 대한 위도를 허용하는 정의 (ISO 표준)가 있다는 것입니다. 이것은 C 언어 구현이 기계에 맞게 조정 되었음을 의미합니다 . C 컴파일러의 데이터 유형은 컴파일러가 대상으로하는 컴퓨터에서 사용할 수있는 것과 일치합니다. 언어에는 이에 대한 위도가 있기 때문입니다. 기계에 36 비트와 같이 비정상적인 단어 크기가있는 경우 해당 유형 int또는 그에 long맞게 만들 수 있습니다. int정확히 32 비트 라고 가정하는 프로그램 은 중단됩니다.

둘째, 이러한 이식성 문제로 인해 두 번째 효과가 있습니다. 어떤 의미에서 K & R의 진술 일종의 자기 실현 예언 이되거나 그 반대 일 수도 있습니다. 즉, 새로운 프로세서의 구현자는 C 컴파일러 지원에 대한 절실한 필요성을 알고 있으며 "모든 프로세서가 80386처럼 보인다"고 가정하는 많은 C 코드가 있음을 알고 있습니다. 아키텍처는 C를 염두에두고 설계되었습니다. C뿐만 아니라 C 이식성에 대한 일반적인 오해도 염두에두고 있습니다. 9 비트 바이트 나 일반적인 용도로 사용하는 기계를 더 이상 도입 할 수 없습니다. 유형을 가정하는 프로그램char정확히 8 비트 너비입니다. 이식성 전문가가 작성한 일부 프로그램 만 계속 작동합니다. 합리적인 노력으로 도구 체인, 커널, 사용자 공간 및 유용한 응용 프로그램으로 전체 시스템을 결합하기에 충분하지 않을 수 있습니다. 다시 말해서, C 유형은 하드웨어가 많은 비 이식성 C 프로그램이 작성된 다른 하드웨어처럼 보이도록 만들어 졌기 때문에 하드웨어에서 사용할 수있는 것처럼 보입니다.

컴퓨터에서 직접 지원하지 않는 데이터 유형 또는 제어 구조의 예가 있습니까?

많은 기계 언어에서 직접 지원되지 않는 데이터 유형 : 다중 정밀도 정수; 연결 목록; 해시 테이블; 문자열.

대부분의 기계 언어에서 직접 지원되지 않는 제어 구조 : 일류 연속; 코 루틴 / 스레드; 발전기; 예외 처리.

이들 모두는 수많은 범용 명령어와 더 많은 기본 데이터 유형을 사용하여 생성 된 상당한 런타임 지원 코드를 필요로합니다.

C에는 일부 시스템에서 지원하지 않는 일부 표준 데이터 유형이 있습니다. C99 이후 C에는 복소수가 있습니다. 두 개의 부동 소수점 값으로 만들어지며 라이브러리 루틴과 함께 작동하도록 만들어졌습니다. 일부 기계에는 부동 소수점 단위가 전혀 없습니다.

일부 데이터 유형과 관련하여 명확하지 않습니다. 머신이 하나의 레지스터를 기본 주소로 사용하고 다른 레지스터를 스케일 변위로 사용하여 메모리 주소 지정을 지원하는 경우 배열이 직접 지원되는 데이터 유형이라는 의미입니까?

또한 부동 소수점에 대해 말하자면 IEEE 754 부동 소수점 표준화가 있습니다. C 컴파일러가 double프로세서에서 지원하는 부동 소수점 형식과 일치하는 이유는 두 가지가 일치하도록 만들어 졌기 때문일뿐만 아니라 해당 표현에 대한 독립적 인 표준이 있기 때문입니다.


2

다음과 같은 것

  • 거의 모든 기능 언어에서 사용되는 목록 .

  • 예외 .

  • 연관 배열 (맵)-PHP 및 Perl에 포함됨.

  • 가비지 수집 .

  • 많은 언어에 포함되어 있지만 CPU에서 직접 지원하지 않는 데이터 유형 / 제어 구조.


2

직접 지원은 프로세서의 명령어 세트에 효율적으로 매핑하는 것으로 이해해야합니다.

  • 정수 유형에 대한 직접적인 지원은 긴 (확장 된 산술 루틴이 필요할 수 있음) 및 짧은 크기 (마스킹이 필요할 수 있음)를 제외하고는 규칙입니다.

  • 부동 소수점 유형을 직접 지원하려면 FPU를 사용할 수 있어야합니다.

  • 비트 필드에 대한 직접적인 지원은 예외적입니다.

  • 구조체와 배열에는 주소 계산이 필요하며 어느 정도 직접 지원됩니다.

  • 포인터는 항상 간접 주소 지정을 통해 직접 지원됩니다.

  • goto / if / while / for / do는 무조건 / 조건부 분기에서 직접 지원됩니다.

  • 점프 테이블이 적용될 때 스위치를 직접 지원할 수 있습니다.

  • 함수 호출은 스택 기능을 통해 직접 지원됩니다.

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