1
votes

I am currently working on a software project in C which has to run on a variety of platforms. I try to remain as close to the C90 standard as possible, but need some commonly supported extensions like stdint.h and an unsigned long long type. I deliberately do not want to "fall back" to the C99 standard since some of the compilers that I use do not support all C99 features like mixed declarations and code etc.

Thus, I currently compile all my code with gcc on my (64-bit Ubuntu) development machine with -Wall -Wextra -pedantic -std=gnu90 (not C90 due to my use of unsigned long long etc. as described above) to address all non-format-compliant code parts. So far, I could adapt my code to get rid of all warnings but one: I cannot find a proper (printf) format for size_t that does not yield a warning from gcc. Consider the following example code which illustrates most of the things that I tried:

#include <stdint.h>
#include <stdio.h>

int main()
{
  printf("%zu", sizeof(int)); /* C99 format specifier for size_t */
  printf("%u", sizeof(int)); /* Treat size_t as unsigned int */
  printf("%lu", sizeof(int)); /* Treat size_t as unsigned long int (only works when size_t is "typedef"ed to unsigned long int) */
  return 0;
}

When compiled with -Wall -Wextra -pedantic -std=gnu90, I get warnings for the first two lines:

test.c:6:3: warning: ISO C90 does not support the ‘z’ gnu_printf length modifier [-Wformat=] test.c:7:3: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]

Although I do not get a warning for the third line, this is only the case on my development system. On other systems, specifically on those where size_t is some "custom" type or not "typedef"ed as unsigned long long, there will still be a warning.

I am well aware that there is no format specifier for size_t in C90, but that there is one in C99. However, I thought that setting -std=gnu90 would give me the 'z' format specifier, but that assumption turned out to be incorrect.

Since I could not get rid of the warning(s), I tried to define a format specifier with some preprocessor definitions like this:

#include <inttypes.h>
#include <stdio.h>

#ifdef __GNUC__
  #define GLUE(x, y, z) x##y##z
  #define GLUE_FORMAT(prefix, size) GLUE(PRI, prefix, size)
  #define UINT_FORMAT(size) GLUE_FORMAT(u, size)
  #define SIZE_T_FORMAT UINT_FORMAT(__SIZEOF_SIZE_T__)
#else /* C99 fall-back */
  #define SIZE_T_FORMAT "zu"
#endif

int main()
{
  printf("%" SIZE_T_FORMAT, sizeof(int));
  return 0;
}

I thought that this should give me the proper format specifier, the only constraint being that size_t is unsigned (which it is on all my target platforms so far). However, that does not work either:

test.c:15:3: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]

Is there any way, given the constraints, to overcome this warning (or to find a more elegant solution without resorting to -std=c99)?

1
@Coconop: %lu would work on my development platform, but not on some othersAndreas Unterweger
@Coconop: I added a short explanation to my original post and updated the code.Andreas Unterweger
Where does __SIZEOF_SIZE_T__ come from? grep __SIZEOF_SIZE_T__ /usr/include/ -rw on my recent Debian gives me nothing ... :-Salk
@alk: That's defined in one of the headers from the GNU extensionsAndreas Unterweger
Normally under C89/C90, you'd use %lu and cast the result of the sizeof expression to unsigned long.John Bode

1 Answers

1
votes

Would you consider a cast? Casting is normally a sign of problems, but at some point it might be simplest to just write

printf("%u", (unsigned) sizeof(int));

You could even combine the cast into a macro

#define usizeof(a) ((unsigned)sizeof(a))
printf("%u", usizeof(int));

which would make it easy enough to use.