Các Thao Tác Cơ Bản Với Mảng Hai Chiều và Triển Khai Hàm Chuỗi Tùy Chỉnh Trong C

1. Tạo Tam Giác Pascal Sử Dụng Mảng Hai Chiều

Tam giác Pascal là một cấu trúc toán học nổi tiếng, trong đó mỗi số là tổng của hai số nằm ngay phía trên nó. Các cạnh bên của tam giác luôn là số 1. Dưới đây là cách triển khai tính toán và hiển thị Tam giác Pascal sử dụng mảng hai chiều trong ngôn ngữ C.

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

/**
 * @brief Tính toán các giá trị cho Tam giác Pascal và lưu vào mảng.
 * 
 * @param numRows Số hàng của Tam giác Pascal cần tạo.
 * @param pascalMatrix Con trỏ tới mảng hai chiều (Variable Length Array - VLA) 
 *                     để lưu trữ các giá trị của tam giác.
 */
void calculatePascal(int numRows, int pascalMatrix[][numRows]) {
    for (int r = 0; r < numRows; ++r) {
        for (int c = 0; c <= r; ++c) {
            if (c == 0 || c == r) {
                pascalMatrix[r][c] = 1; // Các phần tử ở biên luôn là 1
            } else {
                // Mỗi phần tử là tổng của hai phần tử phía trên nó
                pascalMatrix[r][c] = pascalMatrix[r - 1][c - 1] + pascalMatrix[r - 1][c];
            }
        }
    }
}

int main() {
    int inputRows;
    printf("Nhập số hàng cho Tam giác Pascal: ");
    // Đọc số hàng từ người dùng và kiểm tra tính hợp lệ
    if (scanf("%d", &inputRows) != 1 || inputRows <= 0) {
        printf("Giá trị nhập không hợp lệ. Vui lòng nhập số nguyên dương.\n");
        return 1;
    }

    // Khởi tạo mảng hai chiều sử dụng VLA (Variable Length Array)
    // Kích thước của mảng được xác định tại thời điểm chạy chương trình
    int pascalArray[inputRows][inputRows]; 

    // Gọi hàm để tính toán các giá trị của Tam giác Pascal
    calculatePascal(inputRows, pascalArray);

    printf("\nCác phần tử của Tam giác Pascal với %d hàng:\n", inputRows);
    for (int i = 0; i < inputRows; ++i) {
        // Canh lề để hiển thị tam giác đẹp hơn
        for (int space = 0; space < (inputRows - 1 - i) * 2; ++space) {
            printf(" ");
        }
        for (int j = 0; j <= i; ++j) {
            printf("%4d", pascalArray[i][j]); // In phần tử với 4 ký tự padding
        }
        printf("\n");
    }

    return 0;
}

2. Tính Tổng Các Hàng và Cột Trong Mảng Hai Chiều

Bài tập này minh họa cách duyệt qua một mảng hai chiều để tính tổng riêng biệt cho từng hàng và từng cột. Chúng ta sẽ sử dụng các vòng lặp lồng nhau để thực hiện thao tác này.

#include <stdio.h>

#define MATRIX_ROWS 3 // Định nghĩa số hàng cố định cho ma trận
#define MATRIX_COLS 4 // Định nghĩa số cột cố định cho ma trận

