C 프로그램이 실행될 때“int”및“char”와 같은 데이터 유형 선언자가 RAM에 저장됩니까?


74

C 프로그램이 실행 중이면 데이터는 힙 또는 스택에 저장됩니다. 값은 RAM 주소에 저장됩니다. 그러나 유형 표시기 (예 : int또는 char)는 어떻습니까? 그들은 또한 저장됩니까?

다음 코드를 고려하십시오.

char a = 'A';
int x = 4;

A와 4가 RAM 주소에 저장되어 있음을 읽었습니다. 그러나 약 ax? 가장 혼란스럽게도 실행은 어떻게 a문자이고 xint 인지 어떻게 알 수 있습니까? 내 말은,는 IS intchar어딘가 RAM에서 언급?

RAM에 어딘가에 10011001이라는 값이 저장되어 있다고 가정 해 봅시다. 내가 코드를 실행하는 프로그램을 생각하면, 어떻게이 10011001가 있는지 여부를 알 수 char또는를 int?

내가 이해하지 못하는 것은 컴퓨터가 10001과 같은 주소에서 변수 값을 읽을 때 int또는 인지 여부를 컴퓨터가 알고있는 방법 char입니다. 라는 프로그램을 클릭한다고 상상해보십시오 anyprog.exe. 코드가 즉시 실행되기 시작합니다. 이 실행 파일이 저장된 변수는 유형인지에 대한 정보가 포함되어 있습니까 int또는 char?


24
이 정보는 런타임시 완전히 손실됩니다. 당신 (그리고 컴파일러)은 메모리가 올바르게 해석되도록 미리 확인해야합니다. 이것이 당신의 대답입니까?
5gon12eder

4
그렇지 않습니다. 수행중인 작업을 알고 있다고 가정하기 때문에 제공 한 메모리 주소에서 찾은 모든 것을 가져 와서 stdout에 씁니다. 기록 된 내용이 읽을 수있는 문자에 해당하면 결국 누군가의 콘솔에 읽을 수있는 문자로 표시됩니다. 일치하지 않으면 횡설수설로 표시되거나 임의의 읽을 수있는 문자로 표시됩니다.
Robert Harvey

22
@ user16307 짧은 대답은 정적으로 유형이 지정된 언어에서 문자를 인쇄 할 때마다 컴파일러가 int를 인쇄 하는 것과 다른 코드생성 한다는 것입니다. 런타임에 더 이상 x문자에 대한 지식이 없지만 컴파일러가 선택한 문자이기 때문에 실행되는 문자 인쇄 코드입니다.
Ixrec

13
@ user16307 항상 숫자 65의 이진 표현으로 저장됩니다. 65로 인쇄되는지 또는 A로 인쇄되는지는 컴파일러가 인쇄하기 위해 생성 한 코드에 따라 다릅니다 . 65 옆에는 실제로 char 또는 int (적어도 C와 같이 정적으로 유형이 지정되지 않은 언어)라는 메타 데이터가 없습니다.
Ixrec

2
완전히 당신이 여기에 대해 물어 개념을 이해하고이를 스스로 구현하는, 당신은 컴파일러 코스를 수강 할 수 있습니다, 예를 들어 코 세라의 하나
mucaho

답변:


122

여러 의견으로 게시 한 질문을 해결하려면 (게시물을 수정해야한다고 생각합니다) :

내가 이해하지 못하는 것은 컴퓨터가 변수의 값을 읽고 10001과 같은 주소를 int 또는 char 인 경우 어떻게 알 수 있는지입니다. anyprog.exe라는 프로그램을 클릭한다고 상상해보십시오. 코드가 즉시 실행되기 시작합니다. 이 exe 파일에 변수가 또는 char로 저장되어 있는지에 대한 정보가 포함됩니까?

코드를 넣겠습니다. 다음과 같이 작성한다고 가정 해 봅시다.

int x = 4;

그리고 RAM에 저장되었다고 가정 해 봅시다.

0x00010004: 0x00000004

첫 번째 부분은 주소이고 두 번째 부분은 값입니다. 프로그램 (머신 코드로 실행)이 실행될 때 볼 수있는 0x00010004것은 모두 value 0x000000004입니다. 이 데이터의 유형을 '알지'않으며, 어떻게 '사용될'지 알지 못합니다.

그렇다면 프로그램이 올바른 일을 어떻게 파악합니까? 이 코드를 고려하십시오.

int x = 4;
x = x + 5;

우리는 여기에 읽고 쓸 수 있습니다. 프로그램 x이 메모리에서 읽을 때 메모리에서 찾습니다 0x00000004. 그리고 당신의 프로그램은 0x00000005그것에 추가 하는 것을 알고 있습니다. 그리고 프로그램이 이것이 유효한 연산임을 '알고'있는 이유는 컴파일러가 타입 안전을 통해 연산이 유효한지 확인하기 때문입니다. 귀하의 컴파일러는 이미 추가 할 수 있다는 것을 확인했습니다 45함께. 따라서 이진 코드가 실행되면 (exe) 확인할 필요가 없습니다. 모든 것이 정상이라고 가정하고 각 단계를 맹목적으로 실행합니다 (사실은 정상이 아닌 나쁜 일이 발생합니다).

그것을 생각하는 또 다른 방법은 다음과 같습니다. 나는 당신 에게이 정보를 제공합니다 :

0x00000004: 0x12345678

이전과 같은 형식-왼쪽의 주소, 오른쪽의 값 어떤 유형의 값입니까? 이 시점에서 코드를 실행할 때 컴퓨터가하는 것처럼 그 값에 대한 많은 정보를 알게됩니다. 12743을 그 값에 더하라고 말하면 할 수 있습니다. 해당 작업의 영향이 전체 시스템에 어떤 영향을 줄지 모르지만 두 숫자를 추가하는 것이 실제로 잘하는 일이므로 할 있습니다. 그 값을 int? 반드시 그런 것은 아닙니다. 두 개의 32 비트 값과 더하기 연산자 만 있으면됩니다.

아마도 혼란의 일부는 데이터를 다시 가져 오는 것입니다. 우리가 가지고 있다면 :

char A = 'a';

컴퓨터 a가 콘솔 에 표시 되는 것을 어떻게 알 수 있습니까? 글쎄, 그것에 많은 단계가 있습니다. 첫 번째는 A메모리의 위치 로 이동하여 읽는 것입니다.

0x00000004: 0x00000061

aASCII 의 16 진수 값 은 0x61이므로 위의 메모리에서 볼 수 있습니다. 이제 기계 코드는 정수 값을 알고 있습니다. 정수 값을 문자로 변환하여 표시하는 방법을 어떻게 알 수 있습니까? 간단히 말해, 컴파일러는 전환을 수행하는 데 필요한 모든 단계를 수행했습니다. 그러나 컴퓨터 자체 (또는 프로그램 / exe)는 해당 데이터의 유형이 무엇인지 모릅니다. 즉, 32 비트 값이 무엇이든 될 수있다 - int, char(A)의 절반 double, 포인터 배열의 일부 (A)의 일부가 string, 명령의 일부 등


다음은 프로그램 (exe)과 컴퓨터 / 운영체제 간의 간단한 상호 작용입니다.

프로그램 : 시작하고 싶습니다. 20MB의 메모리가 필요합니다.

운영 체제 : 사용되지 않는 20MB의 사용 가능한 메모리를 찾아서 넘겨줍니다.

(중요한 사항은 20MB의 메모리를 반환 수 있다는 것입니다. 연속 할 필요조차 없습니다.이 시점에서 프로그램은 이제 OS와 통신하지 않고도 메모리 내에서 작동 할 수 있습니다)

