make my struct instance 'translate' certain numbers into certain strings - c

I want to use enums and structs (without using object oriented) to make my program 'translate' the following integers into the specified strings:
1 = "small"
2 = "medium"
3 = "large"
I want something like this: (the known part will be an integer, that has to be translated into the matching string)
size.num = 3;
printf("%s", size.size);
and the output would be large.
I tried to do it with two structs:
struct Size {
int num;
char* type;
}size_map[] = {
{ 1, "small" },
{ 2, "medium" },
{ 3, "large" },
};
struct outfit {
char* ...;
char** ....;
struct Size size;
}T_shirt;
T_shirt.size.num = 3;
so when I have only a size in number, I can iterate over the size_map to find the matching size (in words) I need.
But is there a simple way to make it automatically 'know' that without the use of size_map? (an enum would be also a problem- I will have to use it the opposite way..)

Why not use simple switch as below?
char *sizeInWord = NULL;
switch(size) {
case 1:
sizInWord = "small";
break;
case 2:
sizInWord = "medium";
break;
case 3:
sizInWord = "large";
break;
default:
sizInWord = "InvalidSize";
break;
}

Related

How to create an array of arrays in C

Using plain c, if I have the following two arrays:
WCHAR depthUnits[2][10] = { _T("feet"), _T("metres") };
WCHAR speedUnits[3][10] = { _T("ft/min"), _T("m/min"), _T("m/s") };
How can I create an array called say allUnits and fill it with the above so that it will behave like so:
allUnits[0][0] = "feet";
allUnits[0][1] = "metres";
allUnits[1][0] = "ft/min";
allUnits[1][1] = "m/min";
allUnits[1][2] = "m/s";
So basically allUnits is an array and each element of that array is itself an array such that allUnits[0] references depthUnits and allUnits[1] references speedUnits.
Thanks.
Sounds like you are looking for an array of pointers to array of 10 char. Further you want the array to have 2 elements.
That would be like:
WCHAR (*allUnits[2])[10];
allUnits[0] = depthUnits;
allUnits[1] = speedUnits;
An example using char instead of WCHAR:
#include<stdio.h>
int main()
{
char depthUnits[2][10] = {"feet", "metres" };
char speedUnits[3][10] = {"ft/min", "m/min", "m/s" };
char (*allUnits[2])[10];
allUnits[0] = depthUnits;
allUnits[1] = speedUnits;
puts(allUnits[0][0]);
puts(allUnits[0][1]);
puts(allUnits[1][0]);
puts(allUnits[1][1]);
puts(allUnits[1][2]);
// Change feet -> Feet using depthUnits
depthUnits[0][0] = 'F';
// See that it also changed allUnits
puts(allUnits[0][0]);
return 0;
}
Output:
feet
metres
ft/min
m/min
m/s
Feet
WCHAR *allUnits[2][3];
/* ... */
allUnits[0][0] = depthUnits[0];
allUnits[0][1] = depthUnits[1];
allUnits[1][0] = speedUnits[0];
allUnits[1][1] = speedUnits[1];
allUnits[1][2] = speedUnits[2];
This uses array initialization, maybe that's what you wanted:
#include <stdio.h>
int main(void)
{
char* allUnits[][3] = {
{"feet", "meters"},
{"ft/min", "m/min", "m/sec"}
};
printf("0 0: %s\n", allUnits[0][0]);
printf("0 1: %s\n", allUnits[0][1]);
printf("1 0: %s\n", allUnits[1][0]);
printf("1 1: %s\n", allUnits[1][1]);
printf("1 2: %s\n", allUnits[1][2]);
}
You can initialize an array of arrays like you would any other array.
WCHAR* depthUnits[] = { _T("feet"), _T("metres") };
WCHAR* speedUnits[] = { _T("ft/min"), _T("m/min"), _T("m/s") };
WCHAR** allUnits[] = { depthUnits, speedUnits };

how to associate enum with array of string

If  I have array string for courses name like
courseName = {"java","math","physics"}
and enum have constant variables with code for courses like
CSC = 320
How to associate them in C language ?
You need some way to map the enumeration to the array index.
A simple array of structures with a "from" and "to" member solve it:
struct
{
int course; // Course enumeration value
unsigned name; // Name array index
} course_to_name_map[] = {
{ JAVA_101, 0 },
// etc...
};
To find the name loop over the mapping array to find the course, and then use the corresponding index to get the name:
char *get_course_name(int course)
{
static const size_t map_element_count = sizeof course_to_name_map / sizeof course_to_name_map[0];
for (unsigned i = 0; i < map_element_count; ++i)
{
if (course_to_name_map[i].course == course)
{
return course_names[course_to_name_map[i].name];
}
}
// Course was not found
return NULL;
}
Note that this is only one possible solution. It's simple but not very effective.
why not:
enum KEY {
KEY_JAVA = 320,
KEY_MATH = 123,
KEY_PHYSICS = 17,
};
char *course_to_name[] = {
[KEY_JAVA] = "java",
[KEY_MATH] = "math",
{KEY_PHYSIC] = "physics",
};
// usage:
course_to_name[KEY_JAVA];
It works quite well as long as courses codes are relatively small.

