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.
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.
I am learning some of the basics of C, and am currently stepping my way through arrays and more specifically how passing by reference works. When the below code is run it returns 10 22. When I read through the code however, based on the last command it seems as though the variable a should return 22 instead of 10 (meaning the full output would be 22 22 instead of 10 22). Why would the variable a not update to 22 in this code?
#include <stdio.h>
void set_array(int array[4]);
void set_int(int x);
int main(void)
{
int a = 10;
int b[4] = { 0, 1, 2, 3 };
set_int(a);
set_array(b);
printf("%d %d\n", a, b[0]);
}
void set_array(int array[4])
{
array[0] = 22;
}
void set_int(int x)
{
x = 22;
}
Arrays are [loosely] "pass by reference". Actually, the array "decays" into an int *.
But, scalars are "pass by value".
In set_int, you set the function scoped copy of x but do not return it to the caller.
Here's the refactored code, with a "call by reference" example:
#include <stdio.h>
void
set_array(int array[4])
{
array[0] = 22;
}
int
set_int(int x)
{
x = 22;
return x;
}
void
set_int_byptr(int *x)
{
*x = 37;
}
int
main(void)
{
int a = 10;
int b[4] = { 0, 1, 2, 3 };
int c = 4;
#if 0
set_int(a);
#else
a = set_int(a);
#endif
set_array(b);
set_int_byptr(&c);
printf("a=%d b=%d c=%d\n", a, b[0], c);
return 0;
}
In C if you want to modify variable passed to function you need to pass the pointer to it:
examples:
int setval(int *obj, int value)
{
*obj = val;
return val;
}
void usage()
{
int x;
setval(&x, 22);
}
void *setpointer(void **ptr, size_t size)
{
*ptr = malloc(size);
return *ptr;
}
void usage1()
{
int *array;
setpointer(&array, 200*sizeof(*array));
}
First we need to get this out of the way, because I honestly believe it will make things less confusing - C does not pass any function arguments by reference, ever. C passes all function arguments by value. Sometimes, those values are pointers. This is not the same thing as pass-by-reference.
Among other things, pass-by-value means that any changes to a formal parameter are not reflected in the actual parameter. In your set_int function, x is a distinct object from a, and any changes to x do not affect a.
If we want a function to modify the value in a parameter, we must pass a pointer to that parameter:
void set_int( int *x )
{
*x = 22; // writes a new value to the thing x points to
}
int main( void )
{
int a = 10;
set_int( &a ); // foo writes a new value to a
return 0;
}
In the above code, we want the function set_int to update the variable a, so we must pass a pointer to a to the function.
x == &a // int * == int *
*x == a // int == int
Thus, writing a new value to the expression *x in set_int is the same as writing a new value to a in main. Any change to x itself is local to set_int.
Things get confusing when we add arrays to the mix. An array is not a pointer; however, unless it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T" and the value of the expression will be the address of the first element.
When you call set_array(b), the expression b "decays" from type "4-element array of int" (int [4]) to type "pointer to int" (int *), and the value of the expression is the same as &b[0].
Here's another confusing thing - in a function parameter declaration, array declarations of type T a[N] and T a[] are interpreted as T *a - a is a pointer to T, not an array of T. So your prototype
void set_array(int b[4])
is interpreted as
void set_array(int *b)
and what it receives is a pointer to the first element. As a practical matter, this means that any changes to array[i] in set_array are reflected in b, but this is fallout from how C specifically treats array expressions, not a difference in parameter passing mechanisms. The argument is still being passed by value, it's just that the argument is a pointer value that's the result of a well-defined conversion operation on array expressions.
You are doing 2 things over here:
1) Pass by value:
the function set_int(), its parameter is passed simply, without any address, which means it is pass by value, and any change made by this function set_int() will not be reflected in the calling function.
2) Pass by reference:
However, in the case of set_array(b), you are passing the array to the called function, and its base address will be passed (Means address of first element of b, that is &b[0]), hence this is pass by reference and any change is made to this value will be reflected in the calling function
which is the reason 22 is updated for b, but 22 didn't get update for a
#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.
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.
I'm getting initialization discards ‘const’ qualifier from pointer target type warning in the line .grid_col = &c_ax_gd i.e. assigning an address expression to a pointer, which is part of a constant structure.
struct color {
double r;
double g;
double b;
double a;
};
struct graph {
double origin[2];
double dim[2];
double scale[2];
double grid_line_width;
double axes_line_width;
struct color *grid_col;
struct color *axes_col;
};
int main(void) {
static const struct color c_ax_gd = {0.5, 0.5, 0.5, 1};
static const double h = 600, w = 800;
const struct graph g = {
.origin = {w / 2, h / 2},
.dim = {w, h},
.scale = {w / 20, h / 20},
.grid_line_width = 0.5,
.axes_line_width = 1,
.grid_col = &c_ax_gd,
.axes_col = &c_ax_gd
};
....
}
I found the following in C99 standard
More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:
an arithmetic constant expression,
a null pointer constant,
an address constant, or
an address constant for an object type plus or minus an integer constant expression.
An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type
My question is, doesn't that mean &c_ax_gd is an address constant? If so, how does using an address constant inside an initializer for a constant structure discards the const qualifier?
The problem is somewhere else. Even if a struct is const, if it has pointers as members, the objects those pointers point to are not automatically const. You can see that from the following example.
struct example {
int * p;
};
int
main()
{
/* const */ int a = 1;
const struct example s = {&a};
*(s.p) = 2;
return 0;
}
Therefore, if you uncomment the /* const */ and assign the address of a to s.p, you're discarding the const qualifier on a. That's what your compiler warns you about.
g is const, which means all its members are const (including grid_col). So grid_col is a const pointer to struct color and &c_ax_gd is a pointer to const struct color.
You are tryting to initialize const struct color pointer with a struct color pointer and that's why you get the warning.
C99 6.5.16.1
both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right
If you ignore the warning, you'll just get undefined behavior if you modify the value grid_col points to.