C

[C] C언어 300제 - 활용

JIN-JJS 2025. 6. 28. 22:01

201. scanf()  함수 100% 활용하기 (scanf)

더보기

• 소스

#include <stdio.h>

#define scan(d,x,y) printf(#d "형(%"#x")을 입력하세요 \n"); \ 
                    scanf(#x, &y); \ 
                    printf("scanf %"#x":"#x"\n\n",y)

int main() 
{
    char ch = 0;
    short int si = 0;
    int i = 0;
    int o8 = 0;
    int x16 = 0;
    unsigned ui = 0;
    long l = 0;
    float f = 0;
    char s[100] = {0,};

    scan(char,%c,ch);
    scan(short,%hd,si);
    scan(int,%d,i);
    scan(unsigned,%u,ui);
    scan(long,%d,l);
    scan(8진수,%o,o8);
    scan(16진수,%x,x16);
    scan(고정소수점,%f,f);  
    scan(문자열,%s,s);

    scanf("%4d %3d",&i,&o8);
    printf("%d, %d \n",i,o8);         
}

 

• 설명

• 매크로 함수 scan

#define scan(d,x,y) printf(#d "형(%"#x")을 입력하세요 \n"); \
>> 매개변수 d와 x는 #d,#x에 의해 문자열로 변환되며,
매크로 함수가 여러 줄에 걸쳐 사용될 때는 행 계속 문자(\)를 사용해야 한다.
 
scanf(#x, &y); \
>> 3번째 줄에서 선언한 매크로 함수 scan의 연속된 줄이다.
매개변수 x는 #x에 의해 문자열로 변환 되며, 매개변수 y는 &y처럼 변환된다.
매크로 함수가 여러 줄에 걸쳐 사용될 때는 행 계속 문자(\)를 사용해야 한다.
 
printf("scanf %"#x":"#x"\n\n",y)
>> 매개변수 x는 #x에 의해 문자열로 변환된다.
 
 
scan(char,%c,ch); 함수는 아래와 같이 치환된다.
printf("char 형(%%c)을 입력하세요 \n");
scanf("%c",&ch);
printf("scanf %%c:%c \n\n",ch)

 

• 결과 화면

202. printf() 함수 100% 활용하기 (printf)

더보기

• 소스

#include <stdio.h>

int main() 
{
    int i;
    char *pi;
    char *string = "books";
    
    // 문자, 정수값
    printf("[%c]\n",'A');                       // [A]
    printf("[%d]\n",7);                         // [7]
    printf("[%i]\n",7);                         // [7]
    printf("[%5d]\n",7);                        // [    7]
    printf("[%05d]\n",7);                       // [00007]
    printf("[%+d]\n",-12345);                   // [-12345]
    printf("[%+d]\n",+12345);                   // [+12345]
    printf("[% d]\n",-12345);                   // [-12345]
    printf("[%u]\n",12345);                     // [12345]
    printf("[%u]\n",-12345);                    // [4294954951]

    // 8진수, 16진수
    printf("[%x]\n",0xFF);                      // [ff]
    printf("[%X]\n",0xFF);                      // [FF]
    printf("[%#x]\n",0xFF);                     // [0xff]
    printf("[%#x]\n",12345);                    // [0x3039]
    printf("[%o]\n",0123);                      // [123]
    printf("[%#o]\n",0123);                     // [0123]


    // 고정 소수점
    printf("[%f]\n",3.141592);                  // [3.141592]
    printf("[%5f]\n",3.141592);                 // [3.141592]
    printf("[%.f]\n",3.141592);                 // [3]
    printf("[%.2f]\n",3.141592);                // [3.14]
    printf("[%2.2f]\n",3.141592);               // [3.14]
    printf("[%5.5f]\n",3.141592);               // [3.14159]
    printf("[%20.5f]\n",3.141592);              // [       3.14159]
    printf("[%-20.5f]\n",3.141592);             // [3.14159       ]

    // 부동 소수점
    printf("[%e]\n",3.141592);                  // [3.141592e+000]
    printf("[%E]\n",3.141592);                  // [3.141592E+000]
    printf("[%5e]\n",3.141592);                 // [3.141592e+000]
    printf("[%.e]\n",3.141592);                 // [3e+000]
    printf("[%.2e]\n",3.141592);                // [3.14e+000]
    printf("[%2.2e]\n",3.141592);               // [3.14e+000]
    printf("[%5.5e]\n",3.141592);               // [3.14159e+000]
    printf("[%20.5e]\n",3.141592);              // [    3.14159e+000]
    printf("[%20.2E]\n",1.2e+10);               // [      1.20E+010]
    printf("[%-20.2E]\n",1.2e+10);              // [1.20E+010      ]
    
    // 스마트형
    printf("[%g]\n",3.141592);                  // [3.14159]

    // 문자열형
    printf("[%s]\n",string);                    // [books]
    printf("[%10s]\n",string);                  // [     books]
    printf("[%-10s]\n",string);                 // [books     ]
    printf("[%2s]\n",string);                   // [books]
    printf("[%2.2s]\n",string);                 // [bo]
    printf("[%3.2s]\n",string);                 // [bo]
    printf("[%010s]\n",string);                 // [00000books]
    
    // 포인트형
    printf("[%p]\n",&i);                        // [0012FF7C] -> Windows 환경 값
    printf("[%p]\n",&pi);                       // [0012FF78] -> Windows 환경 값
}

 

• 설명

1) 문자 및 정수 출력 서식

%c : 문자 하나 출력

%d, %i : 부호 있는 10진수 정수

%u : 부호 없는 10진수 정수

%+d : 부호 표시 포함 출력 (+, -)

% d : 양수는 앞에 공백, 음수는 -

%5d : 최소 5자리 확보, 공백 채움

%05d : 5자리 확보, 0으로 채움

 

2) 8진수, 16진수 출력 서식

%x / %X : 16진수 출력 (소문자/대문자)

%#x / %#X : 접두어 0x, 0X 포함

%o : 8진수 출력

%#o : 접두어 0 포함한 8진수

 

3) 실수 출력 서식

%f : 고정 소수점 표기

%.2f : 소수점 아래 2자리까지 출력

%5.2f : 전체 최소 너비 5자리, 소수점 2자리

%e, %E : 지수 표기법 (소문자/대문자)

%g : 자동으로 %f 또는 %e 선택 (불필요한 0 생략)

 

4) 문자열 출력 서식

%s : 문자열 전체 출력

%10s : 오른쪽 정렬, 최소 너비 10칸

%-10s : 왼쪽 정렬, 최소 너비 10칸

%.2s : 문자열 일부 출력 (앞 2글자)

%010s : 문자열 오른쪽 정렬 + 0으로 채우기 (권장X, 구현마다 다름)

 

5) 포인터 출력 서식

%p : 메모리 주소 출력
(주소 값은 시스템/컴파일러/실행마다 다름)

 

• 결과 화면

 

203. 삼각형 출력하기 (for)

더보기

• 소스

#include <stdio.h>

int main() 
{
    int i,j;

    for(i=0; i<5; i++)
    {
        for(j=0; j<=i; j++)
        {
            printf("*");
        }
        printf("\n");
    }
}

 

• 설명

i는 0~4, j는 0~i까지 순환되면서 삼각형을 출력한다.

 

• 결과 화면

 

204. 값을 입력받아 홀수 / 짝수 구분하기 (%)

더보기

• 소스

#include <stdio.h>

int main() 
{
    int num;

    printf("숫자를 입력하세요 :");

    scanf("%d",&num);

    if(num%2==1) printf("%d은 홀수입니다. \n",num);
    else printf("%d는 짝수입니다. \n",num);
}

 

• 설명

연산자 %는 num의 값을 2로 나눈 후 나머지를 구하는 기능을 한다.

num%2==1이면 홀수이며, else 면 2로 나누어지면서  나머지 값이 없는 2의 배수로 짝수이다.

 

• 결과 화면

 

205. 1~100까지 홀수의 합 구하기

더보기

• 소스

#include <stdio.h>

int main() 
{
    int i, hap = 0;

    for(i=0; i<100; i++)
    {
        if(i%2)
        {
            hap+=i;
        }
    }

    printf("1~100까지의 홀수의 합 : %d \n",hap);
}

 

• 설명

i%2에서 1로 참이므로 hap에 i를 계속 더해간다. 100까지 홀수를 더하게 된다.

 

• 결과 화면

 

206. 21~50 범위의 난수 발생시키기

더보기

• 소스

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() 
{
    int i, rand_num;
    srand(time(NULL));

    for(i=0; i<10; i++)
    {
        rand_num = rand()%30+21;
        printf("[%d]",rand_num);
    }
}

 

• 설명

srand(time(NULL)); : 난수 발생기를 초기화하며, 초기화는 한 번만 해주면 된다.

rand() : rand() 함수는 0~32767 범위의 값을 발생시킨다.

rand()%30 : 0~32767을 30으로 나눈 나머지 (0~29)

rand()%30+21; : 0~32767을 30으로 나눈 나머지 (0~29)에 21을 더해, 21~50의 값의 범위의 난수를 생성한다.

 

• 결과 화면

207. 변수의 번지 출력하기 (&연산자)

더보기

• 소스

#include <stdio.h>

int main() 
{
    int i = 127;
    int j;

    printf("%d, %x \n",i,i);
    printf("%#x \n",&i);
    printf("%#x \n",&j);
}

 

• 설명

%d는 10진수, %x는 16진수로 출력한다.

%#x로 메모리 번지를 출력한다.

 

• 결과 화면

208. 정수값을 16진수 문자열로 변환하기 (itoa)

더보기

• 소스

// Windows
// #include <stdio.h>
// #include <stdlib.h>

// int main() 
// {
//     char buff[100];
//     int radix = 16;

//     itoa(10, buff, radix);
//     puts(buff);             // "a"

//     itoa(255, buff, radix);
//     puts(buff);             // "ff"
// }

// macOS
#include <stdio.h>

int main() 
{
    char buff[100];

    sprintf(buff, "%x", 10);
    puts(buff);             // "a"

    sprintf(buff, "%x", 255);
    puts(buff);             // "ff"
}

 

• 설명

itoa() 함수는 Windows의 <stdlib.h>에는 있지만, 표준 C에는 포함되어 있지 않다.

macOS에서 대체하여 sprintf() 함수를 사용 가능하다.

 

• 결과 화면

 

209. 2진수 / 16진수 문자열을 정수값으로 변환하기 (strtol)

더보기

• 소스

#include <stdio.h>
#include <stdlib.h>

int main() 
{
    char string1[] = "1010";        // 2진수
    char string2[] = "ff";          // 16진수
    char *stop;
    long value;

    value = strtol(string1, &stop, 2);
    printf("2진문자열\"%s\"을 정수로 바꾸면 %d입니다. \n",string1,value);

    value = strtol(string2, &stop, 16);
    printf("16진문자열\"%s\"을 정수로 바꾸면 %d입니다. \n",string2,value);
}

 

• 설명

2진 및 16진 문자열을 정수로 변환한다.

strol() 함수는 2진수 및 16진수 뿐만 아니라, 8진수 등도 모두 정수로 변환할 수 있다.

 

• 결과 화면

 

210. 2진수 문자열을 16진수 문자열로 변환하기 (strtol, itoa)

더보기

• 소스

// Windows
// #include <stdio.h>
// #include <stdlib.h>

// int main() 
// {
//     int radix = 16;
//     int base = 2;
//     char string1[] = "10101011";            // 변환할 기수가 16진수
//     char *stop;
//     long value;
//     char buff[100];

//     value = strtol(string, &stop, base);    // 10진수로 변환
//     itoa(value, buff, radix);               // 16진수 문자열로 변환
//     puts(buff);                             // "ab"
// }

// macOS
#include <stdio.h>
#include <stdlib.h>

