Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Notifications
Mark all as read
Q&A

Is it OK to use scanf with a void pointer?

+6
−0

I've created a function that calls scanf passing a void pointer as argument:

void read(const char *format, void *p) {
    scanf(format, p);
}

And tested it with different types:

int n;
read("%d", &n);
printf("read int: %d\n", n);

float f;
read("%f", &f);
printf("read float: %f\n", f);

char s[100];
read("%s", s);
printf("read string: %s\n", s);

I've made these tests in Ubuntu 20.04.3, with clang 10.0.0-4ubuntu1 and gcc 9.3.0. Both were compiled with the options -std=c11 -pedantic-errors -Wall -Wextra -Werror, without any errors/warnings, and both worked fine (all data were correctly read and printed).

But "it worked" doesn't necessarily mean it's correct (specially in C), hence the question: is scanf supposed to work like that (receiving a void pointer and correctly assigning the data to it, according to the format specifier), or is it a big coincidence and should be avoided? Are there any cases that could fail (excluding the obvious ones, such as the format doesn't match the second argument)?


PS: The point here is not to discuss the merits of the read function (if it's useless, or should check the return value of scanf, or should use va_list/vfscanf instead, etc). I'm just interested in knowing if this behaviour of scanf (accepting a void pointer and correctly "guessing" its type based on the specifier) is expected (defined by the standard, for instance). In case it's not, an explanation about why it worked is also appreciated.

Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

2 answers

+5
−0

Void pointers are compatible with every other object pointer type and as mentioned in another answer, 7.21.6/10 speaks of the type of the pointed at object, not the type of the pointer. This is consistent with the rules of effective type/pointer aliasing (6.5/6), which have to be applied as well, since the passed pointer does not necessarily point to a chunk of memory with an object of a declared type (could as well be a void pointer returned from malloc). scanf has to be regarded as a "lvalue" access following the rules of effective type. Examples:

float f;
scanf("%d", &f); // undefined behavior, 7.21.6/10 and 6.5/7
void* v = &f;
scanf("%f", v); // well-defined behavior, object f has declared and effective type float

void* p = malloc(n); // location pointed at by p has no declared type
scanf("%d", p); // well-defined, *p is now to be regarded as effective type int

For scanf to make any sense, it will have to internally cast the passed pointer to a pointer to the type of the specified conversion specifier. Any type information that the passed pointers might have had is lost through the varadic function/va_list parameter passing anyway.

Notably, there's a whole lot of scenarios where scanf can go wrong, so it is mostly to be used for debugging purposes - it is not a function recommended to be used in any professional release. If your program for some reason must use console input, then use fgets as far as possible.

Why does this post require moderator attention?
You might want to add some details to your flag.

1 comment thread

Perhaps that's another question, but in `void* p = malloc(n); scanf("%d", p);`, what happens if `n` i... (2 comments)
+4
−0

From section 7.21.6.2 of this draft:

[T]he result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

So per the spec, it is the type of the object, not the pointer to the object, that must match the format specifier. A compiler that doesn't interpret the quoted code to have the behavior you saw would on this point not be spec-compliant.

Why does this post require moderator attention?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »