C에서 변수의 데이터 유형을 언급해야하는 이유


19

일반적으로 C에서는 변수 선언의 데이터 유형을 컴퓨터에 알려야합니다. 예를 들어 다음 프로그램에서 두 개의 부동 소수점 숫자 X와 Y의 합을 인쇄하려고합니다.

#include<stdio.h>
main()
{
  float X=5.2;
  float Y=5.1;
  float Z;
  Z=Y+X;
  printf("%f",Z);

}

컴파일러에게 변수 X의 유형을 알려 주어야했습니다.

  • 컴파일러 X가 자체 유형을 결정할 수 없습니까 ?

예, 이렇게하면 가능합니다.

#define X 5.2

이제 컴파일러에게 다음과 X같은 유형을 알려주지 않고 프로그램을 작성할 수 있습니다 .

#include<stdio.h>
#define X 5.2
main()
{
  float Y=5.1;
  float Z;
  Z=Y+X;
  printf("%f",Z);

}  

따라서 C 언어에는 자체적으로 데이터 유형을 결정할 수있는 기능이 있습니다. 필자의 경우 Xfloat 유형 이라고 결정했습니다 .

  • main ()에서 무언가를 선언 할 때 왜 데이터 유형을 언급해야합니까? 왜 컴파일러는 자체에 변수의 데이터 유형 판별 할 수 main()는에서와 같이가 #define.

14
실제로이 두 프로그램은 서로 다른 출력을 줄 수 있다는 점에서 동일하지 않습니다! 5.2A는 double최초 프로그램으로 이중 리터럴 반올림하므로 float정밀도 제 발사 5.1 위로의 이중 표현하는 동안, 다음, 수레로 추가 double하고에 추가 double하여 값 5.2 double첨가, 다음 에 그 계산 결과를 반올림 float정밀도 . 반올림이 다른 위치에서 발생하기 때문에 결과가 다를 수 있습니다. 이것은 동일한 프로그램의 동작에 영향을 미치는 변수 유형에 대한 하나의 예일뿐입니다.

12
당신이 할 때 #define X 5.2, X그것은 문자 그대로와 전처리 할 대체되도록 상수, 변수가 아니라 5.2당신이 언급 어디서나 X. 을 재 할당 할 수 없습니다 X.
scriptin

16
참고로, 이것은 축복과 저주입니다. 한편으로 컴파일러가 실제로 당신을 위해 그것을 할 수있을 때 몇 개의 문자를 입력해야합니다 (C ++ auto은 실제로 원하는 것을 수행합니다). 반면에, 코드가하는 일을 알고 있다고 생각하고 실제로 다른 것을 입력했다면, 이와 같은 정적 타이핑은 큰 문제가되기 전에 일찍 오류를 잡을 것입니다. 정적 타이핑, 유형 유추, 동적 타이핑 등 모든 언어가 균형을 이룹니다. 일부 작업의 경우 추가 타이핑이 실제로 가치가 있습니다. 다른 사람들에게는 낭비입니다.
Cort Ammon-복원 모니카

Ocaml 및 / 또는 Haskell에 대해 알아보십시오. 유형 추론 능력에 만족하실 것입니다.
바 실레 Starynkevitch

답변:


46

변수 선언을 #defines와 비교하고 있습니다. 올바르지 않습니다. 을 사용하면 #define식별자와 소스 코드 스 니펫 간의 매핑을 만듭니다. C 프리 프로세서는 문자 그대로 해당 식별자를 제공된 스 니펫으로 대체합니다. 쓰기

#define FOO 40 + 2
int foos = FOO + FOO * FOO;

컴파일러와 쓰기와 같은 결과

int foos = 40 + 2 + 40 + 2 * 40 + 2;

자동 복사 및 붙여 넣기로 생각하십시오.

또한 일반 변수는 재 할당 #define할 수 있지만로 만든 매크로는 재 할당 할 수는 없습니다 #define. FOO = 7"rvalues"에 할당 할 수 없기 때문에 식은 컴파일러 오류 40 + 2 = 7가됩니다. 불법입니다.

그렇다면 왜 타입이 필요한가? 일부 언어는 분명히 유형을 제거합니다. 이는 특히 스크립팅 언어에서 일반적입니다. 그러나 일반적으로 변수에는 고정 유형이 없지만 값이있는 "동적 타이핑"이라는 것이 있습니다. 이것은 훨씬 유연하지만 성능도 떨어집니다. C는 성능을 좋아하므로 매우 간단하고 효율적인 변수 개념이 있습니다.

“스택”이라는 메모리가 많이 있습니다. 각 지역 변수는 스택의 영역에 해당합니다. 이제 문제는이 영역의 길이가 몇 바이트입니까? C에서 각 유형에는을 통해 쿼리 할 수있는 잘 정의 된 크기가 있습니다 sizeof(type). 컴파일러는 스택에서 올바른 공간을 확보 할 수 있도록 각 변수의 유형을 알아야합니다.