프로그램 : 메모리의 첫 번째 스팟은 32 비트 정수 변수라고 가정하겠습니다 x.

(컴파일러는 다른 변수에 대한 액세스가 메모리의이 지점에 절대로 닿지 않도록합니다. 시스템에는 첫 번째 바이트가 가변적 x이거나 해당 변수 x가 정수라는 말이 없습니다. 비유 : 가방이 있습니다. 이 가방에 노란 색의 공만 넣을 것입니다. 나중에 가방에서 무언가를 꺼낼 때, 뭔가 파란색이나 큐브를 꺼내는 것이 충격적 일 것입니다. 프로그램은 이제 첫 번째 메모리 스팟이 변수 x이고 정수라고 가정합니다.이 바이트의 메모리에 다른 것이 쓰여지거나 다른 것으로 추정되는 경우 컴파일러는 이런 일이 발생하지 않도록합니다. 일어나지 않는다)

프로그램 : 이제 2가정하고있는 처음 4 바이트에 씁니다 x.

프로그램 :에 5를 추가하고 싶습니다 x.

  • X 값을 임시 레지스터로 읽습니다.

  • 임시 레지스터에 5를 더합니다

  • 임시 레지스터의 값을 첫 번째 바이트로 다시 저장합니다 x.

프로그램 : 사용 가능한 다음 바이트가 char 변수라고 가정합니다 y.

프로그램 : avariable에 쓸 것 y입니다.

  • 라이브러리는 바이트 값을 찾는 데 사용됩니다 a

  • 바이트는 프로그램이 가정하는 주소에 기록됩니다 y.

프로그램 : 내용을 표시하고 싶다 y

  • 두 번째 메모리 스팟의 값을 읽습니다.

  • 라이브러리를 사용하여 바이트에서 문자로 변환

  • 그래픽 라이브러리를 사용하여 콘솔 화면 변경 (픽셀을 검은 색에서 흰색으로 설정, 한 줄 스크롤 등)

(그리고 여기에서 계속됩니다)

당신이 아마 끊어지고있는 것은-메모리의 첫 번째 자리가 더 이상 없을 때 어떻게됩니까 x? 또는 두 번째는 더 이상 없습니다 y? 사람이 읽을 때 어떤 일이 발생 xA와 char또는 y포인터로? 요컨대 나쁜 일이 일어납니다. 이러한 것 중 일부는 잘 정의 된 동작을 가지고 있으며 일부는 정의되지 않은 동작을 가지고 있습니다. 정의되지 않은 동작은 정확히 아무것도 아닙니다. 프로그램이나 운영 체제 충돌에 이르기까지 모든 일이 발생할 수 있습니다. 잘 정의 된 동작조차 악의적 일 수 있습니다. x내 프로그램에 대한 포인터로 변경 하여 프로그램이 포인터로 사용하도록하면 프로그램이 내 프로그램을 시작하도록 할 수 있습니다. 이것은 정확히 해커가하는 일입니다. 컴파일러는 다음 int x과 같이 사용하지 않도록 도와줍니다 .string그리고 그 자연의 것들. 기계어 코드 자체는 유형을 인식하지 못하며 지시 사항에 지시 된 대로만 수행합니다. 또한 런타임에 발견되는 많은 양의 정보가 있습니다. 프로그램에서 사용할 수있는 메모리 바이트는 무엇입니까? x첫 번째 바이트 또는 12 번째 바이트에서 시작 합니까 ?

그러나 실제로 이와 같은 프로그램을 작성하는 것이 얼마나 끔찍한 지 상상할 수 있습니다 (조립 언어로 가능). 당신은 당신의 변수를 '선언'에 의해 시작 - 당신은 자신이 그 바이트 1은 말할 x바이트 2가, y그리고 당신이 코드로드 및 저장하는 레지스터의 각 라인을 작성할 때, (인간)을 당신은 어느 쪽이 기억해야 x하고 어떤 하나는 y시스템이 모르기 때문에입니다. 그리고 당신은 (인간으로서) 어떤 유형 x과 유형을 기억해야합니다 y. 왜냐하면 시스템은 전혀 모릅니다.


놀라운 설명. "정수 값을 문자로 변환하여 표시하는 방법을 어떻게 알 수 있습니까? 간단히 말해서, 컴파일러는 전환을 위해 필요한 모든 단계를 수행해야합니다." 여전히 나를 위해 안개입니다. CPU가 RAM 레지스터에서 0x00000061을 가져 왔습니다. 이 시점에서 화면에 표시된 내용으로 전환하는 다른 지침 (exe 파일에 있음)이 있다고 말하고 있습니까?
user16307

2
@ user16307 예, 추가 지침이 있습니다. 작성하는 각 코드 줄은 많은 명령으로 바뀔 수 있습니다. 어떤 캐릭터를 사용해야하는지, 어떤 픽셀을 수정해야하는지, 어떤 색으로 바꿀지 등에 대한 지시 사항이 있습니다. 실제로 보이지 않는 코드도 있습니다. 예를 들어 std :: cout을 사용한다는 것은 라이브러리를 사용하고 있음을 의미합니다. 콘솔에 쓰는 코드는 한 줄일 수 있지만 호출하는 함수는 더 많은 줄이 될 것이며 각 줄은 많은 기계 명령어로 바뀔 수 있습니다.
Shaz

8
@ user16307 Otherwise how can console or text file outputs a character instead of int 메모리 위치의 내용을 정수 또는 영숫자로 출력하는 명령 시퀀스가 ​​다르기 때문입니다. 컴파일러는 변수 유형에 대해 알고 컴파일 타임에 적절한 명령 시퀀스를 선택하여 EXE에 기록합니다.
찰스 E. 그랜트

2
바이트 코드 (또는 바이트 코드)가 보통 중간 언어 (Java Bytecode 또는 MSIL과 같은)를 나타내므로 런타임에서 활용할 수 있도록이 데이터를 실제로 저장할 수 있으므로 "바이트 코드 자체"에 대해 다른 문구를 찾을 수 있습니다. 또한 그 문맥에서 어떤 "바이트 코드"가 참조되어야하는지 명확하지 않습니다. 그렇지 않으면 좋은 대답입니다.
jpmc26

6
@ user16307 C ++ 및 C #에 대해 걱정하지 마십시오. 이 사람들이 말하는 것은 컴퓨터와 컴파일러의 작동 방식에 대한 현재의 이해 이상입니다. 당신이 이해하려고하는 목적으로, 하드웨어는 유형, 문자 또는 정수 또는 기타에 대해 아무것도 모릅니다. 컴파일러에게 일부 변수가 int라고 말했을 때 메모리 위치가 int 인 경우 메모리 위치를 처리하는 실행 코드를 생성했습니다. 메모리 위치 자체에는 유형에 대한 정보가 없습니다. 프로그램이 그것을 int로 취급하기로 결정한 것입니다. 런타임 유형 정보에 대해 들었던 모든 것을 잊어 버리십시오.
Andres F.

43

주된 질문은 "컴파일 타임에 유형이 지워지고 런타임에 유지되지 않는 경우 컴퓨터가 코드를 실행할지 여부를 해석하는 코드인지 int또는 코드를 해석하는 코드를 실행 할지 어떻게 알 수 char있습니까?" "

답은 ... 컴퓨터는 그렇지 않습니다. 그러나 컴파일러 알고 있으며 처음에는 바이너리에 올바른 코드를 넣을 것입니다. 변수가로 입력 된 경우 char컴파일러는 변수 를 처리하기위한 코드를 int프로그램에 넣지 않고 코드를 처리하도록 코드를 넣습니다 char.

