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

72 Views Asked by At

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?

2

There are 2 best solutions below

1
gulpr On BEST ANSWER

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.

0
greg spears On

I've added variable iii for some precision indexing of argv. Incrementing argv simply wasn't having the results you expected. Other minor mods added using index and argc to attain desired print out.

Runnable code is here for you to see and experiment with, if desired.

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

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

    return 0;
}

Output:


    Bring a towel