It's not 100% clear in my mind what's going on to make you want to be paranoid in your situation. You say that you are maintaining a table of function pointers and need to verify them. But this actually suggests to me that other developer(s) are maintaining that table, not you, and that you don't trust that table and want a way to verify, independently, that certain function addresses in the table point to routines that you expect to be located there. (If you were maintaining the table yourself, then it seems to me that all you'd need to do is make sure that you put the right function pointers in the right places so there'd be no need for such paranoia.)
A table also implies to me that all of the functions have the same signature (declaration.)
To repeat what's already been written in various ways here, you don't really get to place data (string values) into the code space. Sure, back in the day when most computers were von Neumann, you could technically achieve it if the compiler provided non-standard methods for you. But in these days, with many Harvard and von Neumann machines around, C is pretty careful about remaining relevant to both architecture families. Suppose there existed a custom compiler keyword which would place the name of a function (or any given string you wanted to use) at the head of a function or at a reliable location nearby. This string would be in the code space. But on some processors (x86, for example) this space may be marked "execute-only" and therefore cannot even be read. Or in a Harvard machine, different instructions (or perhaps no instructions, at all) would have to be used to access the data, as data, rather than as code. All of this would make any such "additions" to a C compiler rather complex and of little general value so it would be very unlikely for a C compiler vendor to bother -- even if it were possible. But it's not even possible across the board, because as I just mentioned the x86 can have its code segments marked as "execute only" and this pretty much means the whole idea isn't going to fly.
Okay. So that's at least one argument about why you should abandon the idea of co-locating literals with function body code in C. And while I think there are statements in other answers that might be used to infer similarly, I've mentioned a very specific case where the hardware itself may prevent the idea and then there would be nothing that a compiler/linker/toolchain could do about it. Not even an __asm{} body can help you here. So abandon the idea.
So what should you do if you don't trust function pointers in a table? (I frankly haven't encountered a situation where I would want to distrust tables -- but my experience is admittedly limited to 40 years of C development by one person and that obviously doesn't cover every case.) You will need to pick your poison.
Let's assume that while your code uses a function pointer table for making the calls, it's still possible that other code (perhaps written by those other programmer(s)) may also directly call these functions, as well. Put another way, these functions you want to verify aren't ONLY called via the table but may also be called in any of the usual ways available to C code that has visibility to them.
One suggestion I can offer that doesn't necessarily have to break such existing code is to ask your counter-part(s) programmers writing/maintaining these routines to add one additional parameter to them. This added parameter can be positioned anywhere, I suppose, though I might suggest placing it at the end. This parameter would be a pointer to a special datatype pointer. If the pointer is null, then the code does the usual thing. But if the pointer isn't null, then the pointer is used to update the referenced location with a "special value," that may be a magic integer or a string literal.
To avoid breaking older code that might directly reference those functions, you can use an old trick. For modules that need to reference the functions, directly, they usually use an include file of some kind that declares the functions. In that file, use:
#define foo(p1, p2, ..., pn) ((foo)(p1, p2, ..., pn, 0))
Of course, the actual function will look something like:
extern <type> (foo)(<type> p1, <type> p2, ..., <type> pn, magiccode_t* pz) {
if ( pz != 0 ) {
// write a special, unique code into *pz and return
}
// Do normal things
// .
// .
// . }
It's not "pretty" but it can get the job done.
By wrapping the function name with (), you keep the pre-processor from attempting to replace the macro using the definition that is found in the include file for it.
For your code that must test the function pointer in the table, you'd define a variable of type "magiccode_t" call it something like this:
magiccode_t code;
FnTable[0](0, 0, ..., 0, &code);
if ( code != EXPECTEDMAGICCODE0 ) error;
FnTable[1](0, 0, ..., 0, &code);
if ( code != EXPECTEDMAGICCODE1 ) error;
FnTable[2](0, 0, ..., 0, &code);
if ( code != EXPECTEDMAGICCODE2 ) error;
.
.
.
An unfortunate detail is that your code, when calling the function through the table for normal usage, will need to include a 0 at the end of the parameter list which wasn't needed before. You could adjust things a little by using FnTable as a #define in your own code, rather than as an array element:
#define FnTable(idx, p1, p2, ..., pn) ((FnTable)[idx](p1, p2, ..., pn, 0))
#define FnTableTest(idx, pz) ((FnTable)[idx](0, 0, ..., 0, pz))
Then your testing code would use FnTableTest() in order to get the magic code and test it. But the rest of your code would just use FnTable(i, p1, p2, ..., pn). This doesn't look exactly like an array, as the index itself appears to be a parameter instead. But it may be "livable" for you. In that case, your testing code might look more like:
magiccode_t code;
FnTableTest(0, &code);
if ( code != EXPECTEDMAGICCODE0 ) error;
FnTableTest(1, &code);
if ( code != EXPECTEDMAGICCODE1 ) error;
FnTableTest(2, &code);
if ( code != EXPECTEDMAGICCODE2 ) error;
.
.
.
Just some thoughts.
Added: Above, I elected to show examples where the special parameter is added at the end of the parameter list for each function. But an advantage can be had if you place it into the first position, with C. In that case, you only need to pass that first parameter when requesting the magic code. You may need to cast the function pointer type before calling it that way to avoid error/warning messages, but that's easily done and the routines themselves won't attempt to look at other parameters not passed if the first parameter is not null. Just another variation of the above approach.