将字符串转换为单词数组时的 IOT 指令

IOT instruction when converting string to word array

提问人: 提问时间:12/31/2022 更新时间:1/22/2023 访问量:140

问:

我回来找你谈谈我的功能。目的是将字符串分隔在每个不可打印的 ASCII 字符处,并将上述内容包含在二维数组的新行中。char **my_str_to_word_array(char *str)

不可打印的 ASCII 字符应用作分隔符,不应包含在行中。

例:

char *test = "My name is John Doe.\nI have 0 GPA.\nI will survive." ;
char **array = my_str_to_word_array(test) ;

array[0] = "My name is John Doe." (zero terminated string)
array[1] = "I have 0 GPA." (zero terminated string)
array[2] = "I will survive." (zero terminated string)
array[3] = NULL

我有 2 个问题:

  1. 如果在我的测试中,我在对my_str_to_word_array的调用下方有一个,则传递给的格式将包含在数组中。因此,我得出结论,存在内存读取错误。main()printf()printf()

  2. 当我尝试数组时,出现错误:free()

double free or corruption (out)
[1]    33429 IOT instruction (core dumped)  ./libmy
size_t get_words_number(char const *str)
{
    size_t count = 0;
    const char *i = str;
    while (*i != 0) {
        if (isprint(*i)) {
            count++;
        }
        while (*i != 0 && isprint(*i)) {
            i++;
        }
        i++;
    }
    return count;
}

char **free_corrupted_array(char **array, size_t i)
{
    size_t j = 0;
    while (j < i) {
        free(array[j]);
        j++;
    }
    free(array);
    return NULL;
}

char **fill_array(char **array, const char *str, size_t word_count)
{
    size_t word_size = 0, j = 0;
    const char *i = str;
    while (j < word_count) {
        while (*i != 0 && isprint(*i)) {
            word_size++;
            i++;
        }
        array[j] = strndup(i - word_size, word_size);
        if (!array[j]) {
            return free_corrupted_array(array, j);
        }
        word_size = 0;
        j++;
        while (!isprint(*i)) {
            i++;
        }
    }
    array[j] = NULL;
    return array;
}

char **my_str_to_word_array(char const *str)
{
    char **word_array = NULL;
    size_t word_count = 0;
    if (!str) {
        return NULL;
    }
    word_count = get_words_number(str);
    word_array = malloc(word_count * sizeof(char *));
    if (!word_array) {
        return NULL;
    }
    word_array = fill_array(word_array, str, word_count);
    return word_array;
}
void my_free_word_array(char **word_array)
{
    if (!word_array) {
        return;
    }
    while (*word_array != NULL) {
        free(*word_array);
        word_array++;
    }
    free(word_array);
}
int main(int argc, char **argv)
{
    const char *test = "My name is John Doe.\nI have 0 GPA.\nI will survive.";
    char **word_array = my_str_to_word_array(test);
    while (*word_array != NULL) {
        printf("%s\n", *word_array);
        word_array++;
    }
    printf("Test print original size %lu\n", strlen(test));
    my_free_word_array(word_array);
    return 0;
}

输出:

My name is John Doe.
I have 0 GPA.
I will survive.
Test print original size %lu
Test print original size 50
double free or corruption (out)
[1]    33429 IOT instruction (core dumped)  ./libmy

你看到问题了吗?

数组 c 字符串 malloc

评论

2赞 Harith 12/31/2022
word_array = malloc(size)----> 然后: ---> 更改最初指向的内容,并失去对动态分配的内存的所有访问权限。然后,传递指向未动态分配的指针,因此会出现警告。word_array = fill_array(word_array, str, word_count); word_arrayfree
0赞 WhozCraig 12/31/2022
在此代码中的众多问题中,请查看 .您传递的地址本身从来都不是动态分配的结果。标准库不会神奇地“知道”您传递的地址(可能)是“在”动态区域内的某个地方,找到它的基础,然后释放它。做家务是你的工作。my_free_word_array
0赞 Harith 12/31/2022
ptr1 = ptr2不会将 的内容复制到 。它只复制指针值,这样两个指针现在都指向同一个地址。ptr2ptr1
0赞 12/31/2022
感谢您的解释,但是我该如何解决呢?
0赞 Costantino Grana 12/31/2022
我编辑了该示例,以正确地显示您想要一个以 NULL 结尾的 C 字符串数组。

