0

I am working with some C example, and using function strcpy(), but forgot to include <string.h>, though I have included <stdio.h>. To my surprise code ran successfully. Following is the code I am executing:

#include <stdio.h>

int main() {
   char message[10];
   int count, i;

   strcpy(message, "Hello, world!");

   printf("Repeat how many times? ");
   scanf("%d", &count);

   for(i=0; i < count; i++) {
      printf("%3d - %s\n", i, message);
   }
}

I am using gcc version 3.3.6 (Ubuntu 1:3.3.6 - 15ubuntu1)

I even have not received any compilation warnings.

enter image description here

Why my code is working without including <string.h>?

Thanks in advance.

Abhay Kumar
  • 522
  • 3
  • 9
  • 1
    That's how it goes with *undefined behaviour*. You won't always be so lucky, and the compiler warnings should be taken seriously. – Weather Vane Apr 02 '19 at 14:21
  • I have not received any compiler warning. – Abhay Kumar Apr 02 '19 at 14:21
  • 2
    @AbhayKumar Try enabling warnings with `-Wall -Wextra`. – Maxim Egorushkin Apr 02 '19 at 14:22
  • 3
    Is it certain that the behavior is undefined, @WeatherVane? What if that implementation's `stdio.h` includes `string.h` (plausible, but not guaranteed)? – John Bollinger Apr 02 '19 at 14:22
  • 3
    Well turn up the knob to 11. C4013: 'strcpy' undefined; assuming extern returning int. – Weather Vane Apr 02 '19 at 14:22
  • @JohnBollinger I am not certain, but the compiler thinks that there are type mismatches. In another suituation, say where the return value is being made use of, that **would** be *undefined*? – Weather Vane Apr 02 '19 at 14:24
  • @JohnBollinger Even if it is not, isn't calling functions without prototype is still legal (assuming the needed implementation is linked)? – Eugene Sh. Apr 02 '19 at 14:25
  • 2
    `strcpy(message, "Hello, world!");` that's a definite buffer overflow – asio_guy Apr 02 '19 at 14:27
  • @EugeneSh. AFAIK it is illegal but tolerated by compilers for legacy code. – Weather Vane Apr 02 '19 at 14:32
  • 1
    @EugeneSh., no, calling a function that has no in-scope declaration is *not* legal an C99 or later. There are explicit constraints on the type of the expression designating the called function, and one of the changes in C99 was to delete the rules for implicitly determining a function type from the function call expression. An undeclared identifier has no type -- in fact, such an identifier has no *scope*, even. – John Bollinger Apr 02 '19 at 14:42
  • @JohnBollinger Thank you. – Eugene Sh. Apr 02 '19 at 14:45
  • Note that in the absence of the `` header, you could write `int i = strcpy(13, 45);` and the compiler wouldn't complain (might not complain — it could complain) unless you add options demanding warnings. With a prototype in effect (e.g. because you included ``), the line would generate all sorts of warnings. – Jonathan Leffler Apr 02 '19 at 16:26

4 Answers4

4

The compiler assumes int strcpy() and invokes ancient backwards compatibility behavior. Pointer arguments are one of the types for which this works so it's fine.

Don't depend on it. The safety rails are off.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • Without including `string.h`, I ran into an access violation exception with [strcpy return value]((https://stackoverflow.com/questions/72842195/error-reading-charcters-of-string-on-the-strcpy-return-value) even though there was no compilation error. This is caused by int, being a 32-bit value, != char* in a 64-bit machine. – Leon Chang Jul 05 '22 at 16:14
  • 1
    @LeonChang, but OP throws away the return value so his code works. Indeed on 64 bit, if you used the return value, bad things would happen. – Joshua Jul 05 '22 at 16:23
3

As I recall, GCC 3 defaulted to C89 conformance plus GNU extensions. C89 permitted functions to be called without previously having been declared -- they were assumed to return int, and their parameter types were inferred from the argument types. If the resulting implicit type happened to match the called function's actual type (which was more likely in legacy C code than it is in modern code) then all was good. If not, then the behavior was undefined, but anything can happen with undefined behavior, even what the programmer who wrote the code expected to happen.

Additionally, although the standards don't specify that any of the standard headers include other headers, they are not forbidden from doing so, and in practice, in some C implementations, some of them do. If it happens that your implementation's stdio.h includes string.h or otherwise provides a compatible definition for strcpy(), then that serves just fine with that implementation. If that's what you're relying on, however, whether intentionally or not, then you are at risk of unexpected failure when you try to use your program with a different implementation.

Finally, do note that GCC 3.3 is very old. If at all possible, you should upgrade to something newer. Even the stodgy, long-term stability Linuxes tend to be using at least versions late in the 4.x series, and the latest is GCC 8.3.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • GCC 5.1 (April 2015) was the first to default to C11; no version defaulted to C99. GCC 3 is very, very old (3.4.6 March 2006 is the last 3.x release; 3.3.6 May 2005; 3.3 May 2004). Info from [GCC Releases](https://gcc.gnu.org/releases.html). – Jonathan Leffler Apr 02 '19 at 16:21
2

The string.h include file tells the compiler how strcpy() is defined by giving a declaration of the function, but it doesn't provide the function itself, which instead sits in a library and will be automatically linked with your program.

If the function has not been declared when it reaches it, then the compiler will assume a declaration, based on defaults and how you use the function.

The compiler takes what you wrote, so the addresses of message and Hello, world! and assumes the function to return an int. You take risks by not telling the compiler how it has to compile the code thanks to the include. The outcome is undefined behavior.

By the way, Hello, world! takes 14 characters, 13 + the trailing \0, which is 4 more than in your declaration of message. This is undefined behavior, as the extra 4 chars will be written where they're not supposed to. Seemingly working, crash...

You cannot rely on a program showing an expected result. This is undefined behavior.

Always compile with the warnings options, like -Wall, and consider fixing all warnings.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
Déjà vu
  • 28,223
  • 6
  • 72
  • 100
  • That's a good point about the distinction between declaration and implementation, and one often misunderstood by newbies (+1). Nevertheless, your wording may lead to confusion, because if the header did contain a definition of `strcpy()` then it *would* provide that function. It contains a *declaration*, and that tells the compiler *how* the function is defined. – John Bollinger Apr 02 '19 at 14:53
  • In that case, I hope you don't object to some further tweaks (edited). – John Bollinger Apr 02 '19 at 15:00
0

The compiler knows some "built-in" functions (printf() is another example for some compilers), and makes up for your mistake by providing it. It is a bad habit to rely on this because even if your today's compiler does this, nothing guarantees you that another one will do the same, so the compiled code won't be predictable.

Try compiling with -Wall -Wextra to make the compiler show more warnings, including the ones for your forgotten header : gcc -o input input.c -Wall -Wextra.

Also see here : warning: incompatible implicit declaration of built-in function ‘xyz’