#define타입 주석으로 생성 된 상수가 필요 하지 않습니까? 그것들은 스택에 저장되지 않습니다. 대신, #define복사 및 붙여 넣기보다 약간 유지 관리 가능한 방식으로 재사용 가능한 소스 코드 스 니펫을 만듭니다. 예컨대 소스 코드의 리터럴 "foo"또는 42.87특별 지시로 어느 인라인 컴파일러에 의해 저장되거나 생성 된 이진 데이터의 분리 부에있다.

그러나 리터럴에는 유형이 있습니다. 문자열 리터럴은입니다 char *. 42int더 짧은 유형 (좁은 변환)에도 사용할 수 있습니다. 42.8될 것입니다 double. 리터럴을하고 다른 유형을 원한다면 (예 만드는 , 또는 을 , 당신은 접미사를 사용할 수 있습니다) - 편지를 문자 그대로 한 후 그 변경 방법 컴파일러 취급 문자가. 우리의 경우, 또는 라고 말할 수 있습니다 .42.8float42unsigned long int42.8f42ul

C와 같이 일부 언어에는 정적 입력이 있지만 유형 주석은 선택 사항입니다. ML, Haskell, Scala, C #, C ++ 11 및 Go가 그 예입니다. 어떻게 작동합니까? 마법? 아니요, 이것을“유형 추론”이라고합니다. C # 및 Go에서 컴파일러는 할당의 오른쪽을보고 해당 유형을 추론합니다. 오른쪽이와 같은 리터럴 인 경우 이것은 매우 간단합니다 42ul. 그렇다면 변수의 유형이 무엇인지 분명합니다. 다른 언어에는 변수 사용 방법을 고려한보다 복잡한 알고리즘도 있습니다. 당신이 경우에 예는 x/2다음 x문자열이 될 수 없지만 몇 가지 숫자 유형이 있어야합니다.


설명해 주셔서 감사합니다. 내가 이해하는 것은 변수 유형 (로컬 또는 전역)을 선언 할 때 실제로 컴파일러에게 스택에서 해당 변수에 대해 얼마나 많은 공간을 예약 해야하는지 알려주는 것입니다. 반면에 #define우리는 이진 코드로 직접 변환되는 상수를 가지고 있지만 길이는 얼마든지 메모리에 그대로 저장됩니다.
user106313

2
@ user31782-그렇지 않습니다. 변수를 선언하면 형식은 컴파일러에게 변수의 속성을 알려줍니다. 이러한 속성 중 하나는 크기입니다. 다른 속성에는 값을 나타내는 방법과 해당 값에 대해 수행 할 수있는 작업이 포함됩니다.
Pete Becker

@PeteBecker 그렇다면 컴파일러는 다른 속성을 #define X 5.2어떻게 알 수 있습니까?
user106313

1
잘못된 유형을 전달하여 printf정의되지 않은 동작을 호출 했기 때문 입니다. 내 컴퓨터에서 코드 조각은 매번 다른 값을 인쇄하고, Ideone 에서는 0을 인쇄 한 후 충돌합니다.
Matteo Italia

4
@ user31782 - 호는 "나는 모든 데이터 유형에 관련된 모든 작업을 수행 할 수 있습니다 것 같다" X*Y경우 유효하지 않습니다 XY포인터가 있지만 괜찮아 경우들이있는 거 int의; *X경우 유효하지 않은 X입니다 int,하지만 포인터의 경우는 괜찮습니다.
Pete Becker

4

두 번째 예에서 X는 절대 부동 소수점이 아닙니다. 매크로라고하며 소스에서 정의 된 매크로 값 'X'를 값으로 바꿉니다. #define에서 읽을 수있는 기사는 여기에 있습니다 .

제공된 코드의 경우 컴파일하기 전에 전처리 기가 코드를 변경합니다

Z=Y+X;

Z=Y+5.2;

그것이 컴파일되는 것입니다.

즉, 해당 '값'을 다음과 같은 코드로 바꿀 수도 있습니다.

#define X sqrt(Y)

또는

#define X Y

3
그것은 단지 variadic macro가 아니라 macro라고 불립니다. variadic 매크로는 가변 개수의 인수를 취하는 매크로입니다 (예 :) #define FOO(...) { __VA_ARGS__ }.
hvd

2
나의 나쁜 :) 해결됩니다
제임스 스넬

1

짧은 대답은 하드웨어의 히스토리 / 표현으로 인해 C 유형이 필요하다는 것입니다.

역사 : C는 1970 년대 초에 개발되었으며 시스템 프로그래밍을위한 언어로 사용되었습니다. 코드는 이상적으로 빠르며 하드웨어 기능을 최대한 활용합니다.

