레이블이 헤드 퍼스트 C인 게시물을 표시합니다. 모든 게시물 표시
레이블이 헤드 퍼스트 C인 게시물을 표시합니다. 모든 게시물 표시

2013년 1월 26일 토요일

C 언어 핵심 정리 -스레드 편-


  1. 스레드를 사용하면 한 프로세스가 한꺼번에 여러 가지 일을 할 수 있음
  2. 스레드는 경량 프로세스 임
  3. pthread(포직스 스레드)는 스레드 라이브러리
  4. pthrread_create()는 함수를 실행하는 스레드를 생성
  5. pthrread_join()은 스레드가 종료할 때가지 기다림
  6. 스레드는 전역 변수를 공유
  7. 두 스레드가 동시에 한 변수를 읽고 쓰면 결과를 예측 불가
  8. 뮤텍스는 공유 데이터를 보호하기 위한 락
  9. pthread_mutex_lock() 는 뮤텍스에 락을 검
  10. pthread_mutex_unlock() 는 뮤텍스에 건 락을 해제

C 언어 핵심 정리 -소켓과 네트워킹편-


  1. 텔넷은 간단한 네트워크 클라이언트
  2. socket() 함수로 소켓을 생성
  3. 서버 블랍(BLAB) 
  4.  B(bind) - 바인딩, L(listen) - 듣기, A(accept) - 접수, B(begin) - 대화시
  5. fork()로 한꺼번에 여러 클라이언트를 처리
  6. DNS = 도메인 이름 시스템
  7. getaddrinfo()는 도메인 이름에 대한 주소를 찾음

C언어 핵심 정리 -프로세스와 통신 편-


  1. system()은 터미널에서 명령을 입력한 것 처럼 문자열을 실행
  2. fork() 는 현재 프로세스를 복제
  3. fork() 와 exec()로 자식 프로세스를 생성
  4. 프로세스들은 파이프로 통신
  5. pipe()는 통신 파이프를 생성
  6. exit()는 프로그램을 바로 종료
  7. waitpid()는 프로세스가 종료할 때까지 가다림
  8. filno() 는 디스크립터 번호를 반환
  9. dup2()는 데이터 스트림을 복제
  10. 시그널은 운영체제가 보내는 메시지
  11. sigaction()은 시그널을 직접 처리
  12. 프로그램은 raise() 로 자신에게 시그널을 보낼 수 있음
  13. alarm() 은 지정한 시간 후에 SIGALRM 시그널을 보냄
  14. kill 명령은 시그널을 보냄
  15. 단순한 프로세스는 한번에 한 가지 일만 함

C언어 핵심 정리 -정적 라이브러리, 동적 라이브러리 편-


  1. #include<>는 /usr/include 와 같은 표준 디렉토리를 찾음
  2. -L <라이브러리 경로>는 디렉토리를 표준 라이브러리 디렉토리에 추가
  3. -I <라이브러리 이름> 은 /usr/lib와 같은 표준 디렉토리의 파일에 링크
  4. -I <인클루드 경로>는 디렉토리를 표준 인클루드 디렉토리에 추가
  5. ar 명령은 오브젝트 파일의 라이브러리 아카이브를 생성
  6. 라이브러리 아카이브는 lib<라이브러리 이름>.a 형태의 이름을 가짐
  7. 라이브러리 아카이브는 정적으로 링크
  8. gcc -shared 는 오브젝트 파일을 동적 라이브러리로 변환
  9. 동적 라이브러리는 실행시에 링크
  10. 동적 라이브러리는 운영체제에 따라 이름이 다름
  11. 동적 라이브러리는 .so, .dylib, .dll, .dll.a 확장자를 가질 수 있음

C언 핵심 정리 -고급 함수 편-

