C 언어 포인터와 문자열 실습 문제 해설

1. 배열에서 최소값과 최대값 찾기

find_min_max 함수는 입력된 숫자들 중에서 최소값과 최대값을 탐색하는 역할을 수행합니다. pmin은 최소값이 저장된 메모리 주소를 가리키고, pmax는 최대값이 저장된 메모리 주소를 가리킵니다.

2. 최대값 탐색 및 주소 반환

아래 코드는 배열에서 최대값을 찾아 해당 원소의 주소를 반환하는 예제입니다.

#include <stdio.h>
#define SIZE 5

void read_values(int arr[], int len);
void display_values(int arr[], int len);
int *locate_peak(int arr[], int len);

int main() {
    int data[SIZE];
    int *peak_ptr;
    
    printf("Enter %d integers:\n", SIZE);
    read_values(data, SIZE);
    
    printf("Values entered:\n");
    display_values(data, SIZE);
    
    printf("Processing...\n");
    peak_ptr = locate_peak(data, SIZE);
    
    printf("Result:\n");
    printf("Maximum value = %d\n", *peak_ptr);
    
    return 0;
}

void read_values(int arr[], int len) {
    for (int idx = 0; idx < len; idx++) {
        scanf("%d", &arr[idx]);
    }
}

void display_values(int arr[], int len) {
    for (int idx = 0; idx < len; idx++) {
        printf("%d ", arr[idx]);
    }
    printf("\n");
}

int *locate_peak(int arr[], int len) {
    int peak_pos = 0;
    
    for (int idx = 1; idx < len; idx++) {
        if (arr[idx] > arr[peak_pos]) {
            peak_pos = idx;
        }
    }
    return &arr[peak_pos];
}

locate_peak 함수는 배열 내 최대값의 위치를 찾아 해당 원소의 주소를 반환합니다. 반환된 포인터를 통해 간접적으로 최대값에 접근할 수 있습니다.

3. 문자열 배열과 포인터의 차이

문자열을 배열로 선언할 때와 포인터로 선언할 때의 차이를 이해하는 것이 중요합니다.

3.1 문자열 배열 방식

#include <stdio.h>
#include <string.h>
#define MAX_LEN 80

int main() {
    char buffer1[MAX_LEN] = "Learning makes me happy";
    char buffer2[MAX_LEN] = "Learning makes me sleepy";
    char temp[MAX_LEN];
    
    printf("sizeof vs strlen comparison:\n");
    printf("sizeof(buffer1) = %zu bytes\n", sizeof(buffer1));
    printf("strlen(buffer1) = %zu characters\n", strlen(buffer1));
    
    printf("\nBefore exchange:\n");
    printf("buffer1: %s\n", buffer1);
    printf("buffer2: %s\n", buffer2);
    
    strcpy(temp, buffer1);
    strcpy(buffer1, buffer2);
    strcpy(buffer2, temp);
    
    printf("\nAfter exchange:\n");
    printf("buffer1: %s\n", buffer1);
    printf("buffer2: %s\n", buffer2);
    
    return 0;
}

sizeof(buffer1)은 배열 전체 크기(80바이트)를 반환하고, strlen(buffer1)은 실제 문자열 길이(23자)를 반환합니다. 문자열 상수를 배열 이름에 직접 대입할 수 없으며, strcpy를 통해 내용을 복사해야 합니다.

3.2 문자열 포인터 방식

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

int main() {
    char *ptr1 = "Learning makes me happy";
    char *ptr2 = "Learning makes me sleepy";
    char *swap;
    
    printf("sizeof vs strlen comparison:\n");
    printf("sizeof(ptr1) = %zu bytes (pointer size)\n", sizeof(ptr1));
    printf("strlen(ptr1) = %zu characters\n", strlen(ptr1));
    
    printf("\nBefore exchange:\n");
    printf("ptr1: %s\n", ptr1);
    printf("ptr2: %s\n", ptr2);
    
    swap = ptr1;
    ptr1 = ptr2;
    ptr2 = swap;
    
    printf("\nAfter exchange:\n");
    printf("ptr1: %s\n", ptr1);
    printf("ptr2: %s\n", ptr2);
    
    return 0;
}

