강의를 들었지만, 아직 개념이 모호한 것이 있다. 이해하고 새롭게 알게된 내용 위주로 적었다. 내용상 부족한 부분이 있을 수 있어 참고용으로 보면 좋을 것 같다.

인터프리터 컴파일러를 쓴다는 차이가 있다.
파이썬은 0이나 1같은 정수형 하나 저장하는데, 항상 22바이트를 쓴다.
즉, 파이썬 같은 경우 임베디드나 작은 전자 기기에 들어 갈때, 바이트수가 크게 저장되면 낭비되는 메모리가 크다.
임베디드는 시스템 프로그래밍을 할때 메모리 최적화 성능이 저하됨을 막아야한다.(애초에 용량이 적다.) 그러므로 C언어를 사용하여 메모리를 효율적으로 조절한다.
어떤 아키텍쳐를 쓰냐에 따라 처리 바이트 수가 다르다.
C에서는 보통 stdio.h(표준 입출력)라는 int형 바이트를 고정 시키는 라이브러리를 사용한다.
바이트 고정 과정에서 C언어의 포인터가 하드웨어적으로 왜 중요한지 알려줄려한다. C언어는 하드웨어 친숙한 언어이다.
본격적으로 들어가기 앞서 C언어에 대해서 비유를 하자면 다음과 같다.
C언어는 칼과 같다. 잘쓰면 매우 유용하고 최적화면에서 좋지만, 잘못쓰면 손실을 일으킬수 있다.
먼저, C언어는 사용자가 실수할 수 있는데, 에러를 안띄운다.(파이썬과 다름)
밑에와 같은 경우 원래는 오류가 발생하기 때문에 x == y로 수정해야한다.
if (x = y)
foo();
만약 y = x / * p 이면 결과가 어떨까? x를 포인터 값으로 나눈 값이 y가 될까?
→ 주석 처리가 된다. /* 가 주석처리 코드이기 때문이다.
이렇게 C언어는 엄청 섬세한 언어이다. 띄어쓰기, 괄호에 따라 결과가 완전히 달라질 수 있는 위험성이 있다.
if (flag & FLAG ! = 0) 일때, 본인이 원하는 것이 flag & FLAG가 0이 아닐 때라고 할 수 있다.
그러나 사실 if (flag & (FLAG ! = 0)) 의 의미와 같다. ! =의 연산자가 & 연산자보다 우선 순위가 높기 때문이다. 그러므로 FLAG가 0이 아니면을 먼저 실행한다.
원하는 의도에 따라 괄호를 꼭! 쓰자.
p++의 경우도 (p++)와 같다. 사용자 의도와 다르게 (*p)++ 가 아니라는 의미이다.(가정)
while (c = getc(in) ! = EOF)
put (c, out);
해당 코드에서 의도와 다르게 c가 EOF가 아닐때를 먼저 해석할 수 있다. 그러므로 의도에 따라 괄호를 잘 쳐야한다.
if (x[i] > big);
big = x[i];
첫번째 줄과 두번째 줄과 같은 세트인데, 세미콜론을 잘못 찍으므로써 각각 실행하여 비정상적인 결과가 나올 수 있다. C언어는 위 상황에 에러를 표출하지 않으므로 이러한 사실 때문에 C언어가 코딩하기 어렵다는 것이다.
→ 그럼 실수를 안하기 위해서는 어떻게 하는가? 무조건 if문에 브레이스{} 를 치는 것이 안정적이다.
++ 또 다른 경우로 break; 문을 빼먹어서 case문을 조건에 따라가 아닌 모두 호출하게 될 수 있다.
if (x == 0)
if ( y == 0) error;
else {
z= x+y;
}
일 때, else문은 어디 있는 if문의 else문일까?
→ else문은 첫 if에 해당되는 else가 아니라 두번째 if문의 else이다. 왜냐하면, C언어는 if문 바로 뒤의 else문을 실행하기 떄문이다.
그러므로 브레이스{} 를 꼭 써야한다.
a < b && c < d 도 C언어에서 어렵다는데 못들었다.(클로바 노트 확인)
i = 0;
while (i < n)
y[i] = x[i++];
위와 같은 코드는 이해하기 어렵지 않다. 그러나 아래의 경우 난해하다.
i = 0;
while (i < n)
y[i++] = x[i];
i = 0;
while (i < n) {
y[i] = x[i];
i++;
}
위에 두 의미는 같다. 그런데 결과는 컴파일러마다 해석하는 방식이 달라서 컴파일러마다는 결과가 다르게 나온다.
메크로는 함수가 아니다. 메크로는 의미를 확장시킨다. 대체하는 의미와 같다.
#define max(a,b) ((a)>(b)?(a):(b))
앞으로 메크로를 많이 써야될텐데 주의 깊게 쓰자.
int m = 10;
int * p = &m;
= (int *) malloc(20);
포인터가 메모리 힙에 저장한다. malloc을 통해 20바이트짜리 메모리 공간을 만들어 준다.
(int * )을 통해 캐스팅을 해준다.
위 와 같을때, p가 100이면 p+1라고 하면 int 바이트 수가 4이므로 104라고 하는게 맞는가?
만약에, char이면 1바이트 long은 8바이트 를 가져온다. 그러나 64비트에서만 해당되므로 운영체제마다 다르다.
그래서 정답은 sizeof(int)라고 답하는 것이 정답이다.
위에는 노드 구조체
Node *p = & m;
어쎔션? 디어쎔션? 이라는 개념이 있던데 더 공부해 봐야겠다.
size_t: 워드 타이즈로 해당 시스템에서 어떤 객체나 값이 포함할 수 있는 최대 크기의 데이터를 표현하는 타입으로 반드시 unsigned 형으로 나타낸다.
포인터의 구조체와 타입 변형법에 대해서 좀 더 공부해야겠다.
p = 100
q = 100
일때, 타입이 다르면 어떻게 되는가?
char *q 에서 q+1이다. 근데 q는 char 자료 데이터 라고 가정하면 q+1은 101의 느낌이다. 근데 sizeof하는게 맞는거 같다. 포인터는 자료형 사이즈에 따라 가져오는 바이트 수가 다르기 때문이다.
C언어가 생각보다 (세미콜론, 괄호, 자료형 등) 고려할 사항들이 엄청 많음을 알게되는 시간이었다.
디테일이 높은 사람이 유리할 것 같다. 앞으로 RB트리, AVL 트리를 공부하면서 C언어에 대해 더 잘 알아보는 시간을 가져보겠다.