C strtok和fgets的奇数数组行为

C strtok和fgets的奇数数组行为,c,fgets,strtok,C,Fgets,Strtok,我正在开发一个程序,它充当一个shell解释器,读取带有参数的命令,并创建一个子程序,该子程序使用execvp()执行命令。我一直在做一些字符串操作来收集字符数组*args[],尤其是使用fgets和strtok 这是我的代码 #include <stdio.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #define MAX_LINE 80 int main

我正在开发一个程序,它充当一个shell解释器,读取带有参数的命令,并创建一个子程序,该子程序使用
execvp()
执行命令。我一直在做一些字符串操作来收集字符数组
*args[]
,尤其是使用
fgets
strtok

这是我的代码

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define MAX_LINE 80

int main(void){
    //initialize variables
    char *args[MAX_LINE/2 + 1];
    char input[MAX_LINE];
    //char input[MAX_LINE] = "some sentence unknown"; // <-- this line works fine..

    int counter = 0;
    printf("COMMANDER>");
    fflush(stdout);

    //receive input
    fgets(input,MAX_LINE,stdin);

    //parse input
    char *parser;
    parser = strtok(input," \r\t");

    //parse line
    while(parser != NULL){
                args[counter] = parser;
        counter++;  
        parser = strtok(NULL," ");
    }

    //print results
    int i = 0;
    for(i = 0; i < counter + 1;i++){
        printf("1");
        printf(" - %d: %s\n",i,args[i]);    
    } 

    return 0;
}
我的问题是那片空白。我不知道它从哪里来,不管我做什么,它都会出现

据我所知,它可能是字符串末尾的一个\n字符或其他字符,但将其作为
execvp(args[0],args)
传递到
execvp中会创建一个错误,因为它将此空行解释为“”的参数

main
开头有一行我已经注释掉了,它只是一个字符串赋值。如果使用此分配而不是
fgets
,程序将正常工作,我将获得所需的输入:

COMMANDER>some sentence unknown
1 - 0: some
1 - 1: sentence
1 - 2: unknown
1 - 3: (null)
谢谢你的阅读。我对我的C有点生疏,所以我自己坚持了几个小时,仍然找不到解决办法。

如果你阅读例如,你会看到它说

如果出现文件结尾或找到换行符,解析将停止,在这种情况下,str将包含该换行符

[我的重点]

您看到的“那个空白”是在字符串末尾添加的新行
fgets


然而,代码中有一个更糟糕的问题

您定义了指针数组,但未初始化此数组。在C语言中,未初始化的局部(和非静态)变量实际上是未初始化的。它们的内容将是不确定的,看起来几乎是随机的

更具体地说,您碰巧在
args[counter]
处得到一个空指针纯粹是运气

在没有初始化的情况下尝试以任何方式使用此指针将导致错误

简单的解决方案是显式初始化数组,使其充满空指针:

char *args[MAX_LINE/2 + 1] = { NULL };

以上将“零初始化”所有元素,对于指针,这意味着它们将为
NULL

fgets()
读取的行包含一个尾随的换行符
'\n'
。必须将其作为分隔符包含在由
strtok()
识别的字符列表中。此外,您必须将相同的列表传递给两个调用

您还忘了将偏移量
count
处的条目设置为
NULL
。具有自动存储的本地对象未初始化。还请注意,不应为
%s
转换说明符传递指向
printf
的空指针:尽管某些实现测试参数并输出特定字符串,但它具有未定义的行为

以下是更正的版本:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define MAX_LINE 80

int main(void){
    char *args[MAX_LINE/2 + 1];
    char input[MAX_LINE];
    int counter;

    printf("COMMANDER>");
    fflush(stdout);

    //receive input
    if (fgets(input, sizeof input, stdin)) {
        //parse input
        char *parser;
        parser = strtok(input, " \f\n\r\t");

        //parse line
        count = 0;
        while (parser != NULL) {
            args[counter] = parser;
            counter++;  
            parser = strtok(NULL, " \f\n\r\t");
        }
        args[counter] = NULL;

        //print results
        int i;
        for (i = 0; i <= counter; i++) {
            printf("1 - %d: %s\n", i, args[i] ? args[i] : "(null)");
        } 
    }
    return 0;
}
#包括
#包括
#包括
#包括
#定义最大行80
内部主(空){
字符*args[最大行/2+1];
字符输入[最大行];
整数计数器;
printf(“指挥官>”);
fflush(stdout);
//接收输入
if(fgets(输入,输入大小,标准输入)){
//解析输入
char*解析器;
parser=strtok(输入,“\f\n\r\t”);
//解析行
计数=0;
while(解析器!=NULL){
args[计数器]=解析器;
计数器++;
parser=strtok(NULL,“\f\n\r\t”);
}
args[计数器]=NULL;
//打印结果
int i;

对于(i=0;我看我建议将分隔符字符串更改为
“\n\r\t”
esc特别是在第二个
strtok
中,您忽略了对任何控制字符的分隔。这样做,完美地解决了问题,谢谢!谢谢!这很有意义。结尾的空指针最初被指定为语句,但我在问这个问题之前删除了它。
char *args[MAX_LINE/2 + 1] = { NULL };
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define MAX_LINE 80

int main(void){
    char *args[MAX_LINE/2 + 1];
    char input[MAX_LINE];
    int counter;

    printf("COMMANDER>");
    fflush(stdout);

    //receive input
    if (fgets(input, sizeof input, stdin)) {
        //parse input
        char *parser;
        parser = strtok(input, " \f\n\r\t");

        //parse line
        count = 0;
        while (parser != NULL) {
            args[counter] = parser;
            counter++;  
            parser = strtok(NULL, " \f\n\r\t");
        }
        args[counter] = NULL;

        //print results
        int i;
        for (i = 0; i <= counter; i++) {
            printf("1 - %d: %s\n", i, args[i] ? args[i] : "(null)");
        } 
    }
    return 0;
}