使用 strtok 转储的分段错误核心

Segmentation fault core dumped using strtok

我正在尝试编写自己的 scanf,它获取格式字符串中的数字以限制输入的大小。 like 而不是 "%d" 它使用 "@5%d" 表示输入不能超过 5 位数字。

我正在使用以下代码

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



/*
 * 
 */
char** str_split(char* a_str, const char a_delim);
void myscanf(char *input_format, ... );

int main(int argc, char** argv) {
    int x;
    myscanf("@2%d", &x);

    printf("%d", x);

    return 0;
}

void myscanf(char *input_format, ... ){
   va_list args;
   va_start(args, input_format);
   char** tokens;
   tokens = str_split(input_format, '@');
   tokens=tokens++;
   while(*tokens){

       int number;
       char format;
       char** parts;

       parts=str_split(*tokens,'%');
       number=atoi(*(parts));
       format= **(parts+1);

       char s[number+1];
       fgets(s,number+1,stdin);

       if(strlen(s)>number)
           perror("buffer overflow");

       switch(format){
           case 'd':{

                int* integer = va_arg(args,int*);
                *integer = atoi(s);
                break;
           }
           case 'f': {

               float* floatingpoint = va_arg(args,float*);
               *floatingpoint = atof(s);
               break;
           }
           case 'c': {
               if(strlen(s)>1)
                    perror("buffer overflow");
               char* character = va_arg(args,char*);
               *character = s[0];
               break;
           }
           case 's': {
               char *string = va_arg(args,char*);
               strcpy(string, s);
               break;
           }
       }
       tokens++;
   }
}

char** str_split(char* a_str, const char a_delim)
{
    char** result    = 0;
    size_t count     = 0;
    char* tmp        = a_str;
    char* last_comma = 0;
    char delim[2];
    delim[0] = a_delim;
    delim[1] = 0;

    /* Count how many elements will be extracted. */
    while (*tmp)
    {
        if (a_delim == *tmp)
        {
            count++;
            last_comma = tmp;
        }
        tmp++;
    }

    /* Add space for trailing token. */
    count += last_comma < (a_str + strlen(a_str) - 1);

    /* Add space for terminating null string so caller
       knows where the list of returned strings ends. */
    count++;

    result = (char **) malloc(sizeof(char*) * count);

    if (result)
    {
        size_t idx  = 0;
        char* token = strtok(a_str, delim);

        while (token)
        {
            assert(idx < count);
            *(result + idx++) = strdup(token);
            token = strtok(0, delim);
        }
        assert(idx == count - 1);
        *(result + idx) = 0;
    }

    return result;
}

当我执行这个时,控制台显示

Segmentation fault core dumped.

我用gdb调试了一下,显示是strtok的问题。 gdb 输出:

GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...(no debugging symbols found)...done.
/home/core: No such file or directory.
(gdb) r
Starting program: /home/a.out 

Program received signal SIGSEGV, Segmentation fault.
strtok () at ../sysdeps/x86_64/strtok.S:186
186 ../sysdeps/x86_64/strtok.S: No such file or directory.

如果有人能解决这个问题,我将不胜感激。

您正在使用字符串文字 ("@2%d") 调用 myscanfmyscanf 使用它调用 str_splitstr_split 将调用 strtok 并且 strtok 试图写入它,这是不允许的。

一个简单的解决方案是创建一个缓冲区

char buffer[] = "@2%d";
myscanf(buffer, &x);

我注意到的另一个小错误:
tokens=tokens++; 是未定义的行为,您应该只使用 tokens++; 来增加 tokens。 您已经注意到我对 strcpy 的评论并在示例代码中对其进行了更改。