It's undefined behavior to call functions with a different set of arguments from its definition. The definition of memcpy is memcpy(void * , void * , size_t), and calling it with no arguments is obviously incompatible with the three required arguments here.
> I don't expect compiler to care about it, just put function call into object code and move on.
memcpy actually has a few semantics that cannot be legally expressed in C (specifically, strict aliasing doesn't kick in). All modern compilers actually define memcpy as a compiler-builtin, and they rely on memcpy's semantics for optimization purposes.
Or, put more bluntly, memcpy does not correspond to a function call in object code. C is not a thin wrapper around assembly code, and has not been so for quite some time.
> memcpy actually has a few semantics that cannot be legally expressed in C (specifically, strict aliasing doesn't kick in).
Are you sure? I hear this a lot, but I don't think it's really true. char pointers are allowed to alias pointers to any other type in standard C (C17 6.5p7,) so unless I'm missing something, a correct C implementation of memcpy could just cast both its arguments to char pointers and copy them char-by-char.
I agree with your premise that the call to memcpy() is undefined, but I think that's entirely because of the conflicting definition, and has nothing to do with any special semantics of memcpy, so it would equally apply to any other standard library function.
> Are you sure? I hear this a lot, but I don't think it's really true. char pointers are allowed to alias pointers to any other type in standard C (C17 6.5p7,) so unless I'm missing something, a correct C implementation of memcpy could just cast both its arguments to char pointers and copy them char-by-char.
I'm not completely certain. I have definitely seen this assertion before made by people more well-versed in the C standard than I, but I don't recall the exact argument. I think it may be the case that the write-via-char causes the effective type of memory to change (so it can only be read by char from that point on), but again, I don't trust my judgement here.
I think it might be an out-of-date assertion, but I'm not sure either since I also hear it from people who seem to be well-versed in the C standard. The only place in C17 where I can see memcpy singled out (6.5p6) also mentions copying "as an array of character type." The definition of memcpy itself just describes it as copying characters. It's true that some types can have bit patterns that are "trap representations," that is, they cause undefined behaviour when used, but memcpy can also create trap representations. You could memcpy to copy the bit pattern of a signalling NaN from an int into a float, for example.
memmove on the other hand can't be implemented in standard C, but it can be implemented on most platforms using only implementation-defined behaviour, because casting a pointer to an intptr_t is implementation-defined, but on most platforms with a flat memory model, it gives you the linear memory address.
Well, no, in this test program memcpy is declared as having no arguments and it has to be called with no arguments. It's perfectly consistent, unless memcpy is so magical that some specific undefined behaviour is allowed (not likely).
The actual test comes later: the real memcpy with three arguments, if it exists, is mislinked into the test executable; if memcpy cannot be found linking fails.
The test program would hopefully crash badly in case it is actually run, but the test is not about memcpy behaviour or about what the parameters of memcpy are.
It's not the declaration of memcpy that matters, it's the definition. And the definition of memcpy is given by the C standard. In the case of external functions, it's the user's responsibility to ensure that the declaration is compatible with the definition.
If you want to use a non-standard function that coincidentally happens to be memcpy, you have to use special compiler flags to inform the compiler that C library semantics are not in effect (e.g., -ffreestanding). Of course, the entire point of these checks is to figure out if the C library has these functions in the first place, so using these options during the tests would defeat the purpose.
> The actual test comes later: the real memcpy with three arguments, if it exists, is mislinked into the test executable; if memcpy cannot be found linking fails.
The actual test both compiles and links the executable, although it (obviously) doesn't attempt to run it. Whether it fails in the compile or the link step is immaterial to the test, as it fails either way.
I agree. If the compiler authors make some additional "magic", they are also responsible to "unmagic" the code which checks: "if memcpy is defined as having no arguments, and then a call to it is compiled (the call is never executed), will the memcpy routine from the standard library (which must be linkable only using the name) be successfully linked?"
> I don't expect compiler to care about it, just put function call into object code and move on.
memcpy actually has a few semantics that cannot be legally expressed in C (specifically, strict aliasing doesn't kick in). All modern compilers actually define memcpy as a compiler-builtin, and they rely on memcpy's semantics for optimization purposes.
Or, put more bluntly, memcpy does not correspond to a function call in object code. C is not a thin wrapper around assembly code, and has not been so for quite some time.