extern declaration 2D array in C? - c

If I define a simple array :
int tableint[][2] = {
{1, 2},
{2, 4},
{3, 9},
{4, 16},
{5, 25}
};
and code in the very same file as :
printf("Value = %d\n",sizeof(tableint));
printf("Value = %d\n",sizeof(tableint[0]));
printf("Num of rows = %d\n",sizeof(tableint)/sizeof(tableint[0]));
Output is as expected:
Value = 40
Value = 8
Num of rows = 5
But if I move the same 'tableint' 2D array in a separate file and retain above mentioned print statements in the previous main file but just adding an extern as follows:
extern int tableint[][2];
I get an error in the following line:
printf("Value = %d\n",sizeof(tableint));
as : invalid application of 'sizeof' to incomplete type 'int[][2]'
I am curious to know why it worked when the 2D array was there within the same file and not when I moved it into another file?
And, the error gets resolved if I define the extern as:
extern int tableint[5][2];
Is there any way where I don't need to mention the number of rows in extern here?

An array declared in this fashion - with [] in the declaration - will have complete type if and only if the declaration includes an initializer. Without an initializer the type is incomplete.
That's your difference that made it work in the first case.
If you really need to declare it as extern, then you have no choice but to specify all sizes explicitly, as long as you require this sizeof technique to work (or as long as you want to see the array size as a constant expression).
If you don't care about being able to use that sizeof trick and if you'd be happy with seeing the array size as run-time value, then you could export it from that "separate file" as another variable or as function.

This is because C source files are treated as separate, individual translation units, than can be compiled independent to each other (for instance with gcc -c ...). The information stored in some unit is not accesssible to another one until linkage phase is concerned.
With that said, the spec is clear that you cannot take sizeof operator for an incomplete type, as N1570 6.5.3.4/1 The sizeof and _Alignof operators says:
The sizeof operator shall not be applied to an expression that has
function type or an incomplete type, to the parenthesized name of such
a type, or to an expression that designates a bit-field member.

Related

keil error #28 expression must have a constant value

I'm using Keil uVision to compile this code for an embedded project.
void doSomething(void)
{
unsigned char a = 0x01;
unsigned char b = 0x02;
typedef struct
{
void *pVoid;
} test_t;
test_t t[] = {{&a}, {&b}};
}
On the last line I receive an error
error: #28: expression must have a constant value
I've read that this is an issue where the compiler does not know the size of the variable. I don't understand what that means.
The variables a and b are of a defined type, so their pointers will always be the same size? Regardless of their type, this is embedded so pointers are all the same size?
It works to make var a and b static, why? This being embedded, I don't want the memory allocated continuously so that is not a solution here.
UPDATE:
I'm using Keil uVision 4.72.10.0 with Armcc v5.03.0.76 - I was able to get Keil to compile this, using the "--c99" flag, as found here.
Your function is perfectly fine in modern C, or even in C99, but C90 had stricter rules for initializers, and your code does not comply with those.
Here is the relevant provision of C90:
All the expressions in an initializer for an object that has static
storage duration or in an initializer list for an object that has
aggregate or union type shall be constant expressions.
(C90 6.5.7/4; emphasis added)
Structure types are aggregate types, so that applies to your code (when interperted according to C90). Where a and b identify function-scope variables, the expressions &a and &b are not constant expressions, so your code does not conform (to C90).
C99 drops the bit about aggregate or union type, and C2011 adds a provision for objects with thread storage duration (new in that version of C) to yield:
All the expressions in an initializer for an object that has static or
thread storage duration shall be constant expressions or string
literals.
That does not apply to your code.
It seems, then, that your compiler is enforcing C90 rules. Perhaps there is an option to select a more recent standard, but if not, then your best alternative is probably to set the structure members' values with assignment statements instead of an initializer:
test_t t[2];
t[0].pVoid = &a;
t[1].pVoid = &b;

Using non standard declaration of array in C