iterating through a structure in C

I am working on a project that involves the implementation of the Stack data structure via a singly linked list. More specifically, I am wondering if there is a way to automatically cycle(iterate) through the attributes of a struct which happen to be of different data types -- it would greatly help when reading input, or when adding more attributes, so, I don't have to manually change everything.
Specific struct
typedef struct Student
{
char regNumber[30];
char fName[30];
char lName[30];
char email[50];
int phoneNumber;
short age;
} Student;
For example: attribute[0] would be regNumber, attribute[1] would be fName, attribute[n] would be the n^{th} element
I cannot think of any good way to do this that neither uses undefined behavior, nor very weird constructs. And the fact that you want fields of different type does not make it easier.
If I wanted to write code like this (which I don't) I would probably do something like this.
void *get_attr(struct Student *student, int field)
{
switch(field) {
case 0 : return (void*)&student->regNumber;
case 1 : return (void*)&student->fName;
case 2 : return (void*)&student->lName;
case 3 : return (void*)&student->email;
case 4 : return (void*)&student->phoneNumber;
case 5 : return (void*)&student->age;
}
return NULL;
}
and then you can use it like this:
int main()
{
struct Student s = { "xxx333", "Jonny", "BGood", "my#email.com", 12345, 22 };
printf ("%s\n%s\n%s\n%s\n%d\n%d\n",
(char*)get_attr(&s, 0),
(char*)get_attr(&s, 1),
(char*)get_attr(&s, 2),
(char*)get_attr(&s, 3),
*(int*)get_attr(&s, 4),
*(short*)get_attr(&s, 5)
);
}
Spontaneously, I don't see a good way around those casts. One way, but not necessarily a good way, is to do something like this:
union attr_field {
char *c;
int *i;
short *s;
};
enum attr_type { CHAR, INT, SHORT };
struct attr {
union attr_field attr;
enum attr_type type;
};
struct attr get_attr2(struct Student *student, int field)
{
struct attr ret;
switch(field) {
case 0 : ret.attr.c = student->regNumber; ret.type = CHAR; break;
case 1 : ret.attr.c = student->fName; ret.type = CHAR; break;
case 2 : ret.attr.c = student->lName; ret.type = CHAR; break;
case 3 : ret.attr.c = student->email; ret.type = CHAR; break;
case 4 : ret.attr.i = &student->phoneNumber; ret.type = INT; break;
case 5 : ret.attr.s = &student->age; ret.type = SHORT; break;
}
return ret;
}
void print_attr(struct attr a)
{
switch(a.type) {
case CHAR: printf("%s\n", a.attr.c); break;
case INT: printf("%d\n", *a.attr.i); break;
case SHORT: printf("%d\n", *a.attr.s); break;
}
}
int main()
{
struct Student s = { "xxx333", "Jonny", "BGood", "my#email.com", 12345, 22 };
for(int i=0; i<6; i++) {
struct attr a = get_attr2(&s, i);
print_attr(a);
}
}
Note that I sometimes used struct and sometimes pointer to struct as argument to functions. The choice was not due to any particular reason. It just happened to be that way. You can do it either way, and both have their pros and cons. If performance is an issue, I'd go for pointers. Same thing with the union. I could have chosen a char array and used strncpy instead. And I could have skipped the pointers for int and short. Here my thought was something like that it's more clear if ALL union members are pointers. But you have to make your own decisions about all this. If you go for pointers, it might be wise to use the const qualifier where appropriate.
If you really want to do so, I guess you could do something like this:
void *attribs[6];
attribs[0] = (void*)s.regNumber;
printf("%s", (char*)attribs[0]);
That could be combined with the techniques mentioned above. For instance
struct attr attribs[6];
for(int i=0; i<6; i++)
attribs[i] = get_attr2(&s, i);

Initializing user defined type array in C

I need to write a simple library program using the structure Book:
typedef struct book {
char name[NAME_LENGTH];
char author[AUTHOR_NAME_LENGTH];
char publisher[PUBLISHER_NAME_LENGTH];
char genre[GENRE_LENGTH];
int year;
int num_pages;
int copies;
} Book;
When the program runs the user can take out books from the library/add books/print books etc:
#define BOOK_NUM 50
int main(){
Book books[BOOK_NUM] = { 0 };
int opt = 0, books_counter = 0, *books_counter_p;
books_counter_p = &books_counter;
do {
print_menu();
scanf("%d", &opt);
while (opt < 1 || opt > 5) {
printf("Invalid option was chosen!!!\n");
print_menu();
scanf("%d", &opt);
}
switch (opt) {
case 1:
// Add book to library
add_book(books, books_counter_p);
break;
case 2:
// Take a book from library
break;
case 3:
// Return book
break;
case 4:
// Print all books
break;
case 5:
// Release all memory allocated for library
break;
default:
printf("We should not get here!\n");
}
} while (opt != 5);
return 0;
}
I need some way to store the books so I can change the library as the program runs(I don't know in advance how many books the user will add/remove during the program run).
I don't want to use dynamic memory, can I define an empty array of books this way: Book books[BOOK_NUM] = { 0 }; ?
It seems to work but 0 is an int type and not a Book type, will I run into troubles?
Book books[BOOK_NUM] = { 0 } /* creating & initializing array of structure variable */
when you initialize a struct variable with initializer { }, all of its other members will be initialized to default value & since in your case struct book declaration is global, all members of structure will automatically initialized to 0 for int & floating point,'\0' for char and NULL for pointer variable.
Or you can use memset() like
memset(&books, 0, sizeof(books));
books[BOOK_NUM] = { 0 }; // copiler gives warning
Proper way to do this is:
books[BOOK_NUM] = {{0}}; //set the first field of the first struct to 0 and all the rest to 0 implicitly

Switch Statement in C

I have the following if-statements:
if (strcmp(registerName, "zero"))
return 00000;
else if (strcmp(registerName, "at"))
return 00001;
else if (strcmp(registerName, "v0"))
return 00010;
else if (strcmp(registerName, "v1"))
return 00011;
It's actually really long - about 20 if-statements. Therefore, I would like to use a switch. How would I convert that to switch when each statement has a different condition?
I tried something as the code below, but it does not work:
int registerAddress;
switch(registerAddress) {
case 1 (strcmp(registerName, "zero")):
regsiterAddress = 00000;
break;
case 2 (strcmp(registerName, "at")):
registerAddress = 00001;
break;
}
You can't — switch statements in C only work on primitive types, not on strings. You could use, say, a hash table or a search tree to optimize the matching, but for only 20 options that may not be worth the trouble.
What you could do, to clean up the code, is set up a mapping table:
struct str2Num {
char *str;
int num;
};
const struct str2Num registerMap[] = {
{ "zero", 00000 },
{ "at", 00001 },
{ "v0", 00010 },
{ "v1", 00011 },
{ NULL, 0 } /* end marker */
};
and do your matching like this:
int i;
for (i = 0; registerMap[i].str != NULL; i++) {
if (strcmp(registerName, registerMap[i].str) == 0) {
return registerMap[i].num;
}
}
/* handle no-match case here */
In fact, if you sorted the table alphabetically, you could even use bsearch() for fast matching.
You can only switch on integers, so this will not work.
If all you're doing is converting a string to an int, store the info in an array and look through it.
struct {
const char *name;
int value;
} fooMapping[] = {
{"zero",0},
{"at",1}
....
};
int foo2value(const char *name)
{
size_t i;
for(i = 0; i < sizeof fooMapping/sizeof fooMapping[0]; i++) {
if(strcmp(name, fooMapping[i].name) == 0)
return fooMapping[i].value;
}
return -1;
}
In a switch,
switch(number) {
case 1;
case 2;
case 7;
}
you are basically saying, if number = 1, then case 1. If number = 7, case 7. So what you need to do is assign each text value, in your case "zero""at""v0" and "v1", you would need to put these into an array, and in the switch statement, instead of switch(number) you would switch an integer that would correspond with the index number of whichever text you had. So if array[3] was = "v0", you would assign an integer to the index number (3) and then switch(integer). Hope this helped.
Why not use the ? operator like so:
return
strcmp(registerName, "zero")? 00000:
strcmp(registerName, "at") ? 00001:
strcmp(registerName, "v0") ? 00010:
strcmp(registerName, "v1") ? 00011:
...
Since switch-case only works with numbers or single chars, I would use a tool like GNU's gperf to create a perfect hash and switch on that value (followed by a strcmp() to be certain of an exact match). That ought to give you the desired performance improvement.

Resources