런타임에 유형을 유지해야하는 이유 다음 같습니다.

  • 동적 입력 : 동적 입력에서는 런타임에 유형 검사가 수행되므로 런타임에 유형을 알아야합니다. 그러나 C는 동적으로 입력되지 않으므로 유형을 안전하게 지울 수 있습니다. (이것은 매우 다른 시나리오입니다. 동적 유형과 정적 유형은 실제로 같은 것이 아니며 혼합 유형 언어에서는 정적 유형을 지우고 동적 유형 만 유지할 수 있습니다.)
  • 동적 다형성 : 런타임 유형에 따라 다른 코드를 실행하는 경우 런타임 유형을 유지해야합니다. C에는 동적 다형성이 없으며 (실제로 +운영자 와 같은 특수 하드 코딩 된 경우를 제외하고는 다형성 이 전혀 없으므로) 그런 이유로 런타임 유형이 필요하지 않습니다. 그러나 런타임 유형은 정적 유형과 다릅니다. 예를 들어 Java의 경우 이론적으로 정적 유형을 지우고 런타임 유형을 다형성을 위해 유지할 수 있습니다. 또한 유형 조회 코드를 분산하고 특수화하여 객체 (또는 클래스)에 넣는 경우 런타임 유형 (예 : C ++ vtables)도 필요하지 않습니다.
  • 런타임 반영 : 프로그램이 런타임에 해당 유형을 반영 할 수있게하려면 런타임에 유형을 유지해야합니다. Java로 쉽게 확인할 수 있습니다. 런타임시 1 차 유형을 유지하지만 컴파일시 일반 유형에 대한 유형 인수를 지우므로 유형 생성자 ( "원시 유형")에만 반영 할 수 있지만 유형 인수에는 반영 할 수 없습니다. 다시 C에는 런타임 반영이 없으므로 런타임에 유형을 유지할 필요가 없습니다.

C에서 런타임에 유형을 유지하는 유일한 이유는 디버깅을위한 것이지만 디버깅은 일반적으로 사용 가능한 소스를 사용하여 수행 된 다음 소스 파일에서 단순히 유형을 찾을 수 있습니다.

유형 삭제는 매우 정상입니다. 형식 안전성에 영향을 미치지 않습니다. 컴파일 타임에 형식이 검사됩니다. 일단 컴파일러가 프로그램이 형식 안전하다고 만족하면 해당 형식은 더 이상 필요하지 않습니다. 정적 다형성 (일명 과부하)에 영향을 미치지 않습니다. 일단 과부하 해결이 완료되고 컴파일러가 올바른 과부하를 선택하면 더 이상 유형이 필요하지 않습니다. 타입은 또한 최적화를 안내 할 수 있지만, 옵티마이 저가 타입에 따라 최적화를 선택한 후에는 더 이상 필요하지 않습니다.

런타임에 유형을 유지하는 것은 런타임에 유형으로 무언가를 수행하려는 경우에만 필요합니다.

Haskell은 가장 엄격하고 가장 엄격한 형식 안전 정적 형식 언어 중 하나이며 Haskell 컴파일러는 일반적으로 모든 형식을 지 웁니다. (유형 클래스에 대한 메소드 사전의 전달은 예외입니다.)


3
아니! 왜? 그 정보에는 무엇이 필요합니까? 컴파일러 char는 컴파일 된 이진 파일 을 읽기위한 코드를 출력합니다 . 그것은의 코드를 출력하지 않고 int,의 코드를 byte출력하지 않으며, 포인터의 코드를 출력하지 않고 단순히 의 코드 출력 합니다 char. 유형에 따라 런타임 결정이 없습니다. 유형이 필요하지 않습니다. 그것은 완전히 그리고 전혀 관련이 없습니다. 모든 관련 결정은 이미 컴파일 타임에 이루어졌습니다.
Jörg W Mittag

2
없습니다. 컴파일러는 단순히 바이너리에 문자를 인쇄하기위한 코드를 넣습니다. 기간. 컴파일러는 해당 메모리 주소에 char이 있다는 것을 알고 있으므로 이진으로 char을 인쇄하기위한 코드를 넣습니다. 이상한 이유로 그 메모리 주소의 값 이 문자 가 아닌 경우 모든 지옥이 느슨해집니다. 기본적으로 전체 보안 익스플로잇이 작동하는 방식입니다.
Jörg W Mittag

2
CPU가 프로그램의 데이터 유형에 대해 알고 있다면 지구상의 모든 사람들이 누군가가 새로운 유형을 발명 할 때마다 새로운 CPU를 구입해야 할 것입니다. public class JoergsAwesomeNewType {};보다? 방금 새로운 유형을 발명했습니다! 새로운 CPU를 구입해야합니다!
Jörg W Mittag

9
아뇨. 컴파일러는 바이너리에 어떤 코드를 넣어야하는지 알고 있습니다. 이 정보를 유지할 근거는 없습니다. int를 인쇄하는 경우 컴파일러는 int를 인쇄하기위한 코드를 넣습니다. 문자를 인쇄하는 경우 컴파일러는 문자를 인쇄하기위한 코드를 넣습니다. 기간. 그러나 그것은 약간의 패턴입니다. 문자를 인쇄하기위한 코드는 특정 방식으로 비트 패턴을 해석하고, int를 인쇄하기위한 코드는 다른 방식으로 비트를 해석하지만, int 인 비트 패턴을 비트 패턴과 구별 할 수있는 방법은 없습니다 문자이고, 비트 열입니다.
Jörg W Mittag

2
@ user16307 : "Doesnt exe 파일은 어떤 주소가 어떤 유형의 데이터인지에 대한 정보를 포함합니까?" 아마도. 디버그 데이터로 컴파일하면 디버그 데이터에 변수 이름, 주소 및 유형에 대한 정보가 포함됩니다. 때로는 디버그 데이터가 .exe 파일 (이진 스트림)에 저장됩니다. 그러나 이것은 실행 코드의 일부가 아니며 디버거에서만 응용 프로그램 자체에서 사용되지 않습니다.
Ben Voigt

12

컴퓨터는 어떤 주소가 무엇인지 "알지 못하지만"프로그램 지시 사항에 무엇이 들어 있는지에 대한 지식을 알고 있습니다.

char 변수를 쓰고 읽는 C 프로그램을 작성할 때 컴파일러는 해당 데이터 조각을 char로 작성하는 어셈블리 코드를 작성하고 메모리 주소를 읽고이를 char로 해석하는 다른 코드가 있습니다. 이 두 작업을 함께 묶는 유일한 것은 해당 메모리 주소의 위치입니다.

읽을 시간이 오면 명령어는 "어떤 데이터 유형이 있는지보십시오"라고 말하지 않고 "해당 메모리를 부동으로로드"와 같은 것을 말합니다. 읽을 주소가 변경되었거나 플로트가 아닌 다른 것으로 해당 메모리를 덮어 쓴 경우 CPU는 해당 메모리를 플로트로 행복하게로드하기 때문에 모든 종류의 이상한 일이 발생할 수 있습니다.

나쁜 유추 시간 : 창고가 메모리이고 물건을 선택하는 사람들이 CPU 인 복잡한 운송 창고를 상상해보십시오. 창고 '프로그램'의 한 부분은 선반에 다양한 품목을 놓습니다. 다른 프로그램은 창고에서 물건을 가져 가서 상자에 넣습니다. 그들이 뽑힐 때, 그들은 확인되지 않고 단지 쓰레기통에 들어갑니다. 전체 창고는 모든 것이 동기화되어 작동하며 올바른 항목은 항상 적절한 장소에 항상 있어야합니다. 그렇지 않으면 실제 프로그램과 마찬가지로 모든 것이 충돌합니다.