I came across the following code which declares char * array in C in a non-standard way:
/* Message Type description array */
char *msgType[M_LAST_MSG] =
{
[M_INIT_MSG] "Init",
[M_RESET_MSG] "Serdes Reset"
};
M_INIT_MSG, M_RESET_MSG and M_LAST_MSG are enumerations with corresponding values of 0, 1 and 2.
According to the formal C documentations, the variables inside this array are string (literals) so what is the purpose of using those enumerations in that manner and is there any documentation to back it up?
Compiled with ARM gcc compiler gcc-arm-none-eabi.
This syntax allows you to initialize specific elements of an array by index. You can use either int or enum values to specify which array element to initialize. This way, the values you assign don't need to be consecutive.
If for example you had this:
int x[5] = { [2] 3, [4] 7 };
It would be equivalent to this:
int x[5] = { 0, 0, 3, 0, 7 };
In the above example, the enum values specify that elements 0 and 1 of the array are initialized to "Init" and "Serdes Reset".
From section 6.7.8 of the C99 standard:
18 Each designator list begins its description with the
current object associated with the closest surrounding brace
pair. Each item in the designator list (in order) specifies
a particular member of its current object and changes the
current object for the next designator (if any) to be that member.
The current object that results at the end of the designator list is
the subobject to be initialized by the following initializer.
33 EXAMPLE 9 Arrays can be initialized to correspond to the
elements of an enumeration by using designators:
enum { member_one, member_two };
const char *nm[] = {
[member_two] = "member two",
[member_one] = "member one",
};
EDIT:
Note that the syntax from the standard includes a = while OP's example does not. The syntax without = is apparently an old syntax supported by GCC. Compiling OP's example gives the following warning:
warning: obsolete use of designated initializer without ‘=’
The GCC documentation states the following:
An alternative syntax for this that has been obsolete since GCC 2.5 but GCC still accepts is to write ‘[index]’ before the element value, with no ‘=’.
That's a GNU extension. It was standardized in C99 with slightly different syntax, namely an equal sign between the [index] and the value and no way to specify a range of indices. They are called designated initializers.
The C standard shows an example of probably the most widespread use, provide a string description for enums:
33 EXAMPLE 9 Arrays can be initialized to correspond to the elements of an enumeration by using designators:
enum { member_one, member_two };
const char *nm[] = {
[member_two] = "member two",
[member_one] = "member one",
};
It even allows nifty stuff like
EXAMPLE 11 Designators can be used to provide explicit initialization when unadorned initializer lists might be misunderstood:
struct { int a[3], b; } w[] =
{ [0].a = {1}, [1].a[0] = 2 };

Is there anything wrong with `something_t* x = malloc(sizeof(*x))`?

I'm writing some extremely repetitive code in C (reading XML), and I found that writing my code like makes it easier to copy and paste code in a constructor*:
something_t* something_new(void)
{
something_t* obj = malloc(sizeof(*obj));
/* initialize */
return obj;
}
What I'm wondering is, it is safe to use sizeof(*obj) like this, when I just defined obj? GCC isn't showing any warnings and the code works fine, but GCC tends to have "helpful" extensions so I don't trust it.
* And yes, I realize that I should have just written a Python program to write my C program, but it's almost done already.
something_t* obj = malloc(sizeof(*obj));
What I'm wondering is, it is safe to use sizeof(*obj)
like this, when I just defined obj?
You have a declaration consisting of:
type-specifier something_t
declarator * obj
=
initializer malloc(sizeof(*obj))
;
The C standard says in section Scopes of identifiers:
Structure, union, and enumeration tags have scope that begins just
after the appearance of the tag in a type specifier that declares the
tag. Each enumeration constant has scope that begins just after the
appearance of its defining enumerator in an enumerator list. Any other
identifier has scope that begins just after the completion of its
declarator.
Since obj has scope that begins just after the completion of its declarator, it is guaranteed by the standard that the identifier used in the initializer refers to the just defined object.
While we are giving the sizeof like this.
something_t* obj = malloc(sizeof(obj));
It will allocate the memory to that pointer variable as four bytes( bytes allocated to a pointer variable.)
something_t* obj = malloc(sizeof(*obj));
It will take the data type which is declared to that pointer.
For example,
char *p;
printf("%d\n",sizeof(p));
It will return the value as four.
printf("%d\n",sizeof(*p));
Now it will return value as one. Which is a byte allocated to the character. So when we are using the *p in sizeof it will take the datatype. I don't know this is the answer you are expecting.
It's safe. For example,
n = sizeof(*(int *)NULL);
In this case, NULL pointer access doesn't occur, because a compiler can caluculate the size of the operand without knowing the value of "*(int *)NULL" in run time.
The C89/90 standard guarantees the 'sizeof' expression is a constant one; it is translated into a constant (e.g. 0x04) and embedded into a binary code in compilation stage.
In C99 standard, the 'sizeof' expression is not always a compile-time constant, because of introducing variable length array. For example,
n = sizeof(int [*(int *)NULL]);
In this case, the value of "*(int *)NULL" needs to be known in run time to caluculate the size of 'int[]'.

Copying array in C v/s copying structure in C