int main() 
{
    int base = 2;                       
    char string1[] = "10101011";        // 변환할 기수가 16진수
    char *stop;
    long value;
    char buff[100];

    value = strtol(string1, &stop, base);   // 10진수로 변환
    sprintf(buff, "%x", value);             // 16진수 문자열로 변환
    puts(buff);                             // "ab"
}

 

• 설명

itoa() 함수는 Windows의 <stdlib.h>에는 있지만, 표준 C에는 포함되어 있지 않다.

macOS에서 대체하여 sprintf() 함수를 사용 가능하다.

 

• 결과 화면

 

211.  소문자를 대문자로 변환하기

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main() 
{
    char string[] = "abcdefghijklmnopqrstuvwxyz";
    unsigned i,len;

    puts(string);

    len = strlen(string);           // 문자열의 길이

    for(i=0; i<len; i++)
    {
        string[i] = string[i]&0xDF; // 0xDF는 2진수 1101 1111
    }

    puts(string);
}

 

• 설명

소문자 'a'는 2진수로 0110 0001이며, 대문자 'A'는 2진수로 0100 0001이다.

그러므로 소문자 'a'의 앞에서 3번째 비트를 마스크(비트 AND)해주면, 대문자 'A'로 변환된다.

 

• 결과 화면

212. 비트 연산을 사용하여 변수값을 O으로 만들기

더보기

• 소스

#include <stdio.h>

int main() 
{
    int i = -5;

    printf("i = %d \n",i);

    i = i^i;

    printf("i = %d \n",i);
}

 

• 설명

i의 값이 서로 같으므로, i^i의 값은 무조건 0이 된다.

i의 값이 양수이건, 음수이건 i의 값이 크건 작건, 무조건 0이 되는 원리이다.

 

• 결과 화면

 

213. 비트 쉬프트 연산을 사용하여 곱셈 구현하기 (<<)

더보기

• 소스

#include <stdio.h>

int main() 
{
    char value = 2;

    value = value << 1;
    printf("value : %d \n",value);
}

 

• 설명

2의 값은 2진수로 00000010이다. 그러므로 이 값을 왼쪽으로 1비트 쉬프트하면, 00000100이 되므로,

곱셈을 한 것과 같은 효과를 볼 수 있다.

 

• 결과 화면

 

214. 비트 쉬프트 연산을 사용하여 나눗셈 구현하기(>>)

더보기

• 소스

#include <stdio.h>

int main() 
{
    char value = 4;

    value = value >> 1;
    printf("value : %d \n",value);
}

 

• 설명

4의 값은 2진수로 00000100이다. 그러므로 이 값을 오른쪽으로 1비트 쉬프트하면, 00000010이 되므로,

나눗셈을 한 것과 같은 효과를 볼 수 있다.

 

• 결과 화면

 

215. 문자열에서 특정 문자열의 인덱스 구하기 (strstr)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main() 
{
    char string[] = "This os a book";
    char *pos;
    
    pos = strstr(string, "book");

    if(pos) // if(pos != NULL)
    {
        printf("book을 %d번째에서 찾았습니다. \n",pos-string);
        printf("%s \n",&string[pos-string]);
    }
}

 

• 설명

strstr() 함수는 문자열을 검색할 때 사용하며, 문자열을 검색하였을 경우에는 그 문자열의 번지를 반환하고,

그렇지 않은 경우에는 NULL을 반환한다.

 

pos가 NULL이 아니면 "book"을 찾은 것이므로, 검색된 번지는 항상 string의 번지보다 같거나 크기 때문에 String 번지를 빼면

검색된 위치를 구할 수 있다.

 

• 결과 화면

 

216. 문자열을 콤마와 공백으로 분리하기 (strtok)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main() 
{
    char string[] = "a12, b34, c56";
    char *token;
    
    token = strtok(string, ", ");

    while (token)
    {
        puts(token);
        token = strtok(NULL,", ");
    }
}

 

• 설명

문자열 string을 콤마와 공백으로 분리하여 출력한다.

 

• 결과 화면

217. 문자열을 공백으로 분리하여 여러 개의 문자열로 만들기 (strtok)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main() 
{
    char string[] = "(a12), (b34), (c56)";
    char *token;
    
    token = strtok(string, "(123456), ");

    while (token)
    {
        puts(token);
        token = strtok(NULL,"(123456), ");
    }
}

 

• 설명

문자열 string을 "(123456), "로 분리하여 출력한다.

구분 문자열 "(123456), "은 구분하고자 하는 모든 문자를 사용하면 된다.

 

• 결과 화면

 

218. 문자열에서 숫자만 추출해내기 (isdigit)

더보기

• 소스

#include <stdio.h>
#include <ctype.h>

int main() 
{
    char *string = "(02) 1111-2222";
    char buff[20] = {0,};
    int i = 0;

    while (*string)
    {
        if(isdigit(*string))          // 문자가 숫자이면
        {
            buff[i++] = *string;      // buff에 저장
        }
        string++;                     // 다음 문자로 이동
    }
    puts(buff);                       // buff 출력 (숫자만)
}

 

• 설명

전화번호나 주민등록번호처럼 숫자 외의 문자가 섞여 있는 문자열에서 숫자만 필요한 경우 사용한다.

 

• 결과 화면

219. 문자열에서 알파벳만 추출해내기 (isalpha)

더보기

• 소스

#include <stdio.h>
#include <ctype.h>

int main() 
{
    char *string = "temperature: 200";
    char buff[20] = {0,};
    int i = 0;

    while (*string)
    {
        if (isalpha(*string))        // 문자가 알파벳(a~z, A~Z)일 경우
        {
            buff[i++] = *string;     // 버퍼에 저장
        }
        string++;                    // 다음 문자로 이동
    }
    puts(buff);                      // 결과 출력
}

 

• 설명

텍스트에서 영문자만 추출하거나, 숫자 및 특수문자 제거 후 정제된 문자열 추출이 가능하다.

 

• 결과 화면

 

220. 문자열에서 한글만 추출해내기

더보기

• 소스

#include <stdio.h>
#include <ctype.h>

int main() 
{
    unsigned char *string = "대한민국 Korea";
    unsigned char buff[20] = {0,};
    int i = 0;

    while (*string)
    {
        if (*string > 127)           // 아스키 범위(0~127)를 넘는 문자만 추출
        {
            buff[i++] = *string;     // 버퍼에 저장
        }
        string++;                    // 다음 문자로 이동
    }
    puts(buff);                      // 결과 출력
}

 

• 설명

문자열 "대한민국 Korea"에서 한글 문자(혹은 127 이상의 바이트값을 갖는 문자)만 추출하여 출력한다.

 

• 결과 화면

 

221.  알파벳이 아닌 첫 문자의 위치 검출하기 (strspn)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main() 
{
    char *string = "it's a good ";
    char *find = "abcdefghijklmnopqrstuvwxyz";
    int nIndex;

    nIndex = strspn(string,find);
    printf("%d 위치에서 알파벳이 아닌 첫 문자를 찾았습니다. \n",nIndex);
}

 

• 설명

문자열 string에서 소문자가 아닌 문자가 있는 위치를 검색한다.

모든 문자가 일치되면 문자열의 길이가 반환된다.

 

• 결과 화면

222. 문자열에서 숫자가 시작되는 위치 추출하기 (strcpn)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main() 
{
    char *string = "사자 3마리, 코끼리 5마리, 사슴 4마리";
    char *find = "0123456789";
    int index;

    index = strcspn(string,find);

    printf("%d 위치에서 일치되는 첫 숫자를 발견했습니다. \n",index);
}

 

• 설명

UTF-8 기준으로 한글 한 글자는 3바이트이다.

[0][1][2] '사' (3 bytes)

[3][4][5] '자' (3 bytes)

[6] '공백' (1 byte)

[7] '3' (1 byte)

 

• 결과 화면

 

223. 문자열을 특정 문자 위치에서 잘라내기 (strchr)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main() 
{
    char string[] = "a.book";
    char *pstr = string, *pfind = string;
    int i = 0;

    while (pfind)
    {
        pfind = strchr(pstr,'a');
        
        if(pfind == NULL)
        {
            pfind = strchr(pstr,'.');
            if(pfind) pstr = &string[++i];
        }
        else pstr = &string[++i];
    }
    puts(pstr);
}

 

• 설명

문자 'a' 및 '.'가 검색되면 pfind는 검색된 번지를 가리키며, 검색된 문자가 없을 경우 pfind는 NULL 값을 가진다. 

만약 'a' 또는 '.'문자가 검색되었다면, pstr은 그 문자가 검색된 다음 번지를 가리킨다.

 

• 결과 화면

224. 문자열의 좌우 공백 제거하기 (isspace)

더보기

• 소스

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main() 
{
    char *string = " 123  ";                  
    char buff[20] = {0,};
    int i = 0;

    printf("string의 길이 : %d \n", strlen(string)); 

    while(*string)
    {
        if(!isspace(*string))
        {
            buff[i++] = *string;          
        }
        string++;
    }
    puts(buff);
    printf("string의 길이 : %d \n", strlen(buff));
}

 

• 설명

*string의 값이 공백이 아닌 경우에만 buff 문자열에 복사하여 길이를 출력한다.

 

• 결과 화면

 

225. 문자열의 좌우 특정 문자들 제거하기 (strspn, strcspn)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main()
{
    char string[] = " .: ;abc; . ";  
    char *sep = " .:;";              
    int nIndex1, nIndex2;

    nIndex1 = strspn(string, sep); 
    nIndex2 = strcspn(&string[nIndex1], sep); 
    (&string[nIndex1])[nIndex2] = 0; 
    printf("%s \n", &string[nIndex1]);
}

 

• 설명

 

1. strspn(string, sep)
→ 문자열 처음부터 시작해서 구분자(sep)만 있는 문자 개수 세기
→ " .:;"까지 총 6글자 → nIndex1 = 6

 

2. strcspn(&string[nIndex1], sep)
→ string[6] = 'a' 부터 시작해서 구분자가 나오기 전까지 세기
→ "abc" → 총 3글자 → nIndex2 = 3

 

3. (&string[nIndex1])[nIndex2] = 0;
→ string[6 + 3] = string[9] = ';' 자리에 널 문자 넣기 → 문자열 종료

 

4. printf("%s \n", &string[nIndex1]);
→ string + 6 = "abc\0..." 출력됨


 

 

• 결과 화면

 

226. 문자열을 NULL로 채우기 (strset)

더보기

• 소스

// Windows
// #include <stdio.h>
// #include <string.h>

// int main()
// {
//     char buff[] = "암호는 Korea입니다.";
    
//     puts(buff);                  // buff 내용 출력
//     strset(buff, 0);             // buff 내용을 '\0'으로 모두 채움
//     printf("[%s]\n", buff);      // buff 내용 다시 출력 (빈 문자열이 됨)
// }

// macOS
#include <stdio.h>
#include <string.h>

int main()
{
    char buff[] = "암호는 Korea입니다.";
    puts(buff);

    memset(buff, 0, strlen(buff));  // POSIX 표준 대체: 메모리를 '\0'으로 채움
    printf("[%s]\n", buff);
}

 

• 설명

strset()은 문자열을 특정 문자로 모두 덮어쓰는 Windows 전용 함수이며, 여기선 buff의 내용을 모두 '\0'으로 지워 빈 문자열로 만들고 있다.

memset() 함수는 메모리의 특정 영역을 지정한 바이트 값으로 일정 크기만큼 반복해서 채우는 함수이다.

 

• 결과 화면

 

227. 문자열의 첫 글자를 대문자로 변환하기

더보기

• 소스

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main()
{
    char buff[] = "boy is man", *pos = buff;

    while (pos) {
        if (isalpha(buff[0]) && pos == buff) {
            buff[0] &= 0xDF;  // 'b' → 'B' (대문자화)
            pos++;
        }
        else if (pos = strpbrk(pos, " ")) {
            ++pos;           // 다음 단어의 첫 글자 위치로 이동
            *pos &= 0xDF;    // 소문자를 대문자로 변경
        }
    }

    printf("%s", buff);
}

 

