3

The parameters for memcpy are memcpy(void *_Dst, const void *_Src, size_t _Size)

But with the following code, I pass a constant array as parameter '_Src' and it still copy the content of the array into the destination, even though I didn't pass a const void *.

int tab[3] = { 1, 2, 3 };
    
memcpy(tab, (int[3]) { 7, 8, 9 }, sizeof(int) * 3);
    
printf("%d %d %d \n", tab[0], tab[1], tab[2]); // Output: 7 8 9

Does the expression (int[3]) { 7, 8, 9 } returns a pointer to the first element of the array?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
subski
  • 57
  • 7
  • 1
    I suspect that https://stackoverflow.com/questions/1461432/what-is-array-to-pointer-decay answers your question. – Bill Lynch Sep 27 '21 at 19:04
  • 2
    Or maybe you are asking why passing non-`const` pointer as a `const` pointer is OK? Or why `int` is converted to `void`? Which point is confusing you? – Eugene Sh. Sep 27 '21 at 19:08
  • The compound literal is an array. Like all arrays, it decays to a pointer when passed to a function. The wording of your question is not pedantically correct, but *"a pointer to the first element"* is indeed being passed to the function. – user3386109 Sep 27 '21 at 19:11
  • 1
    "compound literal" like `(int[3]) { 7, 8, 9 }` are **not** `const`. You can write `(int[3]) { 7, 8, 9 }[1] = 42` and it is perfectly defined by C standard, though the utility is questionable. – tstanisl Sep 27 '21 at 19:18

2 Answers2

4

When a function's parameter is declared as a pointer to a const type, it "prevents" the function from modifying what the pointer points to (i.e. not through the parameter itself, but it could with a cast), but it doesn't restrict the actual parameter type to be const.

A variable of non-const qualified type may be safely converted to a const qualified type. This is allowed as per section 6.3.2.3p2 of the C standard:

For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal

dbush
  • 205,898
  • 23
  • 218
  • 273
  • 1
    `const` at best suggests a function will not change the object; it does not prevent it. `void foo(const int *p) { * (int *) p = 0; } int main(void) { int x = 1; foo(&x); return x; }` is strictly conforming C code that exits with success. – Eric Postpischil Sep 27 '21 at 19:43
  • @EricPostpischil: Without the cast, ```*p = 0``` results in a compilation error. Isn't that preventing a change? I know there's plenty of ways to change something without the compiler knowing it, I'm just trying to understand the implication that ```const``` is nearly meaningless. – sj95126 Sep 27 '21 at 20:25
  • @sj95126: The fact that different code, `*p = 0`, results in no change being made because the compiler objects, does not alter the fact that `* (int *) p = 0` does compile and does change the value. Since the value can be changed by the function, it is false that `const` **prevents** the function from changing the value. It may hinder the function from changing the value by requiring some additional code, but ultimately it does not prevent it. This is somewhat due to the history of C development; `const` was added late, and it is largely a programmer aid, not a control… – Eric Postpischil Sep 27 '21 at 20:57
  • … Thus, using `const` on the type a function parameter points to suggests to the function caller that the object will not be changed through that parameter and assists the function author in not changing it accidentally by causing compiler diagnostics if they attempt such a change without using a cast. But that is largely all it is in this usage, just an aid, not an absolute prevention. If an object is originally **defined** with `const`, especially if it is static, then the system **may** put it in read-only memory and prevent a program from modifying it. But that is a different use case. – Eric Postpischil Sep 27 '21 at 20:59
4

This

(int[3]) { 7, 8, 9 }

is a compound literal of the type int[3] that used as a function argument is implicitly converted to a pointer to its first element.

On the other hand (The C Standard, 6.3.2.3 Pointers)

1 A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

and

2 For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.

So in this call

memcpy(tab, (int[3]) { 7, 8, 9 }, sizeof(int) * 3);

the compound literal is converted at first to a pointer of the type int * to its first element that then is converted to the type void * according to the first quote and then to the type const void * according to the second quote.

The qualifier const means that the pointed object will not be changed within the function. It does not mean that the expression used as an argument is a constant expression.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335