Using difference of pointers with printf("%.*s")

Viewed 98

The problem I'm facing has to do with intptr_t data type and the way fprintf() takes arguments for the %.*s format. The %.*s format expect field precision to have type int, and maybe that's not unreasonable per se.

Not in this case though:

#include <stdio.h>
#include <stdint.h>

int main() {
    char fname[] = "Some_File.txt";
    FILE *write = fopen(fname, "w");
    
    if (write != NULL) {

        printf("\n\tType below :\n\n");
        char in[501] = ""; char *p;

        while (1) {
            fgets(in, MAX_LN, stdin);

    /*** Region with compiler warnings begins ***/
            if ((p = strstr(in, "/end/")) != 0) {
                intptr_t o = p - in;
                fprintf(write, "%.*s", o, in);
    /*** Region with compiler warnings ends ***/
                fclose(write);
                break;
            } else {
                fputs(in, write);
            }
        }
    }
}
  • If I compile this, it doesn't play well with %.*s, and the compiler points that out:

    warning: field precision should have type 'int', but argument has type 'intptr_t' (aka 'long') [-Wformat]

  • If I make it int o;, it plays well with %.*s but of course isn't ideal, and the compiler says as much:

    warning: implicit conversion loses integer precision: 'long' to 'int' [-Wshorten-64-to-32]

Now, this is demo code, and the max size that o can hold is 500 here, however, in my actual code, it can be 10,000 or even 100,000 (still very much within the size of a 32-bit int, isn't it?)

So what will resolve this best with the least changes?

Compiled on Clang (might be very similar on GCC) with -Wall -Wextra -pedantic.

2 Answers

The difference of two pointers is type ptrdiff_t. "... is the signed integer type of the result of subtracting two pointers;"

// intptr_t o = p-in;
ptrdiff_t o = p - in;

Given these both point in char in[501], the difference also fits in an int.
Simply cast. The .* expects a match int, not a intptr_t nor ptrdiff_t.

// fprintf(write,"%.*s",o,in);
fprintf(write,"%.*s", (int) o, in);

Or all at once:

fprintf(write,"%.*s", (int) (p - in), in);

The type of a pointer difference such as p-in is ptrdiff_t, not intptr_t. Anyway, one alternative in this case would be to use fwrite:

if ((p = strstr(in, "/end/")) != NULL) {
    size_t len = (size_t)(p - in);  // p - in is an offset into data with a size
    fwrite(in, sizeof(char), len, write);
    fclose(write);
    break;
} else {
    fputs(in, write);
}

Then add error checks.

Related