Đọc array of string bằng C

Mọi người cho mình hỏi là mình có làm một bài tập liên quan đến việc tạo function xóa bỏ empty string.
Mình đã thực hiện viết xong function nhưng mình nghĩ lỗi nằm ở phần này nên mình chỉ show đơn giản 1 phần như sau:

   char hello[][1] = {"hd", "", "f"};
    int i = 0;
    while(hello[i] != NULL)
    {
        printf("%s", hello[i]);
        i++;
    }

Mình thắc mắc là khi mình đọc tài liệu thì array of string sẽ dừng lại khi đạt NULL pointer, nhưng nếu đúng như suy nghĩ mình thì nó sẽ in ra

hdf

Nhưng kết quả lại là

hdfzC≈+≈  Φ√  ╨

//Cụ thể phần code mà mình triển khai:

void rm_empties(char **words)
{
    int i=0, j, k, len, p = 0;
    while(words[i] != NULL)
    {
        len = 0;
        
        for(j=0; words[i][j] != '\0'; j++)
            len++;
        if(len == 0)
        {
            k = i;
            while(words[k+1] != NULL) // words[k+1] không dừng lại khi gặp NULL
            {
                words[k] = words[k+1];
                k++;

            }

            p++;

        }
        

            i++;

    }

        words[(i-p)] = NULL;

}

hello[][1] khởi tạo có 1 ô nhớ cho mỗi phần tử à. Không đủ chứa dữ liệu. Do đó kết quả sẽ rơi vào undefined behavior

Cụ thể: "hd" sẽ chiếm 3 bytes. "" chiếm 1 byte và "f" chiếm 2 bytes. Bao gồm cả ký tự rỗng \0
Lưu ý: khai báo 'h' sẽ chiếm 1 byte trong khi khai báo "h" sẽ chiếm 2 bytes.

2 Likes

Mình cảm ơn. Mình đã chỉnh vùng nhớ rộng hơn.
Nhưng phần mình chưa hiểu là

  char hello[][20] = {"hd", "", "f"};
    int i = 0;
    while(hello[i] != NULL)
    {
        printf("%s", hello[i]);
        i++;
    }

Thì không phải là hello[3] == NULL huh bạn?

hello[3] trỏ tới địa chỉ của byte nằm trước nó trong stack frame được allocate cho function chứa các biến này. Địa chỉ của stack thì khác 0 nên hello[3] không thể NULL được.

Bạn tham khảo thêm về stack memory ở:

Bên cạnh đó, hello là array nên không thể NULL: https://qr.ae/pvae3n

2 Likes

Nếu vậy thì chỉ có cách tính length của array trước thay vì chạy thông qua looping và xác định giá trị cuối phải không bạn? Ý mình là array/array[0] = number of strings in array.
Nhưng có một nhược điểm là khi làm kiểu vậy thì truyền địa chỉ vào function sẽ không hoạt động như thế được.
Mình có hỏi 1 số bạn học của mình thì họ bảo đề sai, việc viết dựa trên chỉ định là dùng

void rm_empties(char **words)

không thể nào thực hiện được.

void rm_empties(char **words)
{
    int length;
    int index = 0;
    char str[100][20];
    while (words != NULL)
    {
        for (length = 0; words[index][length] != '\0'; ++length);
        if (length > 0)
        {
            for(int k = 0; k < length; k++)
            {
                str[index][k] = words[index][k];
            }
            index++;
        }
        words++;
    }
    words = &str; 
}

Con trỏ (pointer) và mảng (array) là 2 khái niệm khác nhau.

Khai báo một con trỏ tới con trỏ, không thấy liên quan gì đến câu hỏi về đọc array of string cả.

Mong bạn đưa lên đề một cách đầy đủ để mọi người hình dung được vấn đề của bạn. Hiện tại mình không hiểu bạn đang cần gì cả.
Có lẽ bạn đang gặp https://en.wikipedia.org/wiki/XY_problem

2 Likes

phải kiểm tra while (words[i] != NULL) mới đúng :V

thực hiện được, vì words trỏ tới mảng con trỏ :V vẽ hình ra cho dễ hình dung

words            words[0]
[0xaabbccdd] --> [0x11111111] --> ['h', 'e', 'l', 'l', 'o', '\0']
                 words[1]
                 [0x22222222] --> ['\0']
                 words[2]
                 [0x33333333] --> ['h', 'i', '\0']
                 words[3]
                 [0x44444444] --> ['a', 'b', 'c', '\0']
                 words[4]
                 [0x55555555] --> ['\0']
                 words[5]
                 [0x66666666] --> ['d', 'e', '\0']
                 words[6]
                 [0x00000000] -->

thì sau khi xóa có thể words nó thành

words            words[0]
[0xaabbccdd] --> [0x11111111] --> ['h', 'e', 'l', 'l', 'o', '\0']
                 words[1]
                 [0x33333333] --> ['h', 'i', '\0']
                 words[2]
                 [0x44444444] --> ['a', 'b', 'c', '\0']
                 words[3]
                 [0x66666666] --> ['d', 'e', '\0']
                 words[4]
                 [0x00000000] -->
                 words[5]
                 [0x00000000] -->
                 words[6]
                 [0x00000000] -->

nếu strlen(words[i]) == 0 ko biết có cần gọi free(words[i]) ko :V rất có thể là cần


viết hàm dùng 1 con trỏ read, 1 con trỏ write là được thôi :V

void rm_empties(char **words) {
    char** pread = words;
    char** pwrite = words;
    while (*pread != NULL) {
        if (**pread == '\0') {
            free(*pread);
            *pread = NULL;
            ++pread;
        } else {
            *pwrite = *pread; // gán giá trị con trỏ, cho *pwrite trỏ tới chuỗi mà *pread trỏ tới, ko cần strcpy
            if (pwrite != pread) *pread = NULL; // nếu pread khác pwrite thì cho *pread trỏ tới NULL vì *pread đã có *pwrite trỏ tới rồi
            ++pwrite;
            ++pread;
        }
    }
}
2 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?