[C/C++] 헷갈리기 쉬운 기초 문법들
▪ freopen
- 파일을 stdin, stdout으로 출력할 때에 freopen을 사용하면 된다.
1 2 | freopen("input.txt", "r", stdin); freopen("output.txt", "w+", stdout); |
▪ 출력 flushing
- 샘플 입력을 처리하는 도중에 timeout이 발생하면 그 앞의 출력까지도 하지 못하는 상황이 발생하는데 이를 막기 위해 출력을 바로 바로 하는 옵션이다.
1 | setbuf(stdout, NULL); |
- 문제는 출력이 많은 경우 이러한 방식으로 출력하면 printf()를 수행하는 데만도 많은 시간을 쏟게 된다. 이럴 경우 아래와 같이 옵션을 고쳐줄 수 있다.
1 2 | char buffer[32767]; setvbuf(stdout, buffer, _IOFBF, sizeof(buffer)); |
▪ MAX_INT, MIN_INT
- 가장 작은 정수인 MIN_INT는 부호 bit만 1이고 나머지는 0이다.
1 | const long long MIN_INT = 1 << 63; |
- 가장 큰 정수인 MAX_INT는 부호 bit만 0이고 나머지는 1이다.
1 | const long long MAX_INT = ~(1 << 63); |
- 참고로 이는 음수 표현을 "2의 보수법"을 사용하기 때문인데 "1의 보수법"이 양수값의 모든 bit를 반전시키고 "2의 보수법"은 반전시킨 bit에 1을 더하는 방식이다. 1이 0000…0001이니까 "1의 보수법"으로는 -1이 1111…1110이고 "2의 보수법"으로는 1111…1111이다.
- 1의 보수법 대신 2의 보수법을 사용하는 이유는 0이 하나밖에 없고 덧셈을 할 때 한번에 가능하기 때문이다. 예를 들어, -1과 1이 더해져 0이되는 과정을 보자. 1의 보수법에서는 0000…0001 + 1111…1110 = 1111…1111이 되고 여기에 end around carry가 더해져 결국 0000…0000이 되는데 2의 보수법에서는 이것을 무시하므로 0000…0001 + 1111…1111 = 0000…0000이 되어버린다.
▪ System time 출력
1 2 3 4 5 6 7 8 9 10 11 12 | #include <windows.h> LARGE_INTEGER li1, li2, liFreq; QueryPerformanceFrequency(&liFreq); QueryPerformanceCounter(&li1); // do write your code QueryPerformanceCounter(&li2); printf("time: %ld / %ld\n", (double)(li2.QuadPart - li1.QuadPart), (double)liFreq.QuadPart); |
▪ Structure 선언법
1 2 3 4 | typedef struct { int val; struct node* next; } node; |
- typedef를 활용하고, self pointer는 struct라는 키워드도 함께 붙여야 한다.
▪ Heap allocation and deallocation
1 2 | node* aNode = (node*) malloc(sizeof(node)); free(aNode); |
- malloc과 free를 사용하면 된다.
▪ 배열의 생성과 접근
- []와 *는 자유롭게 호환된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> void test(char* input) { printf("%s\n", input); printf("%c\n", input[3]); // 다시 index로 접근해도 무방하다 } int main() { char string[] = "Hello World"; // 선언 방식 유심히 기억하기 test(string); // char* 형으로 넘겨줘도 된다. return 0; } |
- heap allocation시에는 포인터로만 선언 가능하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h> #include <stdlib.h> void test(int* input) { printf("%d\n", input[3]); // 다시 index로 접근해도 무방하다 } int main() { int* ptr = (int*) malloc(sizeof(int)*5); // 선언 방식 유심히 기억하기 for (i = 0; i < 5; i++) { ptr[i] = i; // index로 접근해도 무방하다 } test(ptr); return 0; } |
▪ 함수 포인터
- 함수 이름 앞에 *가 붙는 형태, return type은 일반 함수처럼 있고 input param은 type만 있고 이름은 없는 형태이다.
1 | int (*FuncPtr) (int, int); |
- 함수의 대입은 리턴 타입과 input param의 타입이 일치하는 함수에만 가능하다.
1 2 3 4 5 6 | int add (int first, int second) { ... } FuncPtr = add; FuncPtr = &add; // this is also allowed |
- typedef를 이용하여 타입 선언을 재활용할 수 있다.
1 2 3 4 | typedef int (*FuncPtr)(int, int); FuncPtr func = NULL; func = add; |
- 전형적인 사용 패턴
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <stdio.h> typedef int (*CalcFuncPtr)(int, int); int plus (int first, int second) { return first + second; } int minus (int first, int second) { return first - second; } int calculator (int first, int second, CalcFuncPtr func) { return func(first, second); } int main(int argc, char** argv) { CalcFuncPtr calc = NULL; int a, b; char op; int result = 0; scanf ("%d %c %d", &a, &op, &b); switch (op) { case '+': calc = plus; break; case '-': calc = minus; break; } result = calculator (a, b, calc); printf ("result : %d", result); return 0; } |