I encounter the following piece of code, but I don't know why do it in this way. Could any one explain it to me?
char *MPIDI_CH3_Pkt_type_to_string[MPIDI_CH3_PKT_END_ALL+1] = {
[MPIDI_CH3_PKT_EAGER_SEND] = "MPIDI_CH3_PKT_EAGER_SEND",
[MPIDI_CH3_PKT_EAGER_SEND_CONTIG] = "MPIDI_CH3_PKT_EAGER_SEND_CONTIG",
};
Zack
This is just using designated initializers, as in the question Designated initializers and omitted elements. For example,
const char *foo[42] = {[1] = "hello", [2] = "world"};
The code given in the question appears to be a way of stringizing constants.
You have to declare an array of char * pointers. And you have two manifest constants -MPIDI_CH3_PKT_EAGER_SEND and MPIDI_CH3_PKT_EAGER_SEND_CONTIG - with some values that you are not supposed to "know". Now, you want to make elements with indices MPIDI_CH3_PKT_EAGER_SEND and MPIDI_CH3_PKT_EAGER_SEND_CONTIG to point to corresponding string literal values (and leave all other array elements as null pointers).
How can you do that?
In C89/90 you can do it by initializing everything to nulls first and then use assignment to set the proper elements to proper values
char *MPIDI_CH3_Pkt_type_to_string[MPIDI_CH3_PKT_END_ALL+1] = { 0 };
MPIDI_CH3_Pkt_type_to_string[MPIDI_CH3_PKT_EAGER_SEND] = "MPIDI_CH3_PKT_EAGER_SEND";
MPIDI_CH3_Pkt_type_to_string[MPIDI_CH3_PKT_EAGER_SEND_CONTIG] = "MPIDI_CH3_PKT_EAGER_SEND_CONTIG";
In C99 you can use designed initializer feature to do the same thing in the initializatiuon, as a one-liner, which is exactly what is done in your example.
So, the answer to your "why" question would be: because the language feature used in your declaration does exactly what the user wanted to do. There's no other answer.
P.S. The real "why" question here is why they are using char * pointers to point to string literals (instead of const char * pointers). That's a really big "why".
Related
This question already has answers here:
How to initialize only few elements of an array with some values?
(4 answers)
Closed 4 years ago.
I'm currently wrapping my head around proper state machines, and I've found this article which seemed to be exactly what I've been searching for:
https://aticleworld.com/state-machine-using-c/
In the process of understanding table of the table based state machine I've stumbled over this piece of code which in my opinion is quite hard to read.
So this is as far as I can come:
eSystemState is a typedefed enum which indicates the current state.
eSystemEvent is a typedefed enum which indicates the current event.
pfEventHandler is a typedefed function pointer returning eSystemState.
afEventHandler is a typedefed, two dimensional array (limited by "last_State" and "last_Event") of function pointers returning eSystemState.
Next up are some event handlers for each type of event.
Then beginning of main, some initialization and there it comes.
The previously defined type afEventHandler gets filled with content, but the operations which are used there are a big mystery to me.
I know that for every curly brackets a new 'row' gets filled, but what do the square brackets around the enums mean, and why can one apparently assign a value to whatever comes out of this?
I know that enums are essentially numbers, and I suspect it is some (to me) super obscure way to address a specific element in an array.
Am I wrong?
// Table to define valid states and event of finite state machine
static afEventHandler StateMachine = {
[Idle_State] ={[Card_Insert_Event]= InsertCardHandler },
[Card_Inserted_State] ={[Pin_Enter_Event] = EnterPinHandler },
[Pin_Eentered_State] ={[Option_Selection_Event] = OptionSelectionHandler},
[Option_Selected_State] ={[Amount_Enter_Event] = EnterAmountHandler},
[Amount_Entered_State] ={[Amount_Dispatch_Event] = AmountDispatchHandler},
};
This syntax is for a designated initializer. It allows you to initialize specific elements of an array.
The value in square brackets is the index of the array element to initialize. In this case it is an enum value, but itcould be any integer.
The square brackets mark array subscripts. The enum identifiers inside them are simply ordinary expressions, replaced by the enum values.
C allows you to initialize arrays using explicit subscripts, called designators. For example, this initialize A[1] to 4:
int A[3] = { [1] = 4 };
(The other elements default to zero.)
With a two-dimensional array, the syntax gets more complicated:
int B[3][4] =
{
[1] = { [3] = 7 }
};
That initializes B[1][3] to 7. That is the syntax you are seeing in the code you refer to. It then uses enum identifiers like Idle_State instead of the sample constants 1, 3, and 7 shown above.
I've found it myself, it's a "designated initializer" which indeed specifies which element shall be set by a chosen value.
It's been introduced in C99 which explains why i didn't know about it. Other elements get initialized to 0.
I'll leave the question here for others who may wonder about the same.
How to initialize only few elements of an array with some values?
int a[10][10] = {
{},
{[5] = 8}
};
Means that in the second row the element 5 is gonna be initialized to eight, others will be zero.
In the C code I am analyzing, there are a lot of multidimensional (struct) arrays which are initialized with a different number of curly brackets e.g. {{0}} or {{{0}}}.
However, replacing these by {0} also works perfectly.
Is there a (functional) difference between using one or more sets of curly brackets ({}) occurrences ?
No, there is no functional difference. The C standard allows to leave out intermediate {}. In particular, the form { 0 } is an initializer that can be used for all data types.
You have two choices: either { 0 }, which works for any aggregate or union type and zero initializes every member, or using the correct form which must correspond to all members correctly.
Just to reiterate what Jens has already said, {0} works for any type. It is the "universal zero initializer" in C.
See C11 draft, 6.7.9 Initialization.
So, to initialize a 3D array either {0} or {{{0}}} can be used. Personally I'd use {0} as it's easier to type and read and works for every type. That means, the following are all valid initializations:
int main(void)
{
int x = {0,};
int *p = {0,};
int *q = {0};
int arr[3][3][3][3] = {0};
}
More importantly, if you happen to have some unknown/opaque type, for example from a third-party libraries, then the only portable way
to initialize them is using {0}. Any other way of zero-ing it (such as using memset() or directly some_type_t state = 0;) would require some internal knowledge of the type involved and risks being non-portable.
I'm looking through the "Processor Modeling Guide" provided by a company named OVP (a product similar to qemu). In it, there's a little code snippet resembling the following:
static or1kDispatchTableC dispatchTable = {
// handle arithmetic instructions
[OR1K_IT_ADDI] = disDefault,
[OR1K_IT_ADDIC] = disDefault,
[OR1K_IT_ANDI] = disDefault,
[OR1K_IT_ORI] = disDefault,
[OR1K_IT_XORI] = disDefault,
[OR1K_IT_MULI] = disDefault
};
I've never seen syntax like this before. irrelevant stuff about C++ removed
At the moment I don't have the ability to download/look at their stuff to look at how anything is defined, hence my question. If you recognize this syntax, can you weigh in?
edit
or1kDispatchTableC is a typedef for a pointer of type or1kDispatchTableCP, but I still don't have anything on what or1kDispatchTableCP is.
Well, assuming your first line is a typo, or or1kDispatchTableC is an array type, so that this is actually an array declaration, this looks like a C11 explicitly initialized array. The line
[OR1K_IT_ADDI] = disDefault,
initializes element OR1K_IT_ADDI to disDefault. Both of those need to be constant expressions -- OR1K_IT_ADDI is probably a #define or an enum tag.
I'm pretty sure that C++11 does NOT support this syntax, though some compilers (that also support C11) might support it as an extension.
From the names, I would guess that this is actually an array of function pointers.
This is called designated initializers and is a C feature (supported since C99). It allows addressing array and structure/union elements directly, filling the gaps with default values.
struct foo { int a[10]; };
struct foo f[] = { [5].a[3] = 20 };
Now this results in 5 elements of struct foo, all initialized to zero followed by a sixth element of struct foo with the 4th element of a initialized to 20.
Like someone else suspected, this is not supported by C++.
I am fairly new to C and I don't understand why the following two statements do not create the same result:
char *fields[14] = {NULL};
const int num_fields = 14;
char *fields[num_fields] = {NULL};
Option 1 works, but option 2 does not. It says "variable-sized object may not be initialized" and it gives a warning "warning: excess elements in array initializer". I use gcc 4.2.1 on OSX.
Thanks for sharing your thoughts!
The second object is called a VLA (Variable Length Array), well defined by C99. To achieve what you want you can use this:
for (i = 0; i < num_fields; i++)
fields[i] = NULL;
The gist of the issue is that const int num_fields is very different from 14, it's not a constant, it's read-only.
Even if you define num_fields with const keyword, compiler interprets it as variable only. you can have alternative for this by defining following macro:
#define num_fields 14
char *fields[num_fields] = {NULL};
Although num_fields has a const qualifier, it is still considered a variable by the compiler.
Therefore, you are attempting to declare a variable-sized array, and initialisers (the {NULL} part) cannot be used in conjunction with them.
Your construction works in C++, where a const int will be treated as a compile-time constant ("constant expression"), and hence available for use as a compile-time array size.
(This aspect was one of B. Stroustrup's design goals for C++, to eliminate the need for compile-time macros if possible)
However in C, your definition of "num_fields" effectively declares a read-only memory location with your preset value, and hence is not under C rules a "constant expression" valid at compile time, and hence may not be used as an array size at the outermost 'program' scope.
I would like to define an array of string like that:
#define sup (const char**) ("string1", "string2")
but it fails when I try to print the first string:
printf("The string: %s\n",sup[0]);
how to do it in the proper way?
I would advice against doing this with macros altogether, but if you are really interested in what is going on with the code --more than in how this should actually be tackled, here is an explanation.
There is a simple issue in the code, and a more obscure one. The very simple is that to declare an array you don't use parenthesis but rather curly braces:
#define sup (const char**){"str1", "str2"} // still wrong!!
The less simple issue is that arrays are not pointers. The curly brace initializer can be used to initialize an array of two const char*, but that is not the same as a const char**. If you change the code to:
#define sup (const char*[2]){"str1", "str2" }
It should work.
What is going on under the hood with the previous version? Well, the compiler is seeing the declaration of a pointer (well, casting to a pointer) and the initializer. It is assuming that you want to initialize the pointer with the first element (incompatible pointer, but the cast is explicit... you must know what you want if you forced the cast), and then ignore the remainder. Basically the compiler translates your code to [*]:
#define sup (const char**)"str1"
And that will cause havoc at runtime. It is interesting to note that if you had used a proper variable and then initialized the pointer with it, it would have worked, because while arrays are not pointers (I insist, keep that in mind) arrays do decay into pointers:
const char* tmp[] = { "hi", "there" };
const char** sup = tmp; // fine, tmp decays into &tmp[0]
[*] There's a bit of handwaving there... the compiler translates the code, once inserted at the place of use of the macro by the preprocessor, but the translation is equivalent to what I wrote if you were to edit the macro manually.
I think that doing this kind of preprocessor tricks, especially with arrays, isn't such a good idea. You should instead have a real global string table, like this:
const char const * sup[]={"String 1", "String 2", "String 3"};
in one of the .c files, and put its extern declaration in a header to be included wherever such strings are needed:
extern const char const * sup[];
(the first const is to avoid modifications to each string literal - which is UB -, the second to avoid replacing the pointers stored in sup; if you want to allow this last action, remove the second const)
An alternative approach would be to define sup directly in the header as a static global variable (i.e. with internal linkage); I've seen this done before with integer constants to make sure they are immediately known to the compiler in every translation unit (so it can put them as immediate values in the generated assembly), but I don't think that with string pointers it can give any significant performance boost.
I have a header that is common to all my projects.
#define MAX_STUDENTS 3
char STUDENT[] = { "Manny", "Joe", "Jack" };
The code looks like:
for( int i=0; i<MAX_STUDENTS; i++ )
{ Do Something with STUDENT[i]; }
Claude