• 설명

첫 문자가 영문자이면 대문자로 교체하고, 공백을 검색한 후, 그 다음 문자를 대문자로 변환한다.

 

• 결과 화면

 

228. 문자열에서 특정 위치의 문자 교체하기 (strnset)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main()
{
    char string[] = "암호는 Korea입니다.";
    char *pstr;

    pstr = strstr(string, "Korea");  // "Korea"의 위치를 포인터로 받음
    if (pstr) {
        memset(pstr, '*', 5);       // "Korea"를 ***** 로 덮어씀, strnset() 대체
    }

    puts(string);                    // 결과 출력
}

 

• 설명

macOS 환경이라 strnset() 함수 대신 memset() 함수로 대체

"Korea" 문자열을 찾아, 검색된 위치로부터 5바이트 만큼의 문자를 모두 '*'문자로 교체한다.

• 결과 화면

 

229. 문자열에 대한 임시 저장소 만들기 (strdup)

더보기

• 소스

#include <stdio.h>
#include <string.h>
#include <stdlib.h>   // malloc, free, strdup 선언 포함

int main()
{
    char buff[] = "문자열 복제하기";
    char *dup;

    dup = strdup(buff);              // 문자열 buff를 동적으로 복제
    if (dup)
    {
        strcpy(buff, "다른 문자열");  // 원본 buff 변경
        puts(buff);                 // "다른 문자열" 출력
        puts(dup);                  // "문자열 복제하기" 출력 (복제된 원본 유지)
        free(dup);                  // 동적 메모리 해제
    }
}

 

• 설명

strdup() 함수를 이용해 문자열 buff를 동적으로 복제한 뒤, 원본 buff를 변경해도 복제된 문자열은 변하지 않는다.
즉, dup은 원본과 별도의 메모리에 복사된 문자열을 가리키며, 작업이 끝나면 free()로 메모리를 해제해야 한다.

 

• 결과 화면

 

230. 메모리를 1MB 할당하고 해제하기 (malloc, free)

더보기

• 소스

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    char *pbuf;

    pbuf = malloc(100 * 10000);                 // 약 1MB 동적 메모리 할당
    if (pbuf)
    {
        memset(pbuf, 0, 100 * 10000);           // 할당된 메모리 0으로 초기화
        strcpy(&pbuf[0], "서울시 양천구 목동");  // 문자열 저장
        puts(&pbuf[0]);                         // 저장된 문자열 출력
        free(pbuf);                             // 동적 메모리 해제
    }
}

 

• 설명

1000명에 대한 주소값(100바이트)을 저장하기 위한 메모리를 할당한다.

할당된 메모리 버퍼를 모두 NULL(0)로 채운다.

malloc() 함수에 의해 할당된 메모리는 반드시 free() 함수에 의해 해제되어야 한다.

 

• 결과 화면

 

231.  메모리를 100MB 할당하고 해제하기

더보기

• 소스

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MEGA_BYTE 1048576  // 1MB = 1024 * 1024 bytes

int main()
{
    char *pbuf;

    pbuf = malloc(100 * MEGA_BYTE);  // 약 100MB 메모리 동적 할당
    if (pbuf)
    {
        memset(pbuf, 0, 100 * MEGA_BYTE);  // 전체 메모리를 0으로 초기화

        strcpy(&pbuf[0], "서울시 양천구 목동");           // 시작 부분에 문자열 복사
        puts(&pbuf[0]);                                  // 출력

        strcpy(&pbuf[10485700], "부산시 강서구 미음동");  // 약 10MB 근처에 다른 문자열 복사
        puts(&pbuf[10485700]);                           // 출력

        free(pbuf);                                      // 메모리 해제
    }
}

 

• 설명

100MB 메모리를 동적으로 할당하고 사용 후 해제한다.

메모리는 유한한 자원이기 때문에 한 프로그램에서 메모리를 독점하여 사용하는 것은 좋지 않다.

그러므로 프로그래밍 시에는 항상 꼭 필요한 만큼의 메모리를 할당하여 사용하는 습관을 갖는 것이 좋다.

 

• 결과 화면

 

232. void형 포인터를 사용한 다양한 배열 복사하기

더보기

• 소스

#include <stdio.h>

void array_copy(void *dest, const void *src, int size);

int main()
{
    char array1[100] = "array of char";
    char array2[100] = { 0, };
    int  array3[5]   = { 1, 2, 3, 4, 5 };
    int  array4[5]   = { 0, };

    printf("array1 : [%s]\n", array1);
    printf("array2 : [%s]\n", array2);
    printf("array3 첫 원소 : [%d]\n", array3[0]);
    printf("array4 첫 원소 : [%d]\n", array4[0]);

    array_copy(array2, array1, sizeof(array1));  
    array_copy(array4, array3, sizeof(array3));

    printf("array1 : [%s]\n", array1);
    printf("array2 : [%s]\n", array2);
    printf("array3 마지막 원소 : [%d]\n", array3[4]);
    printf("array4 마지막 원소 : [%d]\n", array4[4]);
}

void array_copy(void *dest, const void *src, int size)
{
    while (size--)
    {
        *(char *)dest = *(const char *)src;
        dest = (char *)dest + 1;   
        src  = (const char *)src + 1; 
    }
}

 

• 설명

1. array_copy()는 void *로 포인터를 받아 바이트 단위(char)로 캐스팅해서 1바이트씩 복사한다.

 

2. array2는 array1의 문자열을 복사받고, array4는 array3의 정수 배열 내용을 복사한다.

 

3. sizeof(array1)과 sizeof(array3)은 각각 전체 배열 크기(바이트 단위)를 전달하므로 정확한 메모리 복사가 이루진다.

 

4. *(char *)dest = *(const char *)src;를 통해 실제 메모리 내용을 복사한다.

 

• 결과 화면

 

233. 텍스트 파일을 한 줄씩 쓰기 (fopen, fputs, fclose)

더보기

• 소스

#include <stdio.h>

int main()
{
    FILE *fp;

    // 파일을 쓰기 모드("w+")로 열기
    fp = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file.txt", "w+");  

    if (fp != NULL)
    {
        // 문자열을 파일에 여러 번 출력
        fputs("대한민국 \n", fp);     
        fputs("대한민국 \n", fp);
        fputs("대한민국 \n", fp);
        fputs("대한민국 \n", fp);
        fputs("대한민국 \n", fp);
        fclose(fp);            
    }
}

 

• 설명

파일을 쓰기 및 생성 모드로 개방한다.

개방된 파일에 대한민국을 다섯 줄 쓴다.

파일을 닫는다.

 

• 결과 화면

 

234. 텍스트 파일을 한 줄씩 읽기 (fgets)

더보기

• 소스

#include <stdio.h>

int main()
{
    FILE *fp;
    char buff[100];

    fp = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file.txt", "r");  

    if (fp != NULL)
    {
        while (!feof(fp))
        {
            fgets(buff,100,fp);
            if (feof(fp)) break;
            printf("%s", buff);
        }

        fclose(fp);            
    }
}

 

• 설명

파일을 읽기 모드(fopen)로 열고, 파일 끝에 도달할 때까지(feof) 한 줄씩 읽어옵니다(fgets).
텍스트 파일의 줄 끝에 있는 0x0D(CR), 0x0A(LF) 문자는 자동으로 \n으로 처리됩니다.

 

• 결과 화면

 

235. 텍스트 파일 쓰기 (fwrite)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main()
{
    FILE *fp;
    char *string = "우리강산\n";

    fp = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file.txt", "w+");   

    if( fp != NULL )
    {
        fwrite( string, 1, strlen(string), fp );
        fwrite( string, 1, strlen(string), fp );
        fwrite( string, 1, strlen(string), fp );
        fclose( fp );               
    }
}

 

• 설명

파일을 쓰기 및 생성 모드로 개방한다.

fwrite() 함수는 fputs() 함수처럼 사용할 수 있으며,

fputs() 함수와 다른 점은 바이너리 모드로 파일을 개방 시 NULL 값을 쓸 수 있다는 것이다.

 

• 결과 화면

 

236. 텍스트 파일 읽기 (fread)

더보기

• 소스

#include <stdio.h>
#include <string.h>

int main()
{
    FILE *fp;
    char buff[100] = {0,};
    int len;

    fp = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file.txt", "r");   

    if( fp != NULL )
    {
        while (!feof(fp))
        {
            len = fread(buff,1,9,fp);
            if(ferror(fp)||len<9) break;
            printf("read: %d, %s",len,buff);
        }
        fclose( fp );               
    }
}

 

• 설명

fread()를 써서 UTF-8 한글 문자열을 깨지지 않고 출력하려면 읽을 바이트 수를 3의 배수로 맞추고, 널문자를 넣어줘야 한다.

macOS는 깨진다..

 

원래 출력값은

read : 9, 우리강산

read : 9, 우리강산

read : 9, 우리강산

 

• 결과 화면

 

237. 이진 파일 쓰기 (fwrite)

더보기

• 소스

#include <stdio.h>

int main()
{
    FILE *fp;
    char buff[5];

    fp = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file.bin", "w+b");   

    buff[0] = '@';
    buff[1] = 0;                // NULL
    buff[2] = 0x01;
    buff[3] = 0x03;
    buff[4] = 0x61;

    if( fp != NULL )
    {
        fwrite(buff,1,5,fp);
        fclose(fp);             
    }
}

 

• 설명

바이트 배열 buff의 5바이트 데이터를 바이너리 읽기/쓰기 모드로 연 파일에 쓴다.

 

• 결과 화면

 

238. 이진 파일 읽기 (fread)

더보기

• 소스

#include <stdio.h>

int main()
{
    FILE *fp;
    char buff;

    fp = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file.bin", "rb");   

    if( fp != NULL )
    {
        while (!feof(fp))
        {
            fread(&buff,1,1,fp);
            if(!feof(fp)) printf("%d(%#x)",buff,buff);
        }
        fclose(fp);
    }
}

 

• 설명

파일을 이진 읽기 모드로 개방 후,

파일의 끝에 도달할 때까지 데이터를 한 바이트씩 읽어서 출력한다.

 

• 결과 화면

 

239. 파일을 다른 디렉터리로 이동하기 (rename)

더보기

• 소스

#include <stdio.h>

int main()
{
    char *filename = "/Users/jeonjoonsu/Desktop/무제 폴더/file.txt";
    char *movefile = "/Users/jeonjoonsu/Desktop/무제 폴더/무제 폴더/file.txt";

    if( rename( filename, movefile ) != 0 )
    {
        perror("파일 이동 에러");
    }
    else
    {
        puts("파일이 이동되었습니다.");
    }
}

 

• 설명

renam() 함수는 파일의 이름을 변경할 뿐만 아니라, 파일을 다른 경로로 이동도 한다.

 

• 결과 화면

 

240. 파일 복사하기 (fread, fwrite)

더보기

• 소스

#include <stdio.h>

int main(void)
{
    FILE *fpR, *fpW;
    char buff;
    size_t len;

    fpR = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file.bin", "rb");
    if (fpR == NULL)
    {
        perror("파일 읽기 개방 에러");
        return 1;
    }

    fpW = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file_copy.bin", "wb");
    if (fpW == NULL)
    {
        perror("파일 쓰기 개방 에러");
        fclose(fpR);
        return 1;
    }

    while ((len = fread(&buff, 1, 1, fpR)) > 0)
    {
        if (fwrite(&buff, 1, 1, fpW) != 1)
        {
            perror("파일 쓰기 에러");
            fclose(fpR);
            fclose(fpW);
            return 1;
        }
    }

    if (ferror(fpR))
    {
        perror("파일 읽기 에러");
        fclose(fpR);
        fclose(fpW);
        return 1;
    }

    fclose(fpR);
    fclose(fpW);

    puts("파일을 성공적으로 복사하였습니다.");
    return 0;
}

 