答:

1赞 Costantino Grana 12/31/2022 #1

错误:

  1. get_words_number越界(相差 1),并且可能会在字符串之后读取任意内存(请检查我在 中包含的示例)。main
  2. 您需要在阵列中增加一个插槽来放置一个终止 .NULL
  3. 如果以后需要输入指针,请停止抖动输入指针(在打印循环中和打印循环中)。my_free_word_arraymain
  4. 编辑:正如 Fe2O3 评论的那样,我错过了函数中的另一个错误。您还应该确保在最后一个循环中。fill_array*i!=0

建议:

  1. 下次通过包含所有必需的标头来制作一个最小的、可重现的示例;
  2. strndup不是标准的(除非您具有并将定义定义为 1)。__STDC_ALLOC_LIB____STDC_WANT_LIB_EXT2__
  3. 您根本不需要该函数。free_corrupted_array
  4. 编辑:如果您也在检查,检查是没有用的. 不可打印,因此无需进行第一次检查。*i!=0isprint(*i)0
#define _CRT_SECURE_NO_WARNINGS
#ifdef __STDC_ALLOC_LIB__
#define __STDC_WANT_LIB_EXT2__ 1
#else
#include <stdlib.h>
#include <string.h>
char *strndup(const char *str, size_t size)
{
    return strncpy(calloc(size + 1, 1), str, size);
}
#endif

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

size_t get_words_number(char const *str)
{
    size_t count = 0;
    const char *i = str;
    while (*i != 0) {
        if (isprint(*i)) {
            count++;
        }
        while (*i != 0 && isprint(*i)) {
            i++;
        }
        if (*i != 0) { // <--- This was missing
            i++;
        }
    }
    return count;
}

void my_free_word_array(char **word_array) // <--- Moved up
{
    if (!word_array) {
        return;
    }
    for (size_t i = 0; word_array[i] != NULL; ++i) { // <--- Stop thrashing word_array
        free(word_array[i]);
    }
    free(word_array);
}

char **fill_array(char **array, const char *str, size_t word_count)
{
    size_t word_size = 0, j = 0;
    const char *i = str;
    while (j < word_count) {
        while (*i != 0 && isprint(*i)) {
            word_size++;
            i++;
        }
        array[j] = strndup(i - word_size, word_size); 
        if (!array[j]) {
            my_free_word_array(array); // <--- No need for another free here
            return NULL;
        }
        word_size = 0;
        j++;
        while (*i != 0 && !isprint(*i)) {
            i++;
        }
    }
    array[j] = NULL;
    return array;
}

char **my_str_to_word_array(char const *str)
{
    char **word_array = NULL;
    size_t word_count = 0;
    if (!str) {
        return NULL;
    }
    word_count = get_words_number(str);
    word_array = malloc((word_count + 1) * sizeof(char *)); // <--- You need a +1 here
    if (!word_array) {
        return NULL;
    }
    word_array = fill_array(word_array, str, word_count);
    return word_array;
}

int main(int argc, char **argv)
{
    char test[] = "My name is John Doe.\nI have 0 GPA.\nI will survive.\nThis will be removed from the string";
    *strrchr(test,'\n') = 0;
    char **word_array = my_str_to_word_array(test);
    if (word_array) {
        for (size_t i = 0; word_array[i] != NULL; ++i) { // <--- Stop thrashing word_array
            printf("%s\n", word_array[i]);
        }
        printf("Test print original size %zu\n", strlen(test));
        my_free_word_array(word_array);
    }
    return 0;
}
1赞 chux - Reinstate Monica 12/31/2022 #2

