이번엔 C 언어에 대한 복습이다.
Python, C, C++ 이 3대장 중에 제일 까다로운 녀석...
당연히 예전에 배웠던 것 죄다 까먹은 상태인데, 얼른 복습해봅시다!!
1. Hello C
일단 기본적으로, VS Code를 사용할 것이며 Terminal의 경우 Git-bash를 사용한다.
Git-bash는 Linux 계열의 터미널인데, 뭐.. 자세한 특징은 너무 deep한 내용 같아서 스킵!
How to Compile?
gcc(Compiler를 쓰겠습니다. 번역 해주세요~) hello.c(해당 파일을) -o(Object파일로 만들어 주세요~) hello(요 이름으로)
위와 같은 구조로 compile을 진행한다.
이제 ERROR가 없다면 hello 라는 010101로 구성된 실행 가능해진 object 파일이 생긴다.
2. Compiler ( C ) , Interpreter ( Python ) 비교
Interpreter는 py 파일을 바로 실행하지만, Compiler는 바로 실행이 불가해서 C 파일을 compile 하는 1 step이 추가된다.
Interpreter는 prototype을 만들 때 편하다. 코드를 그 때 그 때 실행하면서 어디가 error인지 확인하기 편하다.
반면, C는 전체 Code가 통번역 되지 않으면 실행자체가 되지 않는다. 100줄의 코드를 짜도, 5줄의 코드만 실행하는 것이 불가하다. 파이썬은 가능한데..ㅠ
대신에 Compiler는 전체를 번역하고 문제가 없을 때 실행이 되는 것이기 때문에 개발단계에선 번거롭지만, 전체 프로그램을 실행만 하면 되는 exe 파일로 이미 번역이 되었기에, 실행속도가 굉장히 빠르다.
개발단계 - 파이썬 / 실제 Serious 한 작동파일 - C
C programming은 항상 main 이라는 함수 안에서 시작과 끝이 된다. 그 안에서 CALL이 되고 종료!
Python에서 Global namespace와 같은 역할을 한다고 생각하자.
C는 Python과 달리 identation , 띄어쓰기는 아무 의미가 없다. 대신 괄호로 구분이 필요하다.
C는 명령어가 하나 입력될 때마다 세미콜론 ";" 을 입력해야 C가 거기까지 인지를 한다.
Output function 이름이 다르다. Py : print / C : printf
C 에선 preprocessor Macro 를 쓴다. Header file을 먼저 선언해주는 것 like import in Py
Header에 #을 이용해 특정 영어표현을 상수로 고정 시킬 수도 있다.
3. Variables in C
C를 굉장히 Low LV 언어라고 생각했는데, 생각보다 high LV 언어이다.
일례로, 예전엔 variable 선언할 때마다 컴퓨터에 어디에 어떻게 주소를 assign 할 지 일일히 다 지정을 해줘야 했는데 C언어는 거기까진 한 필요가 없다는 것
우리는 우리 입맛에 맞게 코딩을 하면, 알아서 컴퓨터가 이해하는 01010101언어로 주소를 변환해 variable을 저장한다.
User는 단순히 Symbolic한 Name만 지정해주면 되는 것! 이 얼마나 편한가....(ㅠㅠ) -> 이것들을 도와주는 것이 Compiler이다.
User는 Algorithm의 Logical Flow에 집중을 하면 된다.
앞서 말한 것들을 Compiler가 잘 도와줄 수 있게 User가 제공해야 하는 것! 아래 것들을 Variable을 선언할 때 제공해줘야한다. Python에선 Data type 같은 거까지 입력 필요 없었는데, C에선 Compiler를 위해 이런 것들을 제공해야 한다ㅠㅠ
a. Name / b. Data Type / c. Scope(이 변수가 언제 살아나서 언제 죽는지)
- a. 이름은 잘 지어라 , Pre Defined Constant의 경우 ALL대문자, 여러 단어가 합쳐진 Var의 경우 AbcDef 또는 Abc_def 이렇게 대문자 or Underscore로 구분한다.
- b. Compiler는 이 Variable의 type을 보고 memory를 얼마나 할당할 지 결정한다. 그리고 int+int / float + float 이런 것도 기계LV에선 많이 다르다. 정확한 Data type 구분이 필요하다.
대표적인 dtype : int / float / char / double / _Bool 아으 구찮지만 dtype을 다 써줘야 한다. Variable 선언할 때!
요걸 바탕으로 각 dtype 마다 몇 Byte가 기본적으로 할당되는 지 확인이 가능하다.
(sizeof 함수는 Byte 단위로 출력이된다. ex: 8 -> 32bit)
#include "stdio.h"
int main(void){
int intVar;
printf("int size if %d\n",sizeof(intVar));
char chVar;
printf("cgar size if %d\n",sizeof(chVar));
float fVar;
printf("float size if %d\n",sizeof(fVar));
double dVar;
printf("double size if %d\n",sizeof(dVar));
_Bool bVar;
printf("bool size if %d\n",sizeof(bVar));
return 0;
}
/*
int size if 4
cgar size if 1
float size if 4
double size if 8
bool size if 1
*/
※ double 의 경우, 좀 더 긴 정수/소수점을 표현해 정확히 실수를 표현하고 싶을 때 8Byte = 64bit 으로 표현하는 Dtype이다.
※ 사실 Bool의 경우 1bit로도 충분하지만 memory를 기본적으로 byte단위로 구분하기에 1byte 할당
※ _Bool의 경우 T/F 를 1/0으로 표현한다. 그래서 bool true/false로 표현하려면 #include <stdbool.h> 必( + Py와 달리 True가 아니고 true 요렇게 소문자로 표현)
중요한 것은, Variable이 한 번 선언되면 DataType은 바뀔 수 없다는 것.
1~255는 8bit로도 충분히 표현이 가능하다.(2^8 = 256) 그런데 int type은 기본적으로 4byte = 32bit라 메모리가 크게 잡혀있어서 이런 경우엔 Memory를 아끼고 싶어진다. 또는 반대로 Memory가 기본 보다 더 필요할 수도 있다.
이런 것들을 customized 하게 컨트롤 하는 방법?
- Long / Short (주식 선물거래 아님)
int intVar;
printf("int size if %d\n",sizeof(intVar));
short int intVar1;
printf("s int size if %d\n",sizeof(intVar1));
long int intVar2;
printf("l int size if %d\n",sizeof(intVar2));
long long int intVar3;
printf("ll int size if %d\n",sizeof(intVar3));
int size if 4
s int size if 2
l int size if 4
ll int size if 8
double dVar;
printf("double size if %d\n",sizeof(dVar));
long double dVar2;
printf("double size if %d\n",sizeof(dVar2));
//double size if 8
double size if 16
다른 type은 long / short 안 되고 int는 다 되며 double은 long 까지만 허락해준다.
long int 해봤자 매모리 size할당은 같고, long long 까지 해줘야 2배인 8byte할당이다. short int는 0.5배인 2byte!
double의 경우 원래 8byte인데 long 하나 붙쳐주니까 무려 16byte(128bit)까지 뻥튀기가 된다 ㄷㄷ
unsigned int uintVar;
printf("uintVar size is %d\n",sizeof(uintVar));
//uintVar size is 4
Unsigned 는 양수 or 음수의 int만 쓸 때 부호를 나타내주는 1bit 마져 아끼고 싶을 경우에 쓰는 dtype이다. 메모리 크기는 기존 int와 같이 4byte이다.
- c. Scope
main 함수 안에서 선언된 것들은 Local Var, main 밖에 선언된 것들은 Global Var -> 항상 살아있다.
즉, 특정 중괄호{ } 안에서 선언된 Var은 그 괄호가 끝나면 사라진다 -> Local Var( Global Var은 어떤 중괄호에도 귀속되어 있지 않다.)
Varialbe initialize는 반드시 하는 게 좋다. 안 하고 dtype만 쓰면, Garbage value가 할당된다.
바꿀 수 없는 상수를 하고 싶으면 const double pi = 3.14; 이런 식으로 할 수 있다.
(#DEFINE과의 차이? Scope 가 다르다. #DEFINE은 전체 Global Scope, const는 해당 var이 선언된 중괄호 내로 scope가 한정된다.)
4. Operators in C
전반적으로 python과 비슷하다 +, - , * , / .... bitwise의 경우 아래를 참고
x++ , x-- 할 때 주의?점
int x =1;
int y;
int z;
printf("x:%d,y:%d,z:%d\n",x,y,z);
y = x ++;
printf("x:%d,y:%d,z:%d\n",x,y,z);
z = ++y;
printf("x:%d,y:%d,z:%d\n",x,y,z);
int d = z + ++y; // x = 2, y = 3, z=2, d=5
printf("x:%d,y:%d,z:%d,d:%d\n",x,y,z,d);
/*
x:1,y:16,z:0
x:2,y:1,z:0
x:2,y:2,z:2
x:2,y:3,z:2,d:5
*/
원칙은 간단하니, 헷갈리지 말자!
5. Memory in C
Compiler 관점에서 Memory를 한 번 뜯어보기.
Memory 주소는 f f f f 이렇게 Hexa(16)로 4개를 표현한다. x0000 ( lowest ) -> xffff( highest )
4개의 f로 주소를 표현하니까 주소는 총 16bit로 표현하는 것이다.(사실 우리의 컴퓨터는 32 or 64bit이긴 함)
(0~15까지 16개를 표현하는 게 Hexa, f 하나이고 그 f 하나는 2^4 니까 4bit으로 표현이 가능하다. 그런 f가 4개니까 총 16bit = 2byte 로 주소를 표시한다.)
아래로 갈수록 메모리 주소가 크다 ( FFFF )
내 코드정보는 주소가 낮은 program text에 저장, 중괄호 밖에서 선언한 Global Var 등 global data는 주소가 낮은 작은 쪽에 저장한다.(Global data section)
요 놈들은 프로그램이 실행되는 내내 바뀌지 않는 것들이다.
반면, Local Variable은 주소가 큰 Run-time stack에 저장이 된다. 주소가 큰 부분은 프로그램이 실행됨에 따라서 늘었다 줄었다 한다. 어떤 함수를 실행하냐에 따라서 새로운 Local Var이 생겼다 없어졌다 하니까!
이름에 있는 Stack ( Last in First out ) -> 가장 나중에 들어온 놈이 가장 먼저 나간다. 이것이 Local Var을 관리하는데 편한 Type이다. 그리고 Runtime => 프로그램이 실행되는 중에 변할 수 있다는 뜻
R4 는 Global Var의 시작점으로 딱 박혀있는 레지스터이다. 이놈에서 몇을 더해서 어떤 global var을 가져올 지 판단한다.
R5( = Frame Pointer ) 는 현재 동작 중인 함수의 가장 높은 부분 주소값이 담겨져 있는 곳이다. 그 함수가 Local Var을 많이 쓰면 쓸수록 Runtime Stack 내에서 함수의 memory 주소가 점점 위로 늘어나고(메모리 주소 값이 작아지는 방향) 그 끝지점은 R6( = Stack Pointer)로 표시한다. 즉, R6 ~ R5 로 이 함수가 Memory 주소를 어디서부터 어디까지 사용중인지 확인 할 수 있다.
Global Variable의 경우 위에서 부터 내려온다.(Memory 주소가 커지는 방향으로) 즉 지금 inGlobal은 하나라서 낮은 주소 값에 offset 0으로 저장되어 있는데, Global var이 추가될수록 offset이 +1 , +2 이렇게 커지는 방향으로 Var들이 Global 영역에 저장된다. ( 주소값이 증가되는 방향 )
반면, Local var은 반대방향이다. 첫 놈이 주소 Offset이 0이면, 그 이후 선언된 놈들은 Offset이 줄어든다.(-1,-2..) 위 Memory 그림에서 윗 방향으로! Memory 주소 값이 줄어드는 방향으로!
- E. O. D -
'SW 만학도 > C' 카테고리의 다른 글
Review 3 - Pointer & Array in C (0) | 2024.07.04 |
---|---|
Review 2 - Control Structures / Functions in C (0) | 2024.07.03 |
5. Pointer - Motivation in C (1) | 2024.03.26 |
4. Function ic C - Grammer (1) | 2024.03.25 |
3. Control Structures in C - Condition ( While / For Loop ) (0) | 2024.03.25 |