int main() {
    int matrixData[MATRIX_ROWS][MATRIX_COLS];

    printf("Nhập các phần tử cho ma trận (%d hàng, %d cột):\n", MATRIX_ROWS, MATRIX_COLS);
    // Vòng lặp để nhập dữ liệu cho ma trận từ người dùng
    for (int r = 0; r < MATRIX_ROWS; ++r) {
        for (int c = 0; c < MATRIX_COLS; ++c) {
            printf("matrixData[%d][%d]: ", r, c);
            if (scanf("%d", &matrixData[r][c]) != 1) {
                printf("Lỗi nhập liệu. Vui lòng nhập số nguyên.\n");
                return 1;
            }
        }
    }

    printf("\nMa trận đã nhập:\n");
    // Vòng lặp để in ma trận ra màn hình
    for (int r = 0; r < MATRIX_ROWS; ++r) {
        for (int c = 0; c < MATRIX_COLS; ++c) {
            printf("%4d", matrixData[r][c]);
        }
        printf("\n");
    }

    // Tính tổng của từng hàng
    printf("\nTổng của mỗi hàng:\n");
    for (int r = 0; r < MATRIX_ROWS; ++r) {
        int currentRowTotal = 0;
        for (int c = 0; c < MATRIX_COLS; ++c) {
            currentRowTotal += matrixData[r][c];
        }
        printf("Tổng hàng %d: %d\n", r + 1, currentRowTotal);
    }

    // Tính tổng của từng cột
    printf("\nTổng của mỗi cột:\n");
    for (int c = 0; c < MATRIX_COLS; ++c) {
        int currentColTotal = 0;
        for (int r = 0; r < MATRIX_ROWS; ++r) {
            currentColTotal += matrixData[r][c];
        }
        printf("Tổng cột %d: %d\n", c + 1, currentColTotal);
    }

    return 0;
}

3. Tìm Giá Trị Lớn Thứ Hai Trong Mảng Hai Chiều

Thuật toán này hướng dẫn cách tìm giá trị lớn nhất và giá trị lớn thứ hai trong một mảng hai chiều. Chúng ta sẽ sử dụng một phương pháp duyệt qua mảng một lần để xác định hai giá trị này.

#include <stdio.h>
#include <limits.h> // Bao gồm INT_MIN để khởi tạo giá trị nhỏ nhất có thể

#define SEARCH_ROWS 3
#define SEARCH_COLS 3

int main() {
    int dataMatrix[SEARCH_ROWS][SEARCH_COLS];
    int totalElements = SEARCH_ROWS * SEARCH_COLS;

    if (totalElements == 0) {
        printf("Ma trận rỗng, không thể tìm giá trị lớn thứ hai.\n");
        return 0;
    }

    printf("Nhập các phần tử cho ma trận (%d hàng, %d cột) để tìm giá trị lớn thứ hai:\n", SEARCH_ROWS, SEARCH_COLS);
    // Nhập dữ liệu cho ma trận
    for (int r = 0; r < SEARCH_ROWS; ++r) {
        for (int c = 0; c < SEARCH_COLS; ++c) {
            printf("dataMatrix[%d][%d]: ", r, c);
            if (scanf("%d", &dataMatrix[r][c]) != 1) {
                printf("Lỗi nhập liệu. Vui lòng nhập số nguyên.\n");
                return 1;
            }
        }
    }

    printf("\nMa trận đã nhập:\n");
    // In ma trận đã nhập
    for (int r = 0; r < SEARCH_ROWS; ++r) {
        for (int c = 0; c < SEARCH_COLS; ++c) {
            printf("%4d", dataMatrix[r][c]);
        }
        printf("\n");
    }

    int largestValue = INT_MIN;       // Khởi tạo giá trị lớn nhất bằng giá trị nguyên nhỏ nhất
    int secondLargestValue = INT_MIN; // Khởi tạo giá trị lớn thứ hai bằng giá trị nguyên nhỏ nhất

    // Duyệt qua tất cả các phần tử của ma trận
    for (int r = 0; r < SEARCH_ROWS; ++r) {
        for (int c = 0; c < SEARCH_COLS; ++c) {
            int currentValue = dataMatrix[r][c];

            if (currentValue > largestValue) {
                secondLargestValue = largestValue; // Giá trị lớn nhất cũ giờ trở thành lớn thứ hai
                largestValue = currentValue;       // Cập nhật giá trị lớn nhất mới
            } else if (currentValue > secondLargestValue && currentValue < largestValue) {
                // Nếu currentValue lớn hơn secondLargestValue nhưng nhỏ hơn largestValue,
                // thì nó là giá trị lớn thứ hai mới
                secondLargestValue = currentValue;
            }
        }
    }

    // Kiểm tra kết quả
    if (secondLargestValue == INT_MIN) {
        // Điều này xảy ra nếu tất cả các phần tử đều giống nhau, hoặc chỉ có một phần tử duy nhất,
        // hoặc tất cả các phần tử đều là INT_MIN.
        printf("\nKhông thể tìm được giá trị lớn thứ hai. Có thể tất cả các phần tử giống nhau hoặc không đủ phần tử riêng biệt.\n");
    } else {
        printf("\nGiá trị lớn nhất trong ma trận: %d\n", largestValue);
        printf("Giá trị lớn thứ hai trong ma trận: %d\n", secondLargestValue);
    }

    return 0;
}

