Search code examples
crestrict-qualifier

Is it valid to use "restrict" when there is the potential for reallocating memory (changing the pointer)?


I am attempting some optimization of code, but it is hard to wrap my head around whether "restrict" is useful in this situation or if it will cause problems.

I have a function that is passed two strings (char*) as well as an int (int*).

The second string is copied into memory following the first string, at the position indicated by the int. If this would overrun the allocation of memory for the first string, it must reallocate memory for the first string before doing so. A new pointer is created with the new allocation, and then the original first string pointer is set equal to it.

char* concatFunc (char* restrict first_string, char* const restrict second_string, int* const restrict offset) {
size_t block = 200000;
size_t len   = strlen(second_string);
char* result = first_string;

if(*offset+len+1>block){
  result = realloc(result,2*block);
}
memcpy(result+*offset,second_string,len+1);
*offset+=len;
return result;
}

The above function is repeatedly called by other functions that are also using the restrict keyword.

char* addStatement(char* restrict string_being_built, ..., int* const restrict offset){
  char new_statement[30] = "example additional text";

  string_being_built = concatFunc(string_being_built,&new_statement,offset);
}

So in the concatFunc the first_string is restricted (meaning memory pointed to will not be changed from anywhere else). But then if I am reallocating a pointer that is a copy of that, is that going to cause undefined behavior or is the compiler smart enough to accommodate that?

Basically: What happens when you restrict a pointer parameter, but then change the pointer.


Solution

  • What happens when you restrict a pointer parameter, but then change the pointer.

    It depends on how the pointer was changed - and in this case, memcpy() risks UB.

    With char* result = first_string;, inherits the restrict of char* restrict first_string.

    After result = realloc(result,2*block);, result is as before and accessing via result does not collide with accessing through second_string or offset or result is new memory and accessing via result does not collide with accessing through second_string or offset.

    Yet can the compiler know the newly assigned result has those one of two above properties of realloc()? After all, realloc() might be a user defined function and compiler should not assume result now has the restrict property anymore.

    Thus memcpy() is in peril.


    is the compiler smart enough to accommodate that?

    I do not see it can, other than warn about memcpy() usage.

    Of course OP can use memmove() instead of memcpy() to avoid the concern.


    As I see it, a simplified example would be:

    char* concatFunc (char* restrict first_string, char* restrict second_string) {
      int block = rand();
    
      first_string = foo(first_string, block);
    
      // first_string at this point may equal second_string,
      // breaking the memcpy() contract
    
      memcpy(first_string, second_string, block);
      return first_string;
    }
    

    Or even simpler

    char* concatFunc (char* /* no restrict */ first_string, char* restrict second_string) {
      return memcpy(first_string, second_string, 2);
    }