CPU가 레지스터에서 0x00000061을 찾아서 가져 오는지 어떻게 설명 할 수 있습니까? 콘솔 프로그램이 이것을 int가 아닌 문자로 출력한다고 가정합니다. 그 exe 파일에는 0x00000061의 주소가 char이고 ASCII 테이블을 사용하여 문자로 변환되는 명령 코드가 있다는 것을 의미합니까?
user16307

7
"모든 충돌"은 실제로 가장 좋은 시나리오입니다. "가장 이상한 일"은 두 번째로 좋은 시나리오이며, "미묘하게 이상한 일이"더 나쁘고 최악의 경우는 "누군가 의도적으로 원하는 방식으로 의도적으로 조작 한 일이 등 뒤에 발생", 일명 보안 악용.
Jörg W Mittag

@ user16307 : 프로그램의 코드는 컴퓨터에 해당 주소를 가져와 사용중인 인코딩에 따라 표시하도록 지시합니다. 메모리 위치의 해당 데이터가 ASCII 문자인지 전체 가비지인지에 관계없이 컴퓨터는 신경 쓰지 않습니다. 다른 값으로 예상되는 값을 갖도록 해당 메모리 주소를 설정했습니다. 어셈블리 프로그래밍을 시도하는 것이 도움이 될 수 있다고 생각합니다.
whatsisname

1
@ JörgWMittag : 실제로. 버퍼 오버플로를 예로 들어 생각했지만 더 혼란스럽게 만들 것이라고 결정했습니다.
whatsisname

@ user16307 : 화면에 데이터를 표시하는 것은 프로그램입니다. 전통적인 유닉스에서는 터미널 (DEC VT100 직렬 터미널을 에뮬레이트하는 소프트웨어)-모니터와 키보드가있는 하드웨어 장치로 모뎀에 들어 오는 모든 것을 모니터에 표시하고 키보드에 입력 한 모든 것을 모뎀에 보냅니다. DOS에서는 DOS (실제로는 VGA 카드의 텍스트 모드이지만 무시해도됩니다)와 Windows에서는 command.com입니다. 프로그램은 실제로 문자열을 인쇄하는 것을 알지 못하고 일련의 바이트 (숫자)를 인쇄합니다.
slebetman

8

그렇지 않습니다. C가 머신 코드로 컴파일되면 머신은 많은 비트를 보게됩니다. 이러한 비트가 해석되는 방식은 일부 추가 메타 데이터가 아닌 비트에 대해 수행중인 작업에 따라 다릅니다.

소스 코드에 입력 한 유형은 컴파일러 전용입니다. 데이터는 어떤 유형의 데이터를 사용해야하는지, 그리고 최대한 활용하기 위해서는 데이터가 적절한 방식으로 만 사용되도록 노력합니다. 컴파일러가 소스 코드의 논리를 검사 할 수있는만큼 좋은 작업을 수행하면 머신 코드는이를 표현할 수있는 방법이 없기 때문에 머신 코드로 변환하고 유형 데이터를 버립니다 (적어도 대부분의 머신에서). .


내가 이해하지 못하는 것은 컴퓨터가 변수의 값을 읽고 10001과 같은 주소를 int 또는 char 인 경우 어떻게 알 수 있는지입니다. anyprog.exe라는 프로그램을 클릭한다고 상상해보십시오. 코드가 즉시 실행되기 시작합니다. 이 exe 파일에 변수가 또는 char로 저장되어 있는지에 대한 정보가 포함되어 있습니까? –
user16307

@ user16307 아니요, int 또는 char인지에 대한 추가 정보는 없습니다. 다른 사람이 나를 이길 수 없다고 가정하고 나중에 몇 가지 예를 추가하겠습니다.
8bittree

1
@ user16307 : exe 파일에는 해당 정보가 간접적으로 포함됩니다. 프로그램을 실행하는 프로세서는 프로그램을 작성할 때 사용되는 유형에 신경 쓰지 않지만 다양한 메모리 위치에 액세스하는 데 사용되는 명령에서 많은 부분을 추론 할 수 있습니다.
Bart van Ingen Schenau

@ user16307 실제로 약간의 추가 정보가 있습니다. exe 파일은 정수가 4 바이트라는 것을 알고 있으므로 "int a"를 쓸 때 컴파일러는 변수에 대해 4 바이트를 예약하므로 이후 a와 다른 변수의 주소를 계산할 수 있습니다.
Esben Skov Pedersen

1
@는 차이 (유형의 크기 옆) 실질적인 차이가 없다 user16307 int a = 65char b = 'A'코드 컴파일되면이.

6

대부분의 프로세서는 서로 다른 유형의 데이터 작업에 대한 서로 다른 지침을 제공하므로 유형 정보는 일반적으로 생성 된 머신 코드에 "구워집니다". 추가 형식 메타 데이터를 저장할 필요가 없습니다.

구체적인 예가 도움이 될 수 있습니다. 아래의 머신 코드는 SuSE Linux Enterprise Server (SLES) 10을 실행하는 x86_64 시스템에서 gcc 4.1.2를 사용하여 생성되었습니다.

다음 소스 코드를 가정하십시오.

int main( void )
{
  int x, y, z;

  x = 1;
  y = 2;

  z = x + y;

  return 0;
}

위의 소스에 해당하는 생성 된 어셈블리 코드의 장점은 다음과 같습니다 gcc -S.

main:
.LFB2:
        pushq   %rbp               ;; save the current frame pointer value
.LCFI0:
        movq    %rsp, %rbp         ;; make the current stack pointer value the new frame pointer value
.LCFI1:                            
        movl    $1, -12(%rbp)      ;; x = 1
        movl    $2, -8(%rbp)       ;; y = 2
        movl    -8(%rbp), %eax     ;; copy the value of y to the eax register
        addl    -12(%rbp), %eax    ;; add the value of x to the eax register
        movl    %eax, -4(%rbp)     ;; copy the value in eax to z
        movl    $0, %eax           ;; eax gets the return value of the function
        leave                      ;; exit and restore the stack
        ret

다음 ret에 나오는 추가 항목이 있지만 토론과 관련이 없습니다.

%eax32 비트 범용 데이터 레지스터입니다. %rsp스택 에 마지막으로 푸시 된 주소를 포함하는 스택 포인터 저장 용으로 예약 된 64 비트 레지스터 입니다. 현재 스택 프레임 의 주소를 포함하는 프레임 포인터%rbp 를 저장하기 위해 예약 된 64 비트 레지스터 입니다. 스택 프레임은 함수를 입력 할 때 스택에 작성되며 함수의 인수 및 로컬 변수를위한 공간을 예약합니다. 프레임 포인터에서 오프셋을 사용하여 인수 및 변수에 액세스합니다. 이 경우 변수의 메모리는에 저장된 주소의 "아래"12 바이트입니다 . x%rbp

위의 코드에서 x(1에 저장된 -12(%rbp)) 의 정수 값을 명령어를 %eax사용하여 레지스터에 복사합니다.이 값은 movl한 위치에서 다른 위치로 32 비트 단어를 복사하는 데 사용됩니다. 그런 다음 (에 저장된 ) addl의 정수 값을 이미 y저장된 값에 추가하는 을 호출 합니다 . 우리는 그 결과에 저장 입니다 . -8(%rbp)%eax-4(%rbp)z

