일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 다트 언어
- 구조체
- Houdini
- c#
- HTML
- Algorithm
- C언어 포인터
- jupyter
- c언어
- 도커
- jupyter lab
- 플러터
- 유니티
- Python
- Data Structure
- vim
- C++
- c# winform
- Flutter
- 포인터
- gitlab
- 깃
- dart 언어
- C# delegate
- c# 윈폼
- docker
- Unity
- git
- github
- c# 추상 클래스
- Today
- Total
목록Programming/C (54)
nomad-programmer
비트 필드는 구조체와 흡사하나 구조체는 바이트 단위로 멤버를 사용할 수있지만 비트 필드는 비트 단위로 멤버를 사용할 수 있다. 그래서 많은 상태를 저장하거나 비트별로 제어해야 하는 경우에 비트 필드를 사용한다. 비트 필드의 멤버는 unsigned형(unsigned int형)과 int형의 멤버를 가질 수 있고 비트 필드 변수의 크기는 int형 크기와 같은 4바이트(32비트)이다. signed와 unsigned는 char, short, int, long 등의 자료형 앞에 사용되어 부호가 있는 정수와 부호가 없는 정수를 나타내는 자료형으로 사용되며 signed는 보통 생략하여 사용한다. 비트 필드처럼 unsigned가 단독으로 사용되면 unsigned int형을 간략하게 표현한 형태이다. 다음은 비트 필드를 ..
#include #include #include void Menu(){ puts(""); puts("1. 문자열 입력"); puts("2. 문자열 삭제"); puts("3. 문자열 출력"); puts("4. 문자열 정렬"); puts("5. 종료"); } void ClearBuffer(){ while(getchar() != '\n'); } char* Input(){ char temp[100]; char* str; printf("100자 이하의 문자 입력: "); scanf("%s", temp); ClearBuffer(); str = (char*)malloc(sizeof(char)*strlen(temp)+1); strcpy(str, temp); return str; } void Remove(char* st..
다음은 9 X 9 배열을 포인터 변수 int (*arr)[3][3]에 저장하여 메모리에 사용하는 응용 예제이다. #include #include typedef int (*ARR9)[9]; typedef int (*ARR33)[3][3]; // int (*arr)[9] void PrintArray(ARR9 arr){ for(int i=0; i
다음은 쉬프트 비트 연산을 이용하여 4바이트를 2바이트로 분할하는 예제이다. #include #define LOWORD(x) ((unsigned short)x) #define HIWORD(x) ((unsigned int)x >> 16 & 0xFFFF) int main(int argc, const char * argv[]) { int n = 0x44434241; printf("%x\n", LOWORD(n)); printf("%x\n", HIWORD(n)); return 0; } */ 결과 4241 4443 */ 다음은 1바이트의 두 정수를 2바이트로 2바이트의 두 정수를 4바이트로 결합하는 예제이다. #include typedef unsigned char BYTE; typedef unsigned short..
XOR를 이용하면 손쉽게 데이터를 교환할 수 있다. 예를 들어, 연산 결과를 F라 하면 F^a 는 b가 되고 F^b는 a가 된다. 다음의 예제를 살펴보자. #include // 매크로를 이용한 데이터 교환 #define SWAP(a, b) { a^=b; b^=a; a^=b; } // 함수를 이용한 데이터 교환 void Swap(int* a, int* b){ *a ^= *b; *b ^= *a; *a ^= *b; } int main(int argc, const char * argv[]) { int a = 10; int b = 20; printf("a: %d, b: %d\n", a, b); Swap(&a, &b); printf("a: %d, b: %d\n", a, b); SWAP(a, b); printf("a..
가변 인자가 특정 자료형이 아닌 여러 자료형이 전달된다면 첫 번째 인자에 인자의 개수와 자료형의 정보까지도 전달해야 한다. 그래서 printf()와 같은 함수는 첫 번째 인자를 문자열로 만들고 '%c, %d, %s' 등을 사용하여 자료형과 인자의 개수를 전달한다. '%c' 이면 문자를 인자로 전달하고, '%d' 이면 정수를 인자로 전달하고, '%s' 이면 문자열의 시작 주소를 인자로 전달한다. #include #include #include void MiniPrintf(char *fmt, ...){ int i = 0; va_list ap; va_start(ap, fmt); while(fmt[i] != '\0'){ if(fmt[i] != '%'){ putchar(fmt[i]); } else{ switch(..
포인터의 선언과 그 포인터의 의미 단순한 포인터 선언 타입 의미 int* np; 1차원 포인터 int형의 주소를 저장 int** npp; 2차원 포인터 int형의 2차원 주소를 저장 단순한 배열 선언 타입 의미 int arr[3]; 1차원 배열 int형 값들을 저장 int arr[2][3]; 2차원 배열 int형 값들을 저장 배열과 포인터 선언 타입 의미 int* arr[3]; 1차원 포인터 배열 int형 주소값 3개를 저장 int (*arr)[3]; 2차원 배열 포인터 포인터 연산시, int형 3개를 건너뛰는 2차원 배열 포인터이며 2차원 배열의 주소를 저장 int (*arr)[2][3]; 3차원 배열 포인터 포인터 연산시, int형 2x3개를 건너뛰는 3차원 배열 포인터이며 3차원 배열의 주소를 저장..
구조체는 멤버 변수들의 크기에 따라 구조체의 크기가 결정되지만 구조체의 크기를 예측하여 동적 메모리를 생성하거나, 파일에 저장하거나, 네트워크에 전송하는 등의 작업을 하면 예상했던 구조체의 크기와 달라 오류가 발생하는 경우가 있다. 이유는 컴파일러가 프로그램 성능을 향상시키기 위하여 일정한 크기 형태로 구조체 변수들을 주소에 매핑시키기 때문이다. 매핑 : 패딩 바이트를 추가한 바이트 정렬 그래서 구조체의 크기는 모든 멤버 변수 크기의 합과 다를 수 있다. 다음은 구조체의 크기를 출력하는 예이다. #include typedef struct { char c; int n; } _data1; typedef struct { short s; int n; } _data2; typedef struct { int n1;..
컴퓨터는 메모리에 0과 1만을 저장할 수 있으므로 컴퓨터는 문자를 표현하기 위해 각각의 문자와 대응하는 숫자를 정의하고 있는데 이것을 문자 코드라 한다. 대표적인 문자 코드가 ASCII 코드이며 영문자, 숫자, 기호 등을 ANSI 라는 표준화 기구에서 정의한 7비트 문자 코드(0~127) 이다. 그래서 ASCII코드를 ANSI코드라고도 한다. 정확히 말하면 ANSI코드는 ASCII 코드에 1비트를 더확장(128~255)하여 정의한 8비트 문자 코드이다. C언어에서는 ASCII코드를 사용하여 문자를 표현한다. 사실 ASCII코드 외에도 많은 문자 코드가 존재하며 문화, 지역, 나라, 운영체제, 성능 등 여러 가지 요소들에 따라 문자 코드의 정의와 사용이 다랄져야 하기 때문에 문자 코드는 상당히 복잡하다. ..
[] 연산자를 사용한 int형 배열 요소 접근 #include int main() { int i_arr[6] = { 10, 20, 30, 40, 50, 60 }; printf("%d %d %d %d %d %d\n", i_arr[0], i_arr[1], i_arr[2], i_arr[3], i_arr[4], i_arr[5]); printf("%d %d %d %d %d %d\n", (&i_arr[2])[-2], (&i_arr[2])[-1], (&i_arr[2])[0], (&i_arr[2])[1], (&i_arr[2])[2], (&i_arr[2])[3]); return 0; } // 결과 /* 10 20 30 40 50 60 10 20 30 40 50 60 */ &i_arr[2] 를 중심으로 +, - 연산을 하..
c_arr 변수의 메모리 주소가 12ff7c 라고 가정한다. #include void main() { char c_arr[2][2] = {'A', 'B', 'C', 'D'}; printf("%x %x %x %x\n", &c_arr[0][0], c_arr, c_arr[0], c_arr[1]); printf("%x %x %x %x\n", &c_arr[0][0]+1, c_arr+1, c_arr[0]+1, c_arr[1]+1); } // 결과 /* 12ff7c 12ff7c 12ff7c 12ff7e 12ff7d 12ff7e 12ff7d 12ff7f */ &c_arr[0][0]는 char형 1바이트의 주소를 의미하며 여기에 +1을 한 것은 1바이트의 주소를 덧셈하라는 의미이다. c_arr은 char형 배열 2*2의..
c_arr 변수가 0x12ff78의 주소를 사용한다고 가정한다. 형 변환 연산자를 사용하여 상수를 char형 주소로 변환한 후 * 연산자를 사용하여 char형 1바이트에 접근한다. #include int main() { char c_arr[5] = { 'A', 'B', 'C', 'D', 'E' }; printf("%c %c %c %c %c\n", *(char*)0x12ff78, *(char*)0x12ff79, *(char*)0x12ff7a, *(char*)0x12ff7b, *(char*)0x12ff7c); return 0; } // 결과 /* A B C D E */ 정수 0x12ff78 ~ 0x12ff7c에 (char*) 형 변환 연산을 사용하여 char형 주소로 변환한 후 *를 붙여 메모리 자체(값)를..
#include void main() } int n = 100; // 12ff7c printf("%x\n", &n); printf("%d %d\n", n, *&n); } // 결과 /* 12ff7c 100 100 */ int형 변수 n의 주소가 12ff7c라면 시작 주소는 &n이고, 이 주소에 *를 붙여 *&n 은 변수 n의 4바이트 크기의 메모리를 의미하게 된다. 값은 100이다. *&n 은 n과 같은 메모리의 이름이라고 해도 무방하다. 주소 12ff7c ~ 12ff7f의 4바이트 메모리 이름은 n과 *&n 이다. & 연산자와 * 연산자는 상반된 개념을 가지고 있다. & 연산자는 변수(메모리 이름) 앞에서만 사용하고 * 연산자는 주소 앞에서만 사용한다. 예를 들어 *N과 가이 사용하면 N은 주소이고 &..
형 변환은 암묵적인 형 변환과 명시적인 형 변환이 있다. 암묵적인 형 변환은 컴파일러에 의해 자동으로 변환되고 명시적인 형 변환은 () 연산자를 사용하여 변환한다. char형 변수(1바이트)를 int형 변수(4바이트)에 대입하는 경우 암묵적인 형 변환(자동 형 변환)이 수행된다. 작은 자료형을 큰 자료형으로 변환하는 경우 컴파일러는 자동으로 형 변환을 수행한다. 에를 들어 (char)는 char형으로 자료형을 변환하라는 의미이고 (char*)는 char형 주소로 자료형을 변환하라는 의미이다. #include int main() { char c = 'A'; printf("%x %x %x\n", &c, (char*)&c, (int*)&c); printf("%x %x %x\n", &c+1, (char*)&c+..
C로 프로그램을 만든다면 아마 성능과 규모 확장에 대해 신경이 많이 쓰일 것이다. 코드를 컴파일하기 위해 gcc를 사용한다면 다음과 같은 다른 GNU 도구들을 살펴볼 필요가 있다. gdb gdb(GNU Project Debugger)를 사용하면 컴파일된 프로그램을 실행하는 동안 코드를 조사할 수 있다. 성가신 버그를 추적하고 싶을 때 이 도구는 매우 유용하다. gdb는 명령행에서 직접 사용할 수 있고 Xcode나 여타 다른 통합 개발 환경에서 사용할 수 있다. https://www.gnu.org/software/gdb/ GDB: The GNU Project Debugger GDB: The GNU Project Debugger [bugs] [GDB Maintainers] [contributing] [cur..
코드는 언제나 테스트되어야 한다. 테스트를 자동화하면 개발이 훨씬 수월해진다. C 프로그래머가 사용할 수 있는 테스트 프레임워크는 매우 많다. 그 중 AceUnit이라고 불리는 프레임워크도 있다. http://aceunit.sourceforge.net/ AceUnit AceUnit Latest release version: aceunit-0.12.0 What is AceUnit? AceUnit (Advanced C and Embedded Unit): a comfortable C code unit test framework. AceUnit is JUnit 4.x style, easy, modular and flexible. AceUnit can be used in resource constraint env..
sizeof 연산자는 데이터가 얼마나 많은 메모리 공간을 차지하는지 알려준다. 그런데 어떤 "범위의 값"을 가질 수 있는지 알고 싶으면 어떻게 해야 할까? 예를 들어 컴퓨터에서 int형이 4바이트를 차지한다고 알고 있을 때, 저장할 수 있는 가장 큰 값은 무엇일까? 아니면 가장 작은 음의 값은? 차지하는 바이트 수로 크기를 계산할 수 있다만 어려울 수 있다. 대신 "limits.h" 헤더 파일에 정의된 매크로를 사용할 수 있다. long형 변수가 저장할 수 있는 가장 큰 값은 LONG_MAX 매크로에 정의되어 있고 short형 변수가 저장할 수 있는 가장 작은 값은 SHRT_MIN을 사용하면 알 수 있다. 다음 코드는 int와 short가 저장할 수 있는 범위를 보여준다. #include #include..
카운터처럼 작동하는 함수를 다음과 같이 작성할 수 있다. int count = 0; int counter() { return count++; } 이 코드에 문제가 무엇일까? 이 코드는 count라는 전역 변수를 사용한다. 이 변수가 전역 변수이므로 어떤 함수도 count의 값을 바꿀 수 있다. 큰 프로그램을 만들 때는 엉뚱하게 전역 변수를 바꾸는 실수를 범할 수 있기 때문에 너무 많은 전역 변수를 사용하는 건 좋지 않다. 다행히도 C는 전역 메모리에 저장하지만 특정 함수나 파일만 사용할 수 있는 변수도 만들 수 있다. // static 키워드를 사용하면 counter() 함수가 종료되어도 이 값이 유지된다. int counter() { // count는 전역 변수지만, 이 함수만 이 변수에 접근할 수 있..
뮤텍스는 공유 데이터를 보호하는 락이다. 여러 스레드가 한 변수를 동시에 갱신하면, 결과는 예측할 수 없는 상태가 된다. #include #include #include #include #include // 2백만개 int number = 2000000; void error(char *msg) { fprintf(stderr, "%s: %s", msg, strerror(errno)); exit(1); } void *decay_number(void *t) { for (int i = 0; i < 100000; i++) { number = number - 1; } return NULL; } int main(int argc, char *argv[]) { void *result; int ste; pthread_t ..
프로세스가 언제나 답이 되지는 못한다. 다음과 같은 이유가 있기 때문이다. 프로세스를 생성하려면 시간이 걸린다. 어떤 컴퓨터는 프로세스를 새로 만들려면 시간이 꽤 오래 걸린다. 아주 오랜 시간은 아니지만 약간의 시간이 걸린다. 추가로 수행할 일이 0.02~0.03초 걸리더라도 매번 프로세스를 새로 만드는 건 그리 효율적이지 않다. 프로세스는 데이터를 공유하기가 까다롭다. 자식 프로세스를 만들면 부모 프로세스의 모든 데이터를 완전히 복사하게 된다. 그러나 데이터를 복사했으므로, 자식 프로세스가 부모 프로세스에 데이터를 다시 보내려면 파이프와 같은 메커니즘을 사용해야 한다. 프로세스는 그저 어렵기만 하다. 프로세스를 생성하려면 많은 코드를 짜야 한다. 그러면 프로그램은 길어지고, 지저분해지기 마련이다. 스레..