15

I have code equivalent to the following:

const int* const n = new int;
printf("input: ");
scanf("%d", n);
delete n;

Now, since n is a pointer to a CONSTANT integer, this shouldn't work (I'm expecting a compiler error). However, this seems to work properly and even stores the value of the input into *n.

I want to know, why doesn't this give me an error; why does it work? Shouldn't scanf be unable to alter the value of *n?

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Crazycolorz5
  • 747
  • 6
  • 12

5 Answers5

23

The prototype for scanf is:

int scanf ( const char * format, ... );

The ellipsis means it takes variable arguments. C (and C++) have no way to check the validity of these parameters. That's why you won't get an error if you pass the address of a double here or even the double variable itself. It's up to the programmer to verify the correct parameter is passed.

pcarter
  • 1,516
  • 14
  • 21
  • "C (and C++) have no way to check the validity of these parameters" That's not _totally_ true, last time I checked it, clang reported wrong scanf usage with warnings. But it's true that there is no way to check validity by the type system. – cubuspl42 Oct 27 '15 at 13:47
  • AFAIK most compilers will warn about the `const` even though it's a variadic function, none of the parameters can be `const` and as well as there are special chekcs for the the format string I think it's very likely that there are this kind of checks too. I am not sure because I almost never use `scanf()`. – Iharob Al Asimi Oct 27 '15 at 13:48
  • 1
    Try for example passing `unsigned int` to `printf()` where a `"%f"` specifier goes. – Iharob Al Asimi Oct 27 '15 at 13:51
  • 10
    @cubuspl42: That is a courtesy of the compiler (and the function prototypes being attributed in the library header), but not part of the language standard. – too honest for this site Oct 27 '15 at 14:29
5

scanf has next to no type safety, it merrily does what you tell it to. This is because of the way variable-argument lists are implemented in C. They expect the types to be of the kind which you tell it.

So if you give scanf a conversion specifier which doesn't match, you will invoke undefined behavior, which occurs in run-time. Similarly, there is probably no way for the compiler to tell that the pointer passed is of type const type* const. A good compiler may give a diagnostic if it spots something fishy, but is by no means required to do so.

Since most cases of undefined behavior occur in run-time, it is generally the programmer's responsibility to know about the various forms of undefined behavior and avoid them.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • It is best to avoid `scanf` and variable-argument lists entirely, because of the lack of type safety among other things. – Lundin Oct 27 '15 at 13:51
  • However, the object isn't `const`: it was created with `new int`. –  Oct 27 '15 at 13:51
  • @Hurkyl In C and C++ you can always apply a stricter `const`-ness. It is perfectly fine to have a const pointer pointing at a non-constant object. – Lundin Oct 27 '15 at 13:53
  • Yes; but the point is that the thing being modified is not a const object, despite the fact the address was obtained through a pointer to `const int`. –  Oct 27 '15 at 13:55
  • The lack of `&` is why `scanf` is trying to modify the non-constant `int` object, rather than the constant (and not-an-`int`) pointer object. –  Oct 27 '15 at 13:56
  • @Hurkyl Aah okay, it was me who misread the question. Hang on. – Lundin Oct 27 '15 at 13:56
3

Since scanf is variadic, it's syntactically legal to pass pretty much anything in, thus it should compile too.

While n is a pointer to const int, it is pointing at an object that is, in fact, not a const object. Consequently, modifying that int object (e.g. by using a const_cast to convert the pointer to int*) is well-defined behavior.

Finally, the standard documentation of fscanf says

the 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.

The pointer is, in fact, pointing to an object of the appropriate type; in conclusion, I believe this is well-defined (but confusing) behavior.

2

The compiler could issue a warning but it wont stop you from making mistakes like this, it's undefined behavior, const is used just as an indicator, it does not prevent compilation. And it works properly because the pointer is not really const, although it's UNDEFINED BEHAVIOR to pass a const pointer but this is not really one.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • 2
    You missed the core of the issue - the vararg nature of `scanf`. @pcarter did not. – cubuspl42 Oct 27 '15 at 13:44
  • 1
    May just want to clarify that in general it is undefined behavior, but in his particular case, it's defined behavior because the `int` really is non-const. – Johannes Schaub - litb Oct 27 '15 at 13:49
  • @ᐅJohannesSchaub-litbᐊ I think I did, that's why I say *strictly*. – Iharob Al Asimi Oct 27 '15 at 13:50
  • Well it's the exact opposite. *Strictly* it is defined behavior in his code, even though a warning may appear. – Johannes Schaub - litb Oct 27 '15 at 13:50
  • @ᐅJohannesSchaub-litbᐊ You are 100% right. I am sorry, I got a storm of work now, will check back later how this went. – Iharob Al Asimi Oct 27 '15 at 13:53
  • What makes this undefined behavior? We're not modifying a `const` object, and I am under the impression that pointers to `const int` are sufficiently compatible with pointers to `int` that it's okay to interpret one as the other. –  Oct 27 '15 at 13:54
  • Actually, I am not sooo sure about the defined behavior, perhaps someone else can clarify about this nuance. But not because of the write, but because `int*` is a different type than `const int*`. As fas as I can remember, they have the same representation and are allowed to reinterpet each others byte (starting with C++11, strictly speaking, they added that aliasing exception). But it depends on the description of the `printf` function in detail. If they require exact type match and there is no wiggle room, it's UB (I would be very susprised if that fails, though). – Johannes Schaub - litb Oct 27 '15 at 13:55
  • Yes I know that in c++ `const` also is pretty special apparently. – Iharob Al Asimi Oct 27 '15 at 14:07
1

Well , scanf won't differentiate between arguments passed , but if you compile with warnings enabled ,compiler will warn you about that -

 warning: writing into constant object (argument 2) [-Wformat]
ameyCU
  • 16,489
  • 2
  • 26
  • 41
  • No `const` objects are being modified here. We have a (constant) *pointer to const int*, but it's pointing at a non-const object, so modifying the referent is okay. –  Oct 27 '15 at 13:52
  • @Hurkyl but wont it write to pointer itself ? That previous was a misinterpretation by me therefore retracted . – ameyCU Oct 27 '15 at 13:56
  • It writes *through* the pointer, but does not modify the pointer itself. –  Oct 27 '15 at 13:57
  • @Hurkyl oops !! . May have , so here in this case there will be no UB (not sure though ) – ameyCU Oct 27 '15 at 13:58
  • So warning here is a compiler bug? – paulm Oct 27 '15 at 15:00