This compiles in gcc with no errors or warnings even with -Wall option
meaning that array bounds are checked at run-time and hence compiler can't detect the error
#include<stdio.h>
int main()
{
int a[2][3][4];
a[1][2][100] = 4 ;
return 0;
}
However,
#include<stdio.h>
int main()
{
int a[2][3];
a[1][2][100] = 4 ;
return 0;
}
this generates an error while compiling as :
$ gcc sample.c -Wall
sample.c: In function ‘main’:
sample.c:7: error: subscripted value is neither array nor pointer
Why is this so ? in both the two codes a[1][2][100] is invalid . Still how can the compiler detect this is code2 and not in code1.
Specially when every compiler flattens all multidimensional array into corresponding single dimension arrays, then how can the compiler be selectively aware of this flaw in the code.
Explanation or mention of some book or article where the proper explanation resides will be gratefully accepted :)
The difference is the types. C does no bounds checking but it does do (static) type checking. The type of a in your first example is int[][][] but the type in the second example is int[][].
The "flattening" that you refer to happens in code generation, which is (conceptually, at least) after type checking.
First, array bounds are not checked at runtime or at compile time. Be careful out there.
Second, your second case gives an error because you have a mismatch in array dimension - you're using three subscript operators ([]) on a 2D array. Just because the array happens to be laid out in memory as an array of arrays doesn't mean there is any actual type changing going on with the variable.
Array subscripting is described in the C standard section 6.5.2.1 Array subscripting.
Given
int a[2][3];
the compiler will determine that a[1][2] is of type int. Therefore, accessing element [100] of this is equivalent to:
int x;
x[100] = 4;
This would give you the same error about the subscripted value.
Related
I was writing the following code
#include<stdio.h>
void fun(int n) {
int a[n] = {0};
}
void main() {
int a[4] = {0};
int i = 0;
fun(3);
}
and got this error
test.c: In function 'fun':
test.c:5:5: error: variable-sized object may not be initialized
while if I change the function fun to:-
void fun(int n) {
int a[n], i = 0;
for(i = 0; i < n; i++) {
a[i] = 0;
}
}
it works fine.
I know that the error is occuring because it's not allowed in the compiler's specification but what i want to know is why is it not possible to be implemented?
Is it due to some compile time or run time evaluation issue?
I have seen the answer of other question but i need a more elaborated answer.
Variable Length Array cannot be initialized like this
int a[n]={0};
From C Standards#6.7.9p3 Initialization [emphasis added]
The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.
Using loop is one way to initialize the variable length array's. You can also use memset like this:
memset(a, 0, sizeof a);
Additional:
The C99 compiler should support the Variable Length Array's but they were made optional in C11 compiler.
An easy way is to send the size of array along with other parameters
Remember that you should send size before an array with that size
void fun(int n,int a[n]){
}
Although you have other alternatives like sizeof()
As an addition to H.S. answer:
From C Standards#6.7.9p3 Initialization [emphasis added]
The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.
This is probably because Initializers have to be constant expressions. Constant expression have a definite value at compile time.
A {0} is an incomplete Initializer and the compiler would fill up the remaining values with 0.
If you have a VLA the compiler does not know the length of the Array and thus can not generate the initializer for it.
This depends on your compiler actually.
In old C You couldn't have variable size arrays. In function fun you use a as an array with variable size n. This is not allowed in old C. However, C99 and C11 standards support variable size arrays, so perhaps you have an old compiler. (DevC?) If you want to use some type of variable arrays in older C compilers, you have to use malloc and free.
Perhaps you wanted to write this code in C++? C++ doesn't support variable size arrays also, but the gcc compiler can run this code.
Check this out:
Why aren't variable-length arrays part of the C++ standard?
If you are using DevC, I think that if you change your file from test.c to test.cpp this code will work.
Why didn't I get a compile time error while accidentally printing only one dimension of a 2D array?
#include <stdio.h>
void main() {
int i;
int arr[2][3] = { 1, 2, 3, 4, 5, 6 }; //<- Declared a 2D array
for (i = 0; i < 6; i++) {
printf("%d\n", arr[i]); // <- Accidently forgot a dimension
}
}
I should have received a compile time error but instead I got a group of addresses! Why? What did arr[0] mean in this context to the compiler?
An expression with array type evaluates to a pointer to the first array element in most contexts (a notable exception, among others, is the sizeof operator).
In your example, arr[i] has array type. So it evaluates to a pointer of type int (*)[] (a pointer to an array). That's what's getting printed. Printing a pointer with %d is undefined behavior, because printf() will read the pointer as if it was an int.
Felix Palmen's answer explains the observed behavior.
Regarding your second question: the reason why you don't get a warning is you did not ask for them.
Compilers are notoriously lenient by default and will accept broken code including obvious undefined behavior. This particular one is not obvious because printf() accepts any number of extra arguments after the initial format string.
You can instruct your compiler to emit many useful warnings to avoid silly mistakes and detect non obvious programming errors.
gcc -Wall -Wextra -Werror
clang -Weverything -Werror
option /W3 or /W4 with Microsoft Visual Studio.
gcc and clang will complain about the sloppy initializer for array arr. It should read:
int arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
The print loop is indeed surprising, did you really mean to print the array with a single loop?
Note also that the standard prototype for main without arguments is int main(void).
#include <stdio.h>
char A[];
int main()
{
printf("%c\n",A[1]);
return 0;
}
I can access any element using index . It never gives error . What is the index of max element i can access for 32 bit machine?
It has size 1. Accesses beyond index 0 (including your code, which accesses A[1]) have undefined behavior.
This is 6.9.2 in the C99 standard. char A[]; is a "tentative definition", which roughly speaking means that if the same translation unit contains a proper definition then it's just a declaration of A as an array of char of unknown size. If there's no proper definition then the object is defined anyway as if there were a definition at the end of the translation unit, with a default initializer.
When the declaration char A[]; appears at file scope, it declares an array. A definition of the array should appear somewhere else. If the definition does not appear in the same file (translation unit), then the behavior is as if a definition appeared with one initializer with value zero, as if you had written char A[] = { 0 };.
Code in which the declaration is visible may use the array. However, if the definition of the array is not visible, then the compiler does not know the size of the array. It is the responsibility of the author of the code to use only elements that are actually defined. They must know the size of the array by prior arrangement or by passing some information in the program.
If code uses an element of the array that does not exist, or even calculates an address of an element more than one beyond the end of the array, then the behavior is undefined.
A[1] will be translated as *(A+1) which is basically a memory address . So, printf can print whatever is at that memory location. I assume you can keep referencing the array till anything exists at that location (which gives you garbage)[and you are permitted to access that location].
Edit: GCC 4.6.3 gives warning: array ‘A’ assumed to have one element [enabled by default]
I can access any element using index . It never gives error . What is
the index of max element i can access for 32 bit machine?
There are a great many things that you can do in C which you nevertheless should not do. Accessing out of bounds elements of an array is usually one of those things.
When I compile your code using gcc, I get:
warning: array ‘A’ assumed to have one element
That should be enough to tell you that you should not access any element other than A[0].
It's just a pointer, there are no elements in the table. You should not try to index anything.
I have a matrix struct:
typedef struct Matrix
{
float m[16];
} Matrix;
When I try to call this function:
memcpy(m->m, MultiplyMatrices(m, &translation).m, sizeof(m->m));
I get an error at compile time saying:
error: invalid use of non-lvalue array
MultiplyMatrices returns a Matrix.
I only get this error if I use gcc to compile the file into an object,
if I use g++ to compile the object I get no error.
I am not even sure what the error means, I have a feeling it has to do with the array stored in the Matrix returned by MultiplyMatrices.
If you need to see more code let me know.
This code is from this tutorial: OpenGL Book Chapter 4
p.s. I would like to keep this code strict iso/ansi, if there is no other solution however, then I'll just have to deal with it.
EDIT: I ended up going with creating a temporary Matrix then copying the array.
Matrix tempMatrix;
...
tempMatrix = MultiplyMatrices(m, &translation);
memcpy(m->m, tempMatrix.m, sizeof(m->m));
The return value of MultiplyMatrices() is not an lvalue (like the return value of any function), which means that you can't take its address. Evaluating an array (including an array member of a structure) implicitly takes the address of the first element, so you can't do that.
You can, however, use simple assignment of the containing struct:
*m = MultiplyMatrices(m, &translation);
As long as your struct only contains the one element as you have shown, this is exactly the same.
I am learning C and I am playing around with pointers and arrays. I am trying to create an array of pointers with the code below:
const int NUM_P = 50; // Line 10
char *pS[NUM_P] = { NULL }; // Line 11
I am getting the following warnings and errors when I compile:
→ gcc array.c -o array
array.c: In function ‘main’:
array.c:11: error: variable-sized object may not be initialized
array.c:11: warning: excess elements in array initializer
array.c:11: warning: (near initialization for ‘pS’)
I cannot figure this error out, I have looked online and been unable to find a explanation and solution of the problem.
Can anyone help out?
Cheers
Eef
Your main problem is the initialization line (= {NULL};) because apparently you can't initialize an array that way when the array's size is a variable. If you use #define NUM_P 50 to force the array size to be a true compile-time constant, then the array would not have a variable size, and your initialization method would compile fine.
As a side note, the difference between #define NUM_P 50 and const int NUM_P = 50 is that C89 does not allow the use of variables as array dimensions, but C99 does; however, C99 is not fully supported by all compilers yet. The fact that gcc accepts your character array with a size of NUM_P is not C89-compliant.
Technically, you could use a flag on the command line to tell gcc that you want to compile in C99:
gcc -std=c99 FILE.c
By the way, when learning C, start at the top of the errors listed and work down from there. In this case, the warnings are not of any concern until you've taken care of the error at the top: "variable-sized object may not be initialized".