initializing a structure - c

gcc 4.4.4 c89
I will be adding to this list. So I am looking to NULL terminate it. However, I am getting a warning: "Initialization makes integer from pointer without a cast"
Why does the it mention integer when I haven't used in that structure?
I was thinking in this case arrays and pointers where the same?
struct company_prof
{
char name[32];
size_t age;
char position[32];
} employee[] = {
{ "Job Bloggs", 23, "software engineer" },
{ "Lisa Low" , 34, "Telecomms Technician" },
{ "simon smith", 38, "personal assistist" },
{ NULL , -1, NULL }
};
Many thanks for any suggestions,

You are attempting to initialize a character array with NULL. This does not make any sense. For example, you will get the same warning from
char a[100] = { NULL };
and it makes no sense in exactly the same way.
The "integer" that is mentioned in the diagnostic message is the first element of the character array. char is an integer type in C and when you write
char a[100] = { NULL };
it is an attempt to initialize 0-th element of the array a with NULL. On your platform, NULL is declared as something with pointer type, which is why the diagnostic message is saying that you are attempting to make an integer (a[0]) from a pointer (NULL) without a cast.
An even simpler example might look as follows
char c = NULL;
and it will earn you the same diagnostic message for the very same reasons.
May I ask why you are attempting to initialize a char with NULL? What was your intent?
If you don't intend to write into the name and position arrays after initialization, maybe you should use pointers instead of arrays as in
struct company_prof
{
const char *name;
size_t age;
const char *position;
} employee[] = {
{ "Job Bloggs", 23, "software engineer" },
{ "Lisa Low" , 34, "Telecomms Technician" },
{ "simon smith", 38, "personal assistist" },
{ NULL , -1, NULL }
};
Formally, in this case NULL makes perfect sense. But not in case of array.
But less formally the purpose of that { NULL, -1, NULL } record at the end of the array is not clear to me though. Is is a terminating element of some kind? Why don't you just use the exact size of the array instead of creating a terminating element?

In this case, you're statically allocating the space for the strings in the structure. The space for the strings aren't really a separate entity from the structure, it's just a block of space with in it. So you can't assign a pointer to those fields because they can't refer to anything else -- they're just convenient names for referring to the beginning of those chunks within the structure.
The reason you get the bit about the conversion is that characters (e.g., name[0]) are small integers, and it's trying to stuff the NULL pointer into the first character of each string. That first character is too small to hold a pointer, so it's giving you a warning about that.
You probably want to use "\0" instead of NULL here. That's an empty string or null string which is a bit different from a null pointer.
Edit: An alternative that you might consider for getting the size of the list is not using a sentinel value, but defining a constant that calculates it from the sizes in bytes. A typical construction for that looks like:
static const int num_employees = sizeof(employee) / sizeof(employee[0]);

You're using char[32] as your struct members.
You can't convert from NULL to a char[N]. So whats happening is that NULL is being treated as zero. The warning is coming from the compiler deciding to convert the NULL to a 0.
As the initialisation of the char[N] is occuring inside an array initialising it to 0 is valid, it just sets all the values to zero. e.g.
struct wrapper { char i[2] } w = { 0 }
will set the internal i to {0,0}
This means your memory layout looks like this.
|==============name============||--||==========position===========|
0123456789012345678901234567890101230123456789012345678901245678901
Job.Bloggs0000000000000000000000(23)software.engineer00000000000000 // first
... // rest
00000000000000000000000000000000(-1)0000000000000000000000000000000 // sentinel
This may or may not be what you want .. but it does mean that looping over thises values will not be what you may expect: In particular this will not work:
p = employee;
while( p.name != NULL )
{
printf("%s", p.name);
++p;
};
It will just run off printing garbage from memory and eventually segfault.
You might try something like this instead
p = employee;
while( p.name[0] != 0 )
{
printf("%s", p.name);
++p;
};
But that wont work for names like "\0foo" (Which you might consider invalid anyway...)

Related

what is the value of an empty cell of a 2d array?

