提问人: 提问时间:12/31/2022 更新时间:1/22/2023 访问量:140
将字符串转换为单词数组时的 IOT 指令
IOT instruction when converting string to word array
问:
我回来找你谈谈我的功能。目的是将字符串分隔在每个不可打印的 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 个问题:
如果在我的测试中,我在对my_str_to_word_array的调用下方有一个,则传递给的格式将包含在数组中。因此,我得出结论,存在内存读取错误。
main()
printf()
printf()
当我尝试数组时,出现错误:
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
你看到问题了吗?
答:
1赞
Costantino Grana
12/31/2022
#1
错误:
get_words_number
越界(相差 1),并且可能会在字符串之后读取任意内存(请检查我在 中包含的示例)。main
- 您需要在阵列中增加一个插槽来放置一个终止 .
NULL
- 如果以后需要输入指针,请停止抖动输入指针(在打印循环中和打印循环中)。
my_free_word_array
main
- 编辑:正如 Fe2O3 评论的那样,我错过了函数中的另一个错误。您还应该确保在最后一个循环中。
fill_array
*i!=0
建议:
- 下次通过包含所有必需的标头来制作一个最小的、可重现的示例;
strndup
不是标准的(除非您具有并将定义定义为 1)。__STDC_ALLOC_LIB__
__STDC_WANT_LIB_EXT2__
- 您根本不需要该函数。
free_corrupted_array
- 编辑:如果您也在检查,检查是没有用的. 不可打印,因此无需进行第一次检查。
*i!=0
isprint(*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 页......而且遵循起来非常复杂......
评论
word_array = malloc(size)
----> 然后: ---> 更改最初指向的内容,并失去对动态分配的内存的所有访问权限。然后,传递指向未动态分配的指针,因此会出现警告。word_array = fill_array(word_array, str, word_count);
word_array
free
my_free_word_array
ptr1 = ptr2
不会将 的内容复制到 。它只复制指针值,这样两个指针现在都指向同一个地址。ptr2
ptr1