简体   繁体   中英

Weird behaviour of C program in MacOS

I've been writing a shell program in C. The program is working as expected in Linux (Ubuntu 16.04) but I'm getting unexpected output in 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;
    }
}

In Linux:

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

In MacOS (with debug statements):

(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??

I don't understand that why are the contents of char* argv[] getting modified across fork() ?

I've also tried it in the default clang compiler and brew's gcc-4.9 , the results are same.

When a program behaves different for no good reason, that's a VERY good sign of undefined behavior. And it is also the reason here.

The array buf is local to the function input and ceases to exist when the function exits.

One way of solving this is to declare buf in main and pass it to input . You will also need the size of the buffer for 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");
}

Another solution (although I suspect many will frown upon it) is to declare buf as static , but then you would need to change BUF_SIZE to a #define or a hard coded value, since you cannot have a static 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");
}

I removed the cast to void* since it's completely unnecessary. I also changed the while loop to a for loop to make the loop variable local to the loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM