-1

I'm using Visual studio for my code development and when I used the function

getche()

The compiler gave me this warning

 warning C4013: 'getche' undefined; assuming extern returning int

But the getche() function is working just as expected , why compiler is showing such a warning , and How can I eliminate this warning?

unwind
  • 391,730
  • 64
  • 469
  • 606
Sorcrer
  • 1,634
  • 1
  • 14
  • 27

2 Answers2

5

As your compiler is telling you, getche has not been defined.

Usually, you would include a header (e.g. #include <conio.h>) that will define the function somewhat similar to:

int getche(void);

There is a rule for the C language that allows usage of functions which have not been defined before. These functions are assumed to take the exact argument types they are passed (none, so void) and return int. Due to this feature (which is evil™) and the fact that getche does indeed get linked in due to your linker settings, your program actually works correctly.

Be aware that getche is deprecated and you should use _getche instead.

danielschemmel
  • 10,885
  • 1
  • 36
  • 58
4

One small thing that I want to add: correcting such errors are non-trivial, as further errors can result from a missing declaration.

Take for example the simple call foo(4). Assume there are no further informations on how this call works. What is the compiler able to read from such a call?

  1. The number of parameters (pretty obvious).
  2. The types of the parameters (4 is usually expanded to a 4 byte value, 4LL on 64 bit machines (gcc) to a 8 byte value).
  3. The order in which the parameters are supposed to be pushed on stack (calling convention - most of the time it's the C convention, but it depends on your compiler settings).

What is the compiler not able to read from that call:

  1. What kind of value the call returns.

That's right. Because foo might be defined in another module, or even in a system library. The symbol foo, without a declaration, is totally unknown to the compiler. Because it just compiles your code, but it's not responsible for the linkage of your symbols (I say symbols because this can include variables and functions).

Now, what does the compiler have to do?

  1. It needs to generate instructions for each call. Depending on the calling convention these instructions can differ wildly. For example, with cdecl the caller has to push the arguments on the stack, and this before each call of a function. With stdcall the callee has to clean-up the stack.

  2. It needs to know what types the arguments are. Well, actually it does not care so much for the type, but for the size the objects occupy. A 20 byte struct object needs 20 bytes on the stack, doesn't it? And each parameter needs to be put on it (call-by-value).

  3. And it needs to reserve enough memory on the stack for the return value of the function. Because: whatever the function is going to return is stored in that memory. And if the function returns a 20 byte struct object, it needs 20 bytes. So far, so good.

Now, anything but the return value can be read from a call - the return value must be provided by a declaration. If this declaration is not provided, the compiler will just reserve sizeof(int) (4 bytes most of the time) on the stack for the return value and will leave the rest to the linker.

The linker sees the symbol foo(4 bytes) and will start to look for any corresponding definitions of foo(4 bytes). And if it finds that (say, in another module of yours, or in the libc of your system, or as syscall wrapper), then the linker is content and it will create the executable.

Everything's fine, isn't it? Uhm, no, it isn't.

For example, there is a function called memmem provided on GNU systems that is activated on defining _GNU_SOURCE before including string.h. If you don't happen to define _GNU_SOURCE before including that function, the declaration of memmem will be skipped, and the compiler will not be able to determine what return the function has and will automatically assume 4 bytes. This is "OK" while you are on a 32 bit system - but if your program is compiled as 64 bit executable and the haystack that you pass to memmem is a 64 bit pointer that points above 0xFFFFFFFF, the upper 32 bit of your pointer are being erased (as if you'd have written return (pointer&0xFFFFFFFFULL);). And this new pointer can point to what I call the "bad nirvana" in correspondence to the "good nirvana". The "good nirvana" is NULL, everyone can see that this pointer should not be dereferenced. To do this for pointers that point to the "bad nirvana" is more difficult.

The linker will not have any problems with your memmem call, because our system libraries will still know that function. You get it even without asking for it, but it will betray you if you use it.

So keep in mind: even if your compiler does not need the declaration, you still want to provide one.

Dachschaden
  • 134
  • 1
  • 2