-고급 함수-

  1. 함수 포인터를 사용하면 함수를 데이터처럼 전달할 수 있음
  2. 함수 포인터는 *와 &연산자가 필요 없는 유일한 포인터지만, 원하면 사용가능
  3. 모든 함수의 이름은 함수에 대한 포인터
  4. qsort()는 배열을 정려
  5. 모든 정렬 함수는 비교 함수에 대한 포인터를 필요
  6. 비교 함수는 두 데이터의 순서를 결정
  7. 함수 포인터의 배열을 사용하면 데이터형에 따라 다른 함수를 실행
  8. 인자의 개수가 바뀔 수 있는 함수를 "가변 인자 함수"라고 함
  9. stdarg.h를 사용하면 가변 인자 함수를 만들 수 있음

C언어 핵심 정리 -데이터구조, 동적메모리 편-

-데이터 구조-

  1. 동적 데이터 구조는 재귀적 구조체를 사용
  2. 재귀적 구조체는 자신과 비슷한 데이터에 대한 포인터를 한개 이상 갖고 있음
  3. 연결리스트는 동적 데이터 구조이
  4. 연결 리스트 안에 데이터를 쉽게 삽입할 수 있음
  5. 연결 리스트는 배열보다 확장하기 쉬움

-동적 메모리-

  1. 스택은 지역 변수를 저장하기 위해 사용
  2. 스택과 달리 힙 메모리는 자동으로 해체되지 않음
  3. malloc()는 힙에 메모리를 할당
  4. free()는 힙에 있는 메모리를 해제
  5. strdup()는 문자열을 힙에 복사
  6. 할당된 메모리에 접근할 수 없을 때 메모리 누수가 발생
  7. valgrind로 메모리 누수를 찾아낼 수 있음.

C언어 핵심 정리 -구조체,공용체,비트필드 편-

-구조체-

  1. 구조체는 데이터 형을 한데 묶습니다.
  2. 구조체 필드는 점(.)표기법으로 읽을 수 있습니다
  3. 배열과 같은 표기법으로 구조체를 초기화 할 수 있습니다
  4. -> 연산자를 사용하면 구조체 포인터의 필드를 쉽게 접근할 수 있음
  5. typedef로 데이터형의 별명을 만들 수 있음
  6. 구조체와 공용체의 필드이름을 지정해 초기화 할 수 있음

-공용체와 비트필드-

  1. 공용체는 여러형의 데이터를 한 곳에 보관할 수 있음
  2. 열거형으로 여러 기호를 만들 수 있음
  3. 비트 필드를 사용하면 구조체에 저장된 데이터를 비트 단위로 제어 가능

C언어 핵심 정리 -데이터형, 여러파일-

-데이터 형-

  1. char도 숫자형
  2. 아주 큰 정수에는 long형
  3. 작은 정수에는 short형
  4. 일반적인 정수에는 int형
  5. int형은 컴퓨터의 따라 크기가 달라짐
  6. 일반적인 실수에는 float형
  7. 정교한 실수에는 double형

-여러 파일-

  1. 함수 선언과 정의를 분리
  2. 선언은 헤더 파일에 넣기~
  3. #include<>는 라이브러리 헤더 파일에 사용
  4. #include " "는 지역 헤더 파일에 사용
  5. 오브젝트 코그를 파일에 저장하면 빨리 컴파일 할 수 있음
  6. make 로 컴파일 하긔~

C언어 핵심 정리 -데이터 스트림 편-


  1. printf()나 scanf()같은 C 함수는 표준 출력 및 표준 입력과 통신 합니다
  2. 표준 입력은 기본적으로 키보드를 읽습니다.
  3. 표준 에러는 에러 메시지를 출력하기 위해 따로 만든 출력 스트림입니다
  4. fopen("<파일이름>", mode)로 직접 데이터 스트림을 만들수 있음
  5. 표준 출력은 기본적으로 화면에 출려
  6. 리다이렉션으로 표준 입력, 출력, 에러가 연결된 스트림으로 바꿀수 있음
  7. fprintf(stderr, ...)으로 표준 에러에 출력할 수 있습니다.
  8. mode에서 쓸 때는 "w", 읽을 때는 "r", 추가할 때는 "a"로 열기 모드를 지정
  9. 명령행 인자는 문자열 포인터의 배열로 main()함수에 전달
  10. getopt() 함수를 사용하면 옵션을 쉽게 읽을 수 있음

