C 수업 들으면서 가졌던 궁금한 점 몇 가지?

2013-03-18 00:17

C 수업 들으면서 C의 한계도 조금 느꼈고, 자바와 왜 지금과 같이 발전했는지도 조금은 느낄 수 있었다. C 수업 들으면서 궁금했던 점 몇 가지 공유해 본다. 각각에 대해 대충 궁금한 점은 풀렸는데 다른 사람들은 어떻게 생각할까?

  • C는 프로젝트에 main function을 하나 이상 가질 수 없다. 물론 빌드에서 제외하면 가능하다. 하지만 빌드하는 전체 소스에서 main function은 하나여야 한다. 하지만 java는 각 클래스마다 하나의 main method를 가질 수 있다. 왜 C는 하나 밖에 가질 수 없을까?
  • C는 함수명이 같으면서 인자가 다른 함수를 가질 수 없다. 즉, 자바의 오버라이딩이 되지 않는다고 한다. C를 설계할 때 왜 이렇게 설계했을까?
  • C의 type에서 int는 4byte, long도 4byte, long long은 8byte이다. 과거에는 int가 2byte였다고 한다. 그 이유는 뭘까? 그리고 이후에 왜 4byte로 변경되었을까?
    • 짐작하건데 16bit에서 32bit로 넘어 오면서 기본 int 단위가 4byte가 됐으리라 생각한다. 그럼 64bit로 발전해 가고 있는 지금은 어떻게 되는거지? int는 8byte, long은 4byte, long long은 8byte가 되는건가? 그럼 long long long이 나와서 16byte 처리를 해줘야 되는 건가? 설마 int와 long의 byte가 역전되지는 않겠지? 신입사원이 연봉이 올라가면서 기존 2,3년차 선배의 연봉을 역전하는 사태처럼 말이야. 암튼 64bit로 발전해 가고 있는 시점에 C는 어떻게 발전해 갈 것인지 궁금하네. 다른 언어도 마찬가지고...
  • https://www.facebook.com/javajigi/posts/229720613835567 에서도 이와 관련해 좋은 답변들을 많이 주셨네요.

4개의 의견 from SLiPP

2013-03-18 00:46

1]번은 제가 보기에는 JVM에서 돌아가는 것과 실제 바이너리의 차이 때문이기도 합니다. 실제 실행파일이 실행될 때, entry point가 있는데, 이것들이 최초 파라매터를 처리해서 실제 실행파일의 entrypoint를 호출하게 되고, 이게 보통 main으로 지정되어 있어서 그렇습니다. 자바도 jvm 파라매터로 시작할 main을 지정해줘야 하는것과 비슷해지는 거죠.

2] C가 최초에 단순히 어셈블러로 거의 1:1 매칭을 해주기 위해서가 아닐까 싶습니다. C++만 되어도 오버라이딩이 들어가는 것을 보면, 최대한 단순하게 만들고 싶었던것으로 보입니다.

3] 짐작하신대로 CPU 내부의 Register 크기에 따라서 변경하는 것이 원칙이었는데, 32bit에서 int가 4byte로 너무 많이 사용되어서, 64bit로 가면서 int를 8바이트로 바꿔버리면 문제가 생긴다고 생각해서 long 이 8바이트로 변경되었습니다. 다만 원칙적으로는 typedef를 이용해서 16bit, 32bit, 64bit를 기본 형태가 아니라 uint16_t, uint32_t 형태로 정의해서 사용하는 것이 좋은 방법입니다. ㅎㅎㅎ

2013-03-18 07:27

1) Name space가 없기 때문이지요. C에선 모든 함수가 전역 함수라서... main 뿐 아니라 모든 함수가 단 하나만 있어야 하죠. 그리고 최초의 OOP 언어라는 시뮬라 67에서부터 객체는 모두 독립된 단위 프로그램입니다. 의존성이 있어서 복잡할지는 몰라도 독립적으로 돌아갈 수 있어야 하죠. 2) 강대명님 설명에 100% 동의. asm 코딩 좀 해본 사람은 C 코드를 보면 asm 코드가 보입니다. 쉽게 말해서 C는 고급 어셈블러죠. 3) int = word 입니다.

