0

I am trying to get the pid using this code. But when I ran the compiled the code. I get an error message "warning: format specifies type 'unsigned long' but the argument has type 'pid_t' (aka 'int') [-Wformat]".

When I changed the format specifier to just "%lu", it prints without the extra character.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
pid_t pid = getpid();

printf("pid: %lun", pid);
}

I expected pid of "60316". I get pid of "60316n".

2 Answers2

0

n prints out for the same reason p prints. n is not part of a specifier.

      vvv  --- specifier for unsigned long 
"pid: %lun"
 ^^^^^   ^ --- non-specifier characters

Aside:

The pid_t data type is a signed integer type which is capable of representing a process ID

See What is the correct printf specifier for printing pid_t?

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

The format string you pass to printf is a mixture of ordinary characters and format specifiers. The ordinary characters print as themselves, and the format specifiers cause one of the extra arguments to be converted and printed. But the type of each extra argument must match the type expected by the format specifiers, more or less exactly.

We can tell from the warning message that pid is an int. So the correct format specifier is %d.

The specifier %lu would have been correct if pid had type unsigned long int.

Format specifiers are sort of their own little miniature programming language. The basic format specifier for type unsigned int is %u, and you can modify it to print unsigned long int instead with the l (letter ell) modifier: %lu.

But when you write %lun, the n wasn't actually part of any format specifier, so it printed as itself.

(Also, you got lucky: despite the mismatch between pid's type and the format specifier %lu, you got a sensible value printed anyway.)

Usually, getting the arguments and the format specifiers to line up is easy: int for %d, unsigned int for %u or %x, long int for %ld, float or double for %f, etc. But what's the right specifier to use for pid, which has type pid_t? Today, on your computer, it looks like pid_t is an int, so %d would be correct, but what if, on some other computer, it has a different type? How can you write one piece of code that will work correctly on any computer? One way is to pick a type that's probably right, and use the specifier for that type, and then use an explicit cast to convert your value to the type you picked. Like this:

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

Or like this:

printf("pid: %lu\n", (unsigned long int)pid);

If the cast isn't necessary (if the format specifier you picked is already correct for the type of pid on your computer), the cast won't hurt.

Finally, congratulations on using a compiler that actually gave you the warning message:

warning: format specifies type 'unsigned long' but the argument has type 'pid_t' (aka 'int')

Too many beginning programmers are stuck using older compilers that don't print warnings like these, and that obviously makes it much more difficult to track down problems caused by mismatched format specifiers! (Your compiler was particularly helpful in that it gave you both the apparent type of pid, namely pid_t, and also the actual underlying type, int.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • So I was simply print out the "n". I understand now. Thanks – jennylearner Oct 04 '19 at 16:44
  • So am I wrong in using an unsigned long for pid? I thought it was okay since the range is larger. – jennylearner Oct 04 '19 at 16:51
  • Yes, it's wrong. If `pid` is really an int, strictly speaking it's wrong to print it as an unsigned with `u` (although you'll almost always get away with it). Printing with an `l` is worse, because `pid` (on your machine, anyway) definitely isn't long. You'll often get away with that, too, on a 32-bit machine where both `int` and `long int` are 32 bits. (But it wouldn't work on my machine, where `int` is 32 bits but `long` is 64.) – Steve Summit Oct 04 '19 at 16:54