C 언어 핵심 정리 -문자열-


  1. 문자열 상수는 읽기 전용 메모리에 저장됨
  2. 문자열의 배열은 배열의 배열
  3. strstr(a,b)는 문자열 a안에 있는 문자열 b의 위치를 반환
  4. strcat()는 두 문자열을 하나로 합침
  5. strcpy()는 문자열을 다른 배열에 복사
  6. string.h 헤더 파일은 유용한 문자열 함수들을 가지고 있음
  7. char string[..][..]로 배열의 배열을 만듬
  8. strcmp() 는 두 문자열을 비교
  9. strchr() 은 문자열 안에 있는 문자의 위치를 반환함
  10. strlen()은 문자열의 길이를 반환

C언어 핵심 정리 -포인터와 메모리


  1. scanf("%i", &x)로 사용자가 x에 직접 숫자를 입력할 수 있음
  2. 문자 포인터 변수x는 char* x로 선언
  3. 배열을 문자열로 초기화 하면, 문자열을 배열에 복사
  4. &x를 'x에 대한 포인터'라고 함
  5. &x는 x의 주소를 반환
  6. 배열 변수를 포인터로 사용할수 있음
  7. *a로 a번지에 있는 내용을 읽을 수 있음
  8. fgets(buf, size, stdin)로 간단히 문장을 입력
  9. 지역변수는 스택에 저장 됨

C언어 핵심 정리 -기초편-


    1. 단순문은 명령입니다
    2. 블럭문은 중괄호{}로 에워 쌉니다
    3. if문은 조건이 참일 때 실행됩니다.
    4. switch 문은 사용하면 한변수를 여러 값과 효율적으로 비교가능
    5. 논리 연산자(&&과 ||)로 조건을 결합
    6. 모든 프로그램에는 main()함수가 있어야 함
    7. #include는 입출력 코드와 같은 외부 코드를 연결
    8. 소스 파일은 .c 확장자를 가짐
    9. c 프로그램을 실행하려면 컴파일 해야함
    10. gcc는 가장 인기 있는 컴파일러
    11. 명령행에 && 연산자 사용하면 컴파일이 성공했을 때만 실행
    12. -o 는 컴파일된 파일 이름을 지정
    13. i++ 은 i에 1 더함, i--는 i에 1 뺌
    14. while는 조건이 참이면 루프를 반복
    15. do-while 는 코드를 적어도 한번은 실행
    16. for문을 사용하면 루프를 간략히 작성

부록 1 // 2. 전처리기, 3 static 키워드, 4.sizeof 키워드

2. 전처리기 지시자

#include <stdio.h> 이게 전처리기 지시자

전처리기 헤더 파일의 내용을 파일 안에 넣는거~
언제나 소스코드 제일 앞에 오고 #으로 시작

#define 는 매크로 만드는 지시자
매크로는 프로그램을 실행하는 동안 값이 바뀌지 않기 때문에 변수가 아니고
매크로는 프로그램이 컴파일 되기 전에 바뀜.

3. static 키워드

예제)

int count = 0;
int counter()
{
     return ++count; //호출될 때 마다 값을 증가 시킴
}

이 코드의 문제는 count라는 전역 변수를 사용함.
어떤 함수도 사용해서 값을 바꿀 수가 있음.
전역 변수를 특정 함수나 파일만 사용할 수 있는 변수를 만드는 법~!

int counter()
{
     static int count = 0;
     return ++count;
}

static 키워드를 사용하면 변수를 전역 메모리에 보관하고 다른 함수가 접근하면 에러 발생

