I think I'm getting rusty, so please bare with me. I'll try to be brief.
Q1. When trying to copy buffers, say buf2 to buf1, does the following check suffice against aliasing?
if (buf2 >= buf1 && buf2 < buf1 + buf1size) {
// aliasing
}
Q2. If so, can we selectively use either memcopy()
or memmove()
depending on the case, like this?
// use memcpy() by default
void *(*funcp)(void *restrict, const void *restrict, size_t) = &memcpy;
// switch to memmove() when aliasing
if ( aliasing ) {
// this cast FORCEFULLY changes the type-qualifiers of the declared parameters
funcp = (void *(*)(void *, const void *, size_t)) &memmove;
}
// later on ...
if ( buf2size <= buf1size ) {
(*funcp)( buf1, buf2, buf2size ); // funcp() works too, I prefer making it explicit
}
It works but I'm not comfortable at all with forcefully casting the type-qualifiers of the parameters when switching to memmove()
. I think the standard confirms my doubts (can never find these darn things when I need them... using C99 btw), but since the code works I'd like to be extra sure, because if it's ok like that it would save me from duplicating buf2, work with the duplicate and freeing it when done.
For any two generic pointers, you can't really do pointer arithmetic on them. This is regulated by the additive operators C17 6.5.6/8:
If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.
Similar text exists for the relational operators (6.5.8) - any two pointers getting compared with them must point at the same array or otherwise the behavior is undefined.
You can in theory convert the pointers to integers in the form of uintptr_t
and do arithmetic on that one. If you know for certain that buf1
points at the beginning of an array of buf1size
items, then you could in theory calculate if buf2
points at the same array or not, by doing integer arithmetic on uintptr_t
. But there isn't much to gain from that.
Instead you could simply write your function as
void func (char* restrict buf1, char* restrict buf2);
And push the responsibility of ensuring that the two buffers don't alias onto the caller.
As for your function pointer selection of either memcpy
or memmove
, then apparently the mainstream compilers (gcc, clang) seem to ignore that one version has restrict
qualified pointers. If that's conforming behavior or not, I'm not sure.