• 설명

while ((len = fread(&buff, 1, 1, fpR)) > 0)

파일 fpR에서 1바이트씩 읽어와 buff에 저장하며, 읽은 바이트 수를 len에 반환한다.

 

if (fwrite(&buff, 1, 1, fpW) != 1)

buff에 저장된 1바이트를 파일 fpW에 쓰고, 정상적으로 1바이트가 쓰였는지 확인한다.

 

• 결과 화면

241.  두 개의 파일 합치기

더보기

• 소스

#include <stdio.h>

#define FILEREAD 4096

int main(void)
{
    FILE *fpR1, *fpR2, *fpW;
    char buff[FILEREAD];
    size_t len;

    fpR1 = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file.bin", "rb");
    if (fpR1 == NULL)
    {
        perror("파일 읽기 개방 에러1");
        return 1;
    }

    fpR2 = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file_copy.bin", "rb");
    if (fpR2 == NULL)
    {
        perror("파일 읽기 개방 에러2");
        fclose(fpR1);
        return 1;
    }

    fpW = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/file2.bin", "wb");
    if (fpW == NULL)
    {
        perror("파일 쓰기 개방 에러");
        fclose(fpR1);
        fclose(fpR2);
        return 1;
    }

    // 첫 번째 파일 복사
    while ((len = fread(buff, 1, FILEREAD, fpR1)) > 0)
    {
        if (fwrite(buff, 1, len, fpW) != len)
        {
            perror("파일 쓰기 에러 1");
            fclose(fpR1);
            fclose(fpR2);
            fclose(fpW);
            return 1;
        }
    }

    if (ferror(fpR1))
    {
        perror("파일 읽기 에러 1");
        fclose(fpR1);
        fclose(fpR2);
        fclose(fpW);
        return 1;
    }
    fclose(fpR1);

    // 두 번째 파일 복사
    while ((len = fread(buff, 1, FILEREAD, fpR2)) > 0)
    {
        if (fwrite(buff, 1, len, fpW) != len)
        {
            perror("파일 쓰기 에러 2");
            fclose(fpR2);
            fclose(fpW);
            return 1;
        }
    }

    if (ferror(fpR2))
    {
        perror("파일 읽기 에러 2");
        fclose(fpR2);
        fclose(fpW);
        return 1;
    }
    fclose(fpR2);

    fclose(fpW);
    puts("파일이 성공적으로 합쳐졌습니다.");
}

 

• 설명

두 개의 파일을 각각 읽어서 버퍼 단위로 출력 파일에 차례대로 쓰는 방식으로 합치고, 작업 중 에러가 발생하면 즉시 종료하며, 모두 완료되면 성공 메시지를 출력한다.

 

• 결과 화면

 

242. 파일에서 특정 문자열 검색하기 (strstr)

더보기

• 소스

#include <stdio.h>
#include <string.h>

void main( void )
{
    FILE *fp;
    char buff[200];
    int line = 1;

    fp = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/무제 폴더/file.txt", "r");
    if (fp == NULL)
    {
        perror("파일 읽기 개방 에러");
        return;
    }

    while (!feof(fp))
    {
        fgets(buff, 200, fp);

        if (strstr(buff, "대한민국"))  // [예제-64]
        {
            printf("Line(%2d) : %s", line, buff);
        }

        line++;
    }

    fclose(fp);
}

 

• 설명

fgets() 함수는 텍스트를 한 줄 읽을 때 사용하며,

만약 한 줄의 길이가 199자가 넘을 경우에는 199바이트까지만 읽기 위해서 200을 사용했다.

대부분의 텍스트 파일은 한 줄이 100바이트를 넘지 않기 때문에 200이면 충분히 한 줄을 읽을 수 있다.

 

읽을 버퍼에 "대한민국"이 있는지 검색한 다음에 해당 버퍼를 출력한다.

• 결과 화면



 

243. 파일에서 특정 문자열 교체하기

더보기

• 소스

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void main(void)
{
    FILE *fpR, *fpW;
    char buff[200];
    char *pbuf, *dup;
    int len, pos1, pos2;

    fpR = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/무제 폴더/file.txt", "r");
    if (fpR == NULL)
    {
        perror("파일 읽기 개방 에러");
        return;
    }

    fpW = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/무제 폴더/file_change.txt", "w+");
    if (fpW == NULL)
    {
        perror("파일 쓰기 개방 에러");
        fclose(fpR);
        return;
    }

    while (!feof(fpR))
    {
        fgets(buff, 200, fpR);
        pbuf = strstr(buff, "대한민국");

        if (pbuf)
        {
            len = strlen(buff);                   // 전체 문자열 길이
            pos1 = pbuf - buff;                   // "대한민국" 위치
            dup = strdup(buff);                   // 원본 백업

            // "대한민국" 제거
            memset(&buff[pos1], 0, len - pos1);
            // "한국" 삽입
            strcat(buff, "한국");

            // 이어지는 나머지 문자열 붙이기
            pos1 = pbuf - buff + strlen("대한민국");
            pos2 = pbuf - buff + strlen("한국");
            strcpy(&buff[pos2], &dup[pos1]);

            free(dup);
        }

        if (!feof(fpR))
            fputs(buff, fpW);
    }

    fclose(fpR);
    fclose(fpW);

    puts("대한민국을 한국으로 모두 교체하였습니다.");
}

 

• 설명

텍스트 파일에서 "대한민국"이라는 문자열을 찾아 모두 "한국"으로 바꾼 후, 변경된 내용을 새로운 파일에 저장하는 작업을 수행

1. 원본 파일을 한 줄씩 읽어 fgets()로 버퍼에 저장한다.

2. strstr()로 "대한민국"을 찾으면 해당 위치를 기준으로 문자열을 분리하고, strnset()으로 원래 문자열 일부를 지우고 "한국"을 삽입 후, 나머지 문자열을 덧붙여 변경한다.

3. 변경한 문자열을 출력 파일에 기록하고, 모든 작업이 완료되면 성공 메시지를 출력한다.

 

• 결과 화면

