Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function call with pointer to non-const and pointer to const arguments of same address

I want to write a function that input an array of data and output another array of data using pointers.

I'm wondering what is the result if both src and dst pointed to the same address because I know compiler can optimize for const. Is it undefined behavior? (I tagged both C and C++ because I'm not sure if the answer may differ between them, and I want to know about both.)

void f(const char *src, char *dst) {
    dst[2] = src[0];
    dst[1] = src[1];
    dst[0] = src[2];
}

int main() {
    char s[] = "123";
    f(s,s);
    printf("%s\n", s);
    return 0;
}

In addition to above question, is this well-defined if I delete the const in original code?

like image 774
Willy Avatar asked Mar 13 '20 09:03

Willy


People also ask

What will happen if a const object calls a non-const member function?

If the function is non-constant, then the function is allowed to change values of the object on which it is being called. So the compiler doesn't allow to create this chance and prevent you to call a non-constant function on a constant object, as constant object means you cannot change anything of it anymore.

Can a const member function call a non-const function?

Const member functions in C++ It is recommended to use const keyword so that accidental changes to object are avoided. A const member function can be called by any type of object. Non-const functions can be called by non-const objects only.

Can a const function return a non-const pointer?

Do not return non-const handles to Class data from const member Functions. From a language point of view, the pointer 'p' is part of the class and then cannot be modified in a 'const' function. But the pointed-to value is not part of the class, and may be modified.

Can a const reference be bound to a non-const object?

No. A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r .


3 Answers

While it is true that the behavior is well-defined - it is not true that compilers can "optimize for const" in the sense that you mean.

That is, a compiler is not allowed assume that just because a parameter is a const T* ptr, the memory pointed to by ptr will not be changed through another pointer. The pointers don't even have to be equal. The const is an obligation, not a guarantee - an obligation by you (= the function) not to make changes through that pointer.

In order to actually have that guarantee, you need to mark the pointer with the restrict keyword. Thus, if you compile these two functions:

int foo(const int* x, int* y) {
    int result = *x;
    (*y)++;
    return result + *x;
}

int bar(const int* x, int* restrict y) {
    int result = *x;
    (*y)++;
    return result + *x;
}

the foo() function must read twice from x, while bar() only needs to read it once:

foo:
        mov     eax, DWORD PTR [rdi]
        add     DWORD PTR [rsi], 1
        add     eax, DWORD PTR [rdi]  # second read
        ret
bar:
        mov     eax, DWORD PTR [rdi]
        add     DWORD PTR [rsi], 1
        add     eax, eax              # no second read
        ret

See this live on GodBolt.

restrict is only a keyword in C (since C99); unfortunately, it has not been introduced into C++ so far (for the poor reason that it is more complicated to introduce in C++). Many compilers do kinda-support it, however, as __restrict.

Bottom line: The compiler must support your "esoteric" use case when compiling f(), and will not have any problem with it.


See this post regarding use cases for restrict.

like image 122
einpoklum Avatar answered Sep 16 '22 18:09

einpoklum


This is well-defined (in C++, not sure in C any more), with and without the const qualifier.

The first thing to look for is the strict aliasing rule1. If src and dst points to the same object:

  • in C, they must be of compatible types; char* and char const* are not compatible.
  • in C++, they must be of similar types; char* and char const* are similar.

Regarding the const qualifier, you might argue that since when dst == src your function effectively modifies what src points to, src shouldn't be qualified as const. This is not how const works. Two cases need to be considered:

  1. When an object is defined to be const, as in char const data[42];, modifying it (directly or indirectly) leads to Undefined Behaviour.
  2. When a reference or pointer to a const object is defined, as in char const* pdata = data;, one can modify the underlying object provided it has not been defined as const2 (see 1.). So the following is well-defined:
int main()
{
    int result = 42;
    int const* presult = &result;
    *const_cast<int*>(presult) = 0;
    return *presult; // 0
}

1)What is the strict aliasing rule?
2)Is const_cast safe?

like image 25
YSC Avatar answered Sep 20 '22 18:09

YSC


This is well-defined in C. Strict aliasing rules do not apply with the char type, nor with two pointers of the same type.

I'm not sure what you mean by "optimize for const". My compiler (GCC 8.3.0 x86-64) generates the exact same code for both cases. If you add the restrict specifier to the pointers, then the code generated is slightly better, but that won't work for your case, the pointers being the same.

(C11 §6.5 7)

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.

In this case (without restrict), you will always get 121 as a result.

like image 23
S.S. Anne Avatar answered Sep 20 '22 18:09

S.S. Anne