함수 앞에 static를 사용하면 같은 소스코드 안에 있는 코드만 이 함수를 사용가능


1줄 요약: static 키워드는 코드의 범위를 통제~

4. sizeof  키워드

어떤 범위를 알고 싶을 때 사용.

예제)

#include <stdio.h>
#include <limits.h>

int main()
{
   printf("이 컴퓨터에서 int 형은 %lu바이트를 차지 합니다. \n", sizeof(int));
   printf("그리고 int형은  %i에서 %i까지 저장할 수 있습니다. \n", INT_MIN, INT_MAX);
   printf("그리고 short형은  %i에서 %i까지 저장할 수 있습니다. \n", SHRT_MIN, SHRT_MAX);
   return 0;
}

매크로 이름은 INT(int), LONG(long), CHAR(char), FLT(float), DBL(double)
에 _MAX 나 _MIN을 붙이면 됨.


















부록 1 중요한 10가지 이야기 중 1. 연산자

1. 연산자


증가과 감소
++i
i의 값을 1 증가시키고, 새 값을 반환

i++
i의 값을 1 증가시키고 이전 값을 반환

--i
i의 값을 1 감소시키고, 새 값을 반환

i--
i의 값을 1 감소시키고, 이전 값을 반환

1줄 요약 ++ -- 가 앞에 있으면 새 값을 반환하고, 뒤에 있으면 원래 값은 그대로


3항 연산자
어떤 값이 참일 때는 1번을 거짓일 때는 2번을 선택하고 싶다면??

(x == 1) ? 2 : 3

이라고 썼다면 x == 1이 참이면 2, 거짓이면 3

비트 조작
~a      a에 있는 각 비트의 반대값을 반환합니다.
a&b     a와 b의 각 비트의 논리곱을 반환합니다.
a|b     a와 b의 각 비트의 논리합을 반환합니다.
a^b     a와 b의 각 비트의 XOR을 반환합니다.
<<     모든 비트를 오른쪽 인자의 수만큼 왼쪽으로 이동
>>     모든 비트를 오른쪽 인자의 수만큼 오른쪽으로 이동

콤마를 사용해 수식을 분리
루프를 돌 때두 개 이상의 연산을 하는법

for(i = 0; i < 10; i++, j++) 콤마(,)로 구분~

12강 보충 - 스레드를 제어해 보장~


#include <stdio.h>
#include <pthread.h>

int beers = 2000000;

void* drink_lots(void* a)
{
int i;
for (i = 0; i < 100000; i++) {
beers = beers - 1;
}
return NULL;
}

int main()
{
pthread_t threads[20];
int t;
printf("벽에 있는 맥주병 개수: %i\n맥주병 개수: %i\n", beers, beers);
for (t = 0; t < 20; t++) {
pthread_create(&threads[t], NULL, drink_lots, NULL);
}

void* result;
for (t = 0; t < 20; t ++) {
pthread_join(threads[t], &result);
}

printf("벽에 %i 병의 맥주가 남아있습니다.\n", beers);
return 0;
}

200 만개의 병을 20개의 쓰레드를 이용하여 10만개씩 동시에 세는 프로그램임.

pthread_create 로 스레드 생성하고, pthread_join로 종료할 때가지 기다리고...

하지만 위의 소스를 실행하면 서로 공유 변수를 쓰기 때문에 계산이 개판이 됨.

고로 스레드를 통제하는 뮤텍스를 이용해 봅시당~


#include <stdio.h>
#include <pthread.h>

int beers = 2000000;
pthread_mutex_t beers_lock = PTHREAD_MUTEX_INITIALIZER;

void* drink_lots(void* a)
{
int i;
pthread_mutex_lock(&beers_lock);
for (i = 0; i < 100000; i++) {
beers = beers - 1;
}
pthread_mutex_unlock(&beers_lock);
printf("맥주 = %i\n", beers);
return NULL;
}

