3

While I was reading this article, I came across this paragraph:

Similarly, don’t make any effort to cater to the possibility that long will be smaller than predefined types like size_t. For example, the following code is ok:

printf ("size = %lu\n", (unsigned long) sizeof array);
printf ("diff = %ld\n", (long) (pointer2 - pointer1));

1989 Standard C requires this to work, and we know of only one counterexample: 64-bit programs on Microsoft Windows. We will leave it to those who want to port GNU programs to that environment to figure out how to do it.

Does really the 1989 Standard C allow that code?

8
  • 1
    With pre C99, long/unsigned long is the widest standard type - little can be lost casting to the widest integer type there. Of course could use rintf ("size = %.0f\n", 1.0* sizeof array); Commented Sep 6, 2017 at 20:23
  • @chux but is that code compliant to the standard? Commented Sep 6, 2017 at 20:25
  • 1
    It is compliant (Aside from maybe overflow in pointer2 - pointer1). The issue remains will it print the expected result? (Did it truncated?) Commented Sep 6, 2017 at 20:25
  • 2
    It is roughly "In C90, if anything standard is going to work, it is this". It isn't guaranteed to work in all scenarios, but it would work in most of them. And there wasn't an alternative specified by the standard that would work better. Since C99, you should use the z (for size_t) and t (for ptrdiff_t) size modifiers: "%zu; %td\n". Commented Sep 6, 2017 at 20:29
  • 1
    This should work in all cases void print_size(size_t sz) { if (sz >= 10) { print_size(sz/10); } printf("%u", (unsigned) (sz%10)); } Commented Sep 6, 2017 at 20:43

1 Answer 1

3

The question as asked:

Does really the 1989 Standard C allow that code?

Yes, if by "allow" we mean "this will compile and run, doing something reasonable and not triggering undefined behavior" (and not "this will do what I want"). The two cases are slightly different.

For size_t, the result will be truncated if it is too large. For ptrdiff_t, the result will be implementation-defined if it is too large.

However, recent versions of Visual Studio (_MSC_VER >= 1800) support the z and t conversions (necessary for C++11 support anyway), so you can use:

size_t size;
printf("size = %zu\n", size);
ptrdiff_t diff;
printf("diff = %td\n", diff);

This will also work on other systems (GNU, BSD, Darwin). For older versions of Visual Studio there are alternatives, but it would require using a different format string on different platforms.

A note on GNU coding standards

The GNU coding standards are for the GNU organization, which has some very specific goals and does not care much about supporting Windows. I would take their coding standards with a grain of salt, unless you have a specific reason to follow them (for example, you want your project to become a GNU project in the future).

Sign up to request clarification or add additional context in comments.

3 Comments

No, the standard doesn't guarantee that %zd will work for a ptrdiff_t argument -- but it doesn't need to, since the correct format for a ptrdiff_t argument is %td'. (%zd is for the signed type corresponding to the unsigned type size_t. There is no portable name for that type -- even POSIX doesn't guarantee that ssize_t qualifies.)
@KeithThompson: Feel free to edit the answer, I was unable to find documentation for Visual Studio.
Actually I don't intend to fully follow these rules, I read other conventions, and I'm reading this, only, to see if I can find something that I missed in the others, or something I like.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.