I wanted to see if restrict
would prevent memcpy
from accessing overlapping memory.
The memcpy
function copies n bytes from memory area src to memory area dest directly. The memory areas should not overlap.
memmove
uses a buffer so there is no risk of overlapping memory.
The restrict
qualifier says that for the lifetime of the pointer, only the pointer itself or a value directly from it (such as pointer + n
) will have access to the data of that object. If the declaration of intent is not followed and the object is accessed by an independent pointer, this will result in undefined behavior.
#include <stdio.h>
#include <string.h>
#define SIZE 30
int main ()
{
char *restrict itself;
itself = malloc(SIZE);
strcpy(itself, "Does restrict stop undefined behavior?");
printf("%d\n", &itself);
memcpy(itself, itself, SIZE);
puts(itself);
printf("%d\n", &itself);
memcpy(itself-14, itself, SIZE); //intentionally trying to access restricted memory
puts(itself);
printf("%d\n", &itself);
return (0);
}
Output ()
Address of itself: 12345
Does restrict stop undefined behavior?
Address of itself: 12345
stop undefined bop undefined behavior?
Address of itself: 12345
Does memcpy
use an independent pointer? Because the output definitely shows undefined behavior and restrict
doesn't prevent access to overlapping memory with memcpy
.
I'm assuming memcpy
has a performance advantage since it copies data directly while memmove
uses a buffer. But with modern computers, should I disregard this potentially better performance and always use memmove
since it guarantees no overlap?
The restrict
keyword is a hint provided to the compiler to allow the generation of code, telling the compiler it should not be bothered with the possibility of pointer aliasing (two different named pointers accessing the same address).
In a function that takes restrict
pointers, the compiler understands that writing to one will not impact the other. When copying from location A to location B, this means it can safely change this code :
- Read from A[0] into register 1
- Write to B[0] from register 1
- Read from A[1] into register 1
- Write to B[1] from register 1
- Read from A[2] into register 1
- Write to B[2] from register 1
- Read from A[3] into register 1
- Write to B[3] from register 1
- ...
Into this sequence:
- Read from A[0] into register 1
- Read from A[1] into register 2
- Read from A[2] into register 3
- Read from A[3] into register 4
- Write to B[0] from register 1
- Write to B[1] from register 2
- Write to B[2] from register 3
- Write to B[3] from register 4
- ...
These two sequences are identical only if A and B do not overlap and the compiler will not optimize into the second one unless you used restrict
(or unless it can guess from the context that it is safe to do so).
If you end up providing aliased pointer to a function that does not expect them, the compiler may be able to warn you at compile time but there is no guarantee that it will. But you really should not expect an error at compile time - instead you should see it as a permission you grant to the compiler when it generates the assembly from your code.
Answering your question about performance - if you know for sure that there is no overlap in your pointers, use memcpy
. If you don't, use memmove
. memmove
will generally check if there is an overlap and end up using memcpy
if there isn't, but you pay for the check.
The memcpy
function copies n bytes from memory area src to memory area dest directly.
By "directly" I suppose you mean that the function avoids copying first from source to a buffer and then from buffer to destination. Although this is likely to be true, the standard does not require it to be true.
memmove
uses a buffer so there is no risk of overlapping memory.
No, memmove
produces a result as if it copied first to a buffer and from there to the destination. It is not required to actually use a buffer in that way, so long as it yields the required result. Any given implementation might or might not do so.
Does memcpy
use an independent pointer? Because the output definitely shows undefined behavior and restrict
doesn't prevent access to overlapping memory with memcpy
.
restrict
doesn't prevent anything, ever. Compilers are not required to diagnose or even notice that you pass aliased pointers to restrict
-qualified parameters. Indeed, that determination often cannot be made at compile time at all.
Indeed, when you call memcpy()
with the arguments you do in your first call, you do provide two independent pointers to the same object as the source and destination arguments. As a result, the program's behavior is undefined.
Your program also exhibits undefined behavior as a result of computing itself - 14
(regardless of whether the resulting pointer is ever dereferenced). If itself
were instead pointing at least 14 bytes into the interior of the allocated object, so that the pointer arithmetic was valid, then the arguments to the second memcpy()
call would again be inconsistent with the requirements of the parameters' restrict
qualification, so the program would exhibit UB for that reason, too.
I'm assuming memcpy
has a performance advantage since it copies data directly while memmove
uses a buffer. But with modern computers, should I disregard this potentially better performance and always use memmove
since it guarantees no overlap?
That's a matter of opinion, and therefore off-topic here. I'll say only that questions of performance are best approached by first measuring, and then making decisions informed by the results of those measurements. Moreover, the performance characteristics of different implementations may vary.
restrict
never stops undefined behaviour. In fact it introduces undefined behaviour in some cases. If restrict
is removed from a piece of code that has no UB, then the code still has no UB; but the converse is not true.
Your code causes undefined behaviour at this line:
strcpy(itself, "Does restrict stop undefined behavior?");
due to overflowing the size of the allocated buffer. After that, all bets are off.
The restrict
qualifier does not prevent buffer overflows.