OP 的代码错过了对 null 字符的检查。@Costantino格拉纳

候选get_words_number()更正和简化:

计数从“非单词”到“单词”的转换。

用于函数中所有字符的定义用途。unsigned char*is...()

#include <ctype.h>
#include <stdbool.h>

size_t get_words_number(char const *str) {
  const unsigned char *ustr = (const unsigned char *) str;
  size_t count = 0;
  bool previous_not_a_word = true;
  
  while (*ustr) {
    count += previous_not_a_word && isprint(*ustr);
    previous_not_a_word = !isprint(*ustr);
    ustr++;
  }
  return count;
}

评论

1赞 Costantino Grana 12/31/2022
好东西。你缺少一个 ,否则它将是一个无限循环。我通过使用 [Compiler Explorer](godbolt.org) 检查是否多次调用来检测到这一点。事实并非如此。ustr++isprint
0赞 chux - Reinstate Monica 12/31/2022
@CostantinoGrana我曾经对事情进行编码,以直接避免像重新召回这样的事情。然而,由于编译器变得越来越聪明,我让它们来处理这些基本的优化,并将我的时间集中在更大的事情上。感谢您报告错误并测试优化。isprint(*ustr)
0赞 Fe2O3 12/31/2022 #3

OP 代码的最大问题是它被分解成如此多的辅助函数,以至于几乎不可读。一个简单的过程被这种碎片化弄得一团糟。

下面是解决此问题的“单次通过”版本。它不涉及辅助函数及其参数和令人抓狂的变量名称。

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

int main() {
    // OP string still as "string literal"
    char *test = "My name is John Doe.\nI have 0 GPA.\nI will survive.";

    // mutable copy of that string
    char *copy = malloc( strlen( test ) + 1 ); // Verify!
    strcpy( copy, test );

    // get one element that is NULL
    size_t cnt = 0;
    char **arr = calloc( ++cnt, sizeof *arr ); // Verify!!

    // chop copy on separators
    char *cp = copy;
    while( *cp ) {
        // skip leading/trailing separators
        while( *cp && !isprint( *cp ) ) cp++;
        if( !*cp ) break; // was 1 or more trailing seps

        // search for end of segment
        char *ep = cp;
        while( isprint( *ep ) ) ep++;

        // remember if this is the final segment
        bool atEnd = *ep == '\0';

        // terminate segment and store pointer
        *ep = '\0';
        arr = realloc( arr, ++cnt * sizeof *arr ); // Verify!!!
        arr[ cnt-2 ] = cp;
        arr[ cnt-1 ] = NULL;

        // move on (only if there is more to examine).
        cp = ep + !atEnd;
    }

    // output
    cnt = 0;
    do
        printf( "%d: %s\n", cnt, arr[ cnt ] ? arr[ cnt ] : "END OF ARRAY" );
    while( arr[ cnt++ ] );

    // cleanup
    free( arr );
    free( copy );

    return 0;
}

特意省略了对堆分配函数返回值的关键验证,以提高此示例代码的清晰度。这些验证留给读者作为练习。

评论

0赞 12/31/2022
我理解你的观点,我之所以细分为子函数,是因为我的学校禁止我有超过 20 行,每行最多必须有 80 列,并且不允许我超过 2 个循环/嵌套条件结构。我不允许使用 libC(在这种情况下 malloc() 除外),因此我重新编码了该程序中调用的所有函数。
1赞 Fe2O3 12/31/2022
愚蠢的、武断的约束......清晰简洁的代码应该是约束,恕我直言......“80列”?!告诉你的导师,世界已经从霍勒里斯卡牌中走出来了。有趣。。。本世纪我们差不多是 1.4 岁了,“打孔卡”在上个世纪末之前就已经死了......
0赞 12/31/2022
我知道。。。他们所谓的“编码风格”的 pdf 长达 25 页......而且遵循起来非常复杂......