Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/email/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 将字符串拆分为令牌并将令牌放入数组-strtok_C_Arrays_Output_Strtok - Fatal编程技术网

C 将字符串拆分为令牌并将令牌放入数组-strtok

C 将字符串拆分为令牌并将令牌放入数组-strtok,c,arrays,output,strtok,C,Arrays,Output,Strtok,token_arr的目的是获取一个字符串和多个令牌,然后将这些令牌放入一个数组中。返回令牌数组 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 (

token_arr的目的是获取一个字符串和多个令牌,然后将这些令牌放入一个数组中。返回令牌数组

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;
}

为什么只打印第一个值?我的代码出了什么问题?

虽然我认为您可能已经根据注释对大部分问题进行了排序,但让我们看看一种解决表达式验证/返回问题的方法,以及一种返回令牌数的方法,以防止令牌化错误导致找到的令牌数少于n个

正如您所了解的,当您将str2本地声明为token_arr时,它具有自动存储持续时间,并且仅在声明它的范围内有效。当token_arr返回时,保存str2的内存被释放以供重用,任何试图在main中引用该内存的尝试都会调用未定义的行为

你有什么选择?1使用strdup为每个令牌动态分配存储,将令牌复制到分配的新内存中,然后将包含令牌的新内存块的起始地址分配给arr[i],例如

或者2使用strlen、malloc和memcpy手动执行相同的操作,例如

现在,每个arr[i]指向一个已分配存储持续时间的内存块,该存储持续时间在该块上调用free或程序结束之前保持有效

如果找到的令牌少于n_怎么办

如果在token_arr中发现少于n_标记,并且您试图通过返回main的表达式使用n_标记,那么您可能会再次调用未定义的行为。为确保仅使用token_arr中的标记,并通过表达式赋值在main中可用-将指向n_标记的指针作为第二个参数传递,并在返回arr;之前将其更新为i值;,e、 g

现在n_tokens back in main只包含在token_arr中实际找到并分配给arr[i]的令牌数

验证每个分配

验证对malloc、calloc、realloc、strdup或任何其他为您分配内存的函数的每个调用都是至关重要的。分配可能失败,而且确实失败。当它这样做时,它通过返回NULL而不是包含新内存块的起始地址的指针来告诉您。检查每个分配

总而言之,您可以做如下操作:

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;
}
内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:1始终保留指向内存块起始地址的指针,以便在不再需要时释放内存块

必须使用内存错误检查程序,以确保您不会试图访问内存或写入超出/超出分配的块的边界,尝试在未初始化的值上读取或建立条件跳转,最后确认释放所有已分配的内存

对于Linux,valgrind是正常的选择。每个平台都有类似的内存检查器。它们都很容易使用,只需运行程序即可

$ ./bin/token_arr
expression[0] = (
expression[1] = 8
expression[2] = +
expression[3] = (
expression[4] = 41
expression[5] = -
expression[6] = 12
expression[7] = )
expression[8] = )
始终确认已释放所有已分配的内存,并且没有内存错误


仔细检查一下,如果您还有其他问题,请告诉我。

虽然我认为您可能已经根据评论对大部分问题进行了排序,让我们看看一种解决表达式验证/返回问题的方法,以及一种返回令牌数量的方法,以防止令牌化中出现错误,从而导致找到的令牌数少于n_

正如您所了解的,当您将str2本地声明为token_arr时,它具有自动存储持续时间,并且仅在声明它的范围内有效。当token_arr返回时,保存str2的内存被释放以供重用,任何试图在main中引用该内存的尝试都会调用未定义的行为

你有什么选择?1使用strdup为每个令牌动态分配存储,将令牌复制到分配的新内存中,然后将包含令牌的新内存块的起始地址分配给arr[i],例如

或者2使用strlen、malloc和memcpy手动执行相同的操作,例如

现在,每个arr[i]指向一个已分配存储持续时间的内存块,该存储持续时间在该块上调用free或程序结束之前保持有效

如果找到的令牌少于n_怎么办

如果在token_arr中发现少于n_标记,并且您试图通过返回main的表达式使用n_标记,那么您可能会再次调用未定义的行为。为确保仅使用token_arr中的标记,并通过表达式赋值在main中可用-将指向n_标记的指针作为第二个参数传递,并在返回arr;之前将其更新为i值;,e、 g

现在n_tokens back in main只包含在token_arr中实际找到并分配给arr[i]的令牌数

有效的 吃每一份

验证对malloc、calloc、realloc、strdup或任何其他为您分配内存的函数的每个调用都是至关重要的。分配可能失败,而且确实失败。当它这样做时,它通过返回NULL而不是包含新内存块的起始地址的指针来告诉您。检查每个分配

总而言之,您可以做如下操作:

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;
}
内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:1始终保留指向内存块起始地址的指针,以便在不再需要时释放内存块

必须使用内存错误检查程序,以确保您不会试图访问内存或写入超出/超出分配的块的边界,尝试在未初始化的值上读取或建立条件跳转,最后确认释放所有已分配的内存

对于Linux,valgrind是正常的选择。每个平台都有类似的内存检查器。它们都很容易使用,只需运行程序即可

$ ./bin/token_arr
expression[0] = (
expression[1] = 8
expression[2] = +
expression[3] = (
expression[4] = 41
expression[5] = -
expression[6] = 12
expression[7] = )
expression[8] = )
始终确认已释放所有已分配的内存,并且没有内存错误


仔细检查一下,如果有进一步的问题,请告诉我。

您正在分配指针数组,但指针指向函数中的局部变量str2,一旦返回main,该变量就不在范围内,因此您正在访问已重用且可能已归零的无效数据。您需要在复制字符串文字的位置分配字符串,并且需要找到将两个分配的指针传递回main的方法,以便准确地释放分配的内存。就目前而言,你有“未定义的行为”。这意味着您看到的是一个有效的响应,以及许多其他可能的行为。除非您使用calloc或memset在arr末尾保留sentinel NULL,否则不需要在char**arr=mallocn_令牌+1*sizeofchar*;。arr只是您正在分配的指针数。如果您只有n_标记,那么您只需要该数量的指针,除非您确保最终指针为NULL,以允许使用while循环进行迭代-但在这种情况下,您需要将所有指针设置为NULL作为开始。您还需要避免写入比您拥有的指针更多的指针。因此,您需要确保我不会超过您当前拥有的n_令牌+1,例如,当ichar **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; }
#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;
}
$ ./bin/token_arr
expression[0] = (
expression[1] = 8
expression[2] = +
expression[3] = (
expression[4] = 41
expression[5] = -
expression[6] = 12
expression[7] = )
expression[8] = )
$ 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)