MacOS中C程序的怪异行为

时间:2019-03-05 05:26:04

标签: c macos fork

我一直在用C编写Shell程序。该程序在Linux(Ubuntu 16.04)中按预期工作,但在MacOS(10.14.2 Mojave)中却得到了意外输出。

/* A shell program.
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


void input(char* argv[]);
void print_arr(char *argv[]); // For debugging

int
main(void)
{
    while (1)
    {
        pid_t pid;
        char *argv[100];

        // Display shell prompt
        write(1, "(ash) $ ", 8);

        // Take user input
        input(argv);
        // print_arr(argv); // DEBUG STATEMENT

        if (argv[0] != NULL)
        {
            // Exit if exit command is entered
            if (strcmp(argv[0], "exit") == 0)
            {
                exit(0);
            }

            // Create child process
            if ((pid = fork()) > 0)
            {
                wait(NULL);
            }
            else if (pid == 0)
            {
                // print_arr(argv); // DEBUG STATEMENT

                execvp(argv[0], argv);
                printf("%s: Command not found\n", argv[0]);
                exit(0);
            }
            else
            {
                printf("Fork Error!\n");
            }
        }       
    }
}


/* Takes input from user and splits it in
   tokens into argv. The last element in
   argv will always be NULL. */
void
input(char* argv[])
{
    const int BUF_SIZE = 1024;
    char buf[BUF_SIZE];
    int i;

    buf[0] = '\0';
    fgets((void*) buf, BUF_SIZE, stdin);

    i = 0;
    argv[i] = strtok(buf, "  \n\0");
    while (argv[i] != NULL)
    {
        argv[++i] = strtok(NULL, "  \n\0");
    }
}

/* Print argv for debugging */
void
print_arr(char *argv[])
{
    int i = 0;
    while (argv[i] != NULL)
    {
        printf("%d: %s\n", i, argv[i]);
        ++i;
    }
}

在Linux中:

(ash) $ ls
// files and folders are listed

在MacOS中(带有调试语句):

(ash) $ ls
0: p?M??
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??

我不明白为什么char* argv[]的{​​{1}}的内容会被修改?

我也在默认的fork()编译器和brew的clang中进行了尝试,结果相同。

1 个答案:

答案 0 :(得分:1)

当程序无端地表现出不同的行为时,这是未定义行为的非常好兆头。这也是这里的原因。

数组buf对于函数input是局部的,并且在函数退出时不再存在。

解决此问题的一种方法是在buf中声明main并将其传递给input。您还将需要fgets的缓冲区大小。

void
input(char * argv[], char * buf, size_t size)
{
    buf[0] = '\0';
    fgets(buf, sizeof buf, stdin);

    argv[0] = strtok(buf, "  \n\0");

    for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, "  \n\0");
}

另一种解决方案(尽管我怀疑很多人对此不屑一顾)是将buf声明为static,但是随后您需要将BUF_SIZE更改为#define或硬编码值,因为您不能拥有静态VLA。

#define BUF_SIZE 1024
void
input(char * argv[])
{
    static char buf[BUF_SIZE];

    buf[0] = '\0';
    fgets(buf, sizeof buf, stdin);

    argv[0] = strtok(buf, "  \n\0");

    for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, "  \n\0");
}

我完全删除了对void*的强制转换。我还将while循环更改为for循环,以使该循环变量在该循环中是本地的。