이제 double값 대신 값을 처리하도록 변경하십시오 int.

int main( void )
{
  double x, y, z;

  x = 1;
  y = 2;

  z = x + y;

  return 0;
}

gcc -S다시 실행 하면 우리에게 다음을 제공합니다.

main:
.LFB2:
        pushq   %rbp                              
.LCFI0:
        movq    %rsp, %rbp
.LCFI1:
        movabsq $4607182418800017408, %rax ;; copy literal 64-bit floating-point representation of 1.00 to rax
        movq    %rax, -24(%rbp)            ;; save rax to x
        movabsq $4611686018427387904, %rax ;; copy literal 64-bit floating-point representation of 2.00 to rax
        movq    %rax, -16(%rbp)            ;; save rax to y
        movsd   -24(%rbp), %xmm0           ;; copy value of x to xmm0 register
        addsd   -16(%rbp), %xmm0           ;; add value of y to xmm0 register
        movsd   %xmm0, -8(%rbp)            ;; save result to z
        movl    $0, %eax                   ;; eax gets return value of function
        leave                              ;; exit and restore the stack
        ret

몇 가지 차이점이 있습니다. movland 대신에 and addl를 사용 movsd하고 addsd배정 밀도 부동 수를 할당하고 추가합니다. 에 임시 값을 저장하는 대신을 %eax사용 %xmm0합니다.

이것이 타입이 머신 코드에 "구워졌다"고 말할 때의 의미입니다. 컴파일러는 단순히 특정 유형을 처리하기 위해 올바른 기계 코드를 생성합니다.


4

역사적 으로 C는 메모리를 여러 유형의 번호 슬롯 그룹으로 구성한 것으로 간주했습니다.unsigned char(항상 8 비트 일 필요는 없지만 "바이트"라고도 함). 메모리에 저장된 것을 사용하는 모든 코드는 정보가 저장된 슬롯을 알고 있어야하며, 정보로 무엇을해야하는지 알아야합니다. 예를 들어 "주소 123 : 456에서 시작하는 4 바이트를 32 비트로 해석합니다. 부동 소수점 값 "또는"가장 최근에 계산 된 수량의 하위 16 비트를 주소 345 : 678에서 시작하여 2 바이트로 저장합니다. " 코드는 한 유형을 사용하여 메모리를 쓰고 다른 유형으로 읽으려고 시도했지만 쓰기에 의해 저장된 비트 패턴은 두 번째 유형의 규칙에 따라 해석되어 결과가 발생할 수 있습니다.

예를 들어, 코드를 0x1234567832 비트 에 저장 unsigned int한 다음 unsigned int주소와 위의 값 중 하나에서 두 개의 연속 16 비트 값 을 읽으려고하면 그 중 절반 unsigned int이 저장된 위치에 따라 코드가 값을 읽을 수 있습니다. 0x1234 및 0x5678 또는 0x5678 및 0x1234.

그러나 C99 표준은 더 이상 메모리가 비트 패턴이 무엇을 나타내는 지 전혀 모르는 일련의 슬롯으로 동작하지 않아도 됩니다. 컴파일러는 메모리 슬롯이 메모리 슬롯에 저장된 데이터 유형을 인식하는 것처럼 작동 할 수 있으며, 다른 unsigned char유형 unsigned char또는 다른 유형을 사용하여 작성된 데이터 와 동일한 유형 또는 동일한 유형을 사용하여 읽은 데이터 만 허용합니다. 와; 컴파일러는 메모리 슬롯이 규칙에 위배되는 방식으로 메모리에 액세스하려고 시도하는 모든 프로그램의 동작을 임의로 손상시키는 힘과 성향을 갖는 것처럼 동작 할 수 있습니다.

주어진:

unsigned int a = 0x12345678;
unsigned short p = (unsigned short *)&a;
printf("0x%04X",*p);

일부 구현은 0x1234를 인쇄하고 다른 구현은 0x5678을 인쇄 할 수 있지만 C99 표준에서는 "FRINK RULES!"를 인쇄하는 것이 합법적입니다. 또는 메모리 위치를 보유 a하는 것이 합법적 일 것이라는 이론에 근거하여 합법적 일 수 있는 유형을 기록하는 하드웨어를 포함하는 하드웨어 및 그러한 하드웨어가 "훌륭한 규칙!" 출력됩니다.

그러한 하드웨어가 실제로 존재하는지는 중요하지 않습니다. 그러한 하드웨어가 합법적으로 존재할 수 있다는 사실은 컴파일러가 그러한 시스템에서 실행되는 것처럼 동작하는 코드를 생성하는 것이 합법적이라는 사실에 유의하십시오. 컴파일러가 특정 메모리 위치가 한 유형으로 작성되고 다른 유형으로 읽히는 것으로 결정할 수있는 경우, 하드웨어가 그러한 결정을 내릴 수있는 시스템에서 실행중인 것으로 가정하고 컴파일러 작성자가 적합하다고 생각하는 모든 수준의 응답으로 응답 할 수 있습니다 .

이 규칙의 목적은 특정 유형의 값을 보유한 바이트 그룹이 특정 시점에 특정 값을 보유하고 해당 그룹을 유추하기 위해 동일한 유형의 값이 쓰여지지 않았 음을 알고있는 컴파일러를 허용하는 것이 었습니다. 바이트 수는 여전히 해당 값을 보유합니다. 예를 들어, 프로세서가 바이트 그룹을 레지스터로 읽은 후 나중에 레지스터에있는 동안 동일한 정보를 다시 사용하려고했지만 컴파일러는 메모리에서 값을 다시 읽지 않고도 레지스터 내용을 사용할 수있었습니다. 유용한 최적화. 규칙의 처음 10 년 동안, 위반하는 것은 일반적으로 변수를 읽는 데 사용되는 유형이 아닌 다른 유형으로 변수를 쓰면 쓰기가 읽은 값에 영향을 줄 수도 있고 영향을받지 않을 수도 있음을 의미합니다. 그러한 행동은 어떤 경우에는 비참 할 수 있지만 다른 경우에는 무해 할 수 있습니다.

그러나 2009 년경 CLANG과 같은 일부 컴파일러의 저자는 표준이 컴파일러가 한 유형을 사용하여 메모리를 작성하고 다른 유형으로 읽는 경우 컴파일러가 원하는 모든 작업을 수행 할 수 있으므로 컴파일러는 프로그램이 입력 할 수있는 입력을 수신하지 않을 것이라고 유추해야합니다 그런 일이 발생합니다. 표준은 유효하지 않은 입력이 수신 될 때 컴파일러가 원하는 모든 작업을 수행 할 수 있다고 말하고 있기 때문에 표준이 요구 사항을 강요 할 수없는 경우 (및 일부 컴파일러 작성자의 관점에서) 생략해야하는 경우에만 적용되는 코드는 생략해야합니다. 관련이 없습니다. 이렇게하면 앨리어싱 위반의 동작이 읽기 요청과 동일한 유형을 사용하여 작성된 마지막 값 또는 다른 유형을 사용하여 작성된 최근 값을 임의로 반환 할 수있는 메모리와 같은 방식으로 변경됩니다.


1
RTTI가없는 방법을 이해하지 못하는 사람에게 유형 정리를 할 때 정의되지 않은 동작을 언급하는 것은 반 직관적 인 것 같습니다
Cole Johnson

