in C, I believe the following program is valid: casting a pointer to an allocated memory buffer to an array like this:
#include <stdio.h>
#include <stdlib.h>
#define ARRSIZE 4
int *getPointer(int num){
return malloc(sizeof(int) * num);
}
int main(){
int *pointer = getPointer(ARRSIZE);
int (*arrPointer)[ARRSIZE] = (int(*)[ARRSIZE])pointer;
printf("%d\n", sizeof(*arrPointer) / sizeof((*arrPointer)[0]));
return 0;
}
(this outputs 4).
However, is it safe, in C99, to do this using VLAs?
int arrSize = 4;
int *pointer = getPointer(arrSize);
int (*arrPointer)[arrSize] = (int(*)[arrSize])pointer;
printf("%d\n", sizeof(*arrPointer) / sizeof((*arrPointer)[0]));
return 0;
(also outputs 4).
Is this legit, according to the C99 standard?
It'd be quite strange if it is legit, since this would mean that VLAs effectively enable dynamic type creation, for example, types of the kind type(*)[variable].
Yes, this is legit, and yes, the variably-modified type system is extremely useful. You can use natural array syntax to access a contiguous 2-D array both of whose dimensions were not known until runtime.
It could be called syntactic sugar as there's nothing you can do with these types that you couldn't do without them, but it makes for clean code (in my opinion).
I would say it is valid. The Final version of the C99 standard (cited on Wikipedia) says in paragraph 7.5.2 - Array declarators alinea 5 :
If the size is an expression that is not an integer constant expression: ...
each time it is evaluated it shall have a value greater than zero. The size of each instance
of a variable length array type does not change during its lifetime.
It even explicitely says that it can be used in a sizeof provided the size never changes : Where a size
expression is part of the operand of a sizeof operator and changing the value of the
size expression would not affect the result of the operator, it is unspecified whether or not
the size expression is evaluated.
But the standard also says that this is only allowed in a block scope declarator or a function prototype : An ordinary identifier (as defined in 6.2.3) that has a variably modified type shall have
either block scope and no linkage or function prototype scope. If an identifier is declared
to be an object with static storage duration, it shall not have a variable length array type.
And an example later explains that it cannot be used for member fields, even in a block scope :
...
void fvla(int m, int C[m][m]); // valid: VLA with prototype scope
void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA
{
typedef int VLA[m][m]; // valid: block scope typedef VLA
struct tag {
int (*y)[n]; // invalid: y not ordinary identifier
int z[n]; // invalid: z not ordinary identifier
};
...
Related
this the snip of the warning i was getting when i tried to compile the program.I am just getting started with pointers and this following program is being flagged by compiler for some reason I am not able comprehend. the code is as follows:
#include <stdio.h>
int dec = 0;
int *d;
d = &dec;
int main() {
return 0;
}
there is no error when I am stuffing these declarations in to main's body. the version of gcc I am using is gcc version 12.2.0(downloaded using MSYS2) and code editor MS visual code.can anybody post an explanation for this?
as i have stated above i have randomly started typing a program to get familiar with pointers, i expected there to be no variation in the treatment of pointers regardless of where they are being declared and intialised.
You're attempting to perform an assignment outside of a function, which is not allowed. What you can do is initialize:
int *d = &dec;
You may use only declarations in file scopes.
In the provided program you are using an assignment statement
d = &dec;
in the file scope. So the compiler issues an error.
Instead you could write for example
#include <stdio.h>
int dec = 0;
int *d = &dec;
int main( void ) {
return 0;
}
As the variable dec has static storage duration then the expression &dec is an address constant and my be used as an initializer for the variable d that also has static storage duration.
From the C Standard (6.7.9 Initialization)
4 All the expressions in an initializer for an object that has static
or thread storage duration shall be constant expressions or string
literals.
and (6.6 Constant expressions)
7 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 a complete object type plus or minus an
integer constant expression.
and
9 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. The
array-subscript [] and member-access . and -> operators, the address &
and indirection * unary operators, and pointer casts may be used in
the creation of an address constant, but the value of an object shall
not be accessed by use of these operators.
I am a bit confused about array declaration in C. I know that it's possible to do this:
int a[20]; // Reserved space for 20 int array
int b[] = {32, 431, 10, 42}; // Length in square brackets is auto-calculated
int *c = calloc(15, sizeof(int)); // Created a pointer to the dynamic int array
But is it possible to do this?:
int my_array[sizeof(int) * 5];
Is it a valid code, or an array length should be a constant expression (in ANSI C)?
This declaration
int my_array[sizeof(int) * 5];
does not declare a variable length array because the expression sizeof(int) * 5 is a constant integer expression. So even your compiler does not support variable length arrays you may use such a declaration.
From the C Standard (6.6 Constant expressions)
6 An integer constant expression117) shall have integer type and shall
only have operands that are integer constants, enumeration constants,
character constants, sizeof expressions whose results are integer
constants, and floating constants that are the immediate operands of
casts. Cast operators in an integer constant expression shall only
convert arithmetic types to integer types, except as part of an
operand to the sizeof operator.
and (6.7.6.2 Array declarators)
4 If the size is not present, the array type is an incomplete type. If
the size is * instead of being an expression, the array type is a
variable length array type of unspecified size, which can only be used
in declarations or type names with function prototype scope; such
arrays are nonetheless complete types. If the size is an integer
constant expression and the element type has a known constant size,
the array type is not a variable length array type; otherwise, the
array type is a variable length array type. (Variable length arrays
are a conditional feature that implementations need not support; see
6.10.8.3.)
A declaration of a variable length array can look like
const int n = 5;
int my_array[sizeof(int) * n];
The support of variable length arrays is optional in C11 and higher.
(This answer answers the question in the title, “Can array length in declaration be non-constant?” The example given in the body, int my_array[sizeof(int) * 5]; does not have a non-constant length.)
Variable length arrays are optional in the current C standard, 2018, meaning a C implementation may choose to support them or not. They were mandatory in the 1999 C standard and made optional in the 2011 standard.
Variable length arrays can be declared only inside functions or there parameters, not at file scope, and they cannot have static or thread storage duration.
sizeof(int) * 5 used in the example statement in your question: int my_array[sizeof(int) * 5];, is a constant expression, so although it does not serve as a good illustration of your primary question, it is legal syntax for C array declaration.
With the exception of C99, variable length arrays are optional in most recent C compiler implementations. (In C99 inclusion of VLA is mandated.)
So, if your compiler supports VLA, the following are an examples:
char string[100] = {0};
scanf("%99s", string);
int VLAarray1[strlen(string)+1];//per question in comments about functions to size array.
memset(VLA1array, 0, sizeof(VLAarray1));//see Note below for initialization
int arrayLen = 0;
scanf("%d", &arrayLen);
int VLAarray2[arrayLen];
memset(VLAarray2, 0, sizeof(VLAarray2));//see Note below for initialization
int nonVLAarray[100] = {0};//initialization during declaration of nonVLA
Note: that VLAs cannot be initialized in any form during its declaration. As with all variables though it is a good idea that it be initialized in subsequent statements by explicitly assigning values to its entire region of memory.
Passing VLAs as function arguments is not included within the scope of your question, but should it be of interest, there is a good discussion on that topic here.
#include <stdio.h>
int func()
{
int a = 3, b = 4;
int c = a * b;
return c;
}
int main()
{
const int N = 10;
int arr[N];
printf("size = %ld\n", sizeof(arr));
int x = 10;
const int SIZE = x;
int buf[SIZE];
printf("size = %ld\n", sizeof(buf));
const int FN = func();
int buf2[FN];
printf("size = %ld\n", sizeof(buf2));
return 0;
}
ubuntu 20 5.4.0-42-generic
gcc 9.3.0
compile:
gcc const_create_arr.c -Wall
show no warning
output:
size = 40
size = 40
size = 48
output corret
the last one FN is init by func(). we know that func() return vlaue should be computed in runtime. But an array definition should provide the true length of this array to compiler to help compiler allocate space. So I think the last one should not be passed at compiling. But it seems corret. I want to know how it works. Whether my gcc has optimise it and compute the func return when it compiling.
In all three cases you're creating a variable length array. For an array to not be a VLA the size needs to be an integer constant expression, and a variable with the const qualifier (no matter how it's initialized) does not qualify as one.
The definition of a VLA can be found in section 6.7.6.2p4 of the C standard regarding array declarators:
If the size is not present, the array type is an incomplete type. If the size is * instead of being an expression, the array type is a variable length array type of unspecified size, which can only be used in declarations or type names with function prototype scope; such arrays are nonetheless complete types. If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.
And the definition of an integer constant expression is given in section 6.6p6:
An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof or _Alignof operator.
There is nothing in this definition that qualifies a const qualified variable as an integer constant expression, so the sizeof operator in each of the three cases is being evaluated at runtime because the arrays are VLAs.
C has variable length arrays, so int x[y] is valid C provided y is defined in advance. It doesn't matter if y is const or not, it just needs to be > 0 to make any sense, as well as small enough that you don't use up the entire stack.
In this case y is 12, so you get a length 48 (12 * sizeof(int) where that's 4). This gets computed after the fact, not in advance like you might expect for something trivial like sizeof(int).
In C++ this is not the case as defined by the standard, however some compilers will still do it the C way.
Consider the following example code:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = malloc(sizeof *a);
*a = 5;
free(a);
return 0;
}
In this example, I allocate the integer a on the heap and initialize it to 5. This line specifically
int *a = malloc(sizeof *a);
is what is confusing me (the sizeof *a part). To me, this looks like I am trying to get the size of the variable before it is even created, but I see this style for initializing pointers is extremely common. When I compile this code with clang, I don't get any errors or warnings. Why does the compiler allow this? As far as I can tell, this is akin to doing something like
int a = a + 1;
without any previous declaration of a. This produces a warning with clang -Wall main.c:
main.c:17:13: warning: variable 'a' is uninitialized when used
within its own initialization [-Wuninitialized]
int a = a + 1;
What makes this line different from the pointer declaration with sizeof?
The operand of the sizeof operator is not evaluated unless it is a variable length array. It is only looked at to determine its type.
This behavior is documented in section 6.5.3.4p2 of the C standard:
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
In this case, it knows that *a has type int, so *a is not evaluated and sizeof *a is the same as sizeof(int).
For most cases sizeof is a compile-time operator. The compiler simply knows the size of the type you pass to it.
Secondly, actually at the time malloc is called the variable a actually have been defined. The variable must have been defined (and allocated) before it can be initialized. Otherwise, where would the initialization value be written?
The problem with
int a = a + 1;
isn't that a doesn't exist, it's that the value of a is indeterminate when you use it in a + 1.
For some types an indeterminate value could contain a trap representation, and if that happens it leads to undefined behavior.
A small note about the sizeof operator: The only time it's not evaluated by the compiler itself at compile-time is for variable-length arrays.
After reading the chapter about structures in the K&R book I decided to make some tests to understand them better, so I wrote this piece of code:
#include <stdio.h>
#include <string.h>
struct test func(char *c);
struct test
{
int i ;
int j ;
char x[20];
};
main(void)
{
char c[20];
struct {int i ; int j ; char x[20];} a = {5 , 7 , "someString"} , b;
c = func("Another string").x;
printf("%s\n" , c);
}
struct test func(char *c)
{
struct test temp;
strcpy(temp.x , c);
return temp;
}
My question is: why is c = func("Another string").x; working (I know that it's illegal, but why is it working)? At first I wrote it using strcpy() (because that seemed the most logical thing to do) but I kept having this error:
structest.c: In function ‘main’:
structest.c:16:2: error: invalid use of non-lvalue array
char c[20];
...
c = func("Another string").x;
This is not valid C code. Not in C89, not in C99, not in C11.
Apparently it compiles with the latest gcc versions 4.8 in -std=c89 mode without diagnostic for the assignment (clang issues the diagnostic). This is a bug in gcc when used in C89 mode.
Relevant quotes from the C90 Standard:
6.2.2.1 "A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type. and if it is a structure or union. does not have any member (including. recursively, any member of all contained structures or unions) with a const-qualified type."
and
6.3.16 "An assignment operator shall have a modifiable lvalue as its left operand."
6.3.16 is a constraint and imposes at least for gcc to issue a diagnostic which gcc does not, so this is a bug.
It's a bug in gcc.
An expression of array type is, in most contexts, implicitly converted to a pointer to the first element of the array object. The exceptions are when the expression is (a) the operand of a unary sizeof operator; (b) when it's the operand of a unary & operator; and (c) when it's a string literal in an initializer used to initialize an array object. None of those exceptions apply here.
There's a loophole of sorts in that description. It assumes that, for any given expression of array type, there is an array object to which it refers (i.e., that all array expressions are lvalues). This is almost true, but there's one corner case that you've run into. A function can return a result of struct type. That result is simply a value of the struct type, not referring to any object. (This applies equally to unions, but I'll ignore that.)
This:
struct foo { int n; };
struct foo func(void) {
struct foo result = { 42 };
return result;
}
is no different in principle from this:
int func(void) {
int result = 42;
return result;
}
In both cases, a copy of the value of result is returned; that value can be used after the object result has ceased to exist.
But if the struct being returned has a member of array type, then you have an array that's a member of a non-lvalue struct -- which means you can have a non-lvalue array expression.
In both C90 and C99, an attempt to refer to such an array (unless it's the operand of sizeof) has undefined behavior -- not because the standard says so, but because it doesn't define the behavior.
struct weird {
int arr[10];
};
struct weird func(void) {
struct weird result = { 0 };
return result;
}
Calling func() gives you an expression of type struct weird; there's nothing wrong with that, and you can, for example, assign it to an object of type struct weird. But if you write something like this:
(void)func().arr;
then the standard says that the array expression func().arr is converted to a pointer to the first element of the non-existent object to which it refers. This is not just a case of undefined behavior by omission (which the standard explicitly states is still undefined behavior). This is a bug in the standard. In any case, the standard fails to define the behavior.
In the 2011 ISO C standard (C11), the committee finally recognized this corner case, and created the concept of temporary lifetime. N1570 6.2.4p8 says:
A non-lvalue expression with structure or union type, where the
structure or union contains a member with array type (including,
recursively, members of all contained structures and unions) refers to
an object with automatic storage duration and temporary lifetime
Its lifetime begins when the expression is evaluated and its initial
value is the value of the expression. Its lifetime ends when the
evaluation of the containing full expression or full declarator ends.
Any attempt to modify an object with temporary lifetime results in
undefined behavior.
with a footnote:
The address of such an object is taken implicitly when an array member is accessed.
So the C11 solution to this quandary was to create a temporary object so that the array-to-pointer conversion would actually yield the address of something meaningful (an element of a member of an object with temporary lifetime).
Apparently the code in gcc that handles this case isn't quite right. In C90 mode, it has to do something to work around the inconsistency in that version of the standard. Apparently it treats func().arr as a non-lvalue array expression (which might arguably be correct under C90 rules) -- but then it incorrectly permits that array value to be assigned to an array object. An attempt to assign to an array object, whatever the expression on the right side of the assignment happens to be, clearly violates the constraint section in C90 6.3.16.1, which requires a diagnostic if the LHS is not an lvalue of arithmetic, pointer, structure, or union type. It's not clear (from the C90 and C99 rules) whether a compiler must diagnose an expression like func().arr, but it clearly must diagnose an attempt to assign that expression to an array object, either in C90, C99, or C11.
It's still a bit of a mystery why this bug appears in C90 mode while it's correctly diagnosed in C99 mode, since as far as I know there was no significant change in this particular area of the standard between C90 and C99 (temporary lifetime was only introduced in C11). But since it's a bug I don't suppose we can complain too much about it showing up inconsistently.
Workaround: Don't do that.
This line
c = func("Another string").x;
with c being declared as
char c[20];
is not valid C in any version of C. If it "works" in your case, it is either a compiler bug or a rather weird compiler extension.
In case of strcpy
strcpy(c, func("Another string").x);
the relevant detail is the nature of func("Another string").x subexpression. In "classic" C89/90 this subexpression cannot be subjected to array-to-pointer conversion, since in C89/90 array-to-pointer conversion applied to lvalue arrays only. Meanwhile, your array is an rvalue, it cannot be converted to const char * type expected by the second parameter of strcpy. That's exactly what the error message is telling you.
That part of the language was changed in C99, allowing array-to-pointer conversion for rvalue arrays as well. So in C99 the above strcpy will compile.
In other words, if your compiler issues an error for the above strcpy, it must be an old C89/90 compiler (or a new C compiler run in strict C89/90 mode). You need C99 compiler to compile such strcpy call.
There are two error in you code:
main(void)
{
char c[20];
struct { int i ; int j ; char x[20];} a = {5 , 7 , "someString"} , b;
c = func("Another string").x;// here of course number one
printf("%s\n" , c);
}
struct test func(char *c)
{
struct test temp;
strcpy(temp.x , c);
return temp; // here is number two , when the func finished the memory of function func was freed, temp is freed also.
}
Write you code like this:
main(void)
{
struct test *c;
struct { int i ; int j ; char x[20];} a = {5 , 7 , "someString"} , b;
c = func("Another string");
printf("%s\n" , c->x);
free(c); //free memory
}
struct test * func(char *c)
{
struct test *temp = malloc(sizeof(struct test));//alloc memory
strcpy(temp->x , c);
return temp;
}
OP: but why is it working?
Because apparently when copying a field of a structure, only type and size matters.
I'll search for doc to back this up.
[Edit] Reviewing C11 6.3.2 concerning assignments, the LValue C, because it is an array, it is the address of that array that becomes the location to store the assignment (no shock there). It is that the result of the function is a value of an expression, and the sub-field reference is also a value of an expression. Then this strange code is allowed because it simple assigns the value of the expression (20-bytes) to the destination location&c[0], which is also a char[20].
[Edit2] The gist is that the result of the func().x is a value (value of an expression) and that is a legit assignment for a matching type char[20] on the left side. Whereas c = c fails for c on the right side (a char[20]), becomes the address of the array and not the entire array and thus not assignable to char[20]. This is so weird.
[Edit3] This fails with gcc -std=c99.
I tried a simplified code. Note the function func returns a structure. Typical coding encourages returning a pointer to a structure, rather than a whole copy of some big bad set of bytes.
ct = func("1 Another string") looks fine. One structure was copied en masse to another.
ct.x = func("2 Another string").x starts to look fishy, but surprisingly works. I'd expect the right half to be OK, but the assignment of an array to an array looks wrong.
c = func("3 Another string").x is simply like the previous. If the previous was good, this flies too. Interestingly, if c was size 21, the compilation fails.
Note: c = ct.x fails to compile.
#include <stdio.h>
#include <string.h>
struct test {
int i;
char x[20];
};
struct test func(const char *c) {
struct test temp;
strcpy(temp.x, c);
return temp;
}
int main(void) {
char c[20];
c[1] = '\0';
struct test ct;
ct = func("1 Another string");
printf("%s\n" , ct.x);
ct.x = func("2 Another string").x;
printf("%s\n" , ct.x);
c = func("3 Another string").x;
printf("%s\n" , c);
return 0;
}
1 Another string
2 Another string
3 Another string