244. 연/월/일 시:분:초 출력하기 (time, localtime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

void main(void)
{
    FILE *fp;
    char buff[200];
    time_t now;
    struct tm t;

    fp = fopen("/Users/jeonjoonsu/Desktop/무제 폴더/무제 폴더/file.txt", "w+");
    if (fp == NULL)
    {
        perror("파일 쓰기 개방 에러");
        fclose(fp);
        return;
    }

    now = time(NULL);                 
    t = *localtime(&now);            
    sprintf(buff, "%d/%d/%d %d:%d:%d", 
        t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
        t.tm_hour, t.tm_min, t.tm_sec);

    fputs(buff, fp);               
    fclose(fp);

    puts(buff);
    puts("시간을 /Users/jeonjoonsu/Desktop/무제 폴더/무제 폴더/file.txt에 저장하였습니다.");
}

 

• 설명

파일을 쓰기 위해 개방 후, 현재 시간을 파일에 저장한다.

 

• 결과 화면

 

245. 출생일로부터 오늘까지의 경과일 수 구하기 (mktime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

#define DAYSEC (24 * 60 * 60)  // 반드시 괄호로 묶어야 합니다.

void main(void)
{
    time_t n1, n2;
    struct tm t1, t2;
    double elapsed;

    t1.tm_year = 103;  // 년 (2003년) — 1900을 뺀 값
    t1.tm_mon  = 10 - 1; // 월 (10월) — 1을 뺀 값
    t1.tm_mday = 15;   // 일 (15일)
    t1.tm_hour = 0;    // 시
    t1.tm_min  = 0;    // 분
    t1.tm_sec  = 0;    // 초

    n1 = time(NULL);              // 현재 시간 얻기
    t2 = *localtime(&n1);         // 현재 시간의 구조체 얻기

    n1 = mktime(&t1);             // 생일을 time_t로 변환
    n2 = mktime(&t2);             // 현재를 time_t로 변환

    n2 = n2 - n1;                 // 시간 차이 계산
    elapsed = (double)(n2 / DAYSEC);  // 초를 일로 변환

    printf("김서진은 태어난 지 %.f일째 입니다.\n", elapsed);
}

 

• 설명

출생년월일을 1970년 1월 1일 0시를 기준으로 한 시간(초)로 변환한다.

오늘 날짜를 출생년월일을 1970년 1월 1일 0시를 기준으로 한 시간(초)로 변환한다.

 

두 날짜 간의 시간(초) 차이를 구한 후, 경과된 시간(초)을 날짜(일)로 환산한다.

경과된 날짜를 출력한다.

 

• 결과 화면

 

246. 각 달의 마지막 날짜 구하기 (mktime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

#define DAYSEC 86400L  // 하루의 초(24*60*60)

int main()
{
    int i;
    time_t now;
    struct tm t1, t2;
    int n1, n2, last;

    now = time(NULL);
    t1 = *localtime(&now);
    t1.tm_mday = 1;  // 각 월의 1일로 초기화
    t2 = t1;

    for (i = 0; i <= 11; i++)
    {
        t1.tm_mon = i;
        t2.tm_mon = i + 1;

        n1 = mktime(&t1);  // i월 1일
        n2 = mktime(&t2);  // (i+1)월 1일

        last = (n2 - n1) / DAYSEC;  // i월의 마지막 날짜 계산

        printf("%d년 %2d월의 마지막 날짜는 %d일입니다.\n",
               t1.tm_year + 1900, t1.tm_mon + 1, last);
    }
}

 

• 설명

1. mktime()을 이용해 각 월의 1일부터 다음 달 1일까지의 초 차이를 계산한다.

2. 초 차이를 하루(86400초)로 나눠 해당 월의 마지막 날짜를 구한다.

3. 1월부터 12월까지 각 월의 마지막 날짜를 출력한다.

 

• 결과 화면

 

247. D-DAY 구하기 (mktime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

#define DAYSEC 86400L  // 하루를 초로 환산한 값

int main()
{
    time_t now;
    struct tm t, dday = { 0, 0, 0, 8, 8, 2028 };  // 2028년 9월 8일 (주의: 월은 0부터 시작)
    int n1, n2, nDday;

    now = time(NULL);
    t = *localtime(&now);  // 현재 시간을 구조체로 저장

    dday.tm_year -= 1900;  // tm 구조체 기준으로 조정
    dday.tm_mon  -= 1;

    t.tm_hour = 0;
    t.tm_min  = 0;
    t.tm_sec  = 0;

    n1 = mktime(&t);       // 오늘 날짜의 초 단위 시간
    n2 = mktime(&dday);    // D-Day 날짜의 초 단위 시간

    nDday = (n2 - n1) / DAYSEC;  // 날짜 차이 계산

    printf("오늘은 날짜는 %s", ctime(&now));
    printf("최수린의 생일 : %d일 남았습니다. (%d/%d/%d) \n",
           nDday, dday.tm_year + 1900, dday.tm_mon + 1, dday.tm_mday);
}

 

• 설명

 

1. mktime()으로 오늘과 D-Day(2028년 9월 8일)를 초 단위로 변환합니다.

2. 두 시간의 차이를 하루 초(86400L)로 나누어 남은 날짜 수를 구합니다.

3. ctime()으로 현재 날짜를 출력하고, 남은 일수와 함께 D-Day 정보를 출력합니다.

 

 

 

• 결과 화면

 

248. 오늘 날짜로부터 크리스마스까지의 남은 시간 구하기

더보기

• 소스

#include <stdio.h>
#include <time.h>

void main(void)
{
    time_t now, future;
    struct tm t, christmas = { 0, 0, 0, 25, 12, 2025 };  // 2025년 12월 25일
    int n1, n2, nChristmas;
    struct tm *diff_tm;

    now = time(NULL);            // 현재 시간 (time_t)
    t = *localtime(&now);        // 현재 시간 (struct tm)

    christmas.tm_year -= 1900;   // tm 구조체 기준 연도 조정
    christmas.tm_mon  -= 1;      // tm 구조체 기준 월 조정 (0~11)

    n1 = mktime(&t);             // 현재 시각을 time_t로 변환
    n2 = mktime(&christmas);     // 크리스마스 시각을 time_t로 변환

    nChristmas = n2 - n1;        // 남은 초 수
    future = 0 + nChristmas;     // 0초(1970-01-01) 기준에서 n초 후 = 남은 초를 시간으로 인식시킴

    diff_tm = localtime(&future);  // 이 시각을 구조체로 변환

    printf("오늘 날짜는 %s", ctime(&now));
    printf("크리스마스까지 남은 시간은 %d개월 %d일 %d시간 %d분 %d초입니다.\n",
           diff_tm->tm_mon, diff_tm->tm_mday,
           diff_tm->tm_hour, diff_tm->tm_min, diff_tm->tm_sec );
}

 

• 설명

 

1. 현재 시간과 2025년 크리스마스의 시간 차이를 초 단위로 계산하고,

2. 그 초 차이를 1970년 기준으로 환산해 localtime()을 통해 월·일·시·분·초로 변환한 뒤,

3. 크리스마스까지 남은 시간을 구조체 형태로 출력한다.

 

• 결과 화면

 

249. 오늘 날짜에 임의의 날짜 더하고 빼기

더보기

• 소스

#include <stdio.h>
#include <time.h>

int main()
{
    time_t now;
    struct tm t, tb;

    now = time(NULL);
    t = *localtime(&now);
    tb = t;

    tb.tm_mon += 100;   // 100개월
    tb.tm_mday += 90;   // 90일
    tb.tm_hour += 80;   // 80시간

    mktime(&tb);

    printf("오늘 날짜는 %d/%d/%d %d:%d:%d입니다. \n",
        t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
        t.tm_hour, t.tm_min, t.tm_sec);

    printf("100개월 90일 80시간을 더한 날짜는 %d/%d/%d %d:%d:%d입니다. \n",
        tb.tm_year + 1900, tb.tm_mon + 1, tb.tm_mday,
        tb.tm_hour, tb.tm_min, tb.tm_sec);

    tb.tm_mon -= 100;   // 100개월
    tb.tm_mday -= 90;   // 90일
    tb.tm_hour -= 80;   // 80시간

    mktime(&tb);

    printf("100개월을 90일 80시간을 뺀 날짜는 %d/%d/%d %d:%d:%d입니다. \n",
        tb.tm_year + 1900, tb.tm_mon + 1, tb.tm_mday,
        tb.tm_hour, tb.tm_min, tb.tm_sec);
}

 

• 설명

 

1. 현재 시간에 100개월, 90일, 80시간을 더한 미래 날짜를 계산하고,

2. mktime()을 사용해 날짜·시간을 자동으로 보정한 후 출력하며,

3. 다시 그만큼의 시간(100개월, 90일, 80시간)을 빼서 원래 시간으로 복원한다.

 

• 결과 화면

 

250. 크리스마스의 요일 구하기

더보기

• 소스

#include <stdio.h>
#include <time.h>

int main()
{
    struct tm christmas = { 0, 0, 0, 25, 12 - 1, 2025 - 1900 };
    char *wday[] = { "일", "월", "화", "수", "목", "금", "토" };
    char buff[100];

    mktime(&christmas);

    strftime(buff, sizeof(buff), "2025년 12월 25일은 %A입니다.", &christmas);
    puts(buff);

    printf("2025년 12월 25일은 %s요일입니다.\n", wday[christmas.tm_wday]);
}

 

• 설명

1. struct tm을 이용해 2025년 12월 25일 날짜 정보를 설정한다.

2. mktime()으로 요일 정보를 포함한 날짜 계산을 보정한다.

3. strftime()과 요일 배열(wday[])을 활용해 해당 날짜의 요일을 출력한다.

 

• 결과 화면

251.  세계 표준 시와 국내 표준 시의 시간 차 구하기 (localtime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

int main()
{
    time_t now, n1, n2;
    struct tm t1, t2;

    time(&now);
    t1 = *localtime(&now);   // 현재 로컬(대한민국) 시간
    t2 = *gmtime(&now);      // 현재 GMT(세계 표준시) 시간

    n1 = mktime(&t1);        // 로컬 시간 → time_t
    n2 = mktime(&t2);        // GMT 시간 → time_t

    printf("세계 표준 시와 대한민국의 시간 차이 : %g 시간\n",
           difftime(n1, n2) / 3600.);
}

 

• 설명

 

1. time(), localtime(), gmtime()을 사용해 현재 로컬 시간과 GMT 시간을 struct tm 형태로 가져옵니다.

2. mktime()을 통해 각각의 시간 구조체를 time_t 타입(초 단위)으로 변환합니다.

3. difftime()로 두 시간의 차이를 구해 몇 시간 차이인지 출력합니다.

 

 

• 결과 화면

 

252. 5초간 지연하는 함수 구현하기 (clock)

더보기

• 소스

#include <stdio.h>
#include <time.h>

void my_sleep(int sec);  // 사용자 정의 sleep 함수 선언

int main(void)
{
    time_t n1, n2;

    time(&n1);       // 시작 시간 저장
    my_sleep(5);     // 5초 동안 지연
    time(&n2);       // 종료 시간 저장

    printf("%g초가 지연되었습니다.\n", difftime(n2, n1));

    return 0;
}

// 사용자 정의 sleep 함수
void my_sleep(int sec)
{
    clock_t start_time = clock();
    // 원하는 시간이 지날 때까지 루프
    while ((clock() - start_time) < sec * CLOCKS_PER_SEC)
        ;
}

 

• 설명

 

1. 현재 시간을 저장한 후 my_sleep(5)로 약 5초간 CPU를 계속 돌며 대기한다.

2. 대기 후 다시 시간을 저장하고, 시작 시간과 종료 시간의 차이를 초 단위로 계산한다.

3. ㅂ222ㅈㄴㄷㅇㄹ사용자 정의 my_sleep 함수는 clock()과 CLOCKS_PER_SEC를 이용해 지정한 초만큼 지연시키는 동작을 수행한다.


 

 

• 결과 화면

 

253. 두 시간 간의 차이 구하기 (mktime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

int main()
{
    struct tm t1, t2;
    time_t n1, n2;
    int n3;

    t1.tm_year = 2020 - 1900;  // 년
    t1.tm_mon = 6 - 1;         // 월 (0부터 시작)
    t1.tm_mday = 15;           // 일
    t1.tm_hour = 2;            // 시
    t1.tm_min = 12;            // 분
    t1.tm_sec = 50;            // 초
    t1.tm_isdst = -1;          // 일광 절약 시간 여부 시스템에 맡김

    t2.tm_year = 2020 - 1900;
    t2.tm_mon = 6 - 1;
    t2.tm_mday = 15;
    t2.tm_hour = 7;
    t2.tm_min = 35;
    t2.tm_sec = 22;
    t2.tm_isdst = -1;          // 일광 절약 시간 여부 시스템에 맡김

    n1 = mktime(&t1);
    n2 = mktime(&t2);

    n3 = (int)difftime(n2, n1);

    printf("시간 1 : %4d-%02d-%02d %02d:%02d:%02d\n",
           t1.tm_year + 1900, t1.tm_mon + 1, t1.tm_mday,
           t1.tm_hour, t1.tm_min, t1.tm_sec);

    printf("시간 2 : %4d-%02d-%02d %02d:%02d:%02d\n",
           t2.tm_year + 1900, t2.tm_mon + 1, t2.tm_mday,
           t2.tm_hour, t2.tm_min, t2.tm_sec);

    printf("시간차이 : %d:%d:%d\n",
           n3 / 3600, (n3 % 3600) / 60, n3 % 60);
}

 

• 설명

 

1. struct tm 구조체에 두 날짜와 시간을 설정하고 tm_isdst = -1로 일광 절약 시간 자동 판단을 지정한다.

2. mktime()으로 time_t 타입으로 변환 후 두 시간의 차이를 초 단위로 계산한다.

3. 각각의 시간과 두 시간의 차이를 시:분:초 형식으로 출력한다.


 

• 결과 화면

 

254. 두 날짜 간의 차이 구하기 (mktime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

int main()
{
    struct tm t1, t2, t3;
    time_t n1, n2, n3;
    struct tm *diff_tm;

    t1.tm_year = 2020 - 1900;
    t1.tm_mon = 6 - 1;
    t1.tm_mday = 23;
    t1.tm_hour = 1;
    t1.tm_min = 12;
    t1.tm_sec = 50;

    t2.tm_year = 2020 - 1900;
    t2.tm_mon = 8 - 1;
    t2.tm_mday = 19;
    t2.tm_hour = 3;
    t2.tm_min = 35;
    t2.tm_sec = 22;

    n1 = mktime(&t1);
    n2 = mktime(&t2);
    n3 = n2 - n1;

    diff_tm = gmtime(&n3);

    printf("날짜 1 : %4d-%02d-%02d %02d:%02d:%02d\n",
           t1.tm_year + 1900, t1.tm_mon + 1, t1.tm_mday,
           t1.tm_hour, t1.tm_min, t1.tm_sec);

    printf("날짜 2 : %4d-%02d-%02d %02d:%02d:%02d\n",
           t2.tm_year + 1900, t2.tm_mon + 1, t2.tm_mday,
           t2.tm_hour, t2.tm_min, t2.tm_sec);

    printf("날짜차이 : %d일 %d시간 %d분 %d초\n",
           diff_tm->tm_yday + 1, diff_tm->tm_hour, diff_tm->tm_min, diff_tm->tm_sec);

    return 0;
}

 

• 설명

 

1. 지정한 두 날짜를 mktime()으로 time_t 타입으로 변환한 후, 두 시간의 초 차이를 계산한다.

2. 초 차이를 gmtime()으로 변환해, 연도 기준이 아닌 일(day), 시(hour), 분(min), 초(sec) 단위로 분해한다.

3. 원래 날짜와 날짜 차이를 출력하여, 두 날짜 사이의 경과 시간을 사람이 읽기 쉬운 형식으로 보여준다.


 

 

• 결과 화면

 

255. 올해의 경과된 날짜 수 구하기 (localtime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

int main()
{
    time_t now;
    struct tm t;

    now = time(NULL);
    t = *localtime(&now);

    printf("올해의 경과일수 : %d\n", t.tm_yday);
}

 

• 설명

오늘의 날짜에 대한 시간(초)을 struct tm 구조체로 변환한다.

이때 tm, tm_yday에는 자동으로 올해의 경과된 일 수가 계산되어 저장된다.

 

• 결과 화면

 

256. 올해의 경과된 주의 수 구하기 (strftime)

더보기

• 소스

#include <stdio.h>
#include <time.h>

int main()
{
    time_t now;
    struct tm t;
    char buff[100];

    now = time(NULL);
    t = *localtime(&now);

    strftime(buff, sizeof(buff), "올해의 경과된 주 : %U주", &t);
    puts(buff);
}

 

• 설명

경과된 일을 구하기 위해서는 strftime() 함수에서 %U 또는 %W를 사용한다.

%U는 일요일을 기준으로 주의 수를 계산하며, %W는 월요일을 기준으로 주의 수를 계산한다.

 

• 결과 화면

 

257. 오늘의 요일 구하기

더보기

• 소스

#include <stdio.h>
#include <time.h>

int main()
{
    time_t now;
    struct tm t;
    char buff[100];

    now = time(NULL);
    t = *localtime(&now);

    strftime(buff, sizeof(buff), "요일 : %a", &t);
    puts(buff);
    strftime(buff, sizeof(buff), "요일 : %A", &t);
    puts(buff);

    printf("%d\n", t.tm_wday);
}

 

• 설명

%A는 영문으로 요일을 전부 표시하며, %a는 영문으로 요일을 축약해서 표시한다.

localtime() 함수의 요일은 일요일이면 0, 월요일이면 1, 화요일이면 2, ... 순서대로 출력된다.

 

• 결과 화면

 

258. 오전 / 오후 표시하기

더보기

• 소스

#include <stdio.h>
#include <time.h>
#include <string.h>

int main()
{
    time_t now;
    struct tm t;
    char buff[100], AMPM[10];

    now = time(NULL);
    t = *localtime(&now);

    strftime(buff, sizeof(buff), "%Y.%m.%d %p %I:%M:%S", &t);
    strftime(AMPM, sizeof(AMPM), "%p", &t);

    if (strcmp(buff, "AM") == 0)
        strcpy(AMPM, "오전");
    else if (strcmp(buff, "PM") == 0)
        strcpy(AMPM, "오후");

    strcat(buff, AMPM);
    puts(buff);
}

 

• 설명

"AM/PM" 또는 "오전/오후"를 구하고, 만약 영문으로 "AM/PM"이 설정되어 있다면, "AM/PM"값을 "오전/오후"로 변경하여 buff에 추가한다.

 

• 결과 화면

 

259. AM / PM 표시하기

더보기

• 소스

#include <stdio.h>
#include <time.h>
#include <string.h>

int main()
{
    time_t now;
    struct tm t;
    char buff[100], AMPM[10];

    now = time(NULL);
    t = *localtime(&now);

    strftime(buff, sizeof(buff), "%Y.%m.%d %H:%M:%S ", &t);
    strftime(AMPM, sizeof(AMPM), "%p", &t);

    if (strcmp(AMPM, "AM") == 0)
        strcpy(AMPM, "오전");
    else if (strcmp(AMPM, "PM") == 0)
        strcpy(AMPM, "오후");

    strcat(buff, AMPM);
    puts(buff);
}

 

• 설명

"AM/PM" 또는 "오전/오후"를 구하고, 만약 한글로 "오전/오후"이 설정되어 있다면, "오전/오후"값을 "AM/PM"로 변경하여 buff에 추가한다.

• 결과 화면

 

260. 문자열로 된 날짜를 time_t 형식으로 변환하기 (atoi, mktime)

더보기

• 소스

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main()
{
    char date[] = "2025-12-17";   // 0123456789
    struct tm t = {0};
    time_t now;

    t.tm_mday = atoi(&date[8]);
    date[7] = 0;
    t.tm_mon = atoi(&date[5]) - 1;
    date[4] = 0;
    t.tm_year = atoi(&date[0]) - 1900;

    now = mktime(&t);
    printf("2025-12-17은 time_t 값으로 변환하면 %ld입니다.\n", now);
}

 

• 설명

atoi() 함수를 사용하여 연월일을 t 구조체에 넣는다.

단, 월은 반드시 -1을 하여야 하며, 년은 1900을 빼야 한다.

 

• 결과 화면

 

261.  문자열로 된 날짜를 struct tm 형식으로 변환하기

더보기

• 소스

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main()
{
    char date1[] = "2025-12-17 02:09:21";  // 0123456789012345678
    struct tm t = {0};

    t.tm_sec  = atoi(&date1[17]);
    t.tm_min  = atoi(&date1[14]);
    t.tm_hour = atoi(&date1[11]);
    date1[10] = 0;
    t.tm_mday = atoi(&date1[8]);
    date1[7]  = 0;
    t.tm_mon  = atoi(&date1[5]) - 1;
    date1[4]  = 0;
    t.tm_year = atoi(&date1[0]) - 1900;

    date1[13] = 0;
    date1[16] = 0;

    mktime(&t);

    printf("struct tm 변환 후 출력: %4d-%02d-%02d %02d:%02d:%02d\n",
           t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
           t.tm_hour, t.tm_min, t.tm_sec);
}

 

• 설명

문자열 "2025-12-17 02:09:21"에서 날짜와 시간을 파싱하여 struct tm 구조체에 저장하고,

mktime() 함수를 사용하여 정리한 후 다시 출력한다.

 

• 결과 화면

 

262. 날짜 및 시간을 다양한 방법으로 출력하기 (_ftime)

더보기

• 소스

// Windows
// #include <stdio.h>
// #include <time.h>
// #include <sys/timeb.h>

// int main()
// {
//     struct _timeb tb;
//     struct tm t;
//     char buff[100];

//     _ftime(&tb);
//     t = *localtime(&tb.time);

//     printf("%4d-%02d-%02d %02d:%02d:%02d.%d\n",
//         t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
//         t.tm_hour, t.tm_min, t.tm_sec, tb.millitm);

//     printf(ctime(&tb.time));     // 예제 174
//     printf(asctime(&t));         // 예제 175
//     puts(_strdate(buff));
//     puts(_strtime(buff));

//     strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S %p (%a)", &t);
//     puts(buff);

//     strftime(buff, sizeof(buff), "%#Y-%#m-%#d %#H:%#M:%#S %p (%a)", &t);
//     puts(buff);

//     strftime(buff, sizeof(buff), "%c", &t);
//     puts(buff);

//     strftime(buff, sizeof(buff), "%x %X", &t);
//     puts(buff);

//     strftime(buff, sizeof(buff), "%#c", &t);
//     puts(buff);

//     strftime(buff, sizeof(buff), "%#x", &t);
//     puts(buff);
// }

// macOS
#include <stdio.h>
#include <time.h>
#include <sys/time.h>

int main()
{
    struct timeval tv;
    struct tm t;
    char buff[100];

    gettimeofday(&tv, NULL);
    localtime_r(&tv.tv_sec, &t);

    printf("%4d-%02d-%02d %02d:%02d:%02d.%03d\n",
           t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
           t.tm_hour, t.tm_min, t.tm_sec, tv.tv_usec / 1000);

    strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S %p (%a)", &t);
    puts(buff);

    strftime(buff, sizeof(buff), "%c", &t);
    puts(buff);

    strftime(buff, sizeof(buff), "%x %X", &t);
    puts(buff);
}

 

• 설명

 

1. 현재 시간을 밀리초 단위까지 포함하여 gettimeofday()와 localtime_r()로 받아와 출력한다.

2. strftime()을 사용해 다양한 날짜와 시간 형식으로 현재 시간을 문자열로 변환하여 출력한다.

3. macOS 환경에서 표준 C 라이브러리와 POSIX 함수를 활용해 정확하고 다양한 시간 정보를 표시한다.


 

 

• 결과 화면

 

263. 야구 게임 만들기

더보기

• 소스

#include <stdio.h>      // fflush, scanf, puts, printf
#include <time.h>       // time
#include <stdlib.h>     // srand, rand
#include <memory.h>     // memset

// 입력 버퍼를 비우는 함수 (표준 C에서 안전하게 사용)
void clear_input_buffer(void) {
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF)
        ;
}

int main()
{
    int com[3] = {0,}; 
    int gamer[3] = {0,}; 
    int guess[10] = {0,}; 
    int count, i; 
    int strike, ball; 
    char yesno;

    srand(time(NULL));  // 난수 초기화

    puts("야구 게임을 시작합니다.");

    while(1)
    {
        // 컴퓨터 숫자 생성 (중복 없이)
        do {
            com[0] = rand() % 10;
            com[1] = rand() % 10;
            com[2] = rand() % 10;
        } while(com[0] == com[1] || com[0] == com[2] || com[1] == com[2]);

        count = 1;

        puts("숫자 0~9를 공백으로 분리하여 3개 입력하고 엔터키를 치세요 !!");

        memset(guess, 0, sizeof(guess));

        while(1)
        {
            strike = 0;
            ball = 0;

            for(i=0; i<10; i++)
            {
                printf("%d ", guess[i]);
            }
            printf("\n");

            printf("3개의 숫자(0~9)를 입력하세요 : ");
            if(scanf("%d %d %d", &gamer[0], &gamer[1], &gamer[2]) != 3) {
                puts("입력이 잘못되었습니다. 숫자 3개를 정확히 입력하세요.");
                clear_input_buffer();
                continue;
            }
            clear_input_buffer();

            // 음수 및 0~9 범위 체크
            if(gamer[0] < 0 || gamer[0] > 9 || gamer[1] < 0 || gamer[1] > 9 || gamer[2] < 0 || gamer[2] > 9) {
                puts("입력한 숫자가 0~9 범위를 벗어났습니다.");
                continue;
            }

            // 중복 입력 체크
            if(gamer[0] == gamer[1] || gamer[0] == gamer[2] || gamer[1] == gamer[2]) {
                puts("중복된 숫자를 입력했습니다. 서로 다른 숫자 3개를 입력하세요.");
                continue;
            }

            // 스트라이크, 볼 계산
            for(i=0; i<3; i++) {
                if(com[i] == gamer[i]) strike++;
                else if(gamer[0] == com[i] || gamer[1] == com[i] || gamer[2] == com[i]) ball++;
            }

            // 사용자 입력 숫자 표시 배열 업데이트
            memset(guess, 0, sizeof(guess));
            guess[gamer[0]] = 1;
            guess[gamer[1]] = 1;
            guess[gamer[2]] = 1;

            printf("\n[%2d회] %d 스트라이크 %d 볼\n\n", count, strike, ball);
            if(strike == 3) break;
            count++;
        }

        printf("게임을 계속하시겠습니까 (y/n)? ");
        if(scanf(" %c", &yesno) != 1) {
            clear_input_buffer();
            puts("입력이 잘못되었습니다.");
            continue;
        }
        clear_input_buffer();

        if(yesno == 'N' || yesno == 'n') break;
    }

    puts("게임을 종료합니다.");
    return 0;
}

 

• 설명

 

1. 컴퓨터가 중복 없는 0~9 사이의 무작위 숫자 3개를 생성한다.

2. 사용자는 3개의 숫자를 입력하며, 컴퓨터 숫자와의 스트라이크/볼 판정을 받는다.

3. 스트라이크를 달성할 때까지 반복하며, 회차 수를 출력하고 게임 재시작 여부를 묻는다.


 

 

• 결과 화면

 

264. 스택 구현하기

더보기

• 소스

#include <stdio.h>   // puts, gets, printf
#include <string.h>  // strlen, memset, memcpy
#include <stdlib.h>  // atoi

int push(int value);
int pop(int *value);

#define STACK_MAX 100

typedef struct tagStack {
    int array[STACK_MAX];
    int top;
    int bottom;
} STACK;

STACK s;

int main() {
    char buff[100], tmp[100];
    char *op = "+-*/%";
    int index;
    int value1 = 0, value2 = 0;

    s.top = STACK_MAX;

    puts("계산식을 1*2처럼 입력하고 엔터키를 치세요.");
    puts("아무것도 입력하지 않으면 계산이 종료됩니다.");

    for (;;) 
    {
        printf("계산식 : ");
        gets(buff);

        if (strlen(buff) == 0) break;

        memset(tmp, 0, sizeof(tmp));

        index = strcspn(buff, op); // 연산자 위치 찾기
        memcpy(tmp, buff, index);  // 연산자 전 숫자 복사

        value1 = atoi(tmp);
        value2 = atoi(&buff[index + 1]);

        switch (buff[index]) {
            case '+': value1 += value2; break;
            case '-': value1 -= value2; break;
            case '*': value1 *= value2; break;
            case '/': value1 /= value2; break;
            case '%': value1 %= value2; break;
            default:
                puts("잘못된 연산자를 사용하였습니다.");
                continue;
        }

        if (push(value1) == -1) {
            puts("더 이상 저장할 수 없습니다.");
        }

        printf("%s = %d, s.top = %d\n\n", buff, value1, s.top);
    }

    value1 = 0;

    for (;;) 
    {
        if (pop(&value2) == -1) break;
        value1 += value2;
    }

    printf("계산의 총합은 %d입니다.\n", value1);
}

int push(int value) 
{
    if (s.top == 0) return -1;
    s.array[--s.top] = value;
    return 0;
}

int pop(int *value) 
{
    if (s.top == STACK_MAX) return -1;
    *value = s.array[s.top++];
    return 0;
}

 

• 설명

 

후입선출(LIFO) 구조인 스택을 사용하여 계산 결과를 저장하고, 최종적으로 모든 계산 결과의 합계를 출력하는 프로그램이다.

1. 사용자에게 사칙연산 식을 입력받아 연산 후 결과를 스택에 저장한다.

2. 연산자는 +, -, *, /, %만 허용하며 잘못된 입력은 무시된다.

3. 입력 종료 후 스택에 저장된 모든 결과를 꺼내어 총합을 계산하고 출력한다.


 

 

• 결과 화면

 

265. 큐 구현하기

더보기

• 소스

#include <stdio.h>   // puts, gets, printf
#include <string.h>  // strlen, memset, memcpy
#include <stdlib.h>  // atoi

int add(int value);
int delete(int *value);

#define QUEUE_MAX 100

typedef struct tagQueue {
    int array[QUEUE_MAX];
    int front;
    int rear;
} QUEUE;

QUEUE q;

int main() {
    char buff[100], tmp[100];
    char *op = "+-*/%";
    int index;
    int value1 = 0, value2 = 0;

    q.front = q.rear = 0;

    puts("계산식을 1*2처럼 입력하고 엔터키를 치세요.");
    puts("아무것도 입력하지 않으면 계산이 종료됩니다.");

    for (;;) 
    {
        printf("계산식 : ");
        gets(buff);  // 문자열 입력

        if (strlen(buff) == 0) break;

        memset(tmp, 0, sizeof(tmp));
        index = strcspn(buff, op);             // 연산자 위치 찾기
        memcpy(tmp, buff, index);              // 첫 번째 숫자 추출
        value1 = atoi(tmp);                    // 문자열 → 정수
        value2 = atoi(&buff[index + 1]);       // 두 번째 숫자 추출

        switch (buff[index]) {
            case '+' : value1 += value2; break;
            case '-' : value1 -= value2; break;
            case '*' : value1 *= value2; break;
            case '/' : value1 /= value2; break;
            case '%' : value1 %= value2; break;
            default:
                puts("잘못된 연산자를 사용하였습니다.");
                continue;
        }

        if (add(value1) == -1) {
            puts("더 이상 추가할 수 없습니다.");
        }

        printf("%s = %d, q.rear = %d\n", buff, value1, q.rear);
    }
        value1 = 0;

        for (;;) {
            if (delete(&value2) == -1) break;
            value1 += value2;
        }

        printf("계산의 총합은 %d입니다.\n", value1);
    
}

int add(int value) {
    if (q.rear == QUEUE_MAX) return -1;
    q.array[q.rear++] = value;
    return 0;
}

int delete(int *value) {
    if (q.front == q.rear || q.front == QUEUE_MAX) return -1;
    *value = q.array[q.front++];
    return 0;
}

 

• 설명

큐(Queue) 구조를 활용해 사칙연산 결과를 순차적으로 저장하고, 입력 종료 후 총합을 계산하는 프로그램이다.

1. 사용자가 입력한 계산식을 파싱하고 결과값을 큐에 저장한다.

2. 큐에 저장된 연산 결과를 앞에서부터 하나씩 꺼내 총합을 계산한다.

3. 입력이 종료되면 누적된 계산 결과의 총합을 출력한다.

 

• 결과 화면

 

266. 단일 링크드 리스트 구현하기

더보기

• 소스

#include <stdio.h>     // printf, puts
#include <stdlib.h>    
#include <string.h>    // strcpy

int add_list(char* name, char* tel, char* addr);
void print_list(void);
void remove_list(void);

typedef struct tagLinkedList  // [예제-119]
{
    char name[30];     // 이름
    char tel[30];      // 전화번호
    char addr[100];    // 주소
    struct tagLinkedList* next;
} ADDR;

ADDR* g_pAddrHead = NULL;

void main(void)
{
    add_list("홍길동", "1111", "서울특별시 종로구");
    add_list("홍길순", "2222", "서울특별시 강서구");
    add_list("Mr.Kim", "3333", "서울특별시 구로구");
    add_list("김C", "4444", "서울특별시 강동구");
    add_list("최C", "5555", "대전광역시 동구");

    print_list();
    remove_list();
}

int add_list(char* name, char* tel, char* addr)
{
    ADDR* plocal, *pn = g_pAddrHead;

    // g_pAddrHead가 초기화되지 않은 경우, 한 번만 실행됨.
    if (g_pAddrHead == NULL)
    {
        g_pAddrHead = malloc(sizeof(ADDR));  // ADDR 구조체 할당
        if (g_pAddrHead == NULL)
            return 0;

        g_pAddrHead->next = NULL; // 처음에는 반드시 NULL로 초기화
        plocal = g_pAddrHead;
    }
    else
    {
        plocal = malloc(sizeof(ADDR));  // ADDR 구조체 할당
        if (plocal == NULL)
        {
            return 0;
        }

        while (pn->next)
        {
            pn = pn->next;
        }

        pn->next = plocal;      // 다음 리스트를 지정
        plocal->next = NULL;    // 다음 리스트를 NULL로 지정
    }

    strcpy(plocal->name, name);  // 새로 할당된 구조체에 이름 복사
    strcpy(plocal->tel, tel);    // 새로 할당된 구조체에 전화 복사
    strcpy(plocal->addr, addr);  // 새로 할당된 구조체에 주소 복사

    return 1;
}

void print_list(void)
{
    int count = 1;
    ADDR* plist;

    plist = g_pAddrHead;

    // 한 개씩 출력
    while (plist)
    {
        printf("No. %d\n", count++);
        puts(plist->name);
        puts(plist->tel);
        printf("%s\n\n", plist->addr);
        plist = plist->next;
    }
}

void remove_list(void)
{
    ADDR* plocal;

    // 한 개씩 메모리 해제
    while (g_pAddrHead)
    {
        plocal = g_pAddrHead->next;
        free(g_pAddrHead);
        g_pAddrHead = plocal;
    }
}

 

• 설명

연결 리스트(Linked List)를 이용하여 주소록을 관리가 가능하다.

 

1. add_list() 함수는 이름, 전화번호, 주소를 받아 연결 리스트에 노드를 추가합니다.

2. print_list() 함수는 연결 리스트에 저장된 주소록 항목들을 순서대로 출력합니다.

3. remove_list() 함수는 할당된 메모리를 모두 해제하여 주소록을 정리합니다.

 

 

• 결과 화면

 

267. 이중 링크드 리스트 구현하기

더보기

• 소스

#include <stdio.h>     // printf, puts
#include <stdlib.h>    
#include <string.h>    // strcpy

int add_list(char* name, char* tel, char* addr);
void print_list(void);
void remove_list(void);

typedef struct tagLinkedList  // [예제-119]
{
    char name[30];     // 이름
    char tel[30];      // 전화번호
    char addr[100];    // 주소

    struct tagLinkedList* prev;    
    struct tagLinkedList* next;
} ADDR;

ADDR* g_pAddrHead = NULL;

int main()
{
    add_list("홍길동", "1111", "서울특별시 종로구");
    add_list("홍길순", "2222", "서울특별시 강서구");
    add_list("Mr.Kim", "3333", "서울특별시 구로구");
    add_list("김C", "4444", "서울특별시 강동구");
    add_list("최C", "5555", "대전광역시 동구");

    print_list();
    remove_list();
}

int add_list(char* name, char* tel, char* addr)
{
    ADDR* plocal, *pn = g_pAddrHead;

    // g_pAddrHead가 초기화되지 않은 경우, 한 번만 실행됨.
    if (g_pAddrHead == NULL)
    {
        g_pAddrHead = malloc(sizeof(ADDR));  // ADDR 구조체 할당

        if (g_pAddrHead == NULL)
        {
            return 0;
        }

        g_pAddrHead->prev = NULL; // 처음에는 반드시 NULL로 초기화
        g_pAddrHead->next = NULL; // 처음에는 반드시 NULL로 초기화
    }
    else // g_pAddrHead가 초기화된 후 계속 실행된다.
    {
        plocal = malloc(sizeof(ADDR));  // ADDR 구조체 할당

        if (plocal == NULL)
        {
            return 0;
        }

        g_pAddrHead->next = plocal;     // 다음 리스트를 지정
        plocal->prev = g_pAddrHead;     // 이전 리스트를 지정
        g_pAddrHead = plocal;           // 현재 리스트를 plocal로 지정
        g_pAddrHead->next = NULL;        // 다음 리스트를 NULL로 지정
    }

    strcpy(g_pAddrHead->name, name);  // 새로 할당된 구조체에 이름 복사
    strcpy(g_pAddrHead->tel, tel);    // 새로 할당된 구조체에 전화 복사
    strcpy(g_pAddrHead->addr, addr);  // 새로 할당된 구조체에 주소 복사

    return 1;
}

void print_list(void)
{
    int count = 1;
    ADDR* plocal;

    plocal = g_pAddrHead;

    // plocal 리스트의 맨 처음으로 이동
    while (plocal->prev)
    {
        plocal = plocal->prev;
    }

    // 한 개씩 출력
    while (plocal)
    {
        printf("No. %d\n", count++);
        puts(plocal->name);
        puts(plocal->tel);
        printf("%s\n\n", plocal->addr);

        plocal = plocal->next;
    }
}

void remove_list(void)
{
    ADDR* plocal;

    // PA를 리스트의 맨 처음으로 이동
    while (g_pAddrHead->prev)
    {
        g_pAddrHead = g_pAddrHead->prev;
    }
    
    // 한 개씩 메모리 해제
    while (g_pAddrHead)
    {
        plocal = g_pAddrHead->next;

        free(g_pAddrHead);

        g_pAddrHead = plocal;
    }

    g_pAddrHead = NULL;     // 재사용을 하기 위한 초기화
}

 

• 설명

이중 링크드 리스트를 이용하여 주소록을 관리가 가능하다.

1. add_list()는 동적 메모리를 할당해 주소록 정보를 담은 노드를 이중 연결 리스트에 추가한다.

2. print_list()는 리스트의 맨 앞에서부터 모든 노드를 순차적으로 출력한다.

3. remove_list()는 리스트의 노드들을 앞에서부터 하나씩 메모리 해제하여 정리한다.

 

• 결과 화면

 

268. 주소록 구현하기

더보기

• 소스

#include <stdio.h>      // printf, puts, fopen, fwrite, fclose, getchar
#include <stdlib.h>     // malloc, free
#include <string.h>     // strcpy, strstr

#define ADDRFILE "/Users/jeonjoonsu/Desktop/무제 폴더/무제 폴더/file.txt"   // 상대 경로로 수정

typedef struct tagLinkedList {
    char name[30];      // 이름
    char tel[30];       // 전화
    char addr[100];     // 주소

    struct tagLinkedList *prev;
    struct tagLinkedList *next;
} ADDR;

ADDR *g_pAddrHead = NULL;
ADDR *g_pFind;
int g_bSaved = 1;

// 함수 선언
void get_addrlist(void);
int add_list(const ADDR *addr);
int find_list(const char *name);
void SetHeadPosition(void);
void SetTailPosition(void);

void Add_addr(void);
void Find_addr(void);
void Modify_addr(void);
void Delete_addr(void);
void Print_addr(void);
void Save_addr(void);
void Remove_addr(void);

// 안전하게 문자열 입력 받기 (fgets + 개행 제거)
void input(char *buf, int size) {
    fgets(buf, size, stdin);
    buf[strcspn(buf, "\n")] = '\0'; // 개행 제거
}

int main(void) {
    int ch;

    get_addrlist();
    puts("주소록 프로그램 Version 1.0");

    while (1) {
        printf("\n[1]등록 [2]검색 [3]수정 [4]삭제 [5]출력 [S]저장 [Q]종료: ");
        ch = getchar();
        while (getchar() != '\n');  // 버퍼 비우기

        switch (ch) {
            case '1': Add_addr(); break;
            case '2': Find_addr(); break;
            case '3': Modify_addr(); break;
            case '4': Delete_addr(); break;
            case '5': Print_addr(); break;
            case 's': case 'S': Save_addr(); break;
            case 'q': case 'Q':
                if (g_bSaved == 0) {
                    printf("\n\n변경된 주소 데이터를 저장하시겠습니까 (y/n)? ");
                    ch = getchar();
                    while (getchar() != '\n');
                    if (ch == 'Y' || ch == 'y') Save_addr();
                }
                Remove_addr();
                return 0;
            default:
                printf("\n1~5 또는 S/Q를 누르십시오.\n\n");
                break;
        }
    }
}

void get_addrlist(void) {
    ADDR addr;
    FILE *fp = fopen(ADDRFILE, "rb");
    if (!fp) {
        perror("파일 개방 에러");
        return;
    }

    while (fread(&addr, sizeof(ADDR), 1, fp)) {
        if (add_list(&addr) == 0)
            printf("주소 데이터를 리스트에 추가할 수 없습니다.\n");
    }

    fclose(fp);
}

int add_list(const ADDR *addr) {
    ADDR *plocal, *pn = g_pAddrHead;

    SetHeadPosition();

    plocal = malloc(sizeof(ADDR));
    if (!plocal) return 0;
    memset(plocal, 0, sizeof(ADDR));
    strcpy(plocal->name, addr->name);
    strcpy(plocal->tel, addr->tel);
    strcpy(plocal->addr, addr->addr);

    if (!g_pAddrHead) {
        g_pAddrHead = plocal;
    } else {
        while (pn->next) pn = pn->next;
        pn->next = plocal;
        plocal->prev = pn;
    }

    return 1;
}

void SetHeadPosition(void) {
    if (!g_pAddrHead) return;
    while (g_pAddrHead->prev) g_pAddrHead = g_pAddrHead->prev;
}

int find_list(const char *name) {
    ADDR *plocal;

    SetHeadPosition();
    plocal = g_pAddrHead;

    while (plocal) {
        if (strstr(plocal->name, name)) {
            g_pFind = plocal;
            return 1;
        }
        plocal = plocal->next;
    }

    return 0;
}

void Add_addr(void) {
    ADDR addr;
    memset(&addr, 0, sizeof(ADDR));

    printf("\n\n등록할 이름: ");
    input(addr.name, sizeof(addr.name));
    if (strlen(addr.name) == 0) return;

    printf("등록할 전화: ");
    input(addr.tel, sizeof(addr.tel));

    printf("등록할 주소: ");
    input(addr.addr, sizeof(addr.addr));

    if (find_list(addr.name)) {
        printf("\n이미 등록된 이름입니다.\n\n");
        puts(g_pFind->name);
        puts(g_pFind->tel);
        puts(g_pFind->addr);
        return;
    }

    if (add_list(&addr)) {
        g_bSaved = 0;
        printf("\n등록되었습니다.\n\n");
    } else {
        printf("\n등록에 실패했습니다.\n\n");
    }
}

void Find_addr(void) {
    char buff[100] = { 0 };
    ADDR *plocal;

    printf("\n\n검색할 이름/전화/주소의 일부를 입력하세요.\n");
    printf("이름/전화/주소: ");
    input(buff, sizeof(buff));

    if (strlen(buff) == 0) return;

    SetHeadPosition();
    plocal = g_pAddrHead;
    g_pFind = NULL;

    while (plocal) {
        if (strstr(plocal->name, buff) ||
            strstr(plocal->tel, buff) ||
            strstr(plocal->addr, buff)) {
            g_pFind = plocal;
            break;
        }
        plocal = plocal->next;
    }

    if (g_pFind) {
        puts(g_pFind->name);
        puts(g_pFind->tel);
        puts(g_pFind->addr);
    } else {
        printf("\n\n%s를 주소록에서 찾을 수 없습니다.\n\n", buff);
    }
}

void Modify_addr(void) {
    char name[100] = {0};
    ADDR addr;

    while (1) {
        printf("\n\n수정할 이름 : ");
        input(name, sizeof(name));
        if (strlen(name) == 0) return;

        if (!find_list(name)) {
            puts("수정할 이름을 찾을 수 없습니다.");
            continue;
        }
        break;
    }

    printf("\n%s에 대한 주소 데이터:\n", name);
    puts(g_pFind->name);
    puts(g_pFind->tel);
    puts(g_pFind->addr);

    printf("\n새로운 정보 입력 (공백이면 기존 유지):\n");

    printf("이름 : ");
    input(addr.name, sizeof(addr.name));
    if (strlen(addr.name) == 0) strcpy(addr.name, g_pFind->name);

    printf("전화 : ");
    input(addr.tel, sizeof(addr.tel));
    printf("주소 : ");
    input(addr.addr, sizeof(addr.addr));

    strcpy(g_pFind->name, addr.name);
    strcpy(g_pFind->tel, addr.tel);
    strcpy(g_pFind->addr, addr.addr);

    g_bSaved = 0;
    printf("수정 완료.\n");
}

void Delete_addr(void) {
    char name[100] = {0};
    ADDR *plocal;

    while (1) {
        printf("\n\n삭제할 이름 : ");
        input(name, sizeof(name));
        if (strlen(name) == 0) return;

        if (!find_list(name)) {
            puts("삭제할 이름을 찾을 수 없습니다.");
            continue;
        }
        break;
    }

    puts(g_pFind->name);
    puts(g_pFind->tel);
    puts(g_pFind->addr);

    printf("%s를 삭제하시겠습니까 (y/n)? ", name);
    int ch = getchar();
    while (getchar() != '\n');

    if (ch == 'Y' || ch == 'y') {
        if (!g_pFind->prev && !g_pFind->next) {
            free(g_pFind);
            g_pAddrHead = NULL;
        } else if (!g_pFind->prev) {
            plocal = g_pFind->next;
            plocal->prev = NULL;
            free(g_pFind);
            g_pAddrHead = plocal;
        } else if (!g_pFind->next) {
            g_pFind->prev->next = NULL;
            free(g_pFind);
        } else {
            g_pFind->prev->next = g_pFind->next;
            g_pFind->next->prev = g_pFind->prev;
            free(g_pFind);
        }

        g_bSaved = 0;
        printf("삭제되었습니다.\n");
    }
}

void Print_addr(void) {
    int count = 1;
    ADDR *plocal;

    SetHeadPosition();
    plocal = g_pAddrHead;

    printf("\n\n");

    while (plocal) {
        printf("번호. %d\n", count++);
        puts(plocal->name);
        puts(plocal->tel);
        puts(plocal->addr);
        printf("-----------------------\n");
        plocal = plocal->next;
    }
}

void Save_addr(void) {
    FILE *fp = fopen(ADDRFILE, "wb");
    if (!fp) {
        perror("파일 개방 에러");
        return;
    }

    SetHeadPosition();

    ADDR *plocal = g_pAddrHead;
    while (plocal) {
        fwrite(plocal, sizeof(ADDR), 1, fp);
        plocal = plocal->next;
    }

    fclose(fp);
    g_bSaved = 1;
    printf("\n데이터 저장 완료.\n");
}

void Remove_addr(void) {
    ADDR *plocal;

    SetHeadPosition();

    while (g_pAddrHead) {
        plocal = g_pAddrHead->next;
        free(g_pAddrHead);
        g_pAddrHead = plocal;
    }

    g_pAddrHead = NULL;
}

 

• 설명

구조체와 이중 연결 리스트를 이용한 주소록 관리 프로그램이다.

1. 사용자 입력을 통해 이름, 전화번호, 주소를 등록하고 파일로 저장하거나 불러올 수 있다.

2. 연결 리스트에 데이터를 추가(add), 검색(find), 수정(modify), 삭제(delete), 출력(print)하는 기능이 구현되어 있다.

3. 시작 시 파일에서 기존 데이터를 읽어오고 종료 시 변경 내용을 저장할 수 있다.

4. 입력은 fgets()를 사용해 안전하게 처리하고, 연결 리스트는 prev, next 포인터로 구성한다.

5. 수정/삭제 시 기존 데이터를 검색한 후 해당 노드를 적절히 연결하거나 해제한다.

 

• 결과 화면

 

269. TCP / IP 이해하기

더보기

• 소스

#include <stdio.h>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

int main(void)
{
    SOCKET s;
    WSADATA wsaData;
    SOCKADDR_IN sin;

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup 실패, 에러 코드 = %d\n", WSAGetLastError());
        return 1;
    }

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET) {
        printf("소켓 생성 실패, 에러 코드 : %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    sin.sin_port = htons(21);

    if (connect(s, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
        printf("접속 실패, 에러 코드 = %u\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    closesocket(s);
    WSACleanup();

    puts("127.0.0.1의 21번 포트에 접속을 성공하였습니다.");
    return 0;
}

 

• 설명

Windows 환경에서 실행하면 정상적으로 작동
다만, 포트 21(FTP)이 로컬에서 열려 있으면 정상적으로 connect()가 성공한다.

>> 추후 Windows 환경에 세팅 후 확인 예정

 

• 결과 화면

 

 

270. TCP / IP 서버 / 클라이언트 프로그램 만들기

더보기

• 소스

// TCP/IP 서버 프로그램(TCPSERVER.EXE)
#include <stdio.h>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

int main(void)
{
    SOCKET s, cs;
    WSADATA wsaData;
    struct sockaddr_in sin, cli_addr;
    int size = sizeof(cli_addr);
    char data[10] = { 0 };

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup 실패, 에러 코드 = %d\n", WSAGetLastError());
        return 1;
    }

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET) {
        printf("소켓 생성 실패, 에러 코드 : %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    sin.sin_family = AF_INET;
    sin.sin_port = htons(10000);
    sin.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(s, (struct sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) {
        printf("바인드 실패, 에러 코드 = %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    if (listen(s, SOMAXCONN) != 0) {
        printf("리슨 모드 실패, 에러 코드 = %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    printf("클라이언트 접속 대기 중...\n");

    cs = accept(s, (struct sockaddr*)&cli_addr, &size);
    if (cs == INVALID_SOCKET) {
        printf("접속 승인 실패, 에러 코드 = %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    puts("클라이언트와 연결되었습니다.");

    int recv_len = recv(cs, data, 3, 0);
    if (recv_len <= 0) {
        printf("데이터 수신 실패, 에러 코드 = %u\n", WSAGetLastError());
    } else {
        data[recv_len] = '\0';  // 널 종료
        printf("수신된 데이터: %s\n", data);
    }

    closesocket(cs);
    closesocket(s);
    WSACleanup();

    return 0;
}

 

// TCP/IP 클라이언트 프로그램(TCPCLIENT.EXE)
#include <stdio.h>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")  // 최신 라이브러리 사용

int main(void)
{
    SOCKET s;
    WSADATA wsaData;
    struct sockaddr_in sin;
    char data[10] = "abc";

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup 실패, 에러 코드 = %d\n", WSAGetLastError());
        return 1;
    }

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET) {
        printf("소켓 생성 실패, 에러 코드 : %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    sin.sin_port = htons(10000);  // 서버와 동일한 포트

    if (connect(s, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
        printf("접속 실패, 에러 코드 = %u\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    if (send(s, data, 3, 0) != 3) {
        printf("데이터 전송 실패, 에러 코드 = %u\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    puts("\"abc\"를 서버에 전송하였습니다.");

    closesocket(s);
    WSACleanup();

    return 0;
}

• 설명

Windows 환경에서 실행하면 정상적으로 작동

>> 추후 Windows 환경에 세팅 후 확인 예정R

 

• 결과 화면

 

 


이 글은 초보자를 위한 C언어 300제 (김은철 지음, 정보문화사)을 참고하여 작성했습니다.