본문 바로가기
프로그래밍/C

[C] 11. C언어의 "고급 주제(기타)"

by iwbap 2024. 6. 14.
728x90

C 언어 학습: 고급 주제

C 언어의 기본을 익힌 후에는 고급 주제를 통해 더욱 깊이 있는 프로그래밍 기술을 습득할 수 있습니다. 이번 글에서는 전처리기 지시자, 라이브러리와 모듈화, 네트워킹 기초(소켓 프로그래밍), 다중 스레딩(pthread)에 대해 알아보겠습니다.


1. 전처리기 지시자 (#define, #include, #if 등)

전처리기 지시자는 컴파일러가 소스 코드를 컴파일하기 전에 처리해야 할 명령을 지정합니다. 주요 전처리기 지시자는 다음과 같습니다:

  • #define : 매크로를 정의합니다.
    [c]
    #define PI 3.14159

 

  • #include : 다른 파일을 포함합니다.
    [c]
    #include <stdio.h>
    #include "myheader.h"

 

  • #if, #elif, #else, #endif : 조건부 컴파일을 수행합니다.

        [c]

       #define DEBUG

       #if defined(DEBUG)
       #define LOG(msg) printf("DEBUG: %s\n", msg)
       #else
       #define LOG(msg)
       #endif

 

 

예제

[c]
#include <stdio.h>

#define PI 3.14159
#define SQUARE(x) ((x) * (x))

int main() {
    printf("PI: %f\n", PI);
    printf("SQUARE(5): %d\n", SQUARE(5));

    return 0;
}
 

위의 예제에서 #define을 사용하여 매크로 PI와 SQUARE를 정의하였고, 이를 통해 상수와 함수를 대체할 수 있습니다.


2. 라이브러리와 모듈화

라이브러리와 모듈화를 통해 코드를 재사용 가능하고, 유지보수가 용이하게 만들 수 있습니다. C 언어에서는 헤더 파일과 소스 파일을 분리하여 모듈화를 구현합니다.

 

1. 헤더 파일 (mylib.h)

[c]
 
#ifndef MYLIB_H
#define MYLIB_H

void sayHello();
int add(int a, int b);

#endif
 
 

2. 소스 파일 (mylib.c)

[c]
 
#include <stdio.h>
#include "mylib.h"

void sayHello() {
    printf("Hello, World!\n");
}

int add(int a, int b) {
    return a + b;
}
 
 

3. 메인 파일 (main.c)

[c]
 
#include <stdio.h>
#include "mylib.h"

int main() {
    sayHello();
    printf("3 + 4 = %d\n", add(3, 4));
    return 0;
}
 
 

4. 컴파일 명령

[sh]

gcc -o main main.c mylib.c

 

위의 예제에서 mylib.h는 함수 선언을 포함하고 있으며, mylib.c는 함수 정의를 포함하고 있습니다. main.c에서 이 헤더 파일을 포함하여 함수를 호출할 수 있습니다.


3. 네트워킹 기초 (소켓 프로그래밍)

소켓 프로그래밍은 네트워크 상에서 통신을 수행하는 방법입니다. C 언어에서는 socket API를 사용하여 소켓을 생성하고, 서버와 클라이언트 간의 통신을 처리합니다.

 

1. 서버 예제

[c]
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    char *hello = "Hello from server";

    // 소켓 파일 디스크립터 생성
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 소켓 설정
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    // 소켓 바인딩
    bind(server_fd, (struct sockaddr *)&address, sizeof(address));
    
    // 소켓 리스닝
    listen(server_fd, 3);

    // 클라이언트 연결 수락
    new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    read(new_socket, buffer, 1024);
    printf("%s\n", buffer);
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");

    return 0;
}
 
 

2. 클라이언트 예제

[c]
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    char buffer[1024] = {0};

    // 소켓 파일 디스크립터 생성
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8080);
    
    // 서버 주소 설정
    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);

    // 서버에 연결
    connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    send(sock, hello, strlen(hello), 0);
    printf("Hello message sent\n");
    valread = read(sock, buffer, 1024);
    printf("%s\n", buffer);

    return 0;
}
 

위의 예제에서 서버는 클라이언트의 연결을 기다리고, 클라이언트는 서버에 연결하여 메시지를 주고받습니다.


4. 다중 스레딩 (pthread)

다중 스레딩은 여러 스레드를 사용하여 동시에 여러 작업을 수행하는 방법입니다. C 언어에서는 pthread 라이브러리를 사용하여 다중 스레딩을 구현할 수 있습니다.

 

스레드 생성 예제

[c]
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void* print_message(void* ptr) {
    char *message;
    message = (char *) ptr;
    printf("%s \n", message);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    const char *message1 = "Thread 1";
    const char *message2 = "Thread 2";
    
    // 스레드 생성
    pthread_create(&thread1, NULL, print_message, (void*) message1);
    pthread_create(&thread2, NULL, print_message, (void*) message2);
    
    // 스레드 종료 대기
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}
 

위의 예제에서 두 개의 스레드를 생성하여 각각의 메시지를 출력합니다. pthread_create 함수는 새로운 스레드를 생성하고, pthread_join 함수는 스레드가 종료될 때까지 대기합니다.


이번 글에서는 C 언어의 고급 주제에 대해 알아보았습니다. 이러한 고급 기능을 통해 보다 복잡하고 효율적인 프로그램을 작성할 수 있습니다. C 언어 학습에 많은 도움이 되길 바랍니다. Happy Coding!

728x90