일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 구조체
- c# winform
- git
- Flutter
- vim
- Data Structure
- c언어
- C언어 포인터
- Houdini
- Algorithm
- c#
- docker
- jupyter lab
- c# 윈폼
- dart 언어
- 포인터
- 유니티
- C# delegate
- C++
- 플러터
- Unity
- Python
- gitlab
- HTML
- 도커
- 다트 언어
- github
- c# 추상 클래스
- 깃
- jupyter
- Today
- Total
목록분류 전체보기 (507)
nomad-programmer
운영체제는 시그널로 프로그램을 제어한다. 시그널은 단지 정수형의 짧은 메시지일 뿐이다. 시그널이 도착하면 프로세스는 하던 일을 멈추고 시그널을 처리해야 한다. 프로세스는 시그널과 시그널 처리기라는 함수를 대응시키는 시그널 매핑 테이블을 살펴본다. 인터럽트 시그널에 대한 기본 시그널 처리기는 단지 exit() 함수를 호출한다. 시그널을 잡아 직접 정의한 코드 실행 때론 프로그램을 인터럽트 걸 때 직접 정의한 코드를 실행하고 싶을 것이다. 예를 들어 프로세스가 열린 파일이나 네트워크 연결을 갖고 있으면 프로그램을 종료하기 전에 리소스를 닫고 정리하고 싶을 것이다. 이럴때 "sigaction" 을 사용하면 코드를 실행하고 명령시킬 수 있다. sigaction은 함수 랩퍼이다. sigaction은 함수에 대한 ..
자식 프로세스가 생성하는 데이터를 실시간으로 읽는 방법 pipe() 함수는 데이터 스트림 두 개를 연다. # 데이터 스트림 0 표준 입력 1 표준 출력 2 표준 에러 3 파이프의 읽는 쪽 (ex: fd[0]) 4 파이프의 쓰는 쪽 (ex: fd[1]) 자식 프로세스가 부모 프로세스에 데이터를 보내야 하므로, 자식 프로세스의 표준 출력과 부모 프로세스의 표준 입력에 연결된 파이프가 필요하다. 파이프는 pipe() 함수로 생성한다. 이 함수는 연결된 두 스트림을 만들어 테이블에 추가한다. 한쪽 스트림에 쓴 데이터는 다른 쪽 스트림에서 바로 읽을 수 있다. pipe() 함수가 디스크립터 테이블에 두 항목을 만들 때, 디스크립터들을 항목이 두 개 있는 배열에 저장한다. // 디스크립터들이 배열에 저장됨 int ..
파일 디스크립터는 데이터 스트림을 나타내는 숫자이다. 데이터 스트림은 말 그대로 프로세스로 들어가고 나오는 데이터의 흐름이다. 표준 입력, 출력, 에러에 대한 데이터 스트림이 있으며, 파일이나 네트워크 연결과 같은 데이터 스트림도 더 만들 수 있다. 프로세스의 출력을 리다이렉션하면 데이터를 보낼 곳을 바꿀 수 있다. 따라서 표준 출력이 하면 대신 파일에 데이터를 보낼 수 있다. 모든 프로세스는 스택과 힙 데이터 공간 외에도 자신이 실행하는 프로그램을 포함하고 있다. 그런데 표준 출력과 같은 데이터 스트림이 어디에 연결되는지 어딘가에 기록해놓아야 한다. 각 데이터 스트림은 파일 디스크립터(File Descriptor)에 의해 표현되는데, 프로그램에서는 단지 숫자로 나타난다. 프로세스는 파일 디스크립터와 이..
exec() 함수를 호출하면 새로운 프로그램을 실행해 현재 함수를 대체한다. 그러면 원래 프로그램은 곧바로 종료된다. fork()는 프로세스를 복제한다. fork()는 현재 프로세스의 완전한 사본을 만든다. 새로 만든 사본은 똑같은 프로그램을 똑같은 위치에서 실행한다. 똑같은 변수를 갖고 있고 변수 안에 들어 있는 값도 똑같다. 단 하나 차이점이라면 사본 프로세스의 PID가 원래 프로세스와 다르다는 것이다. 원래 프로세스는 "부모 프로세스"라고 하며, 새로 생성된 사본은 "자식 프로세스"라고 부른다. 리눅스 및 맥과는 달리 윈도우는 기본적으로 fork()를 지원하지 않는다. 프로세스는 누가 부모이고 누가 자식인지 알 수 있는 방법이 필요하다. 따라서 fork() 함수는 자식 프로세스에는 0을, 부모 프로..
errno 변수는 errno.h 헤더 파일에 정의된 전역 변수이다. 이 변수에는 다음과 같이 표준적으로 정의된 에러 값이 저장된다. EPERM=1 허용되지 않은 연산 ENOENT=2 지정한 파일이나 디렉토리가 없다 ESRCH=3 지정한 프로세스가 없다 이 값으로 errno 변수를 검사할 수 있다. 아니면 string.h 헤더 파일에 정의도니 strerror() 함수를 사용해 표준 에러 메시지를 만들 수 있다. // strerror() 함수는 에러 번호를 메시지로 변환한다. puts(strerror(errno)); 따라서 시스템이 실행하려는 프로그램을 찾지 못하면 errno 변수를 ENOENT로 설정하며, 이 번호는 다음과 같은 표준 에러 메시지에 대응된다. No such file or directory ..
system() 함수를 호출하면 운영체제는 명령 문자열을 해독해 어떤 프로그램을 어떻게 실행할지 결정해야 한다. 여기에는 문제가 발생한다. 운영체제가 문자열을 해독해야 한다는 점이다. system() 함수는 치명적인 단점을 가지고 있다. 이 함수를 사용하기 쉽지만 보안이 허술하다. 예를 들어, echo ' ' >> reports.log 명령을 내리는데 누군가 다음과 같은 명령을 입력하면 어떻게 될까? echo ' ' && ls / && echo '' >> reports.log 명령행에서 실행할 명령을 문자열 안에 삽입(Injection)하면 프로그램은 입력된 내용이 무엇이든 그대로 실행한다. 이런 모호함을 제거하고 어떤 프로그램을 실행하려는지 운영체제에 정확히 알려주도록해야 한다. "exec()" 함수는..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/bfsHTi/btqEY0s6OhO/JskKA0PW38SwxIMzsfGJ41/img.png)
동적 라이브러리를 사용하면 실행 시에 코드를 바꾸기 쉽다. 프로그램을 다시 컴파일할 필요 없이 애플리케이션을 갱신할 수 있다.만약 여러 프로그램이 똑같은 코드를 공유하고 있다면 모든 프로그램을 한꺼번에 모두 갱신할 수 있다. 정적 라이브러리와 동적 라이브러리 중 어떤 것이 더 좋은가? 상황에 따라 다르다. 정적 라이브러리는 컴퓨터 간에 이동하기 쉽게 더 작고 빠른 실행 파일을 만든다. 동적 라이브러리를 사용하면 실행 시에 프로그램의 환경을 더 많이 바꿀 수 있다. 오브젝트 파일 혹은 정적 라이브러리 파일을 링크하고 빌드하면 정적 프로그램이 된다. 별개로 있던 여러 오브젝트 코드로 하나의 실행 파일을 만들고나면, 프로그램을 새로 빌드하지 않고서는 들어간 코드를 바꿀 방법이 없다는 이야기다. 프로그램은 그저..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/0Fcyy/btqEWtJ4X4S/JsqI81JCdgWtIxT6kuRCHK/img.png)
오브젝트 파일(*.o)들을 정적 라이브러리 파일(*.a, *.lib)로 만들어서 사용하면 좋은 점 오브젝트 파일에 존재하는 함수를 하나라도 사용한다면, 빌드할 때 오브젝트 파일의 모든 코드를 가져와 하나의 파일로 빌드된다. 하지만 오브젝트 파일들을 정적 라이브러리로 만들게되면, 사용하고 있는 오브젝트 코드만 가져와 하나의 파일로 빌드된다. 즉, 정적 라이브러리가 아닌 오브젝트 파일을 쓰게 된다면 사용하지도 않는 오브젝트 코드들도 모두 함께 빌드되어버린다. 때문에 파일의 크기가 커지고 느려진다. 오브젝트 파일이 아닌 정적 라이브러리 파일을 쓰도록 하자. 먼저 오브젝트 파일을 생성한다. gcc -I test_code.c encrypt.o checksum.o -o test_code 이런식으로 프로그램을 컴파일..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/bVQRrR/btqEVX45o0n/jSOtkBJzHct5PXDxn1kzaK/img.png)
xrandr --listmonitors 위의 명령으로 현재 연결되어있는 모니터들의 id를 볼 수 있다. 본인은 위와 같은 모니터로 셋팅하여 사용중이다. 즉, 왼쪽의 모니터는 세로로 돌려 사용중이다. 허나 현재 우분투 20.04 LTS 를 사용중인데 재부팅만하면 아래와 같은 그림의 배열로 리셋이 되어버린다. 그렇다고 매번 nvidia-setting을 열어 다시 재배열하기는 너무 귀찮은 일이다. 그래서 xrandr 명령을 이용하기로 하였다. 모니터 재배열을 위한 Shell Script 만들기 /* start_xrandr.sh */ #!/bin/bash xrandr --auto --output DP-4 --rotate left --mode 1920x1200 --left-of DP-0 모니터 1번 (세로 모니터..
가변 개수의 인자를 받는 함수를 가변 인자 함수(Variadic Function)라고 한다. C 표준 라이브러리에는 직접 가변 인자 함수를 만들 수 있게 도와주는 여러 매크로(Macro)를 포함하고 있다. #include // 가변 인자 함수를 정의하려면 stdarg.h 파일을 인클루드해야 한다. #include // 가변 인자 함수 int print_ints(int args, ...){ int tmp; int sum = 0; va_list ap; // 어디에서부터 가변 인자가 시작되는지 알려준다. va_start(ap, args); // 모든 가변 인자 출력 for(int i=0; i
열거형을 구조체의 멤버로 등록하고 사용하는 예제이다. #include // 열거형 정의 enum play_type { RUN, STOP, ATTACK }; // response 구조체안에 열거형을 멤버로 넣었다. typedef struct { char *name; enum play_type type; } response; void run(response r) { printf("%s\n", r.name); puts("run!"); } void stop(response r) { printf("%s\n", r.name); puts("stop!"); } void attack(response r) { printf("%s\n", r.name); puts("attack!"); } int main(void) { res..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/ba9vGA/btqEWAOtoUW/WcwW85Da4Zh6w2txn84c70/img.png)
대규모의 복잡한 프로그램에서 버그를 찾으려면 정말 오랜 시간이 걸릴 수도 있다. 그래서 다양한 도구들을 이용하여 찾아내 수정해야 한다. 그 도구 중 valgrind를 소개한다. 리눅스 운영체제에서 사용하는 도구 중 "valgrind" 라는 것이 있다. 한마디로 메모리 누수를 검사하는 프로그램이다. valgrind 는 heap 영역에 할당된 데이터를 감시할 수 있다. 이 프로그램은 가짜 malloc() 함수를 구현해 작동한다. 여러분의 프로그램이 heap 메모리에 할당할 때 valgrind는 malloc()과 free() 함수에 대한 호출을 가로채 자신이 만든 버전을 실행한다. valgrind가 구현한 가짜 malloc()은 어느 코드가 호출하는지 어느 메모리가 할당되었는지 기록한다. 프로그램이 실행을 마..
#include int main(){ char word[10]; int i = 0; // format을 %9s로 지정하면 최대 9개 문자를 word에 저장한다 // 마지막 문자는 널 문자가 들어간다. while(scanf("%9s", word) == 1){ i++; if(i % 2){ fprintf(stdout, "%s\n", word); } else{ fprintf(stderr, "%s\n", word); } } return 0; } /* string.txt 내용 redirection 예제를 만들어 보았다. 이 예제는 리눅스 혹은 맥에서 실행해야 한다. C언어 소스파일에서 표준출력, 표준에러를 발생하고 그것을 활용하는 예제이다. */ // 컴파일 & 실행 /* gcc redirection_exam.c ..
#include #include void Reverse(char* str) { int len = strlen(str) - 1; char* ptr = str + len; while (ptr >= str) { printf("%c", *ptr); ptr--; } } int main(void) { char* str = "Hello World!!"; Reverse(str); return 0; } // 결과 /* !!dlroW olleH */ ptr 포인터 변수를 널 문자 바로 앞으로 이동시킨 후 포인터 연산을 통하여 처음의 메모리 주소 값으로 점차 다가가도록 하였다. 즉, ptr 포인터 변수는 문자열 끝을 가리키다가 맨 처음으로 돌아오고 반복이 종료된다.
구조체 문법으로 비트 단위를 분리할 수 있다. #pragma warning(disable: 4996) #include // 비트 단위 정보를 다룰 수 있도록 구조체를 선언 (총 1바이트) // 구조체 멤버 하나하나가 1비트 struct BitType { unsigned char bit_0 : 1; unsigned char bit_1 : 1; unsigned char bit_2 : 1; unsigned char bit_3 : 1; unsigned char bit_4 : 1; unsigned char bit_5 : 1; unsigned char bit_6 : 1; unsigned char bit_7 : 1; // 최상위 비트 (MSB) }; int main(void) { struct BitType data;..
공용체를 적재적소에 활용하면 메모리를 절약할 수 있다. 사용자 정의 자료형을 만드는 구조체와 문법 구조가 비슷한 공용체 문법이 있다. 공용체의 요소들은 할당된 메모리를 공유한다. union SharedData { char c_data; short int s_data; int i_data; }; 공용체를 구성하는 각 요소들은 서로 같은 메모리를 공유하는 형태로 된다. SharedData 공용체는 총 4바이트를 사용한다. // 리틀 엔디안 방식이라고 가정 union SharedData tmp; tmp.i_data = 0x12345678; 위와 같이 값을 대입하면 c_data는 0x78, s_data는 0x78 0x56, i_data는 0x78 0x56 0x34 0x12 가 들어간다. #pragma warni..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/bDfJaa/btqESGvqszU/4IZLPZ8nWfxXLG7KErjDx1/img.png)
모든 프로그래머가 완제품 형식의 프로그램을 만들지는 않는다. 프로그래머는 자신의 코드가 노출되면 안되기 때문에 해당 코드를 컴파일해서 라이브러리(*.lib) 형식의 파일로 제공한다. 그리고 라이브러리 안에 있는 함수들이 어떤 형태로 선언된 함수인지 알아야 코드를 자세히 볼 수 없는 사용자들도 사용할 수 있기 때문에 함수의 원형들을 헤더(*.h) 파일에 적어서 함께 제공한다. 예를 들어 두 개의 정수 값을 넘겨 받아서 합산하는 Sum 함수를 라이브러리 형태로 제공한다고 가정하면, 라이브러리 사용자에게는 파일 내부를 볼 수 없는 라이브러리 파일 sum.lib와 라이브러리 파일을 설명하는 헤더 파일 sum.h를 모두 제공해야 한다. // 헤더 파일 sum.h // sum 함수의 원형 int sum(int a,..
함수 포인터(Function Pointer)란 특정 함수를 구성하는 시작 명령의 위치를 가리키는 포인터이다. 함수 포인터를 사용하면 해당하는 함수를 호출하여 실행할 수 있다. 데이터를 가리키는 포인터가 자신이 가리킬 대상의 크기를 명시하듯 함수 포인터는 함수 원형(Function Prototype)을 사용하여 포인터를 선언한다. 더보기 함수 포인터가 함수 원형을 사용하는 이유는 함수 원형을 알아야 함수를 호출할 때 스택 프레임을 구성할 수 있기 때문이다. * 스택 프레임: 스택을 함수 단위로 구역을 나눠 사용할 수 있도록 C언어에서 제공하는 스택 관리 방식. 원형이 같은 함수들 묶기 함수 포인터를 사용하는 가장 큰 이유는 같은 형식의 함수를 그룹으로 묶을 수 있기 때문이다. #pragma warning(..
파일 내부의 작업 위치 탐색 및 확인: fseek, ftell 함수 fseek 함수 파일에 저장된 데이터를 꼭 순차적으로 읽을 필요는 없다. 필요에 따라 fseek함수를 사용하여 원하는 위치로 건너뛰거나, 읽은 위치로 돌아가서 읽었던 데이터를 다시 읽을 수도 있다. // fseek 함수 원형 int fseek(FILE* stream, long offset, int origin); // 함수 사용 형식 fseek(파일 포인터, 이동 거리, 기준 위치); 이 함수는 파일의 데이터를 읽을 기준 위치로 다음과 같은 명령이 존재한다. * SEEK_SET: 파일의 시작 * SEEK_END: 파일의 끝 * SEEK_CUR: 현재 위치 이것들을 이용하여 지정한 기준 위치로부터 사용자가 지정한 "이동 거리" 만큼 이동한..
형식 설명 t 텍스트 속성으로 파일을 사용하겠다는 뜻. 이것으로 바이너리 파일을 열면 오류 발생. 이유는 텍스트 파일은 EOF라는 아스키 값을 사용하여 파일의 끝을 구별하는데 바이러니 파일에서는 찾을 수 없다. b 바이너리 속성의 파일을 사용한다는 뜻. 이 형식이 기본값. 그러므로 형식 지정에 t 또는 b가 없다면 기본적으로 바이너리 형식을 사용한다고 보면 된다. 파일 내용 읽기 모드 "r" 파일의 내용을 읽기(Read)위한 목적으로 파일을 연다. FILE* file = fopen("tmp.bin", "rb"); FILE* file = fopen("tmp.txt", "rt"); 파일 데이터 쓰기 모드 "w" 파일에 데이터를 쓰기(Write)위한 목적으로 파일을 연다. 만약 파일이 지정한 경로에 없다면 그..