[英]Printing array of strings produces bad output
我正在尝试解决一个挑战,但我不知道我的代码有什么问题!
挑战在于:
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 的结果?
这只是添加了额外/不必要的“东西”,因为malloc
的void *
返回值可以分配给任何类型的指针。
如果我们忘了这样做: #include <stdlib.h>
,将没有malloc 的malloc
原型,因此编译器会将返回类型默认为int
。
如果没有强制转换,编译器会在语句上发出错误 [这是我们想要的]。
使用演员表,这个动作在编译时被屏蔽[或多或少]。 在 64 位机器上,编译器将使用截断为 32 位的值 [因为它认为malloc
返回 32 位值] 而不是malloc
的完整 64 位返回值。
这种截断是一个“沉默的杀手”。 应该标记为编译时错误的内容会产生更难调试的运行时错误(可能是段错误或其他 UB)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.