I'm facing a problem initializing an array with pointers to members of a structure. The structure members have to be accessed through a structure pointer. The reason for this is we initialize the pointer at runtime to a memory mapped address location. The following code snippet is an example of the problem;
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
long* lp;
}T;
typedef struct
{
long l;
}F;
F* f;
T t[] =
{
{ &f->l }
};
void init (void)
{
f = (F*) 0x08000100;
}
int main (void)
{
init();
return EXIT_SUCCESS;
}
The compiler output is the following;
gcc -O0 -g3 -Wall -c
-fmessage-length=0 -osrc\Test.o ..\src\Test.c ..\src\Test.c:18:
error: initializer element is not constant
..\src\Test.c:18: error: (near initialization for `t[0].lp')
..\src\Test.c:18: error: initializer element is not constant
..\src\Test.c:18: error: (near initialization for `t[0]')
Build error occurred, build is stopped
The problem here is we initialize the pointer at runtime, the compiler doesn't know where it can find the structure members. We cannot work around the structure pointer as we don't wan't to use the linker script for this.
Any ideas how to get around this one?
T t[] =
{
{ &f->l }
};
The address of an element (e.g. &f->l) is only known at run-time.
Such a value cannot be used for compile-time initialization (which is what's being done here).
The t[] array cannot be filled out until runtime - because the address of F isn't known until runtime.
You could initialize T[] to {NULL} and patch it in post-init.
Another approach is to initialize the members of T to just simply be the offset within the structure, and after you init f, to walk through the array and adjust the pointer locations by adding the address of f. This technique is similar to what is often used in linking.
Something like this:
#define MEMBER_OFFSET_OF(a,b) &(((a*)0)->b)
T t[] =
{
{(long*)MEMBER_OFFSET_OF(F, l)}
};
const int numElementsInT = sizeof(t) / sizeof(t[0]);
void init()
{
f = (F*) 0x08000100;
for (int i= 0; i < numElementsInT; i++)
{
t[i].lp += (unsigned int)f;
}
}
Lets imagine that you could use non-constant data to initialize a global: you still have a huge problem.
When t is initialized, f still has an indeterminate value: this happens before init() executes and assigns your magic address. Because of this, even if you could use &f->l, you'd have to reset all places it's been used, anyway.
Technically speaking for a C90 compiler there is no way around this. For the initialization idiom,
declarator = initialization sequence
the initialization sequence needs to be a constant expression, i.e. one which can be computed at compile-time or at link-time. So,
int a;
int *b[] = { &a };
works, while
void foo() {
int a;
int *b[] = { &a };
}
will not because the address of the automatic a isn't computable before runtime.
If you switch to C99, the latter will work. Your code however still is beyond what a C99 compiler can precompute. If you switch to C++ your code would work, at least Comeau doesn't object.
Edit: of course Roger is correct in that this doesn't solve your problem of having an incorrect dereferencing through a NULL pointer.
Related
I am trying to have an array of arrays of function pointers, but cannot assign to it, only statically initialize it:
#define N_INPUTS 2
#define N_STATES 2
void one()
{
//do stuff
}
void two()
{
//do stuff
}
//etc.
typedef void (*action_map[N_INPUTS])();
action_map my_action_maps[N_STATES];
//this would work:
//action_map am1 = {one, two};
//action_map am2 = {two, one};
//action_map my_action_maps[N_STATES] = { am1, am2 };
void init()
{
action_map am1;
am1[0] = one;
am1[1] = two;
my_action_maps[0] = am1; //error "expression must be a modifiable lvalue"
//however this works:
my_action_maps[0][0] = one;
my_action_maps[0][1] = two;
}
//the idea is to then handle input depending on a state with
//my_action_maps[state][input]();
I am not sure why is this happening, my_action_maps is just an array of pointers to function pointers, isn't it? Why it can initialized with initializer but then it is not modifiable?
This isn't really about function pointers, they are just making it harder to see the issue.
The type action_map is an array of function pointers. am1 and my_action_maps[0] are both of that type. But in C, you cannot assign an array to another array. It's the same issue as this:
int a[3] = {1,2,3};
int b[3] = {4,5,6};
a = b; // error
Current versions of gcc and clang both give a more useful message that explicitly says the problem is assigning to an array type. You might consider switching or upgrading your compiler.
You need to copy the elements one by one with a loop, or with memcpy. Thanks to array-pointer decay, you could do:
memcpy(my_action_maps[0], am1, sizeof(action_map));
Alternatively, you could wrap your action_map type in a struct, since you can assign structs to one another. But then it's a little more awkward to access the members.
typedef struct {
void (*actions[2])();
} action_map;
action_map my_action_maps[2];
void init(void) {
action_map am1;
am1.actions[0] = one;
am1.actions[1] = two;
// or: action_map am1 = { { one, two } };
// or: action_map am1 = { one, two };
my_action_maps[0] = am1; // ok
}
By the way, regarding your function and type declarations using empty parentheses, I suggest reading func() vs func(void) in C99 and Is it better to use C void arguments "void foo(void)" or not "void foo()"?. It may look like typedef void (*action_map[N_INPUTS])(); declares an array of pointers to functions taking no arguments, but it actually declares an array of pointers to functions taking unspecified arguments. This is supported mostly for compatibility with old versions of C and should generally not be used in new programs.
If you do am1[0](1,2,3); you will not get a compile error, and the compiler will happily attempt to pass three arguments to the function one that is not supposed to take any. This is undefined behavior. On some platforms, they might just be ignored, but you won't be alerted that you probably meant something else. On other platforms this may crash or worse. For instance, on a system using a calling convention where the called function is supposed to pop the stack (like the stdcall convention on Windows 32-bit compilers), calling a function with the wrong number of arguments will corrupt the stack.
So a better choice would be
typedef void (*action_map[N_INPUTS])(void);
Then action_map am1; am1[0](1,2,3); will cause a compiler error.
For consistency, I think it is also best to use void when defining a function with no parameters, e.g. void one(void) { ... }.
#include <stdio.h>
int main()
{
typedef struct s
{
int a;
int b[5];
char c[2];
}st;
st vs[1];
vs[0] = {1,{1,2,3,4,5},{'c','d'}};
printf("%d:a\n",vs[1].a);
printf("%d:b[0]\t %d:b[4]\n",vs[0].b[0],vs[0].b[4]);
printf("%c:c[0]\t %c:c[1]\n",vs[0].c[0],vs[0].c[1]);
return 0;
}
why does this doesn't work?
on
gcc -o main *.c
I get this error
main.c: In function 'main':
main.c:15:12: error: expected expression before '{' token
vs[0] ={1,{1,2,3,4,5},{'c','d'}};
But if I have this:
#include <stdio.h>
int main()
{
typedef struct s
{
int a;
int b[5];
char c[2];
}st;
st vs[] = {
{1,{1,2,3,4,5},{'c','d'}}
};
printf("%d:a\n",vs[0].a);
printf("%d:b[0]\t %d:b[4]\n",vs[0].b[0],vs[0].b[4]);
printf("%c:c[0]\t %c:c[1]\n",vs[0].c[0],vs[0].c[1]);
return 0;
}
it works. What is the logic in this.
How can I make it work using st vs[1] method?
You can only do braced initialization when you declare a variable. So,
st vs[] = {
{1,{1,2,3,4,5},{'c','d'}}
};
is allowed. But
vs[0] = {1,{1,2,3,4,5},{'c','d'}};
is not. Because this is not a initialization but assignment.
However, you can use C99's compound literal, see C11, 6.5.2.5:
vs[0] = (struct s){1,{1,2,3,4,5},{'c','d'}};
Initialization is when you declare a variable and provide initial values for it as part of the declaration. For example this is legal:
st vs[1] = { {1,{1,2,3,4,5},{'c','d'}} };
Your code is actually attempting assignment. Assignment is when an existing variable has a value assigned to it. The reason your code doesn't work is that {1,{1,2,3,4,5},{'c','d'}} isn't a value.
In a statement (not a declaration), each expression must be readable by the compiler on its own merit, and a more complicated statement is made up of various expressions joined by operators. So the compiler doesn't know what to do with {1,{1,2,3,4,5},{'c','d'}} - at this stage it has no idea that that is supposed to be a st.
Since C99 there is a new language construct you can use here, called compound literal:
vs[0] = (const st){1,{1,2,3,4,5},{'c','d'}};
Note that this is not a cast operator being applied to some sort of braced expression; it is a single syntactic construct (Typename){ initializers } .
My use of const is a micro-optimization, it may help the compiler store the literal in a read-only block of the executable and allow constant folding.
I'm just a tad confused about initializing multidimensional arrays in C...
This works:
int foo[2][MAX] = {
{2,4,34,43,23,0},
{2,4,34,43,23,0}
};
But this does not:
int foo[2][MAX];
foo = {
{2,4,34,43,23,0},
{2,4,34,43,23,0}
};
How come?
This syntax is for initialization, but in the second case you are using it for assignment which will not work.
type varName = someValue; // this is declaration and initialization
type varName; // this is declaration
varName = someValue; // this is assignment and not initialization
That is initialization can only be done at the declaration time, else it's a normal assignment.
The { syntax is only valid when you're initializing and declaring an array at the same time.
After declaring it, you need to use the complete syntax:
foo[0][0] = 2;
Technically speaking, C only has one-dimensional arrays. You create multidemnsional arrays by making arrays of arrays. The name of an array is converted to a pointer to its first element, and only the outer array is converted to a pointer. It's a pointer to an array of MAX ints, or int(*)[MAX].
As you said, this syntax is used to initialize an array, but in your second piece of code:
int foo[2][MAX];
Here, foo is uninitialized, and then
foo = {
{2,4,34,43,23,0},
{2,4,34,43,23,0}
};
This is assignment, not initialization.
Multidimensional or not, arrays in C are not copyable, which means that there's no way to assign anything to the entire array using core language features after the initialization is complete.
However, it is still possible to copy arrays by using memcpy function. In combination with compound literals it allows you to do what you tried to do. You just have to use a different syntax
int foo[2][MAX];
memcpy(
foo,
(int[2][MAX]) {
{2,4,34,43,23,0},
{2,4,34,43,23,0}
},
sizeof foo);
In C99 (or in gcc as an extension) you can make use of compound literals:
int (*foo)[MAX];
foo = (int [][MAX]) {
{2,4,34,43,23,0},
{2,4,34,43,23,0}
};
But note that foo must be declared as a pointer to MAX int's (not as a 2D array)
You have misunderstood the concept of 'initialization' and 'assignment`.
For example
int a = 10; // Initialization
Initialization is nothing but declaration + assignment.
But
int a; // Declaration
a = 10; // Assignment
When you do like this-
int foo[2][MAX] = {
{2,4,34,43,23,0},
{2,4,34,43,23,0}
};
internally it came to know 2 rows and MAX columns. It will initialize the whole array.
But-
int foo[2][MAX];
foo = {
{2,4,34,43,23,0},
{2,4,34,43,23,0}
};
Here foo represent the starting address of the array. Here you are trying to assign the values to 2D array. when you are trying to assign values to array it doesn't know how many rows and how many columns are there. so it is not possible and not allowed.
When you want to assign the input to array A better solution is scan it from user/ at run time-
int foo[2][MAX],i,j;
for(i=0;i<2;i++){
for(j=0;j<max;j++)
scanf("%d",&foo[i][j]);
}
I come from Java background and trying to understand C structures, pointers and arrays better. Here's the sample code that I am playing with:
If the following works:
#include <stdio.h>
int main(void) {
char string[] = "Hello";
printf("%c",string[0]);
return 0;
}
Why does the following return with an error?
#include <stdio.h>
int main(void) {
typedef struct{
int x;
char string[8];
}ST_DATA;
ST_DATA *my_data;
my_data->x = 100;
my_data->string = "Hello"; // issues a warning, described below
printf("%d",my_data->x); // works fine
printf("%c",my_data->string[0]);
return 0;
}
Following is the error that I am getting:
Compilation error time: 0 memory: 2292 signal:0
prog.c: In function ‘main’:
prog.c:12:18: error: incompatible types when assigning to type ‘char[8]’ from type ‘char *’
my_data->string = "Hello";
I tried with the following changes as well:
a)
my_data->string[] = "Hello";
This will give me the following error:
prog.c: In function ‘main’:
prog.c:12:18: error: expected expression before ‘]’ token
my_data->string[] = "Hello";
^
b)
my_data->string[8] = "Hello";
This returns with a runtime error. Presumably, the error occurs when I am printing the first character.
There must be something stupid that I am doing or expecting (being used to coding with other languages than C), but I can't seem to figure out why this is happening and how to get it to work. I'd greatly appreciate any pointers (ha! get it?)
In your first case, you are automatically allotting memory for char string[] = "Hello". The compiler takes care of memory management here.
In the second, my_data is a pointer and you need to allot memory to it manually, before you assign something to it.
You can:
ST_DATA *my_data = (ST_DATA *)malloc(sizeof(ST_DATA));
It'd be good you spend some time reading about automatic memory allocation and dynamic memory allocation.
strcpy() need to be used to copy strings into string members of structure ST_DATA.
strcpy(my_data->string, "Hello");
Before you have to allocate memory for your structure as,
ST_DATA *my_data = (ST_DATA *) malloc(sizeof(ST_DATA));
EDIT: The structure ST_DATA inside main() is just a declaration which tells compiler that it has members of what type they are. How can you use it until it is allocated memory. You can think ST_DATA as a data type similar to any other data types as int etc which has no meaning when they are not defined as int object which gets memory for i.
ST_DATA *my_data;
my_data->x = 100;
here my_data is a pointer so first allocate memory to it.
char string[] = "Hello";
here string is assigned value with declaration so it if fine. but in next code
my_data->string = "Hello";
is not ok since initialization can be done only with declaration. after declaration for string value has to be assigned using strcpy()
So I have some code that looks like this:
int a[10];
a = arrayGen(a,9);
and the arrayGen function looks like this:
int* arrayGen(int arrAddr[], int maxNum)
{
int counter=0;
while(arrAddr[counter] != '\0') {
arrAddr[counter] = gen(maxNum);
counter++;
}
return arrAddr;
}
Right now the compilier tells me "warning: passing argument 1 of ‘arrayGen’ makes integer from pointer without a cast"
My thinking is that I pass 'a', a pointer to a[0], then since the array is already created I can just fill in values for a[n] until I a[n] == '\0'. I think my error is that arrayGen is written to take in an array, not a pointer to one. If that's true I'm not sure how to proceed, do I write values to addresses until the contents of one address is '\0'?
The basic magic here is this identity in C:
*(a+i) == a[i]
Okay, now I'll make this be readable English.
Here's the issue: An array name isn't an lvalue; it can't be assigned to. So the line you have with
a = arrayGen(...)
is the problem. See this example:
int main() {
int a[10];
a = arrayGen(a,9);
return 0;
}
which gives the compilation error:
gcc -o foo foo.c
foo.c: In function 'main':
foo.c:21: error: incompatible types in assignment
Compilation exited abnormally with code 1 at Sun Feb 1 20:05:37
You need to have a pointer, which is an lvalue, to which to assign the results.
This code, for example:
int main() {
int a[10];
int * ip;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
return 0;
}
compiles fine:
gcc -o foo foo.c
Compilation finished at Sun Feb 1 20:09:28
Note that because of the identity at top, you can treat ip as an array if you like, as in this code:
int main() {
int a[10];
int * ip;
int ix ;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
for(ix=0; ix < 9; ix++)
ip[ix] = 42 ;
return 0;
}
Full example code
Just for completeness here's my full example:
int gen(int max){
return 42;
}
int* arrayGen(int arrAddr[], int maxNum)
{
int counter=0;
while(arrAddr[counter] != '\0') {
arrAddr[counter] = gen(maxNum);
counter++;
}
return arrAddr;
}
int main() {
int a[10];
int * ip;
int ix ;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
for(ix=0; ix < 9; ix++)
ip[ix] = 42 ;
return 0;
}
Why even return arrAddr? Your passing a[10] by reference so the contents of the array will be modified. Unless you need another reference to the array then charlies suggestion is correct.
Hmm, I know your question's been answered, but something else about the code is bugging me. Why are you using the test against '\0' to determine the end of the array? I'm pretty sure that only works with C strings. The code does indeed compile after the fix suggested, but if you loop through your array, I'm curious to see if you're getting the correct values.
I'm not sure what you are trying to do but the assignment of a pointer value to an array is what's bothering the compiler as mentioned by Charlie. I'm curious about checking against the NUL character constant '\0'. Your sample array is uninitialized memory so the comparison in arrayGen isn't going to do what you want it to do.
The parameter list that you are using ends up being identical to:
int* arrayGen(int *arrAddr, int maxNum)
for most purposes. The actual statement in the standard is:
A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
If you really want to force the caller to use an array, then use the following syntax:
void accepts_pointer_to_array (int (*ary)[10]) {
int i;
for (i=0; i<10; ++i) {
(*ary)[i] = 0; /* note the funky syntax is necessary */
}
}
void some_caller (void) {
int ary1[10];
int ary2[20];
int *ptr = &ary1[0];
accepts_pointer_to_array(&ary1); /* passing address is necessary */
accepts_pointer_to_array(&ary2); /* fails */
accepts_pointer_to_array(ptr); /* also fails */
}
Your compiler should complain if you call it with anything that isn't a pointer to an array of 10 integers. I can honestly say though that I have never seen this one anywhere outside of various books (The C Book, Expert C Programming)... at least not in C programming. In C++, however, I have had reason to use this syntax in exactly one case:
template <typename T, std::size_t N>
std::size_t array_size (T (&ary)[N]) {
return N;
}
Your mileage may vary though. If you really want to dig into stuff like this, I can't recommend Expert C Programming highly enough. You can also find The C Book online at gbdirect.
Try calling your parameter int* arrAddr, not int arrAddr[]. Although when I think about it, the parameters for the main method are similar yet that works. So not sure about the explanation part.
Edit: Hm all the resources I can find on the internet say it should work. I'm not sure, I've always passed arrays as pointers myself so never had this snag before, so I'm very interested in the solution.
The way your using it arrayGen() doesn't need to return a value. You also need to place '\0' in the last element, it isn't done automatically, or pass the index of the last element to fill.
#jeffD
Passing the index would be the preferred way, as there's no guarantee you won't hit other '\0's before your final one (I certainly was when I tested it).