4. Triển Khai Thủ Công Các Hàm Thao Tác Chuỗi

Mặc dù thư viện chuẩn C cung cấp các hàm mạnh mẽ để xử lý chuỗi (strcat, strcmp, strcpy, strlen), việc tự triển khai chúng sẽ giúp hiểu rõ hơn về cách chúng hoạt động ở cấp độ thấp. Dưới đây là các phiên bản tùy chỉnh của các hàm này, được xây dựng chỉ bằng cách sử dụng con trỏ và vòng lặp.

#include <stdio.h>
#include <string.h> // Được dùng cho fgets và strcspn (để xử lý đầu vào an toàn), không dùng cho triển khai hàm chuỗi
#include <stdlib.h> // Dùng chung cho một số tiện ích

/**
 * @brief Nối chuỗi nguồn vào cuối chuỗi đích.
 *        Tương tự như strcat() trong thư viện chuẩn.
 * 
 * @param destination Con trỏ tới chuỗi đích.
 * @param source Con trỏ tới chuỗi nguồn (không thay đổi).
 */
void custom_str_concat(char *destination, const char *source) {
    // Di chuyển con trỏ 'destination' đến ký tự null kết thúc chuỗi đích
    while (*destination != '\0') {
        destination++;
    }
    // Sao chép từng ký tự từ chuỗi nguồn sang vị trí hiện tại của chuỗi đích
    while (*source != '\0') {
        *destination = *source;
        destination++;
        source++;
    }
    *destination = '\0'; // Thêm ký tự null kết thúc chuỗi sau khi nối
}

/**
 * @brief So sánh hai chuỗi theo thứ tự từ điển.
 *        Tương tự như strcmp() trong thư viện chuẩn.
 * 
 * @param string1 Con trỏ tới chuỗi thứ nhất (không thay đổi).
 * @param string2 Con trỏ tới chuỗi thứ hai (không thay đổi).
 * @return Số nguyên < 0 nếu string1 nhỏ hơn string2, 0 nếu bằng, > 0 nếu string1 lớn hơn.
 */
int custom_str_compare(const char *string1, const char *string2) {
    // Lặp cho đến khi tìm thấy ký tự khác nhau hoặc kết thúc một trong hai chuỗi
    while (*string1 != '\0' && *string2 != '\0' && *string1 == *string2) {
        string1++;
        string2++;
    }
    // Trả về hiệu ASCII của ký tự đầu tiên không khớp
    return (int)(*string1) - (int)(*string2);
}

/**
 * @brief Sao chép chuỗi nguồn sang chuỗi đích.
 *        Tương tự như strcpy() trong thư viện chuẩn.
 * 
 * @param destination Con trỏ tới chuỗi đích.
 * @param source Con trỏ tới chuỗi nguồn (không thay đổi).
 */
void custom_str_copy(char *destination, const char *source) {
    // Sao chép từng ký tự từ nguồn sang đích cho đến khi gặp ký tự null
    // và gán ký tự null vào cuối chuỗi đích
    while ((*destination++ = *source++) != '\0');
}

/**
 * @brief Tính độ dài của một chuỗi.
 *        Tương tự như strlen() trong thư viện chuẩn.
 * 
 * @param inputString Con trỏ tới chuỗi cần tính độ dài (không thay đổi).
 * @return Độ dài của chuỗi (số ký tự trước ký tự null kết thúc).
 */