@ColeJohnson : 2009 년 이전 컴파일러의 99 %가 지원하는 C의 방언에 대한 공식적인 이름이나 표준이 없다는 것은 너무 나쁩니다. 교시의 관점과 실용적인 관점에서 근본적으로 다른 언어로 간주되어야하기 때문입니다. 35 년 동안 예측 가능하고 최적화 가능한 여러 가지 행동을 발전시킨 방언, 최적화의 목적으로 그러한 행동을 버리는 방언에 동일한 이름이 부여되었으므로 서로 다르게 작동하는 것에 대해 이야기 할 때 혼동을 피하기는 어렵습니다 .
supercat

역사적으로 C는 유형을 가진 느슨한 연주를 허용하지 않는 Lisp 기계에서 실행되었습니다. 30 년 전의 많은 "예측 및 최적화 가능한 동작"은 VAX에서 BSD Unix 이외의 다른 곳에서는 작동하지 않았을 것입니다.
prosfilaes

@prosfilaes : 아마도 "1999 년부터 2009 년까지 사용 된 컴파일러의 99 %"가 더 정확할까요? 컴파일러에 다소 공격적인 정수 최적화 옵션이 있었더라도 바로 그 옵션이었습니다. 나는 1999 년 전에 컴파일러를 보았지만 몰랐을 int x,y,z;때 표현식 x*y > z이 1 또는 0을 반환하는 것 이외의 다른 작업을 수행하지 않거나 앨리어싱 위반이 영향을 미치는 곳을 보장하지 않는 모드는 없었 습니다. 컴파일러가 임의로 이전 값이나 새 값을 반환하게하는 것 이외의 것.
supercat

1
... 여기서 unsigned char"부터 온"유형을 구성하는 데 사용되는 값. 프로그램이 포인터를로 분해하려면 unsigned char[]16 진수 내용을 화면에 간략하게 표시 한 다음 포인터를 지우고 unsigned char[]나중에 키보드에서 16 진수를 받아들이고 포인터로 다시 복사 한 다음 해당 포인터를 역 참조하십시오 입력 한 숫자가 표시된 숫자와 일치하는 경우 동작이 잘 정의됩니다.
supercat

3

C에서는 그렇지 않습니다. 다른 언어 (예 : Lisp, Python)에는 동적 유형이 있지만 C는 정적으로 유형이 있습니다. 즉, 프로그램이 데이터를 올바르게 해석하기위한 유형이 문자, 정수 등인지 알아야한다는 것을 의미합니다.

일반적으로 컴파일러가이를 처리하므로, 잘못된 작업을 수행하면 컴파일 타임 오류 (또는 경고)가 발생합니다.


내가 이해하지 못하는 것은 컴퓨터가 변수의 값을 읽고 10001과 같은 주소를 int 또는 char 인 경우 어떻게 알 수 있는지입니다. anyprog.exe라는 프로그램을 클릭한다고 상상해보십시오. 코드가 즉시 실행되기 시작합니다. 이 exe 파일에 변수가 또는 char로 저장되어 있는지에 대한 정보가 포함되어 있습니까? –
user16307

1
@ user16307 기본적으로 아니요, 모든 정보가 완전히 손실됩니다. 정보가 없어도 작업을 제대로 수행 할 수 있도록 충분히 설계되는 것은 기계 코드에 달려 있습니다. 모든 컴퓨터는 주소에 8 비트의 행이 있다는 점에주의를 기울 10001입니다. 그것은 다음 중 하나입니다 귀하의 작업 또는 컴파일러의 직업, 경우 수동 기계 또는 어셈블리 코드를 작성하는 동안 같은 물건을 유지하기 위해, 따라.
Panzercrisis

1
동적 타이핑이 유형을 유지하는 유일한 이유는 아닙니다. Java는 정적으로 유형이 지정되지만 유형에 동적으로 반영 할 수 있기 때문에 유형을 유지해야합니다. 또한 런타임 다형성, 즉 런타임 유형을 기반으로하는 메소드 디스패치가 있으며 유형도 필요합니다. C ++은 메소드 디스패치 코드를 객체 (또는 클래스) 자체에 넣습니다. 따라서 어떤 의미에서는 유형이 필요하지 않습니다 (물론 vtable은 어떤 의미에서는 유형의 일부이므로 실제로는 유형 유지되지만 Java에서는 메소드 디스패치 코드가 중앙 집중화됩니다.
Jörg W Mittag

내가 쓴 질문에 "C 프로그램이 실행될 때"? 명령 코드 중 exe 파일에 간접적으로 저장되지 않고 결국 메모리에서 발생합니까? 나는 이것을 당신을 위해 다시 쓴다 : CPU가 레지스터에서 0x00000061을 찾아서 가져 오면; 콘솔 프로그램이 이것을 int가 아닌 문자로 출력한다고 가정합니다. 해당 exe 파일 (기계 / 이진 코드)에 0x00000061의 주소가 문자임을 알고 ASCII 테이블을 사용하여 문자로 변환하는 명령 코드가 있습니까? 그렇다면 char int 식별자가 바이너리에 간접적으로 있음을 의미합니까 ???
user16307

값이 0x61이고 char (즉, 'a')로 선언되고 값을 표시하기 위해 루틴을 호출하는 경우 해당 문자를 표시하기위한 시스템 호출이 있습니다. 이를 int로 선언하고 디스플레이 루틴을 호출하면 컴파일러는 0x61 (10 진수 97)을 ASCII 시퀀스 0x39, 0x37 ( '9', '7')로 변환하는 코드를 생성합니다. 결론 : 컴파일러는 코드를 다르게 취급한다는 것을 알고 있기 때문에 생성되는 코드가 다릅니다.
Mike Harris

3

당신은 구별해야 compiletime하고 runtime한 손에 code하고 data다른 한편으로.

기계의 관점에서 당신이 부르는 사이에 차이가 없다 code또는 instructions당신이 부르는는 data. 그것은 모두 숫자로 귀결됩니다. 그러나 우리가 부르는 일부 시퀀스는 우리가 code유용하다고 생각하는 것을 수행하고 다른 시퀀스 는 단순히 crash기계를 작동시킵니다.

CPU가 수행하는 작업은 간단한 4 단계 루프입니다.

  • 주어진 주소 에서 "데이터"를 가져옵니다
  • 명령어를 해독하십시오 (즉, 숫자를 "로 해석" instruction)
  • 효과적인 주소를 읽으십시오
  • 결과 실행 및 저장

이것을 명령주기 라고합니다 .

A와 4가 RAM 주소에 저장되어 있음을 읽었습니다. 그러나 a와 x는 어떻습니까?

a그리고 x프로그램이 변수의 "내용"을 찾을 수있는 주소에 대한 자리 표시 자 변수입니다. 따라서 변수 a가 사용될 때마다 효과적으로a 사용 된 내용의 주소가 있습니다.

가장 혼란스럽게도, 실행은 a가 char이고 x가 int라는 것을 어떻게 알 수 있습니까?

처형은 아무것도 모른다. 소개에서 말한 것에서 CPU는 데이터를 가져 와서이 데이터를 명령어로 해석합니다.

의 printf α- 함수는 그 결과 코드가 얼마나 특별한 메모리 세그먼트를 처리하는 올바른 지침을 제공 즉 당신이 그것으로두고있는 입력의 종류, "알고있다"하도록 설계되었습니다. 물론, 넌센스 출력을 생성 할 수 있습니다. 주소를 사용하면 "% s"와 함께 문자열이 저장되지 않은 printf()경우 0 ( \0)이 있는 임의의 메모리 위치에서만 넌센스 출력이 중지됩니다 .

프로그램의 진입 점도 마찬가지입니다. C64에서는 알려진 모든 주소에 (거의) 프로그램을 넣을 수있었습니다. 어셈블리 프로그램은 sys다음에 주소가 오는 명령으로 시작되었습니다 sys 49152. 어셈블러 코드를 넣는 일반적인 장소였습니다. 그러나 그래픽 데이터를로로드하는 것을 막을 수있는 것은 아무것도 없었으므로이 49152시점부터 "시작"한 후에 기계 충돌이 발생합니다. 이 경우 명령주기 는 "그래픽 데이터"를 읽고 "코드"(물론 의미가 없음)로 해석하려고 시작했습니다. 그 효과는 놀랍습니다.)

