C言語におけるポインタ操作と文字列処理の実践

配列とポインタによるデータ処理

C言語において、ポインタを利用することで配列のデータを効率的に操作できます。以下の例では、整数配列を受け取り、その最小値と最大値をポインタ経由で書き換える関数を定義します。また、2次元配列を異なる種類のポインタ(要素へのポインタと配列へのポインタ)を用いて走査する方法を示します。

#include <stdio.h>

void analyze_values(int *data, int size, int *min_val, int *max_val) {
    if (size == 0) return;
    
    *min_val = *max_val = data[0];
    for (int i = 1; i < size; i++) {
        if (data[i] < *min_val) {
            *min_val = data[i];
        } else if (data[i] > *max_val) {
            *max_val = data[i];
        }
    }
}

void demonstrate_2d_pointers() {
    int matrix[2][3] = {{10, 20, 30}, {40, 50, 60}};
    int *elem_ptr = &matrix[0][0]; // 要素へのポインタ
    int (*row_ptr)[3] = matrix;      // 行へのポインタ

    printf("要素ごとの走査: ");
    for (int i = 0; i < 6; i++) {
        printf("%d ", *(elem_ptr + i));
    }
    
    printf("\n行ごとの走査: ");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", *(*(row_ptr + i) + j));
        }
    }
    printf("\n");
}

int main() {
    int numbers[] = {5, 2, 9, 1, 7};
    int min, max;
    int count = sizeof(numbers) / sizeof(numbers[0]);

    analyze_values(numbers, count, &min, &max);
    printf("最小値: %d, 最大値: %d\n", min, max);

    demonstrate_2d_pointers();
    return 0;
}

文字列とメモリ管理の基礎

文字列操作において、配列宣言とポインタ宣言では動作が異なります。sizeof演算子は割り当てられたメモリサイズを返し、strlenはヌル終端文字までの長さを返します。また、文字列リテラルへのポインタを使用する場合と文字配列を使用する場合で、アドレスの書き換え可否が異なる点に注意が必要です。以下のコードは、ポインタのアドレスを交換することで、効率的に文字列の参照先を切り替える例です。

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

void swap_pointers(char **a, char **b) {
    char *temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    // ポインタによる文字列定義(アドレスの書き換えが可能)
    char *msg1 = "Hello World";
    char *msg2 = "C Programming";
    
    printf("交換前: msg1=%s, msg2=%s\n", msg1, msg2);
    printf("msg1のsizeof: %zu, strlen: %zu\n", sizeof(msg1), strlen(msg1));

    // アドレスの交換
    swap_pointers(&msg1, &msg2);
    
    printf("交換後: msg1=%s, msg2=%s\n", msg1, msg2);

    return 0;
}

文字列の変換と検証

ポインタを用いた文字列トラバーサルは、特定の文字の置換や文字列の切り詰め、フォーマット検証に頻繁に使われます。以下の例では、文字列内の特定の文字を別の文字に置換する関数、指定された文字で文字列を切り詰める関数、そしてID形式のような文字列パターンを検証する関数を実装します。

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

// 文字の置換
void replace_char(char *str, char target, char replacement) {
    while (*str) {
        if (*str == target) {
            *str = replacement;
        }
        str++;
    }
}

// 文字列の切り詰め
void truncate_at(char *str, char delimiter) {
    while (*str) {
        if (*str == delimiter) {
            *str = '\0';
            return;
        }
        str++;
    }
}

// 文字列フォーマットの検証(例:17桁の数字と1桁のチェックコード)
int validate_format(const char *str) {
    size_t len = strlen(str);
    if (len != 18) return 0;

    for (int i = 0; i < 17; i++) {
        if (!isdigit(str[i])) return 0;
    }
    
    char last = str[17];
    if (!(isdigit(last) || last == 'X')) return 0;
    
    return 1;
}

int main() {
    char text[] = "Programming is fun";
    char input[50];
    
    printf("置換前: %s\n", text);
    replace_char(text, 'i', '1');
    printf("置換後: %s\n", text);

    printf("\nID検証 (31010120000721656X): %s\n", 
           validate_format("31010120000721656X") ? "有効" : "無効");
    printf("ID検証 (12345): %s\n", 
           validate_format("12345") ? "有効" : "無効");

    return 0;
}

シーザー暗号によるエンコード・デコード

文字ポインタを操作し、ASCIIコードを利用して文字列のシフトを行うシーザー暗号の実装です。アルファベット範囲外になる場合は、26を加算または減算して循環処理を行います。

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

void shift_text(char *str, int offset) {
    while (*str) {
        if (isalpha(*str)) {
            char base = islower(*str) ? 'a' : 'A';
            // 正のオフセットでエンコード、負のオフセットでデコード処理
            *str = base + (*str - base + offset) % 26;
            // 負の数の剰余演算対応(C言語仕様により結果が負になる場合の調整)
            if (*str < base) *str += 26;
        }
        str++;
    }
}

int main() {
    char secret[80] = "HelloPointer";
    int shift = 3;

    printf("元のテキスト: %s\n", secret);
    
    shift_text(secret, shift);
    printf("エンコード後: %s\n", secret);
    
    shift_text(secret, -shift);
    printf("デコード後: %s\n", secret);

    return 0;
}

コマンドライン引数のソート

標準ライブラリのqsortやポインタ配列を操作して、コマンドラインから渡された引数を辞書順に並べ替える処理です。引数のポインタ配列argvを直接操作することで、メモリコピーを伴わず効率的に並べ替えを行います。

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

// ポインタのスワップ用ヘルパー関数
void swap(char **s1, char **s2) {
    char *temp = *s1;
    *s1 = *s2;
    *s2 = temp;
}

// 簡易バブルソート
void sort_args(char **list, int count) {
    for (int i = 0; i < count - 1; i++) {
        for (int j = 0; j < count - i - 1; j++) {
            if (strcmp(list[j], list[j + 1]) > 0) {
                swap(&list[j], &list[j + 1]);
            }
        }
    }
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("使用法: %s [引数1] [引数2] ...\n", argv[0]);
        return 1;
    }

    // argv[0](プログラム名)を除外してソート
    sort_args(argv + 1, argc - 1);

    printf("ソート結果:\n");
    for (int i = 1; i < argc; i++) {
        printf("%s\n", argv[i]);
    }

    return 0;
}

タグ: C言語 ポインタ 文字列処理 アルゴリズム メモリ管理

6月29日 17:20 投稿