2013-03-18 08:54
  1. 컴파일 시간에 시작점이 정해져야 하기 때문에 그렇습니다. 자바는 런타임에 시작점이 정해지죠. 그래서 어디서 시작할지 JVM에게 클래스 이름을 알려줍니다.
  2. 이 경우는 "오버로딩"이 안되는 것입니다. 어셈블리와 섞어 쓰기 위해서 그렇게 정한 것 같습니다. C++은 오버로딩을 지원하는데, 함수 이름과 파라메터 타입으로 컴파일러가 참조에 사용되는 이름을 새로 만들어주는 네임 맹글링이라는 방법을 사용합니다. 당연히 네임 맹글링 규칙이 복잡할 것이고 그러면 어셈블리에서 호출할 때 호출해야할 C 함수의 이름을 알기 어렵겠죠. 또 하나는 초기에는 맹글링을 하면 이름이 길어지는데 이것도 문제가 됐을 것 같네요. 긴 이름을 사용하면 아무래도 컴파일 할 때 메모리가 더 필요하겠죠. 초기 유닉스는 함수 이름의 길이가 5자로 제한되어 있었습니다. 유닉스 파일 생성 함수 creat ( http://linux.about.com/library/cmd/blcmdl2_creat.htm ) 에 e가 없는 이유입니다.
  3. 기본적으로 int의 크기와 포인터의 크기는 기반 아키텍처의 워드(아키텍처의 기본 처리 단위)의 크기를 따라가기 때문에 그렇습니다. long은 기본적으로 int의 두 배입니다. 제 기억에 long은 16비트일 때 32비트였는데 32비트로 넘어오면서 그대로 32비트로 남은 것 같네요... 64비트로 넘어올 때 문제도 비슷한 문제가 생깁니다. 널리 쓰이는 int의 크기가 갑자기 2배로 널어나면 공간을 너무 많이 차지하게 됩니다. 숫자 표현하는데 64비트가 필요한 경우도 별로 없구요. 그래서 몇가지 모델이 경쟁했는데 현재 널리 쓰이는 모델은 LP64로 int는 32비트고 long과 포인터는 64비트입니다. http://en.wikipedia.org/wiki/64-bit_computing
2013-03-18 23:07

C언어는 정적인 언어이죠.

함수 호출시 어떤 함수가 호출될 것인지를 컴파일 타임에서 정확히 알 수가 있죠. 반대로 생각하면, 컴파일 타임에서 정확히 알려고 한 이유는 C언어가 만들어질때 상황으로는 어쩔수 없는 현실이었다고 생각합니다.

앞의 몇분이 말씀한것처럼 C언어는 어셈블리를 대체하기 위한 고급언어였기 때문이죠. fupfin님 말씀대로 고급어셈블리라고 말할수 있을 것 같습니다. (그나저나 멋진 표현이네요. 고급어셈블리.)

오버로딩의 경우는 컴파일시 정확히 함수를 매칭해주는 것이 아니라 실행시에 메소드를 찾으려는 방법이죠. 컴파일 타임에서는 해당 메소드가 정확히 어떤 메소드가 호출될지 알수가 없죠.

예를 들어 print(Object a) 라는 메소드가 있고 print(A a) 라고 있을 경우

public void execute(B b){ print(b); }

위의 소스가 컴파일 타임에서는 어떤 print메소드가 호출될수 없는 거죠.

즉 C언어는 어셈블리를 대체하는 목적으로 만들어진 것이라면 시뮬라부터 시작된 객체지향 언어는 객체지향 특성을 구현하기 위하여 오버로딩, 오버라이딩이란 개념이 등장한거죠. 그러면서..... C언어와는 다르게 정적으론 이런 개념을 구현할 수없고 동적으로만 구현할 수 밖에 없었던 거죠.

그래서 C언어가 자바같은 객체지향 언어보다 함수 호출속도가 빠른것이죠.

의견 추가하기

연관태그

← 목록으로