Search code examples
csystem-calls

Why is the write system call printing an incorrect number of characters?


I'm trying to implement the echo command using different C functions. Implementing it via printf was fairly simple:

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

int main(int argc, char *argv[]) {
    while (--argc)
        printf(((argc == 1) ? "%s\n" : "%s "), *++argv);
}

This works great:

$ ./a.out Bring a towel
Bring a towel

However, when I tried to implement it using the write system call:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    while (--argc) {
        write(STDOUT_FILENO, *++argv, strlen(*argv));
        write(STDOUT_FILENO, ((argc == 1) ? "\n" : " "), 1);
    }
}

it is printing strings of incorrect lengths:

$ ./a.out Bring a towel
Bringa atow t

I checked the string lengths returned by strlen(*argv) with printf and they seem correct. I also tried flushing after every write using fflush(stdout). Why is my write system call not working correctly?


Solution

  • Your code invokes undefined behaviour as the order of evaluation of function parameters is not specified. There is also no sequence point there. Also, operations on the argv itself invokes undefined behaviour.

    If you rewrite your program:

    int main(int argc, char *argv[]) {
        char **myargv = argv;
        while (--argc) {
            myargv++;
            write(STDOUT_FILENO, *myargv, strlen(*myargv));
            write(STDOUT_FILENO, ((argc == 1) ? "\n" : " "), 1);
        }
    }
    

    It will work fine

    You can see what is going on by modifying your original program.

    int main(int argc, char *argv[]) {
        size_t index = 0;
        size_t len[10];
        while (--argc) {
            write(STDOUT_FILENO, *++argv, (len[index++] = strlen(*argv)));
            write(STDOUT_FILENO, ((argc == 1) ? "\n" : " "), 1);
        }
        for(size_t i = 0; i < index; i++)
        {
            printf("%zu\n", len[i]);
        }
    }
    

    You will see that the first call to strlen is giving you the length of argv[0] in your and godbolt implementation.