포인터 방식에서는 문자열 리터럴의 주소를 직접 저장할 수 있습니다. 포인터 값 자체를 교환하므로 실제 메모리의 문자열 데이터는 변경되지 않고, 단지 참조 위치만 바뀝니다.

4. 2차원 배열의 다양한 접근 방식

2차원 배열을 배열 인덱스, 단일 포인터, 배열 포인터 세 가지 방식으로 접근할 수 있습니다.

#include <stdio.h>

int main() {
    int matrix[2][4] = {{1, 9, 8, 4}, {2, 0, 4, 9}};
    int row, col;
    int *elem_ptr;
    int (*row_ptr)[4];
    
    printf("Method 1: Direct index access\n");
    for (row = 0; row < 2; row++) {
        for (col = 0; col < 4; col++) {
            printf("%d ", matrix[row][col]);
        }
        printf("\n");
    }
    
    printf("\nMethod 2: Element pointer traversal\n");
    int count = 0;
    for (elem_ptr = &matrix[0][0]; elem_ptr < &matrix[0][0] + 8; elem_ptr++) {
        printf("%d ", *elem_ptr);
        if (++count % 4 == 0) printf("\n");
    }
    
    printf("\nMethod 3: Row pointer traversal\n");
    for (row_ptr = matrix; row_ptr < matrix + 2; row_ptr++) {
        for (col = 0; col < 4; col++) {
            printf("%d ", (*row_ptr)[col]);
        }
        printf("\n");
    }
    
    return 0;
}

int (*row_ptr)[4]는 4개의 정수로 구성된 배열을 가리키는 포인터로, 2차원 배열의 각 행을 순회하는 데 사용됩니다.

5. 문자열 내 문자 치환

문자열에서 특정 문자를 다른 문자로 대체하는 함수를 구현합니다.

#include <stdio.h>
#define MAX_TEXT 80

void substitute(char *source, char target, char replacement);

int main() {
    char sentence[MAX_TEXT] = "Programming is difficult or not, it is a question.";
    
    printf("Original text:\n%s\n\n", sentence);
    
    substitute(sentence, 'i', '*');
    
    printf("Modified text:\n%s\n", sentence);
    
    return 0;
}

void substitute(char *source, char target, char replacement) {
    while (*source) {
        if (*source == target) {
            *source = replacement;
        }
        source++;
    }
}

포인터를 이용해 문자열을 순회하며 대상 문자를 찾아 새로운 문자로 치환합니다. 문자열은 배열 형태로 전달되므로 원본이 직접 수정됩니다.

6. 문자열 특정 위치에서 잘라내기

지정된 문자가 처음 나타나는 위치에서 문자열을 종료시키는 함수입니다.

#include <stdio.h>
#define BUFFER_SIZE 80

char *truncate_at(char *text, char delimiter);

int main() {
    char input[BUFFER_SIZE];
    char mark;
    
    while (printf("Enter string: "), gets(input) != NULL) {
        printf("Enter delimiter character: ");
        mark = getchar();
        
        printf("Truncating...\n");
        truncate_at(input, mark);
        
        printf("Result: %s\n\n", input);
        getchar();
    }
    
    return 0;
}

char *truncate_at(char *text, char delimiter) {
    while (*text != '\0' && *text != delimiter) {
        text++;
    }
    
    if (*text == delimiter) {
        *text = '\0';
    }
    
    return text;
}

추가 getchar() 호출은 이전 입력의 개행문자를 소비하여 다음 입력 루프가 정상적으로 동작하도록 합니다.

7. 주민등록번호 유효성 검사

18자리 주민등록번호 형식을 검증하는 프로그램입니다.