Arrays and structures in C store data in memory which is contiguous. Then why is that C does not allow direct copying of arrays using "=" where as it is allowed for structure.
Example:
int a[3] = {1,2,3};
int b[3];
b = a; // why is this not allowed.
struct book b1, b2;
b1.page = 100;
b1.price = 10.0;
b2 = b1; // Why is this allowed
For the first question
You cannot directly write to an array, you can write only to the individual cells to an array.
You can use a for loop to initialize array b or memcpy(&b, &a, sizeof b);
And with the structs the compiler does the memcpy for you.
Correct me if I am wrong.
When you type : b=a , the compiler expects that you are assigning an array to b, but a is just a pointer to the location where the first element of the array is stored so there is a type mismatch.This is why printf("%d",*a); will print 1.
And as for why structures can be assigned, it is because b1 and b2 in the above example are basically variables of the datatype book and variables can be assigned.When variables are assigned the contents are copied and they don't refer to the same memory location.This example might explain what i am saying more clearly:
#include<stdio.h>
typedef struct{int a;}num;
int main()
{
num b,c;
b.a = 10;
c=b;
b.a =11;
printf("%d\n",(c.a));
return 0;
}
The output is 10. This proves that b and c in this example do not point to the same memory.hope this helps.
Assignment requires that the type and therefore size of whatever is being assigned is known to the compiler. So an assignment of form
a = b;
requires that the types of a and b are both known to the compiler. If the types are the same (e.g. both a and b are of type int) then the compiler can simply copy b into a by whatever instructions it deems are most efficient. If the types are different, but an implicit promotion or type conversion is allowed, then the assignment is also possible after doing a promotion. For example, if a is of type long and b is of type short, then b will be implicitly promoted to long and the result of that promotion stored in a.
This doesn't work for arrays, because the size of an array (calculated as the size of its elements multiplied by number of elements) is not necessarily known. One compilation unit (aka source file) may have a declaration (possibly by including a header file)
extern int a[];
extern int b[];
void some_func()
{
a = b;
}
which tells the compiler that a and b are arrays of int, but that they will be defined (which includes giving them a size) by another compilation unit. Another compilation unit may then do;
extern int a[];
int a[] = {3,1,4,2,3}; /* definition of a */
and a third compilation unit may similarly define b as an array of 27 elements.
Once the object files are linked into a single executable, the usages of a and b in all compilation units are associated, and all operations on them refer to the same definitions.
The problem with this comes about because the separate compilation model is a core feature of C. So the compiler, when chewing on the first compilation unit above, has no information about the size of the arrays since it has no visibility of other compilation units, and is required to succeed or diagnose errors without referring to them. Since there is no information about the number of elements in either array available to the first compilation unit, there is no way to work out how many elements to copy from one array to another. The handling of this in C is that the assignment a = b is a diagnosable error in the function some_func().
There are alternative approaches (and some other programming languages handle such cases differently) but they are generally associated with other trade-offs.
The considerations doesn't generally affect struct types, since their size is known at compile time. So, if a and b are of the same struct type, the assignment a = b is possible - and can be implemented by (say) a call of memcpy().
Note: I am making some deliberate over-simplification in the explanation above, such as not considering the case of structs with flexible array members (from C99). Discussing such cases would make the discussion above more complicated, without changing the core considerations.

Declaring C arrays

int array[5][3];
(obviously) creates a multi-dimensional C array of 5 by 3. However,
int x = 5;
int array[x][3];
does not. I've always thought it would. What don't I understand about C arrays? If they only allow a constant to define the length of a C array, is there a way to get around this in some way?
In ANSI C (aka C89), all array dimensions must be compile-time integer constants (this excludes variables declared as const). The one exception is that the first array dimension can be written as an empty set of brackets in certain contexts, such as function parameters, extern declarations, and initializations. For example:
// The first parameter is a pointer to an array of char with 5 columns and an
// unknown number of rows. It's equivalent to 'char (*array_param)[5]', i.e.
// "pointer to array 5 of char" (this only applies to function parameters).
void some_function(char array_param[][5])
{
array_param[2][3] = 'c'; // Accesses the (2*5 + 3)rd element
}
// Declare a global 2D array with 5 columns and an unknown number of rows
extern char global_array[][5];
// Declare a 3x2 array. The first dimension is determined by the number of
// initializer elements
int my_array[][2] = {{1, 2}, {3, 4}, {5, 6}};
C99 added a new feature called variable-length arrays (VLAs), where the first dimension is allowed to be a non-constant, but only for arrays declared on the stack (i.e. those with automatic storage). Global arrays (i.e. those with static storage) cannot be VLAs. For example:
void some_function(int x)
{
// Declare VLA on the stack with x rows and 5 columns. If the allocation
// fails because there's not enough stack space, the behavior is undefined.
// You'll probably crash with a segmentation fault/access violation, but
// when and where could be unpredictable.
int my_vla[x][5];
}
Note that the latest edition of the C standard, C11, makes VLAs optional. Objective-C is based off of C99 and supports VLAs. C++ does not have VLAs, although many C/C++ compilers such as g++ which support VLAs in their C implementation also support VLAs in C++ as an extension.
int x = 5;
int array[x][3];
Yes, it does. It's a C99 variable length array. Be sure to switch to C99 mode and be sure to have array declared at block or function scope. Variable length arrays cannot be declared at file scope.
Try:
const int x=5;
int array[x][3];
As you said x has to be a constant or else think what would happen if in the middle of the program you changed the value of x,what would be the dimension of array:(
But by declaring it constan if you change the value of x you get a compile error.

Resources