将字符串拆分为标记并将标记放入数组 - strtok
Splitting a string into tokens and putting tokens into array - strtok
char** token_arr(char* str, int n_tokens)
{
char**arr = malloc((n_tokens+1)*sizeof(char*));
char str2[n_tokens + 1];
strcpy(str2,str);
int i = 0;
char *p = strtok (str2, " ");
while (p != NULL)
{
arr[i] = p;
//printf("%s\n", arr[i]);
p = strtok (NULL, " ");
i++;
}
return arr;
}
token_arr的目的是得到一个字符串和一些token,然后把token放到一个数组中。返回标记数组。
int main(void) {
char*str1 = "( 8 + ( 41 - 12 ) )";
char**expression = token_arr(str1, 9);
for(int i = 0; i < 9; i++)
printf("expression[%d] = %c\n", i, *expression2[i]);
return 0;
}
输出:
expression2[0] = (
expression2[1] =
expression2[2] =
expression2[3] =
expression2[4] =
expression2[5] =
expression2[6] =
expression2[7] =
expression2[8] =
为什么只打印第一个值?我的代码有什么问题?
虽然我认为您可能已经根据评论对大部分问题进行了排序,但让我们看看如何解决 expressions
的 validation/return 和 return 防止标记化错误导致找到少于 n_tokens
的标记数。
如你所知,当你声明str2
局部于token_arr
时,它具有自动存储期限并且仅在它所在的范围内有效被宣布。当 token_arr
returns 时,持有 str2
的内存被释放以供重新使用,任何在 main()
中引用该内存的尝试都会调用 Undefined Behavior.
你有什么选择? (1)使用strdup
为每个token动态分配存储空间,将token复制到新分配的内存中,然后将包含token的新内存块的起始地址赋值给arr[i]
,如
arr[i] = strdup (p);
或 (2) 使用 strlen, malloc & memcpy
手动做同样的事情,例如
size_t len = strlen(p);
arr[i] = malloc (len + 1);
/* validate - here */
memcpy (arr[i], p, len + 1);
现在每个 arr[i]
都指向一个具有 分配的存储持续时间 的内存块,它一直有效,直到 free
在该块上被调用 - 或者节目结束。
如果找到少于 n_tokens
怎么办?
如果在 token_arr
中发现少于 n_tokens
,并且您尝试在 main()
中使用 n_tokens
到 expressions
,您可能会调用 未定义的行为 再次。为确保您仅使用在 token_arr
中找到并通过分配给 expression
在 main()
中可用的标记 -- 将指针传递给 n_tokens
作为第二个参数,并更新它 i
在 return arr;
之前的值,例如
char **token_arr (const char *str, int *n_tokens)
{
char **arr = malloc(*n_tokens * sizeof *arr);
...
i++;
}
*n_tokens = i; /* assign i to make tokes assigned available */
return arr;
}
现在 main()
中的 n_tokens
仅包含在 token_arr
中实际找到并分配给 arr[i]
的代币数量。
验证每个分配
验证对 malloc, calloc, realloc, strdup
或任何其他为您分配内存的函数的每次调用至关重要。分配可能并且确实会失败。当它出现时,它会让您知道 returning NULL
而不是包含新内存块起始地址的指针。检查每个分配。
总而言之,您可以这样做:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **token_arr (const char *str, int *n_tokens)
{
char **arr = malloc(*n_tokens * sizeof *arr);
char str2 [strlen(str) + 1];
int i = 0;
if (!arr) { /* validate every allocation */
perror ("malloc-n_tokens");
return NULL;
}
strcpy (str2, str);
char *p = strtok (str2, " ");
while (i < *n_tokens && p != NULL) { /* check used pointers */
arr[i] = strdup (p);
if (!arr[i]) { /* strdup allocates -> you must validate */
perror ("strdup-arr[i]");
if (i) /* if tokens stored, break an return */
break;
else { /* if no tokes stored, free pointers */
free (arr);
return NULL;
}
}
p = strtok (NULL, " ");
i++;
}
*n_tokens = i; /* assign i to make tokes assigned available */
return arr;
}
int main (void) {
char *str1 = "( 8 + ( 41 - 12 ) )";
int n_tokens = 9;
char **expression = token_arr (str1, &n_tokens);
if (expression) { /* validate token_arr succeeded */
for (int i = 0; i < n_tokens; i++) { /* n_tokens times */
printf ("expression[%d] = %s\n", i, expression[i]);
free (expression[i]); /* free mem allocated by strdup */
}
free (expression);
}
return 0;
}
(注意:在使用return之前同样检查token_arr
的return)
例子Use/Output
$ ./bin/token_arr
expression[0] = (
expression[1] = 8
expression[2] = +
expression[3] = (
expression[4] = 41
expression[5] = -
expression[6] = 12
expression[7] = )
expression[8] = )
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/token_arr
==8420== Memcheck, a memory error detector
==8420== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8420== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==8420== Command: ./bin/token_arr
==8420==
expression[0] = (
expression[1] = 8
expression[2] = +
expression[3] = (
expression[4] = 41
expression[5] = -
expression[6] = 12
expression[7] = )
expression[8] = )
==8420==
==8420== HEAP SUMMARY:
==8420== in use at exit: 0 bytes in 0 blocks
==8420== total heap usage: 10 allocs, 10 frees, 92 bytes allocated
==8420==
==8420== All heap blocks were freed -- no leaks are possible
==8420==
==8420== For counts of detected and suppressed errors, rerun with: -v
==8420== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。
char** token_arr(char* str, int n_tokens)
{
char**arr = malloc((n_tokens+1)*sizeof(char*));
char str2[n_tokens + 1];
strcpy(str2,str);
int i = 0;
char *p = strtok (str2, " ");
while (p != NULL)
{
arr[i] = p;
//printf("%s\n", arr[i]);
p = strtok (NULL, " ");
i++;
}
return arr;
}
token_arr的目的是得到一个字符串和一些token,然后把token放到一个数组中。返回标记数组。
int main(void) {
char*str1 = "( 8 + ( 41 - 12 ) )";
char**expression = token_arr(str1, 9);
for(int i = 0; i < 9; i++)
printf("expression[%d] = %c\n", i, *expression2[i]);
return 0;
}
输出:
expression2[0] = (
expression2[1] =
expression2[2] =
expression2[3] =
expression2[4] =
expression2[5] =
expression2[6] =
expression2[7] =
expression2[8] =
为什么只打印第一个值?我的代码有什么问题?
虽然我认为您可能已经根据评论对大部分问题进行了排序,但让我们看看如何解决 expressions
的 validation/return 和 return 防止标记化错误导致找到少于 n_tokens
的标记数。
如你所知,当你声明str2
局部于token_arr
时,它具有自动存储期限并且仅在它所在的范围内有效被宣布。当 token_arr
returns 时,持有 str2
的内存被释放以供重新使用,任何在 main()
中引用该内存的尝试都会调用 Undefined Behavior.
你有什么选择? (1)使用strdup
为每个token动态分配存储空间,将token复制到新分配的内存中,然后将包含token的新内存块的起始地址赋值给arr[i]
,如
arr[i] = strdup (p);
或 (2) 使用 strlen, malloc & memcpy
手动做同样的事情,例如
size_t len = strlen(p);
arr[i] = malloc (len + 1);
/* validate - here */
memcpy (arr[i], p, len + 1);
现在每个 arr[i]
都指向一个具有 分配的存储持续时间 的内存块,它一直有效,直到 free
在该块上被调用 - 或者节目结束。
如果找到少于 n_tokens
怎么办?
如果在 token_arr
中发现少于 n_tokens
,并且您尝试在 main()
中使用 n_tokens
到 expressions
,您可能会调用 未定义的行为 再次。为确保您仅使用在 token_arr
中找到并通过分配给 expression
在 main()
中可用的标记 -- 将指针传递给 n_tokens
作为第二个参数,并更新它 i
在 return arr;
之前的值,例如
char **token_arr (const char *str, int *n_tokens)
{
char **arr = malloc(*n_tokens * sizeof *arr);
...
i++;
}
*n_tokens = i; /* assign i to make tokes assigned available */
return arr;
}
现在 main()
中的 n_tokens
仅包含在 token_arr
中实际找到并分配给 arr[i]
的代币数量。
验证每个分配
验证对 malloc, calloc, realloc, strdup
或任何其他为您分配内存的函数的每次调用至关重要。分配可能并且确实会失败。当它出现时,它会让您知道 returning NULL
而不是包含新内存块起始地址的指针。检查每个分配。
总而言之,您可以这样做:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **token_arr (const char *str, int *n_tokens)
{
char **arr = malloc(*n_tokens * sizeof *arr);
char str2 [strlen(str) + 1];
int i = 0;
if (!arr) { /* validate every allocation */
perror ("malloc-n_tokens");
return NULL;
}
strcpy (str2, str);
char *p = strtok (str2, " ");
while (i < *n_tokens && p != NULL) { /* check used pointers */
arr[i] = strdup (p);
if (!arr[i]) { /* strdup allocates -> you must validate */
perror ("strdup-arr[i]");
if (i) /* if tokens stored, break an return */
break;
else { /* if no tokes stored, free pointers */
free (arr);
return NULL;
}
}
p = strtok (NULL, " ");
i++;
}
*n_tokens = i; /* assign i to make tokes assigned available */
return arr;
}
int main (void) {
char *str1 = "( 8 + ( 41 - 12 ) )";
int n_tokens = 9;
char **expression = token_arr (str1, &n_tokens);
if (expression) { /* validate token_arr succeeded */
for (int i = 0; i < n_tokens; i++) { /* n_tokens times */
printf ("expression[%d] = %s\n", i, expression[i]);
free (expression[i]); /* free mem allocated by strdup */
}
free (expression);
}
return 0;
}
(注意:在使用return之前同样检查token_arr
的return)
例子Use/Output
$ ./bin/token_arr
expression[0] = (
expression[1] = 8
expression[2] = +
expression[3] = (
expression[4] = 41
expression[5] = -
expression[6] = 12
expression[7] = )
expression[8] = )
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/token_arr
==8420== Memcheck, a memory error detector
==8420== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8420== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==8420== Command: ./bin/token_arr
==8420==
expression[0] = (
expression[1] = 8
expression[2] = +
expression[3] = (
expression[4] = 41
expression[5] = -
expression[6] = 12
expression[7] = )
expression[8] = )
==8420==
==8420== HEAP SUMMARY:
==8420== in use at exit: 0 bytes in 0 blocks
==8420== total heap usage: 10 allocs, 10 frees, 92 bytes allocated
==8420==
==8420== All heap blocks were freed -- no leaks are possible
==8420==
==8420== For counts of detected and suppressed errors, rerun with: -v
==8420== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。