int custom_str_length(const char *inputString) {
    const char *initialPtr = inputString; // Lưu giữ con trỏ ban đầu
    // Di chuyển con trỏ cho đến khi gặp ký tự null
    while (*inputString != '\0') {
        inputString++;
    }
    // Khoảng cách giữa con trỏ cuối và con trỏ ban đầu chính là độ dài
    return (int)(inputString - initialPtr); 
}

int main() {
    char buffer_dest[100]; // Đủ lớn để chứa chuỗi đích sau các thao tác
    char buffer_src[100];  // Đủ lớn để chứa chuỗi nguồn

    printf("Nhập chuỗi đích (destination): ");
    fgets(buffer_dest, sizeof(buffer_dest), stdin);
    buffer_dest[strcspn(buffer_dest, "\n")] = 0; // Loại bỏ ký tự newline từ fgets

    printf("Nhập chuỗi nguồn (source): ");
    fgets(buffer_src, sizeof(buffer_src), stdin);
    buffer_src[strcspn(buffer_src, "\n")] = 0; // Loại bỏ ký tự newline từ fgets
    
    // --- Thử nghiệm custom_str_concat ---
    printf("\n--- Thao tác Nối chuỗi (custom_str_concat) ---\n");
    char temp_concat_dest[100]; // Dùng bản sao để không ảnh hưởng đến các test sau
    custom_str_copy(temp_concat_dest, buffer_dest); // Sao chép chuỗi đích gốc
    printf("Chuỗi đích ban đầu: '%s'\n", temp_concat_dest);
    printf("Chuỗi nguồn: '%s'\n", buffer_src);
    custom_str_concat(temp_concat_dest, buffer_src);
    printf("Kết quả sau nối: '%s'\n", temp_concat_dest);

    // --- Thử nghiệm custom_str_compare ---
    printf("\n--- Thao tác So sánh chuỗi (custom_str_compare) ---\n");
    char compare_string_a[100], compare_string_b[100];
    printf("Nhập chuỗi thứ nhất để so sánh: ");
    fgets(compare_string_a, sizeof(compare_string_a), stdin);
    compare_string_a[strcspn(compare_string_a, "\n")] = 0;
    printf("Nhập chuỗi thứ hai để so sánh: ");
    fgets(compare_string_b, sizeof(compare_string_b), stdin);
    compare_string_b[strcspn(compare_string_b, "\n")] = 0;

    int compareResult = custom_str_compare(compare_string_a, compare_string_b);
    if (compareResult > 0) {
        printf("'%s' lớn hơn '%s'\n", compare_string_a, compare_string_b);
    } else if (compareResult < 0) {
        printf("'%s' nhỏ hơn '%s'\n", compare_string_a, compare_string_b);
    } else {
        printf("'%s' bằng '%s'\n", compare_string_a, compare_string_b);
    }

    // --- Thử nghiệm custom_str_copy ---
    printf("\n--- Thao tác Sao chép chuỗi (custom_str_copy) ---\n");
    char copied_target[100];
    printf("Chuỗi nguồn để sao chép: '%s'\n", buffer_src);
    custom_str_copy(copied_target, buffer_src);
    printf("Kết quả sau sao chép vào đích: '%s'\n", copied_target);

    // --- Thử nghiệm custom_str_length ---
    printf("\n--- Thao tác Tính độ dài chuỗi (custom_str_length) ---\n");
    printf("Chuỗi để tính độ dài: '%s'\n", buffer_dest); // Sử dụng chuỗi đích gốc
    int stringLength = custom_str_length(buffer_dest);
    printf("Độ dài của chuỗi là: %d\n", stringLength);

    return 0;
}

Thẻ: C language Two-Dimensional Arrays string manipulation Pascal's Triangle Array Operations

Đăng vào ngày 21 tháng 8 lúc 12:45