The C23 standard draft n3220 says the following about the conversion specifier n for fscanf in statement 7.23.6.2p12 (emphasis mine)
n No input is consumed. The corresponding argument shall be a pointer of a signed integer type. The number of characters read from the input stream so far by this call to the fscanf function is stored into the integer object pointed to by the argument. Execution of a %n directive does not increment the assignment count returned at the completion of execution of the fscanf function. No argument is converted, but one is consumed. If the conversion specification includes an assignment-suppressing character or a field width, the behavior is undefined.
I don't see any mention that the corresponding argument for the conversion specification %n (no length modifier) shall be a pointer to int.
Hence the following program should be well defined, since long long int is a signed integer type.
#include <stdio.h>
int main(void){
long long n = -1;
char c;
int num_asn = sscanf("...h", "...%n%c", &n, &c);
printf("num_asn = %i\n", num_asn);
printf("n = %lli\n", n);
printf("c = %c\n", c);
}
I compiled this program with gcc main.c -std=c23 and it linked with the cygwin C library.
I expected the following output:
num_asn = 1
n = 3
c = h
Instead I received the following output
num_asn = 1
n = -4294967293
c = h
When I change n (in my program) to have type int , I receive the expected output.
Example 4, in statement 7.23.6.2p21, in the C23 standard draft n3220, uses a pointer to int for conversion specification %n but doesnt say why:
#include <stdio.h>
/* ... */
int d1, d2, n1, n2, i;
i = sscanf("123", "%d%n%n%d", &d1, &n1, &n2, &d2);
Where in the C23 draft (or in any revision of the C standard) does it mention the corresponding argument for the conversion specification %n shall be a pointer to int for fscanf
sscanfto infer the size of the pointed-to integer type, so there does have to be a single valid size. The asm calling convention for variadic functions applies the "usual argument promotions" (which doesn't do anything to pointers), then passes them unchanged. No extra parameters that specify anything about types. Your experiment successfully demonstrated that the (default) size is smaller thanlong long(64-bit) on your system. I would've used%llxto print(uint64_t)n, to get an output like0xFFFFFFFF00000003clearly showing a little-endian narrow write.