값이 RAM의 어딘가에 10011001로 저장되어 있다고 가정합니다. 코드를 실행하는 프로그램이라면이 10011001이 char인지 int인지 어떻게 알 수 있습니까?

"컨텍스트"-이전 및 다음 지침-데이터를 원하는 방식으로 처리하는 데 도움이됩니다. 머신 관점에서 볼 때 메모리 위치에는 차이가 없습니다. intchar에서 의미가 있습니다 만 어휘이다 compiletime; runtime(조립품 수준에서) 동안 char또는 이 없습니다 int.

내가 이해하지 못하는 것은 컴퓨터가 int이든 char이든 10001과 같은 주소에서 변수 값을 읽을 때 컴퓨터가 어떻게 알 수 있는지입니다.

컴퓨터 아무것도 모른다 . 프로그래머는 않습니다. 컴파일 된 코드는 컨텍스트 를 생성하여 인간에게 의미있는 결과를 생성하는 데 필요합니다.

이 실행 파일에 저장된 변수가 int 또는 char 유형인지에 대한 정보가 포함되어 있습니까?

아니오 . 정보, 그것이를인지 int또는이 char손실됩니다. 그러나 반면에, 컨텍스트 (데이터가 저장되는 메모리 위치를 처리하는 방법을 알려주는 명령)는 유지됩니다. 그래서 묵시적 예, "정보"입니다 묵시적으로 가능합니다.


컴파일 시간과 런타임의 좋은 차이점.
Michael Blackburn

2

이 논의를 C 언어로만 보자 .

언급 한 프로그램은 C와 같은 고급 언어로 작성되었습니다. 컴퓨터는 기계 언어 만 이해합니다. 보다 높은 수준의 언어는 프로그래머에게보다 인간 친화적 인 방식으로 논리를 표현할 수있는 기능을 제공하며, 그런 다음 마이크로 프로세서가 디코딩하고 실행할 수있는 기계 코드로 변환됩니다. 이제 당신이 언급 한 코드를 논의 해 보자.

char a = 'A';
int x = 4;

각 부분을 분석해 보자.

char / int 는 데이터 형식이라고합니다. 이들은 컴파일러에게 메모리를 할당하도록 지시합니다. 이 경우 char1 바이트와 int2 바이트가됩니다. (이 메모리 크기는 다시 마이크로 프로세서에 따라 다릅니다.)

a / x 는 식별자로 알려져 있습니다. 이제 RAM의 메모리 위치에 주어진 "사용자 친화적"이름을 말할 수 있습니다.

= 는 컴파일러에게 메모리 위치에 'A'를, 메모리 위치에 a4 를 저장하도록 지시합니다 x.

따라서 int / char 데이터 형식 식별자는 프로그램 실행 중 마이크로 프로세서가 아닌 컴파일러에서만 사용합니다. 따라서 메모리에 저장되지 않습니다.


ok int / char 데이터 형식 식별자는 메모리에 변수로 직접 저장되지 않지만 명령 코드 중 exe 파일에 간접적으로 저장되지 않고 결국 메모리에서 발생합니까? 나는 이것을 당신을 위해 다시 쓴다 : CPU가 레지스터에서 0x00000061을 찾아서 가져 오면; 콘솔 프로그램이 이것을 int가 아닌 문자로 출력한다고 가정합니다. 해당 exe 파일 (기계 / 이진 코드)에 0x00000061의 주소가 문자임을 알고 ASCII 테이블을 사용하여 문자로 변환하는 명령 코드가 있습니까? 그렇다면 char int 식별자가 바이너리에 간접적으로 있음을 의미합니까 ???
user16307

CPU에는 모든 숫자가 없습니다. 특정 예제의 경우 콘솔에서의 인쇄는 변수가 char 또는 int인지에 따라 달라지지 않습니다. 나는 프로그램이 실행될 때까지 높은 수준의 프로그램이 기계 언어로 변환되는 방법에 대한 세부 흐름으로 대답을 업데이트 할 것입니다.
prasad

2

내 대답은 다소 단순화되어 있으며 C 만 참조합니다.

아니요, 유형 정보는 프로그램에 저장되지 않습니다.

int또는 charCPU에 대한 유형 표시기가 아니 거나 ; 컴파일러에게만

컴파일러가 생성 한 exe int에는 변수가로 선언 된 경우 s 를 조작하기위한 지시 사항이 있습니다 int. 마찬가지로 변수가로 선언 된 char경우 exe에는을 조작하는 명령이 포함됩니다 char.

C에서 :

int main()
{
    int a = 65;
    char b = 'A';
    if(a == b)
    {
        printf("Well, what do you know. A char can equal an int.\n");
    }
    return 0;
}

이 프로그램은 인쇄됩니다 때문에, 그 메시지 char와는 int같은 값을 RAM에.

이제 and에 대한 printf출력 65을 관리 하는 방법이 궁금하다면 "형식 문자열" 에 값을 처리하는 방법을 지정 해야하기 때문 입니다 . (예를 들어, A와 값을 치료하는 것을 의미 하며 , 비록 어느 쪽이든 같은 값의 정수 값으로서 취급하는 것을 의미한다.)intAcharprintf
%cchar%d


2
누군가가를 사용하여 예제를 사용하기를 바랐습니다 printf. @OP : int a = 65; printf("%c", a)출력 'A'합니다. 왜? 프로세서는 신경 쓰지 않기 때문입니다. 여기에는 비트 만 있습니다. 프로그램은 프로세서에 65를 ( 'A'ASCII 의 값으로) 저장 a하고 문자를 출력 하도록 지시했습니다 . 왜? 상관 없기 때문입니다.
Cole Johnson

그러나 왜 C # 사례에서 일부 사람들은 여기에 이야기하지 않습니까? 나는 다른 사람들의 의견을 읽고 C #과 C ++에서 이야기 (데이터 유형에 대한 정보)가 다르고 CPU조차도 컴퓨팅을 수행하지 않는다고 말합니다. 그것에 대한 아이디어가 있습니까?
user16307

@ user16307 CPU가 컴퓨팅을 수행하지 않으면 프로그램이 실행되고 있지 않은 것입니다. :) C #에 관해서는 모르겠지만 대답도 적용됩니다. C ++의 경우 내 대답이 적용된다는 것을 알고 있습니다.
BenjiWiebe

0

가장 낮은 수준에서 실제 실제 CPU에는 유형이 전혀 없습니다 (부동 소수점 단위 무시). 비트 패턴. 컴퓨터는 비트 패턴을 매우 빠르게 조작하여 작동합니다.

그것이 모든 CPU가 할 수있는 전부입니다. int 또는 char과 같은 것은 없습니다.

x = 4 + 5

다음과 같이 실행됩니다.

  1. 레지스터 1에 00000100로드
  2. 레지스터 2에 00000101로드
  3. 레지스터 1을 레지스터 2에 추가하고 레지스터 1에 저장

