繁体   English   中英

打印字符串数组会产生错误的 output

[英]Printing array of strings produces bad output

我正在尝试解决一个挑战,但我不知道我的代码有什么问题!

挑战在于:

  • 创建一个 function 将字符串拆分为单词。
  • 分隔符是空格、制表符和换行符。
  • 这个 function 返回一个数组,其中每个框都包含一个由单词表示的字符串地址。 这个数组的最后一个元素应该等于 0 以强调数组的结尾。
  • 数组中不能有任何空字符串。 得出必要的结论。 不能修改给定的字符串。
  • 注意:唯一允许的 function 是malloc()

错误/问题:我遇到了这个问题,我试图解决它,但我无法确定问题所在。 我创建了一个名为split_whitespaces()的 function 来完成这项工作。 当我在split_whitespaces function 中打印字符串数组时,我得到以下 output:

Inside the function:
arr_str[0] = This
arr_str[1] = is
arr_str[2] = just
arr_str[3] = a
arr_str[4] = test!

当我在main function 中打印字符串数组时,我得到以下 output:

Inside the main function:
arr_str[0] = @X@?~
arr_str[1] = `X@?~
arr_str[2] = just
arr_str[3] = a
arr_str[4] = test!

我创建了一个 function word_count来计算输入字符串中有多少个单词,这样我就可以使用 malloc 和word_count + 1 (空指针)分配 memory。

int word_count(char *str) {
    int i;
    int w_count;
    int state;

    i = 0;
    w_count = 0;
    state = 0;
    while (str[i]) {
        if (!iswhitespace(str[i])) {
            if (!state)
                w_count++;
            state = 1;
            i++;
        } else {
            state = 0;
            i++;
        }
    }
    return (w_count);
}

另一个 function 调用strdup_w来模仿strdup的行为,但仅针对单个单词:

char *strdup_w(char *str, int *index) {
    char *word;
    int len;
    int i;

    i = *index;
    len = 0;
    while (str[i] && !iswhitespace(str[i]))
        len++, i++;;
    word = (char *) malloc(len + 1);
    if (!word)
        return (NULL);
    i = 0;
    while (str[*index]) {
        if (!iswhitespace(str[*index])) {
            word[i++] = str[*index];
            (*index)++;
        } else
            break;
    }
    word[len] = '\0';
    return (word);
}

这是我的完整代码:

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

char **split_whitespaces(char *str);
char *strdup_w(char *str, int *index);
int word_count(char *str);
int iswhitespace(char c);

int main(void) {
    char *str = "This is just a test!";
    char **arr_str;
    int i;

    i = 0;
    arr_str = split_whitespaces(str);
    printf("\nOutside the function:\n");
    while (arr_str[i]) {
        printf("arr_str[%d] = %s\n", i, arr_str[i]);
        i++;
    }
    return (0);
}

char **split_whitespaces(char *str) {
    char **arr_str;
    int i;
    int words;
    int w_i;

    i = 0;
    w_i = 0;
    words = word_count(str);
    arr_str = (char **)malloc(words + 1);
    if (!arr_str)
        return (NULL);
    printf("Inside the function:\n");
    while (w_i < words) {
        while (iswhitespace(str[i]) && str[i])
            if (!str[i++])
                break;
        arr_str[w_i] = strdup_w(str, &i);
        printf("arr_str[%d] = %s\n", w_i, arr_str[w_i]);
        w_i++;
    }
    arr_str[words] = 0;
    return (arr_str);
}

char *strdup_w(char *str, int *index) {
    char *word;
    int len;
    int i;

    i = *index;
    len = 0;
    while (str[i] && !iswhitespace(str[i]))
        len++, i++;;
    word = (char *)malloc(len + 1);
    if (!word)
        return (NULL);
    i = 0;
    while (str[*index]) {
        if (!iswhitespace(str[*index])) {
            word[i++] = str[*index];
            (*index)++;
        } else
            break;
    }
    word[len] = '\0';
    return (word);
}

int word_count(char *str) {
    int i;
    int w_count;
    int state;

    i = 0;
    w_count = 0;
    state = 0;
    while (str[i]) {
        if (!iswhitespace(str[i])) {
            if (!state)
                w_count++;
            state = 1;
            i++;
        } else {
            state = 0;
            i++;
        }
    }
    return (w_count);
}

int iswhitespace(char c) {
    if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
        return (1);
    return (0);
}

对不起,如果有什么事,这是我第一次尝试寻求帮助。

代码中存在多个问题:

  • arr_str = (char **)malloc(words + 1); 您必须将元素的数量乘以元素的大小:

     arr_str = malloc(sizeof(*arr_str) * (words + 1));
  • 使用后在main() function 中释放数组是一种很好的方式。

  • while (iswhitespace(str[i]) && str[i])的测试是多余的:如果w_count计算正确,则不需要测试str[i] 您应该使用strspn()跳过空格, strcspn()跳过单词字符。

  • if (;str[i++]) break; 在循环内部是完全冗余的: str[i]已经过测试,不是 null。

  • while (str[i] &&,iswhitespace(str[i])) len++; i++;; 是坏风格。 如果循环体中有多个简单语句,请使用大括号。

  • strdup_w中的最后一个循环很复杂,您可以简单地使用memcpy(word, str + *index, len); *index += len; memcpy(word, str + *index, len); *index += len;

这是修改后的版本:

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

char **split_whitespaces(const char *str);
char *strdup_w(const char *str, int *index);
int word_count(const char *str);
int iswhitespace(char c);

int main(void) {
    const char *str = "This is just a test!";
    char **arr_str;
    int i;

    arr_str = split_whitespaces(str);
    if (arr_str) {
        printf("\nOutside the function:\n");
        i = 0;
        while (arr_str[i]) {
            printf("arr_str[%d] = %s\n", i, arr_str[i]);
            i++;
        }
        while (i --> 0) {
            free(arr_str[i]);
        }
        free(arr_str);
    }
    return 0;
}

char **split_whitespaces(const char *str) {
    char **arr_str;
    int i;
    int words;
    int w_i;

    i = 0;
    w_i = 0;
    words = word_count(str);
    arr_str = malloc(sizeof(*arr_str) * (words + 1));
    if (!arr_str)
        return NULL;
    printf("Inside the function:\n");
    while (w_i < words) {
        while (iswhitespace(str[i]))
            i++;
        arr_str[w_i] = strdup_w(str, &i);
        if (!arr_str[w_i])
            break;
        printf("arr_str[%d] = %s\n", w_i, arr_str[w_i]);
        w_i++;
    }
    arr_str[words] = NULL;
    return arr_str;
}

char *strdup_w(const char *str, int *index) {
    char *word;
    int len;
    int start;
    int i;

    i = *index;
    start = i;
    while (str[i] && !iswhitespace(str[i])) {
        i++;
    }
    *index = i;
    len = i - start;
    word = malloc(len + 1);
    if (!word)
        return NULL;
    i = 0;
    while (i < len) {
        word[i] = str[start + i];
        i++;
    }
    word[i] = '\0';
    return word;
}

int word_count(const char *str) {
    int i;
    int w_count;
    int state;

    i = 0;
    w_count = 0;
    state = 0;
    while (str[i]) {
        if (!iswhitespace(str[i])) {
            if (!state)
                w_count++;
            state = 1;
        } else {
            state = 0;
        }
        i++;
    }
    return w_count;
}

int iswhitespace(char c) {
    return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
}

从我的顶级评论...

split_whitespaces中,尝试更改:

arr_str = (char **) malloc(words + 1);

进入:

arr_str = malloc(sizeof(*arr_str) * (words + 1));

正如你所拥有的, words是一个计数而不是一个字节长度,所以你没有分配足够的空间,所以你有 UB。


更新:

但是看了一些教程,他们说 malloc 需要一个参数,即要分配的 memory 的大小(以字节为单位),这就是为什么我分配 memory 为 5 个字节! 你能告诉我在没有sizeof() function 的情况下使用malloc的替代方法吗? 我会很感激的。 ——Achraf EL Khnissi

如果没有sizeof ,真的没有干净的方法来指定它。

sizeof不是function [尽管有语法]。 它是一个编译器指令。 它“返回”其参数占用的字节数作为编译时间常数。

如果我们有char buf[5]; ,有 5 个字节,所以sizeof(buf) [或sizeof buf ] 为 5。

如果我们有: int buf[5]; ,有 5 个元素,每个元素的大小为 [通常] 4 个字节,因此总空间(以字节为单位)为sizeof(int) * 5 int 4 * 5 ,即 20。

但是, int可能因架构而异。 在 Intel 8086 上[大约在 1980 年代],一个int是 2 个字节(即 16 位)。 所以,上面的4 * 5是错误的。 它应该是2 * 5

如果我们使用sizeof(int) ,那么sizeof(int) * 5无论架构如何都可以工作。

同样,在 32 位机器上,指针 [通常] 为 32 位。 所以, sizeof(char *)是 4 [字节]。 在 64 位机器上,指针是 64 位,即 8 个字节。 所以, sizeof(char *)是 8。

因为arr_str是: char **arr_str ,我们可以这样做:

arr_str = malloc(sizeof(char *) * (words + 1));

但是,如果arr_str的定义曾经更改(更改为(例如) struct string *arr_str; ,那么如果我们忘记将分配更改为:

arr_str = malloc(sizeof(struct string) * (words + 1));

所以,做:

arr_str = malloc(sizeof(*arr_str) * (words + 1));

是编写更简洁代码的首选惯用方式。 更多语句将自动调整,而无需手动查找所有受影响的代码行。


更新#2:

您可能只是添加删除(char **)演员表的原因:) -- chqrlie

请注意,我删除了(char **)演员表。 请参阅: 我是否会转换 malloc 的结果?

这只是添加了额外/不必要的“东西”,因为mallocvoid *返回值可以分配给任何类型的指针。

如果我们忘了这样做: #include <stdlib.h> ,将没有malloc 的malloc原型,因此编译器会将返回类型默认为int

如果没有强制转换,编译器会在语句上发出错误 [这是我们想要的]。

使用演员表,这个动作在编译时被屏蔽[或多或少]。 在 64 位机器上,编译器将使用截断为 32 位的值 [因为它认为malloc返回 32 位值] 而不是malloc的完整 64 位返回值。

这种截断是一个“沉默的杀手”。 应该标记为编译时错误的内容会产生更难调试的运行时错误(可能是段错误或其他 UB)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM