Is this code legal?:
#include <stdio.h>
typedef struct a_d{
int const x;
} a_d;
int a_d__ctor(a_d *X)
{
*(int*)&X->x = 42; //<-- legal or not?
return 0;
}
int main()
{
a_d a;
a_d__ctor(&a);
printf("a.x=%d\n", a.x);
}
Modyfing an object declared with const qualifier invokes undefined behavior.
According to the Standard (emphasis mine):
C11 6.7.3/6 Type qualifiers
If an attempt is made to modify an object defined with a
const-qualified type through use of an lvalue with non-const-qualified
type, the behavior is undefined.
Related
I have this code here:
#include <stdio.h>
int add(const int* x, const int* y);
int main()
{
int x = 4;
int y = 3;
printf("%d", add(&x, &y));
return 0;
}
int add(int* x, int* y)
{
return *x + *y;
}
When I compile it gives me an error: conflicting type for add
I know I have to put the const into the parameters of the function definition.
But if I add a typedef into the code like this:
#include <stdio.h>
typedef int* int_ptr;
int add(const int_ptr x, const int_ptr y);
int main()
{
int x = 4;
int y = 3;
printf("%d", add(&x, &y));
return 0;
}
int add(int_ptr x, int_ptr y)
{
return *x + *y;
}
It compiled and gave me the output: 7
Why does this happen ?
In const int* x, const int are the specifiers and *x is the declarator. (This separation is specified by the formal grammar of C and is a reason why writing declarations as int* x misrepresents the grammar.) This declaration says that *x is a const int, meaning x is a pointer to const int.
In typedef int* int_ptr, typedef int are the specifiers, and *int_ptr is the declarator. The declaration says that *int_ptr is an int, and typedef is a special specifier that modifies it so that int_ptr is declared to be a type, rather than an object (variable).
In const int_ptr x, const int_ptr are the specifiers, and x is the declaration. So this declaration says that x is a const int_ptr.
Here const is modifying int_ptr; const int_ptr x says that x is a const pointer to an int. In const int *x, const modifies int, so it says *x is a pointer to a const int, meaning x is a pointer to a const int.
For the most part, when a function is declared with parameter type lists, the parameters must have compatible types in each declaration of the function. But there is an exception: C 2018 6.7.6.3 15 says:
… (In the determination of type compatibility and of a composite type, … each parameter declared with qualified type is taken as having the unqualified version of its declared type.)
This says that, when determining whether int add(const int_ptr x, const int_ptr y) is compatible with int add(int_ptr x, int_ptr y), the const qualifiers are ignored. Then the parameter types are the same, so the function declarations are compatible.
In int add(const int *x, const int *y), x and y are not qualified with const. They point to const int, but they themselves are not const. That is, the pointer that is x can be changed (it is not const). The fact that it points to something that is const does not make it const. So the rule about ignoring qualifiers in function parameters does not apply here; there are no qualifiers on x and y. So int add(const int *x, const int *y) and int add(int *x, int *y) do not have compatible parameter types.
The reason for this rule about ignoring qualifiers in parameter types comes from the fact that qualifiers only affect objects, not values. If we have an object x that is const, it should not be changed (through that type). But, if we have gotten the int value 3 from x and are using it in an expression, there would be no meaning to saying 3 is const. It is just a value being used in an expression; there is no memory assigned to it where we could store a new value that would change 3 to 4. Once the value of an object is retrieved from a const int, it is just an int.
Similarly, if we have a volatile int x, the volatile means the compiler must get the value of x each time it is used in an expression, because volatile means something could be changing the memory of x in ways the compiler does not know about. But, once we have gotten the value of x from memory, it is just a value. We are done with the “you have to get it from memory” part, so the volatile has no more effect.
Since function arguments are always passed by value, the qualifiers are irrelevant to the caller. When a function is declared with void foo(const int x), the const has a meaning inside the function: The compiler must issue a diagnostic if anything inside the function attempts to modify x with its const-qualified type. But the caller does not care: The caller only passes a value. When the function starts, it creates a local x for itself, and that x is const, but it has no effect on the caller. So void foo(int x) and void foo(const int x) are compatible function declarations.
#include <stdio.h>
int main()
{
int ary[2][3];
foo(ary);
}
void foo(int (*ary)[3])
{
int i = 10, j = 2, k;
ary[0] = &i;
ary[1] = &j;
for (k = 0;k < 2; k++)
printf("%d\n", *ary[k]);
}
I tried it with many different compilers but I do not understand why this will not compile.
You're assigning to an expression with an array type.
ary inside foo is a variable of type "pointer to array of three ints".
When you assign to it, you're trying to assign a variable of type "pointer to int" to a variable of type "array of three ints".
This is invalid because an array is not an lvalue (6.3.2.1p3):
Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
#include <stdio.h>
void foo(int (*ary)[3]);
int main()
{
int ary[2][3];
int (*x)[3] = &ary[0];
foo(&ary[0]);
}
void foo(int (*ary)[3])
{
int i = 10, j = 2, k;
*ary[0] = i;
*ary[1] = j;
for (k = 0;k < 2; k++)
printf("%d\n", *ary[k]);
}
In C language array, structure and union always pass by with reference. C language not strictly check reference type and also support implicit casting.
void foo(int (*ary)[3]) this is a syntax of passing a function to a function as reference (call back function). but you trying to assign a value to function which is implicitly cast by c does not matter you pass a array reference. that why its showing lvalue error.
LValue implies that if you assign some value using assignment operator (=) there left side must be a variable but in your case it's a callback reference.
Hope its help.
Standard says:
(6.7.3) If an attempt is made to modify an object
defined with a const-qualified type through use of an lvalue with
non-const-qualified type, the behavior is undefined.
And in undefined behaviour section:
An attempt is made to modify an object defined with
a const-qualified type through use of an lvalue with
non-const-qualified type (6.7.3)
The return value of malloc is a block of uninitialized storage. No object has been constructed within that storage.
That said, is the code below legal?
#include
#include
typedef struct {
unsigned char h;
const unsigned int v;
} liber_primus;
int main(int argc, const char **argv)
{
unsigned int lykilord;
if (!(argc - 1))
return 0;
liber_primus *runaljod = malloc(sizeof (*runaljod));
if (!runaljod)
abort();
runaljod->h = *(unsigned char *)argv[1];
*(unsigned int *)&runaljod->v = 3307U;
lykilord = runaljod->h * runaljod->v;
free(runaljod);
return lykilord;
}
When doing pointer arithmetic with offsetof, is it well defined behavior to take the address of a struct, add the offset of a member to it, and then dereference that address to get to the underlying member?
Consider the following example:
#include <stddef.h>
#include <stdio.h>
typedef struct {
const char* a;
const char* b;
} A;
int main() {
A test[3] = {
{.a = "Hello", .b = "there."},
{.a = "How are", .b = "you?"},
{.a = "I\'m", .b = "fine."}};
for (size_t i = 0; i < 3; ++i) {
char* ptr = (char*) &test[i];
ptr += offsetof(A, b);
printf("%s\n", *(char**)ptr);
}
}
This should print "there.", "you?" and "fine." on three consecutive lines, which it currently does with both clang and gcc, as you can verify yourself on wandbox. However, I am unsure whether any of these pointer casts and arithmetic violate some rule which would cause the behavior to become undefined.
As far as I can tell, it is well-defined behavior. But only because you access the data through a char type. If you had used some other pointer type to access the struct, it would have been a "strict aliasing violation".
Strictly speaking, it is not well-defined to access an array out-of-bounds, but it is well-defined to use a character type pointer to grab any byte out of a struct. By using offsetof you guarantee that this byte is not a padding byte (which could have meant that you would get an indeterminate value).
Note however, that casting away the const qualifier does result in poorly-defined behavior.
EDIT
Similarly, the cast (char**)ptr is an invalid pointer conversion - this alone is undefined behavior as it violates strict aliasing. The variable ptr itself was declared as a char*, so you can't lie to the compiler and say "hey, this is actually a char**", because it is not. This is regardless of what ptr points at.
I believe that the correct code with no poorly-defined behavior would be this:
#include <stddef.h>
#include <stdio.h>
#include <string.h>
typedef struct {
const char* a;
const char* b;
} A;
int main() {
A test[3] = {
{.a = "Hello", .b = "there."},
{.a = "How are", .b = "you?"},
{.a = "I\'m", .b = "fine."}};
for (size_t i = 0; i < 3; ++i) {
const char* ptr = (const char*) &test[i];
ptr += offsetof(A, b);
/* Extract the const char* from the address that ptr points at,
and store it inside ptr itself: */
memmove(&ptr, ptr, sizeof(const char*));
printf("%s\n", ptr);
}
}
Given
struct foo {int x, y;} s;
void write_int(int *p, int value) { *p = value; }
nothing in the Standard would distinguish between:
write_int(&s.y, 12); //Just to get 6 characters
and
write_int((int*)(((char*)&s)+offsetof(struct foo,y)), 12);
The Standard could be read in such a way as to imply that both of the above violate the lvalue-type rules since it does not specify that the stored value of a structure can be accessed using an lvalue of a member type, requiring that code wanting to access as structure member be written as:
void write_int(int *p, int value) { memcpy(p, value, sizeof value); }
I personally think that's preposterous; if &s.y can't be used to access an
lvalue of type int, why does the & operator yield an int*?
On the other hand, I also don't think it matters what the Standard says. Neither clang nor gcc can be relied upon to correctly handle code that does anything "interesting" with pointers, even in cases that are unambiguously defined by the Standard, except when invoked with -fno-strict-aliasing. Compilers that make any bona fide effort to avoid any incorrect aliasing "optimizations" in cases which would be defined under at least some plausible readings of the Standard will have no trouble handling code that uses offsetof in cases where all accesses that will be done using the pointer (or other pointers derived from it) precede the next access to the object via other means.
The following code compiles and works:
#include <stdio.h>
void print(void* x)
{
printf("%d", *(int*)x);
}
int main()
{
print(&((struct { int x, y; }){ .x = 1, .y = 2 })); //outputs 1
return 0;
}
Why compiler allows me to get address of rvalue? Is this defined behaviour?
http://ideone.com/iMwNVr
(struct { int x, y; }){ .x = 1, .y = 2 } is a compound literal, and:
C99 §6.5.2.5 Compound literals
If the type name specifies an array of unknown size, the size is determined by the initializer list as specified in §6.7.8, and the type of the compound literal is that of the completed array type. Otherwise (when the type name specifies an object type), the type of the compound literal is that specified by the type name. In either case, the result is an lvalue.