int main()
{
pthread_t threads[20];
int t;
printf("벽에 있는 맥주병 개수: %i\n맥주병 개수: %i\n", beers, beers);
for (t = 0; t < 20; t++) {
pthread_create(&threads[t], NULL, drink_lots, NULL);
}

void* result;
for (t = 0; t < 20; t ++) {
pthread_join(threads[t], &result);
}

printf("벽에 %i 병의 맥주가 남아있습니다.\n", beers);
return 0;
}


굵게 표시한 부분이 스레드를 통제하는 함수(라기보단 전처리기) 입니다.
lock 와 unlock 로 나뉘는데요~
이걸 반복문 전후로 사용법이 있고......


for (i = 0; i < 100000; i++)
{
pthread_mutex_lock(&beers_lock);
beers = beers - 1;
pthread_mutex_unlock(&beers_lock);
}

반복문 안에서 할수도 있습니다.
물론 둘의 과정은 틀리지만 결과는 같습니다.
어느 것이 좋다고는 말할 수는 없지만 최소한 잘못된 참조로 계산이 틀리지는 않습니다.

헤드 퍼스트 C 12강 (thread)


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h> //포직스 스레드 라이브러리 사용

void error(char* msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}

void* does_not(void *a)
{
int i = 0;
for (i = 0; i < 5; i++) {
sleep(1);
puts("Does not!");
}
return NULL;
}

void* does_too(void *a)
{
int i = 0;
for (i = 0; i < 5; i++) {
sleep(1);
puts("Does too!");
}
return NULL;
}

int main()
{
pthread_t t0;
pthread_t t1;
if (pthread_create(&t0, NULL, does_not, NULL) == -1)
error("t0 스레드를 생성할 수 없습니다.");
if (pthread_create(&t1, NULL, does_too, NULL) == -1)
error("t1 스레드를 생성할 수 없습니다.");

void* result;
if (pthread_join(t0, &result) == -1)
error("t0 스레드를 종료할 수 없습니다.");
if (pthread_join(t1, &result) == -1)
error("t1 스레드를 종료할 수 없습니다.");

return 0;
}

------------------ 이 코드는 스레드가 충동하는 문제가 있음 -----------

2013년 1월 23일 수요일

헤드 퍼스트 C 11강 (노크노크 서버)


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>

void error(char* msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}

int open_listener_socket()
{
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1)
error("소켓을 열 수 없습니다.");

return s;
}

void bind_to_port(int socket, int port)
{
struct sockaddr_in name;
name.sin_family = PF_INET;
name.sin_port = (in_port_t)htons(30000);
name.sin_addr.s_addr = htonl(INADDR_ANY);
int reuse = 1;
if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(int)) == -1)
error("소켓에 재사용 옵션을 설정할 수 없습니다.");
int c = bind(socket, (struct sockaddr*)&name, sizeof(name));
if (c == -1)
error("소켓에 바인딩할 수 없습니다.");
}

int catch_signal(int sig, void (*handler)(int))
{
        struct sigaction action;
        action.sa_handler = handler;
        sigemptyset(&action.sa_mask);
        action.sa_flags = 0;
        return sigaction(sig, &action, NULL);
}

int say(int socket, char* s)
{
int result = send(socket, s, strlen(s), 0);
if (result == -1)
fprintf(stderr, "%s: %s\n", "클라이언트에 메시지를 보낼 수 없습니다.", strerror(errno));
return result;
}

int listener_d;

void handle_shutdown(int sig)
{
if (listener_d)
close(listener_d);

fprintf(stderr, "안녕!\n");
exit(0);
}

int read_in(int socket, char* buf, int len)
{
char* s = buf;
int slen = len;
int c = recv(socket, s, slen, 0);
while ( (c > 0) && (s[c-1] != '\n')) {
s += c; slen -= c;
c = recv(socket, s, slen, 0);
}
if ( c < 0 )
return c;
else if (c == 0)
buf[0] = '\0';
else
s[c-1] = '\0';
return len - slen;
}