#include <stdio.h>
#include <string.h>
#define ENTRIES 5

int validate_number(char *number);

int main() {
    char *candidates[ENTRIES] = {
        "31010120000721656X",
        "3301061996x0203301",
        "53010220051126571",
        "510104199211197977",
        "53010220051126133Y"
    };
    
    for (int i = 0; i < ENTRIES; i++) {
        if (validate_number(candidates[i])) {
            printf("%s\tValid\n", candidates[i]);
        } else {
            printf("%s\tInvalid\n", candidates[i]);
        }
    }
    
    return 0;
}

int validate_number(char *number) {
    if (strlen(number) != 18) {
        return 0;
    }
    
    for (int pos = 0; pos < 18; pos++) {
        if (pos == 17) {
            char last = number[pos];
            if (!((last >= '0' && last <= '9') || last == 'X')) {
                return 0;
            }
        } else {
            if (!(number[pos] >= '0' && number[pos] <= '9')) {
                return 0;
            }
        }
    }
    
    return 1;
}

총 길이가 18자인지 확인하고, 마지막 자리는 숫자 또는 'X'만 허용하며 나머지는 모두 숫자여야 합니다.

8. 시저 암호 구현

알파벳을 지정된 값만큼 회전시키는 암호화와 복호화 함수입니다.

#include <stdio.h>
#include <string.h>
#define TEXT_LIMIT 80

void encrypt(char *text, int shift);
void decrypt(char *text, int shift);

int main() {
    char message[TEXT_LIMIT];
    int key;
    
    printf("Enter English text: ");
    gets(message);
    
    printf("Enter shift value: ");
    scanf("%d", &key);
    
    printf("Encrypted text: ");
    encrypt(message, key);
    printf("%s\n", message);
    
    printf("Decrypted text: ");
    decrypt(message, key);
    printf("%s\n", message);
    
    return 0;
}

void encrypt(char *text, int shift) {
    int len = strlen(text);
    for (int i = 0; i < len; i++) {
        if (text[i] >= 'a' && text[i] <= 'z') {
            text[i] = ((text[i] - 'a' + shift) % 26) + 'a';
        } else if (text[i] >= 'A' && text[i] <= 'Z') {
            text[i] = ((text[i] - 'A' + shift) % 26) + 'A';
        }
    }
}

void decrypt(char *text, int shift) {
    int len = strlen(text);
    for (int i = 0; i < len; i++) {
        if (text[i] >= 'a' && text[i] <= 'z') {
            text[i] = ((text[i] - 'a' - shift + 26) % 26) + 'a';
        } else if (text[i] >= 'A' && text[i] <= 'Z') {
            text[i] = ((text[i] - 'A' - shift + 26) % 26) + 'A';
        }
    }
}

암호화는 정방향 회전, 복호화는 역방향 회전을 수행합니다. 음수 방지를 위해 26을 더한 후 모듈로 연산을 적용합니다.

9. 명령행 인자 정렬

버블 정렬을 이용해 명령행 인자를 사전순으로 정렬합니다.

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

void bubble_sort(int count, char *items[]);

int main(int argc, char *argv[]) {
    bubble_sort(argc - 1, argv + 1);
    
    for (int i = 1; i < argc; i++) {
        printf("Hello, %s\n", argv[i]);
    }
    
    return 0;
}

void bubble_sort(int count, char *items[]) {
    for (int i = 0; i < count - 1; i++) {
        for (int j = 0; j < count - 1 - i; j++) {
            if (strcmp(items[j], items[j + 1]) > 0) {
                char *swap = items[j];
                items[j] = items[j + 1];
                items[j + 1] = swap;
            }
        }
    }
}

포인터 배열의 요소만 교환하여 문자열 자체를 복사하지 않고 효율적으로 정렬합니다. argv + 1로 프로그램 이름을 제외한 인자들만 처리합니다.

태그: C Pointers arrays strings memory-management

7월 15일 16:16에 게시됨