iadd 명령어는 레지스터 1과 2가 정수인 것처럼 동작하는 하드웨어를 트리거합니다. 실제로 정수를 나타내지 않으면 나중에 모든 종류의 문제가 발생할 수 있습니다. 최상의 결과는 일반적으로 충돌입니다.

컴파일러는 소스에 주어진 유형에 따라 올바른 명령을 선택하지만 CPU에 의해 실행되는 실제 기계 코드에는 유형이 없습니다.

편집 : 실제 기계 코드는 실제로 4, 5 또는 정수를 언급하지 않습니다. 그것은 단지 두 개의 패턴의 비트이고, 두 개의 비트 패턴을 취하고 그것들을 정수라고 가정하고 그것들을 더하는 명령어입니다.


0

간단히 말해, 형식은 컴파일러가 생성하는 CPU 명령어로 인코딩됩니다.

정보의 유형 또는 크기에 대한 정보는 직접 저장되지 않지만 컴파일러는 이러한 변수의 값을 액세스, 수정 및 저장할 때이 정보를 추적합니다.

실행은 a가 char이고 x가 int임을 어떻게 알 수 있습니까?

그렇지 않지만 컴파일러가 기계 코드를 생성 할 때 알고 있습니다. int와는 char다른 크기의 수 있습니다. char이 바이트의 크기이고 int가 4 바이트 인 아키텍처에서 변수 x는 주소 10001이 아니라 10002, 10003 및 10004에 있습니다. 코드가 값을 xCPU 레지스터 에로드해야하는 경우 , 4 바이트로드 명령을 사용합니다. 문자를로드 할 때 명령을 사용하여 1 바이트를로드합니다.

두 지침 중 하나를 선택하는 방법은 무엇입니까? 컴파일러는 컴파일 중에 결정하며 메모리의 값을 검사 한 후에는 런타임에 수행되지 않습니다.

레지스터의 크기는 다를 수 있습니다. Intel x86 CPU에서 EAX의 너비는 32 비트이고 절반은 AX (16)이며 AX는 AH와 AL (8 비트)로 나뉩니다.

따라서 정수 (x86 CPU에서)를로드하려면 정수에 MOV 명령어를 사용하고, 문자에로드하려면 char에 MOV 명령을 사용합니다. 둘 다 MOV라고하지만 서로 다른 op 코드가 있습니다. 효과적으로 두 가지 다른 지침이됩니다. 변수의 유형은 사용할 명령어로 인코딩됩니다.

다른 작업에서도 마찬가지입니다. 피연산자의 크기에 따라, 부호가 있거나 부호가없는 경우에도 추가를 수행하기위한 많은 지시 사항이 있습니다. 가능한 다른 추가 사항을 나열하는 https://en.wikipedia.org/wiki/ADD_(x86_instruction) 를 참조 하십시오 .

값이 RAM의 어딘가에 10011001로 저장되어 있다고 가정합니다. 내가 코드를 실행하는 프로그램이라면이 10011001이 char인지 int인지 어떻게 알 수 있습니까?

첫째, char은 10011001이지만 int는 00000000 00000000 00000000 10011001입니다. 크기는 서로 다르기 때문에 (위에서 언급 한 것과 동일한 크기의 컴퓨터에서) 그러나 signed charvs 의 경우를 고려하십시오 unsigned char.

메모리 위치에 저장된 내용은 원하는대로 해석 할 수 있습니다. C 컴파일러의 책임 중 하나는 변수에서 저장하고 읽은 내용이 일관된 방식으로 수행되도록하는 것입니다. 따라서 프로그램은 메모리 위치에 저장된 내용을 알고있는 것이 아니라 항상 같은 종류의 내용을 읽고 쓸 것이라는 데 미리 동의합니다. (캐스팅 유형과 같은 것을 세지 않음).


그러나 왜 C # 사례에서 일부 사람들은 여기에 이야기하지 않습니까? 나는 다른 사람들의 의견을 읽고 C #과 C ++에서 이야기 (데이터 유형에 대한 정보)가 다르고 CPU조차도 컴퓨팅을 수행하지 않는다고 말합니다. 그것에 대한 아이디어가 있습니까?
user16307

0

그러나 왜 C # 사례에서 일부 사람들은 여기에 이야기하지 않습니까? 나는 다른 사람들의 의견을 읽고 C #과 C ++에서 이야기 (데이터 유형에 대한 정보)가 다르고 CPU조차도 컴퓨팅을 수행하지 않는다고 말합니다. 그것에 대한 아이디어가 있습니까?

C #과 같은 형식 검사 언어에서는 형식 검사가 컴파일러에서 수행됩니다. 벤지 코드는 다음과 같이 썼다.

int main()
{
    int a = 65;
    char b = 'A';
    if(a == b)
    {
        printf("Well, what do you know. A char can equal an int.\n");
    }
    return 0;
}

단순히 컴파일을 거부합니다. 마찬가지로 문자열과 정수를 곱하려고하면 (추가라고 말했지만 연산자 '+'에 문자열 연결이 과부하되어 작동 할 수 있습니다).

int a = 42;
string b = "Compilers are awesome.";
double[] c = a * b;

컴파일러는 문자열이 얼마나 키스했는지에 관계 없이이 C #에서 기계 코드 생성을 거부합니다.


-4

다른 답변은 기본적으로 모든 소비자 장치에 유형 정보가 저장되지 않는다는 점에서 정확합니다. 그러나 태그 된 아키텍처 를 사용하는 과거 (현재는 리서치 컨텍스트에서) 여러 하드웨어 디자인이있었습니다 . 데이터와 유형 (및 기타 정보도 모두)을 저장합니다. 이것들은 Lisp 기계를 가장 두드러지게 포함 할 것 입니다.

필자는 비슷한 것을 가진 객체 지향 프로그래밍을 위해 설계된 하드웨어 아키텍처에 대해 모호하게 기억하지만 지금은 그것을 찾을 수 없습니다.


3
질문은 구체적으로 Lisp가 아닌 C 언어를 참조하고 있으며 C 언어는 가변 메타 데이터를 저장 하지 않는다고 명시합니다 . C 구현이이를 수행하는 것이 가능하지만 표준은이를 금지하지 않기 때문에 실제로는 발생하지 않습니다. 질문과 관련된 예가있는 경우 특정 인용을 제공 하고 C 언어와 관련된 참조 제공하십시오 .

글쎄, 당신은 Lisp 머신을위한 C 컴파일러를 작성할 수 있지만, 오늘날이 시대에 Lisp 머신을 사용하는 사람은 아무도 없다. 그런데 객체 지향 아키텍처는 Rekursiv 였습니다 .
Nathan Ringo

2
이 답변이 도움이되지 않는다고 생각합니다. OP에 대한 현재의 이해 수준을 넘어서는 문제를 복잡하게 만듭니다. OP가 CPU + RAM의 기본 실행 모델을 이해하지 못하고 컴파일러가 기호 높은 수준의 소스를 실행 가능한 바이너리로 변환하는 방법을 분명히 알 수 있습니다. 태그 메모리, RTTI, Lisp 등은 제 생각에 그 질문자가 알아야 할 것 이상이며, 더 혼란스럽게 할 것입니다.
Andres F.

그러나 왜 C # 사례에서 일부 사람들은 여기에 이야기하지 않습니까? 나는 다른 사람들의 의견을 읽고 C #과 C ++에서 이야기 (데이터 유형에 대한 정보)가 다르고 CPU조차도 컴퓨팅을 수행하지 않는다고 말합니다. 그것에 대한 아이디어가 있습니까?
user16307
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.