10

I just happened to look at the prototype of the printf (and other fprintf class of functions) -

int printf(const char * restrict format, ...);

The keyword restrict if I understand correctly disallows access to the same object through two pointers if one of them is marked restrict.

An example that cites the same from the C standard is here.

One benefit of marking the format as restrict I think is saving the function from the chance that the format string might get modified during the execution (say because of the %n format specifier).

But does this impose a bigger constraint? Does this make the following function call invalid?

char format[] = "%s";
printf(format, format);

Because there is clearly an aliasing here. Why was the restrict keyword added to the format argument of printf?

Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/173127/discussion-on-question-by-ajay-brahmakshatriya-why-is-the-format-in-printf-marke). –  Jun 14 '18 at 09:16

2 Answers2

9

cppreference

During each execution of a block in which a restricted pointer P is declared (typically each execution of a function body in which P is a function parameter), if some object that is accessible through P (directly or indirectly) is modified, by any means, then all accesses to that object (both reads and writes) in that block must occur through P (directly or indirectly), otherwise the behavior is undefined.

(emphasis mine)

It means that:

char format[] = "%s";
printf(format, format);

Is well-defined because printf won't attempt to modify format.

The only thing that restrict makes undefined is 'writing to the format string using %…n while printf is running' (e.g. char f[] = "%hhn"; printf(f, (signed char *)f);).

Why was the restrict keyword added to the format argument of printf?

restrict is essentially a hint the compiler might use to optimize your code better.

Since restrict may or may not make code run faster, but it can never make it slower (assuming the compiler is sane), it should be used always, unless:

  • Using it would cause UB
  • It makes no significant performance improvement in this specific case
Community
  • 1
  • 1
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • When I was reading before posting the question, I was convinced that `restrict` matters only if there is write, but [this](http://port70.net/~nsz/c/c11/n1570.html#6.7.3.1p8) example confused me. It clearly only says accesses won't clash. Doesn't say anything about writes. – Ajay Brahmakshatriya Jun 13 '18 at 17:28
  • @AjayBrahmakshatriya Maybe the wording is a bit sloppy? The pointers in the example have to point to different things because one of them is written to. – HolyBlackCat Jun 13 '18 at 17:31
  • Yes, the wording wasn't exactly clear to me. I agree that if none of the two is written, it shouldn't matter. My concern was if the standard says they should not alias for reads too, can the compiler make some aliasing assumptions based on that. – Ajay Brahmakshatriya Jun 13 '18 at 17:34
  • @HolyBlackCat so my final take from this is that the two pointers are allowed to alias if there is not modification on the object. If that is correct, I am satisfactorily understand why `printf` has `restrict`, for optimizations ofcourse. My original confusion stemmed from my (wrong) assumption that they are not allowed to alias even if both just read. Only if this could be backed by some statement from the standard. But I will take the word of cpp reference to be true for now. Thanks1 – Ajay Brahmakshatriya Jun 13 '18 at 18:27
  • 1
    @HolyBlackCat I think it's a bit better now. As I understand it the reason for `restrict` is to say: "The pointer used for receiving the result of a `%..n` specifier is not allowed to be the same as the pointer to the format string". Kind of make sense. I'll remove DV and clean up comments. Thanks. – Support Ukraine Jun 13 '18 at 18:27
  • @4386427 I had tried to make clear in the question itself that I understand the how restrict helps in the `%..n` case. I just wanted to know if it disallows using `printf(format, format)`. Perhaps I should have made it more clear in the question. – Ajay Brahmakshatriya Jun 13 '18 at 18:30
  • @AjayBrahmakshatriya I think your question was very interesting. And I did understand that you wasn't asking about the meaning of `restrict`. Now that I understand the answer from HolyBlackCat, my guess is that use of `restrict` on the format string allows an implementation of `printf` to update the pointer used for `%hhn` at any time, i.e. as each char is printed. Without `restrict` that wouldn't be allowed. – Support Ukraine Jun 13 '18 at 18:38
  • @4386427 yes, without `restrict`, it would have to make a copy first. That I kind of knew while writing the question itself. My confusion was because I thought it also applies when both pointers are only going to read. You cleared that they are allowed to alias. I think the main problem was that the wordings in the standard weren't clear enough for me. – Ajay Brahmakshatriya Jun 13 '18 at 18:43
1

Why is the format in printf marked as restrict?

int printf(const char * restrict format, ...);

The restrict in some_type * restrict format is a "contract" between the calling code and the function printf(). It allows the printf() to assume the only possible changes to the data pointed to by format occur to what the function does directly and not a side effect of other pointers.

This allows printf() to consist of code that does not concern itself with a changing format string by such side effects.

Since format points to const data, printf() is not also allowed to change the data. Yet this is ancillary to the restrict feature.


Consider pathological code below. It violates the contract as printf() may certainly alter the state of *stdout, which in turn can alter .ubuf.

strcpy(stdout->ubuf, "%s");
printf(stdout->ubuf, "Hello World!\n");

@HolyBlackCat has a good "%n" example.

Key: restrict requires the calling code to not pass as format, any pointer to a string that may change due to printf() operation.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256