int main(int argc, char* argv[])
{
if (catch_signal(SIGINT, handle_shutdown) == -1)
error("인터럽트 처리기를 설정할 수 없습니다.");

listener_d = open_listener_socket();
bind_to_port(listener_d, 30000);
if (listen(listener_d, 10) == -1)
error("들을 수 없습니다.");

struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
puts("연결을 기다립니다.");
char buf[255];
while(1) {
int connect_d = accept(listener_d, (struct sockaddr*)&client_addr, &address_size);
if (connect_d == -1)
error("두 번째 소켓을 열 수 없습니다.");
if (say(connect_d, "인터넷 노크노크 프로토콜 서버\r\n버전 1.0\r\nKnock Knock!\r\n") != -1) {
read_in(connect_d, buf, sizeof(buf));
if (strncasecmp("Who's there?", buf, 12))
say(connect_d, "'Who's there?'라고 질문했어야 합니다!");
else {
if (say(connect_d, "Oscar\r\n") != -1) {
read_in(connect_d, buf,sizeof(buf));
if (strncasecmp("Oscar who?", buf, 10))
say(connect_d, "'Oscar who?'라고 질문했어야 합니다!");
else
say(connect_d, "Oscar silly question, you get a silly answer.\r\n");
}
}
}
close(connect_d);
}
return 0;
}

헤드 퍼스트 C 11강 (socket)


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>

void error(char* msg)
{
        fprintf(stderr, "%s: %s\n", msg, strerror(errno));
        exit(1);
}

int main(int argc, char* argv[])
{
char* advice[] = {
"조금만 드세요.\r\n",
"꽉 끼는 청바지를 입으세요. 뚱뚱해 보이지는 않을 겁니다.\r\n",
"한 마디만 하겠습니다. 옳지 않아요.\r\n",
"오늘만이라도 솔직해지세요. 직장 상사에게 '진정' 생각하고 있는 걸 말하세요.\r\n",
"그 머리 모양은 아니지 싶습니다.\r\n"
};
int listener_d = socket(PF_INET, SOCK_STREAM, 0);

struct sockaddr_in name;
name.sin_family = PF_INET;
name.sin_port = (in_port_t)htons(30000);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if ( bind(listener_d, (struct sockaddr*)&name, sizeof(name)) == -1)
error("포트에 바인딩할 수 없습니다.");
listen(listener_d, 10);
puts("연결을 기다립니다.");
while (1) {
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
int connect_d = accept(listener_d, (struct sockaddr*)&client_addr, &address_size);
char* msg = advice[rand() % 5];
send(connect_d, msg, strlen(msg), 0);
close(connect_d);
}
return 0;
}




gcc 컴파일러로 해야함 vs로 할 시 헤더 파일이 틀려서 안됨.

헤드 퍼스트 C 11강, 12강

은 귀찮아서 생략한다 -_-

헤드 퍼스트 C 10강


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

int score = 0;

void end_game(int sig)
{
printf("\n최종 점수: %i\n", score);
exit(0);
}

int catch_signal(int sig, void (*handler)(int))
{
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(sig, &action, NULL);
}

void times_up(int sig)
{
puts("\n시간 초과!");
raise(SIGINT);
}

void error(char* msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}

int main()
{
catch_signal(SIGALRM, times_up);
catch_signal(SIGINT, end_game);
srandom(time(0));

while(1) {
int a = random() % 11;
int b = random() % 11;
char txt[4];
alarm(5);
printf("\n%i 곱하기 %i는? ", a, b);
fgets(txt, 4, stdin);
int answer = atoi(txt);
if (answer == a * b)
score++;
else
printf("\n틀렸습니다! 점수: %i\n", score);
}

return 0;
}