i am kind of new in c and i am trying to figure things out.
my question is, what i need to have in the place of '/0' , in order for it to skip
the "empty" cells?
i know i could do it the easy way and just have have all the .anoxi values in the condition, but i was just curious.
i have tried putting "", which gives me all the names (doesn't skip any of them) , '' which gives me "[Error] empty character constant" and
null, which gives me "[Error] 'null' was not declared in this scope"
struct t {
char anoxi[10];
char name[10];
char gramma [2];
}
int main() {
struct t array[5][12];
int r;
strcpy(array[4][1].anoxi, "+-1%");
strcpy(array[4][2].anoxi, "+-2%");
strcpy(array[4][5].anoxi, "+-0.5%");
strcpy(array[4][6].anoxi, "+-0.25%");
strcpy(array[4][7].anoxi, "+-1%");
strcpy(array[4][8].anoxi, "+-0.05%");
strcpy(array[0][0].gramma, "M");
strcpy(array[0][1].gramma, "K");
strcpy(array[0][2].gramma, "N");
strcpy(array[0][3].gramma, "O");
strcpy(array[0][4].gramma, "I");
strcpy(array[0][5].gramma, "R");
strcpy(array[0][6].gramma, "L");
strcpy(array[0][7].gramma, "V");
strcpy(array[0][8].gramma, "G");
strcpy(array[0][9].gramma, "A");
strcpy(array[0][10].gramma, "X");
strcpy(array[0][11].gramma, "S");
strcpy(array[1][0].name, "Black");
strcpy(array[1][1].name, "Brown");
strcpy(array[1][2].name, "Red");
strcpy(array[1][3].name, "Orange");
strcpy(array[1][4].name, "Yellow");
strcpy(array[1][5].name, "Green");
strcpy(array[1][6].name, "Blue");
strcpy(array[1][7].name, "Purple");
strcpy(array[1][8].name, "Grey");
strcpy(array[1][9].name, "White");
strcpy(array[1][10].name, "Gold");
strcpy(array[1][11].name, "Silver");
for (r=0; r<12; r++) {
if (array[4][r].anoxi!= '\0') {
printf("%s = %s\n",array[0][r].gramma, array[1][r].name);
}
}
return(0);
}
C has no concept of "empty". Variables in C represent actual physical memory locations, and they contain whatever that memory contains, which is either what they were initialized to contain, or some random value if they were never initialized (note that statics are initialized by default).
This doesn't prevent you from choosing to interpret one of the possible values of a variable as "empty", but that would be your choice, and entirely up to you. You would then have to initialize your variable/array with that value and check for it. Character variables often use the value '\0' for this, which should work for you--just make sure you take care of the difference between single characters and arrays: for example, ... if...gramma[0] == '\0'' ...
There is what you might consider an exception to this: one of the values pointer variables are allowed to take is a value called NULL, which is guaranteed not to point to anything. This is often used to initialize pointer variables but you still have to do the initialization and checking yourself.
Now that you got, it you may want to read my Structs (C), which offers a compact example, that might come in handy*.
First of all, allow me to question the validity of this code. Consider this equivalent example I made:
#include <stdio.h>
struct t {
char anoxi[10];
char name[10];
char gramma [2];
};
int main(void) {
struct t my_array[5][1];
if(my_array[0][0].anoxi != '\0')
printf("%s\n", my_array[0][0].anoxi);
return 0;
}
It will print, in my machine:
gsamaras#gsamaras-A15:~$ gcc -Wall px.c
gsamaras#gsamaras-A15:~$ ./a.out
����
Why?
Because, the memory the array holds is not initialized to anything, so its value is undefined, which invokes UNDEFINED BEHAVIOR!
We could fix this, by initializing every string, like this:
struct t my_array[5][1];
my_array[0][0].anoxi[0] = '\0';
if(my_array[0][0].anoxi[0] != '\0')
printf("edw %s\n", my_array[0][0].anoxi);
Or, as Mike suggested, you could use memset(), like this:
memset (my_array, 0, sizeof (my_array);
Usually we set the value of a variable to a predefined value, which for us, humans tells that this cell/string/whatever is empty.
c does not know that, unless we tell our program to keep an eye out of empty "things". We have to inform our program what is an empty "thing", especially how to identify it!
Here, you have a string and you check array[4][r].anoxi!= '\0', which is always true because the left-hand side is an array, which decays to pointer in this expression, as M.M said.
*I am not writing it here, since the answer is already too long
First of all, initialize the array to blank:
struct t array[5][12] = { 0 };
This means that any members you have not yet assigned contents to will have value 0 (converted to the type of that member). This is so that later on you can see if the member has been assigned something else by checking to see if it is still 0 or not.
Then you can check:
if ( array[4][r].anoxi[0] ) {
// ^^^^
Note that you must check anoxi[0] which is a char object. Checking anoxi, which is an array object, merely checks that the array exists in memory (which it tautologically does), not whether the contents of the array are some particular value.
NB. The != '\0' is redundant, I think it is clearer to omit it but you could use it if you want.

Set to NULL an array of structures on C

I wrote this C code to set to NULLan array of structures on C. Here it goes,
struct Worker
{
char name[50];
unsigned int salary;
};
int main()
{
int n;
struct Worker number[50];
for(n=0;n<50;n++)
{
number[n].name=NULL;
}
}
Compiler is giving this error: main.c:65:26: error: assignment to expression with array type.
In your struct, the field name is an array of 50 chars. Arrays are not pointers and you cannot change them; you can only change their contents.
Therefore, if you want to initialise the name field, you could make the name an empty string by making the first char a null terminator:
*number[n].name = '\0';
Note that you name will always hold 50 chars, but you are using only the chars up to the first terminator.
Because your array isn't a pointer, the following doesn't work, either:
number[0].name = "Tom";
You must fill the contents to the array, probably with strcpy from <string.h>:
strcpy(number[0].name, "Tom");
If you want to test whether a string is empty, test whether the first character is the null terminator:
if (*number[n].name == '\0') ... // string is empty
You can also initialise the array explicitly:
struct Worker number[50] = {
{"Alice", 3200},
{"Bob", 2700},
{"Charlotte", 3000}
};
This will give you 3 workers with names and a salary and 47 workers with empty strings as names and zero salary. You can, of course, fill them in later or reset the first three.
If you initialise the array with:
struct Worker number[50] = {{""}};
you'll get an array of all empty-named, all zero-salary workers that you can fill.
(That said, if your name member were a pointer like char *name;, you could say number[n].name = "Tom"; or number[n].name = NULL;. But that would mean that you'd have to handle all the memory that the pointer points to yourself, which is not easy.
The advantage of your array of 50 chars is that each worker has already 50 chars that you can use for their name. (Well, up to 49 plus a null terminator.) The disadvantage is that you waste memory on most names, because they are shorter and you are not flexible enough if you need a name that is longer. But before you have learned more about pointers and memory management, the array is the way to go. Just make sure that you don't exceed its limits.)
You can't set an array of characters to null like that; there's no point. Literally there's no pointer. Arrays aren't pointers; their memory is already allocated on the stack, so I can't redirect the array how you're trying to do it. You're trying to manually set the array's address.
If however your struct looked like this
struct Worker{
char *name;
unsigned int salary;
};
Then you would be able to set each worker's name to null.
If your interest is to zero out an array of characters, you could do this
int n, j;
struct Worker number[50];
for(n=0;n<50;n++)
for(j=0;j<50;j++)
number[n].name[j]=0;
But I have no idea why you'd want to do that. I'm assuming you may use strcpy to assign values to each worker's name, which takes care of the null terminating character for you.
Your name is array of 50 chars, if you want to clear it instead of:
num[n].name=NULL;
do:
memset(num[n].name, 0, 50);
You can set to NULL pointer not the array.
You can see if array is empty by examining each of arrays elem, like:
int i;
int non_empty = 0
for( i=0; i < sizeof(array)/sizeof(array[0]); ++i)
{
if(array[i] != 0)
{
non_empty = 1;
break;
}
}

2D array using strings

I'm stuck on some homework which isn't graded (its meant for practice).
I have to create a function called find_name that takes 2 arguments. The first argument is a 2D array of names (strings), and the second is a character string which is used to find the name in the 2D array, the function must return 1 if found else 0.
When i call the function (which is empty right now), I get this warning: passing argument 1 of 'find_name' from incompatible pointer type
Here is the important bits.
In Main
char strNameList[][2] = { { "Luca","Daniel"} ,{"Vivan","Desmond"},{"Abdul","Justin"}, {"Nina","Marlene"},{"Donny","Kathlene"} };
char strFindName[] = "\0";
printf("Please enter a name to look for: ");
gets(strFindName);
nSearch = find_name(strNameList, strFindName);
The Function
int find_name(char strNameList[][2], char strLookUp[])
I'm new to C (I'm a student), and I'm completely confused about strings (string arrays etc).
I'm assuming you want a 2D array of char pointers. Your declaration of strNameList is incorrect in both locations in your program. You have:
char strNameList[][2] = { { "Luca","Daniel"} ,{"Vivan","Desmond"},{"Abdul","Justin"}, {"Nina","Marlene"},{"Donny","Kathlene"} };
But char[][N] is declaring a 2D array of chars, not char* Therefore you're being warned by the compiler you're assigning a raft of pointer values to items of type char
Change both your declarations (your variable and your function parameter) to:
const char *strNameList[][2]
which declares an array of unknown length of arrays of two char*, which now matches your initialization lists. Also, the const is added because (a) I'm assuming you are not planning on modify that name list in your function, and (b) writable string literal declarations assigned to char* via initializer is undefined behavior in C, and officially deprecated in C++, so you should not be using it regardless. Likewise, your lookup-name is probably not being modified either, so also declare it const.
Result:
const char * strNameList[][2] = {
{"Luca","Daniel"} ,
{"Vivan","Desmond"},
{"Abdul","Justin"},
{"Nina","Marlene"},
{"Donny","Kathlene"}
};
and in your function:
int find_name(const char * strNameList[][2], const char strLookUp[])
Last but certainly not least, unless you have a crystal ball your find_name() function has no way of knowing with the given information how many names are in the name list being passed. I'd rather you see this now rather than wonder what happened later. you need to either (a) terminate the list with a token-value that find_name() knows about, or (b) pass the number of names in the list to find_name(). To each their own, but I prefer the latter of these:
int find_name(const char * strNameList[][2], size_t nNameListSize, const char strLookUp[])
and invoke it on your caller side by:
find_name(strNameList, sizeof(strNameList)/sizeof(strNameList[0]), strFindName)
Do it this way:
#define STOPPER_NAMELIST NULL
char * strNameList[][2] = {
{ "Luca","Daniel"},
{"Vivan","Desmond"},
{"Abdul","Justin"},
{"Nina","Marlene"},
{"Donny","Kathlene"}
{STOPPER_NAMELIST, STOPPER_NAMELIST}
};
size_t sizeNameList(const char * strNameList[][2])
{
size_t size = 0;
while ((strNameList[size][0] != STOPPER_NAMELIST) &&
(strNameList[size][0] != STOPPER_NAMELIST))
++ size;
return size;
}
int find_name(char * strNameList[][2], char strLookUp[])
{
size_t size = sizeNameList(strNameList);
...
}
...
nSearch = find_name(strNameList, strFindName);
This approach uses an open array ([]) of char * arrays with 2 entries.
Update:
You could add a stopper element to the array carring the names, then there is no need to pass around the array's size along with array itself, as the size could alway be determined by scanning the array members until the stopper is found.
Your function find_name() is looking for a 2-D array of characters ie:
char arr[][2] = { { 'a', 'b'}, ...
if you want to make them strings you need:
char *arr[][2] = { {"John", "Smith"}, ...
Then in the function parameter list you need:
void find_name(char *something[][2])
{
printf("first name: %s, second name: %s\n", something[0][0], something[0][1]);
And in your main() function call it just by:
find_name(arr);

parsing a .conf file in c

Perhaps this bridge likely has been crossed many times and in many ways... reading a simple text .conf file and acting upon its entries.
In my case, the file format is simple.. a series of tokens and assignments, as in:
token_name_1 value
with a tab character as the field delimiter and a unix line-ending for each the record.
The .conf file directly alters certain program configurations, all of them stored in a single structure. Variables of types Integer, float, char[], and *char are represented in the structure.
A quick but boring approach involves, for example:
if (strcasecmp(token,"token_name_1")==0)
token_name_1=value;
But I determined that it would be sweet to do the deed in a nice tight loop. In C.
So it seemed best to construct an array which provides pointers to each of the structure variables I wish to expose; another that provides the name of the variable; and a third which describes the stored data type and a desired default value.
These look like this:
const char* allowed_tokens[] =
{
"loglevel",
"debugecho",
"errorSqlDisable",
"ClearErrorDbOnExit",
"\0" // terminates list
}
int *varpointers[] =
{
&appinfo.nLogLevel,
&appinfo.debugEcho,
&appinfo.OWFSLogLevel,
&appinfo.OWFSLogEchoToDisplay,
0 // terminates list
};
char *varDatatypes_defaults[] =
{
"I|6", // for LOG_INFO
"B|false",
"I|0",
"B|true",
"\0" // terminates list
};
The loop looks like this (pseudocode):
row=0;
while (read a line of the .conf file into cLine)
{
get the token_name and value from cLine
check if allowed_tokens[row]==0 and if true, exit the loop
// example cLine= "debugecho false"
find match to "debugecho" in allowed_tokens. This provides an offset into varpointers and varDatatypes.
get the default data type and default value tokens from varDattypes_defaults[row]
Do the assignment. For example, if the data type=="I":
*varpointers[row]=atoi(value);
++row;
}
This technique works fine, but there are two problems.
It would be preferable to combine the three arrays into a single array. Is there a best practice here?
The array of pointers (varpointers[]) is defined as *int. I did so as I want it to hold pointers. However if the variable pointed to is not an integer data type, warning: initialization from incompatible pointer type is triggered. Of course, char * and int * cannot be mixed... so how otherwise could this be done such that a single pointer array is used?
I realize I can do all this in c++. This luxury is not an option at this point.
You can combine them into one array by structs, e.g.
typedef struct { char *token; void *var; char *defaults; } the_type;
the_type the_data[] = { { "loglevel", (void*)&appinfo.nLogLevel, "I|6" },
{ "debugecho", (void*)&appinfo.debugEcho, "B|false" },
...
};
The generic pointer type is void *. Your code has to ensure you use the correct type when actually writing to the variable being pointed to, e.g. *(int*)the_data[0] = 42;.
I would use an enumeration to specify the types so you don't have to parse a string. The values could be stored in an union.
typedef enum {
BOOLEAN,
INTEGER,
} type_t;
typedef union value {
bool boolean;
int integer;
} value_t;
typedef struct token {
char *name;
type_t type;
value_t value;
} token_t;
Now you can define your defaults like so:
token_t data[] = {
{ "loglevel", INTEGER, { 6 } },
{ "debugecho", BOOLEAN, { false } },
{ "errorSqlDisable", INTEGER, { 0 } },
{ "ClearErrorDbOnExit", BOOLEAN, { true } },
{ 0 }
};
This will get pretty cumbersome if the number of configuration keys gets large. You might want to think about storing the configuration in a hash table or a tree.
Here is a short example that seems to accomplish what you want.
If we are talking about the same data type, use double pointers (you get an array of arrays)
Declare a struct holding your pointers, then use a pointer to your struct to work on.
For declaring a general pointer, you can use void instead of int. But then every time you have to cast the pointer to use it properly.

Why can't I initialize an array of cstrings like this?

char sXSongBuffer[20][30];
sXSongBuffer = {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
Why does this return the error expected expression before ‘{’ token? The reason I want to initialize my array like this is so that I can change its contents like this later:
sXSongBuffer = {"New Song", "More Music From Me"};
You can't assign to arrays in C. C allows initializing arrays with values that are compile-time constants. If you want to change the values later, or set values that are not compile-time constants, you must assign to a particular index of the array manually.
So, your assignment to sXSongBuffer is disallowed in C. Moreover, since sXSongBuffer[0] to sXSongBuffer[19] are arrays too, you can't even say: sXSongBuffer[0] = "New Song";
Depending upon what you want, this may work for you:
/* declare sXSongBuffer as an array of pointers */
char *sXSongBuffer[30] = {
"Thriller",
"Don't Stop Till You Get Enough",
"Billy Jean",
NULL /* set the rest of the elements to NULL */
};
size_t i;
/* and then later in your code */
sXSongBuffer[0] = "New Song";
sXSongBuffer[1] = "More Music From Me";
for (i=2; i < sizeof sXSongBuffer; ++i)
sXSongBuffer[i] = NULL;
But the above only works if you know all your strings at compile time. If not, you will have to decide if you want "big-enough" arrays, or if you need dynamic memory for the strings and/or the number of strings. In both cases, you will want to use an equivalent of strcpy() to copy your strings.
Edit: To respond to the comment:
You're declaring an array of 30 char pointers with the first three elements pointing to buffers the size of the strings, ie the buff pointed to by sXSongBuffer[0] won't hold any string larger than "Thriller" and if he does sXSongBuffer[0] = malloc(32); He'll get a minor memory leek. Also, he'll have to malloc memory for each of the rest of the slots in the array. He should either use 2d char arrays like in the OP + a designated init, or malloc each buffer at run time and copy in the values. He'll also need to remember to free any memory he mallocs.
sXSongBuffer in char *sXSongBuffer[30]; is an array of size 30, with each element being a char *, a pointer to char. When I do:
char *sXSongBuffer[30];
each of those 30 pointers is uninitialized. When I do:
char *sXSongBuffer[30] = { "Thriller", ... };
I set the pointers to different read-only locations. There is nothing preventing me to then "re-point" the pointers somewhere else. It is as if I had:
char *data = "Hello";
printf("%s\n", data);
data = "Hello, world";
printf("%s\n", data);
In the above snippet, I assign data to "Hello" first, and then change it to point to a longer string later. The code I had above in my answer did nothing more than reassign sXSongBuffer[i] to something else later, and since sXSongBuffer[i] is a pointer, the assignment is OK. In particular, sXSongBuffer[0] is a char *, and can point to any valid location that has a char in it.
As I said later in my answer, if the strings aren't known at compile-time, this scheme doesn't work, and one has to either use arrays with "big enough" sizes, or dynamically allocate memory that's big enough.
C does not have general-purpose array literals. The {} list syntax only works when initializing, i.e. when assigning the value in the same statement that declares the variable.
You cannot just write
char sXSongBuffer[20][30];
sXSongBuffer = {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
You must either initialize array at once (but it will containt only 3 items):
char * sXSongBuffer[]= {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
Or either use stnrcpy on every item:
char sXSongBuffer[20][30];
strncpy(sXSongBuffer[0],"Thriller",29);
strncpy(sXSongBuffer[1],"Don't Stop Till You Get Enough",29);
strncpy(sXSongBuffer[2],"Billy Jean",29);
Take a look at Designated Initializers.
#include <stdio.h>
int main (void) {
char a[6][6] = { [2] = "foo", [4] = "bar" };
for (int i=0; i<6; ++i)
printf("%d == %s\n", i, a[i]);
return 0;
}
This is a c99 feature. Compile with:
gcc -W -std=c99 2dInit.c -o 2dInit
This outputs:
0 ==
1 ==
2 == foo
3 ==
4 == bar
5 ==
In your case you want to do:
char sXSongBuffer[20][30] = {
[0] = "Thriller",
[1] = "Don't Stop Till You Get Enough",
[2] = "Billy Jean"
};

Resources