It's not an operator, it's a formatting code understand by the printf family of functions. That includes printf, sprintf, snprintf, fprintf, and a few more. They all take a format string, and they output that format string verbatim, except when they encounter certain % codes. When they encounter a format code, they match it up with one of the following parameters, which they then print according to the code. As the format string can contain an arbitrary number of format codes, all these functions are variadic (they take a variable number of arguments).
int printf(const char *format, ...); // Note the ... for the variable number of args
printf("Hello World"); // Just the format string, no additional arguments
// Insert two variable strings into a fixed format string
printf("Message Title: %s\n Message Body:\n%s\n", title, message);
// Insert numbers into a string
printf("You have %i new messages.\n", numNewMessages);
// Display a number followed by a literal % symbol
printf("Formating spammer's hard drive: %i%% complete.", i);
// Display both strings and numbers
printf("%i %s have died.", numPeople, colonistType);
You may note there are two different escape codes being used in there. The "\n" is processed by the C compiler and translated into a new line character. This is in contrast to the % escape codes, which are only understood by the printf function, not by the C compiler. By the time printf gets those strings, the "\n" characters have already been translated (during the compilation step). The printf function must then translate the % escape codes (during runtime). You'll note that whenever you use escape codes, it interferes with usage of the escape code for non-special purposes. To deal with this, most implementations that use escape codes will have an escape code for the escape code. Hence, the C compiler uses "\\" to print a slash, so if you wanted a literal "\n" in a string, without it being escaped into a newline character, you'd write it as "\\n". Similarly, to print a "%" symbol with printf, you'd escape it with an extra % and end up with "%%". In a related issue, quotation marks which delimit the start and end of strings also need to be escaped if they're to appear in the string: "\"".
Btw, since the C compiler doesn't understand the format strings (only the printf family of functions do), it can't perform any type checking of the string against the supplied arguments. Hence, if you do strange things like the following:
// The compiler is unable to warn of these
printf("%s", someStringVariable); // Print the string (demo of correct usage)
printf("%i", someStringVariable); // Print the address of the string (in decimal)
printf("%x", someStringVariable); // Print the address of the string (in hex)
printf("%f", someStringVariable); // Interpret the address of the string as a floating point value and print that floating point value (bug!)
// These are bad and should almost certainly generate a warning, if only the compiler could
printf("%i", 1, 2, 3); // Ignoring some parameters. (bad)
printf("%i"); // Try to print a non-existent parameter. (very bad)
// This is just plain unsafe
printf(stringVariable); // ... (very very bad)
That last one probably needs a bit of explaination. Compare it to the "Hello World" example at the top, and it seems quite natural. If have a string you want to print, and it echoes the format string, just print it as the format string. Sure..., unless that string just happens to have unexpected format codes embedded in it. If that comes from user input, or worse, a network packet, then it can poke around all the values on the stack and display them however the person who constructed that string wants. This could reveal a lot of potentially sensitive information. Or, it can be used to obtain information useful to creating a remote exploit, like how far away the return address on the stack is. By printing your stack contents as a series of hex values (%x), they could examine the output to see about where the stack pointer lies. What's even worse, is the %n code, which writes the number of bytes output so far into (what it assumes to be) an integer pointer. This could allow an attacker to write a value of their choosing to some part of your stack, overwriting a local variable. Who knows what that could accomplish.
Of course, you are right that % is a modulus operator in C, however, if you consider they way things are parsed, anything within a string can't be an operator.