컴파일 타임에 타입을 유추 할 수는 있었지만 이미 컴파일 시간이 느려질 것입니다 ( XKCD의 '컴파일'만화를 참조하십시오 . 이는 C가 게시 된 후 10 년 이상 'hello world'에 적용되었습니다 ). 런타임시 유추 유형은 시스템 프로그래밍의 목표에 맞지 않았을 것입니다. 런타임 추론에는 추가 런타임 라이브러리가 필요합니다. C는 첫 PC보다 오래 전에왔다. 어느 쪽이 256 RAM을 가지고 있습니다. 기가 바이트 또는 메가 바이트가 아니라 킬로바이트입니다.

귀하의 예에서 유형을 생략하면

   X=5.2;
   Y=5.1;

   Z=Y+X;

그러면 컴파일러는 X & Y가 float이고 Z를 동일하게 만들었다는 것을 행복하게 해결할 수있었습니다. 사실, 현대의 컴파일러는 X & Y가 필요하지 않으며 Z를 10.3으로 설정하기 만하면됩니다.

계산이 함수 안에 포함되어 있다고 가정합니다. 함수 작성기는 하드웨어에 대한 지식 또는 해결중인 문제점을 사용하려고 할 수 있습니다.

float보다 double이 더 적합할까요? 더 많은 메모리를 사용하고 느리지 만 결과의 정확도는 더 높습니다.

float에서 int로 변환하는 데 비용이 들지 않지만 소수는 중요하지 않기 때문에 함수의 반환 값이 int (또는 long) 일 수 있습니다.

float + float가 오버플로되지 않도록 반환 값을 두 배로 만들 수도 있습니다.

이러한 모든 질문은 오늘날 작성된 대부분의 코드에서 무의미 해 보이지만 C가 제작 될 때 매우 중요했습니다.


1
예를 들어, 타입 선언이 선택 사항이 아닌 이유를 설명하지 않기 때문에 프로그래머가 명시 적으로 선언하거나 컴파일러가이를 유추하도록 선택할 수 있습니다.
gnat

1
실제로 그것은 @gnat하지 않습니다. 나는 텍스트를 수정했지만 그 당시에는 아무런 의미가 없었습니다. 도메인 C는 실제로 1 바이트, 2 바이트 또는 4 바이트 또는 문자열 또는 단어 내에 5 비트로 17을 저장하기로 결정하기 위해 설계되었습니다.
itj

0

C는 형식 유추가 없습니다 (컴파일러가 변수 유형을 추측 할 때 호출되는 방식). 1970 년대 초 에 개발되었습니다

많은 최신 언어에는 유형 (루비, 자바 스크립트, 파이썬 등)을 지정하지 않고 변수를 사용할 수있는 시스템이 있습니다.


12
언급 한 언어 (Ruby, JS, Python) 중 어느 것도 언어 기능으로 유형 유추를 가지고 있지 않지만 구현에서는 효율성을 높이기 위해 언어 유추를 사용할 수 있습니다. 대신 값에는 유형이 있지만 변수 또는 다른 표현식에는없는 동적 입력 을 사용 합니다.
amon

2
JS는하지 않습니다 수 있도록 당신이 생략 유형을 - 단지 당신이 무엇이든지 선언 할 수 없습니다입니다. 값이 변수가 아닌 유형 (예 : trueis boolean) 인 동적 타이핑을 사용 합니다 (예 : var x모든 유형의 값을 포함 할 수 있음). 또한, C에서 출시되기 10 년 전에는 문제가되는 간단한 사례에 대한 유형 유추가 있을 수 있습니다.
scriptin

2
그렇다고 진술을 허위로 만들지는 않습니다 (무엇을 강요하기 위해 허용해야합니다). 기존의 유형 추론은 C의 유형 시스템이 역사적 맥락의 결과라는 사실을 변경하지 않습니다 (특별히 언급 된 철학적 추론 또는 기술적 한계와 반대)
Tristan Burnside

2
C만큼이나 오래되었던 ML이 형식 유추를 가지고 있다는 점을 고려하면 "구식이다"는 좋은 설명이 아닙니다. C를 사용하고 개발 한 상황 (컴파일러를 위해 매우 작은 설치 공간을 요구하는 소형 기계)이 더 가능성이 높습니다. Haskell, ML, heck C # 에는 형식 유추가있는 언어의 일부 예 대신 동적 입력 언어를 언급 한 이유가 무엇인지 모릅니다 . 더 이상 모호한 기능은 아닙니다.
Voo

2
@ 브래드 변수 이름의 첫 번째 문자가 있기 때문에 포트란은 좋은 예가 아니다 이다 사용하지 않는 유형 선언 implicit none이 경우 당신은 해야한다 유형을 선언합니다.
dmckee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.