iterating through a structure in C - 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);

Related

C: Reading property of struct pointer causes property to change?

I have a program that stores two different structs (tri & quad) in an array of a special struct defined below:
struct inst_ptr
{
void* p;
unsigned type;
};
The structs tri & quad have a superbase property
The following code displays how the structs are defined:
struct inst
{
char name[50];
int id;
};
struct tri
{
struct inst superbase;
};
struct quad
{
struct inst superbase;
};
Function for creating new instances (all located outside of main):
struct inst_ptr insts[100]; //instance container
int cinsts=0; //instance count
int inst_new(int type)
{
switch(type)
{
case TYPE_TRI:
{
struct tri i;
insts[cinsts].p=&i;
}
case TYPE_QUAD:
{
struct quad i;
insts[cinsts].p=&i;
}
}
insts[cinsts].type=type;
cinsts++;
return cinsts-1; //return index of instance in array
}
In my main function I create a sample tri, writing and reading it's values:
struct tri* t1=(struct tri*)insts[inst_new(TYPE_TRI)].p;
strcpy(t1->superbase.name, "tri1");
t1->superbase.id=70;
printf("id: %d\n", t1->superbase.id);
printf("id: %d\n", t1->superbase.id); <- problem occurs here and onward
Printing the id the first time works, printing 70 in the output. However, the second time it prints a long random integer.
Output:
id: 69
id: 1973802146
id: 1973802146
As you shown above, after the second read it keeps printing 1973802146. I'm not really sure what I'm doing wrong but I have a feeling I'm missing an extremely basic concept relating to pointers and structs.
You are saving the address of local variables in the array. That's bad because those variables doesn't exist once the function returns. You should use dynamic allocation instead.
Besides that you switch need break statements. And perhaps also a default case.
Like:
int inst_new(int type)
{
switch(type)
{
case TYPE_TRI:
{
struct tri *p = malloc(sizeof *p);
if (p == NULL) exit(1);
insts[cinsts].p=p;
break;
}
case TYPE_QUAD:
{
struct quad *p = malloc(sizeof *p);
if (p == NULL) exit(1);
insts[cinsts].p=p;
break;
}
default:
{
// Unknown type.. add error handling here
exit(1);
}
}
insts[cinsts].type=type;
cinsts++;
return cinsts-1; //return index of instance in array
}
As an alternative you can do:
int inst_new(int type)
{
insts[cinsts].p = NULL
switch(type)
{
case TYPE_TRI:
{
insts[cinsts].p=malloc(sizeof(struct tri));
break;
}
case TYPE_QUAD:
{
insts[cinsts].p=malloc(sizeof(struct quad));
break;
}
}
if (insts[cinsts].p == NULL) exit(1); // malloc failed
insts[cinsts].type=type;
cinsts++;
return cinsts-1; //return index of instance in array
}

Call c functions from fortran(type enum)

Recently,I call c function from fortran with iso_c_binding.But I found some c code.such as:
typedef enum
{
STRUMPACK_FLOAT,
STRUMPACK_DOUBLE,
STRUMPACK_FLOATCOMPLEX
} STRUMPACK_PRECISION;
typedef enum
{
STRUMPACK_MT,
STRUMPACK_MPI_DIST
} STRUMPACK_INTERFACE;
typedef struct
{
int solver;
STRUMPACK_PRECISION precision1;
STRUMPACK_INTERFACE interface1;
}STRUMPACK_SparseSolver;
int STRUMPACK_init(STRUMPACK_SparseSolver * S,
STRUMPACK_PRECISION precision1, STRUMPACK_INTERFACE interface1, int verbose)
{
S->precision1 = precision1;
S->interface1 = interface1;
switch (interface1)
{
case STRUMPACK_MT:
{
switch (precision1)
{
case STRUMPACK_FLOAT:
printf("srtumpack_float %d\n", verbose);
break;
case STRUMPACK_DOUBLE:
printf("srtumpack_double %d\n", verbose);
break;
default:
printf("ERROR: wrong precision!");
}
}
break;
default:
printf("ERROR: wrong interface!");
}
return 0;
}
I don't know that call this c subfunction with Fortran.because of this structs:
typedef enum
{
STRUMPACK_MT,
STRUMPACK_MPI_DIST
} STRUMPACK_INTERFACE;
I don't know how to solve this problem . I will appreciate any contribution, suggestion about the problem.
Thanks
That isn't a structure, but an enum (enumeration) supported by ISO binding entity ENUM.
In case your binding miss enumerations the following is a workaround.
Enums in C are more or less constant integers which values are assigned by compiler in generally increasing way. You can also force values for each enum member using assignment as in:
typedef enum
{
STRUMPACK_FLOAT = 0,
STRUMPACK_DOUBLE,
STRUMPACK_FLOATCOMPLEX = 100
} STRUMPACK_PRECISION
In this case i.e. we impose to STRUMPACK_FLOATCOMPLEX the value 100. We made the same with the first member, in any case the first member have value 0 by default. The second member STRUMPACK_DOUBLE will get the value 1 as progressive increment from previous member.
Anyway you can get better info on how enum works googling on the net.
In your case the easier way to solve the problem is to convert enums in definitions and using int's as type like in:
#define STRUMPACK_FLOAT 0
#define STRUMPACK_DOUBLE 1
#define STRUMPACK_FLOATCOMPLEX 2
typedef int STRUMPACK_PRECISION;
#define STRUMPACK_MT 0
#define STRUMPACK_MPI_DIST 1
typedef int STRUMPACK_INTERFACE;
typedef struct
{
int solver;
STRUMPACK_PRECISION precision1;
STRUMPACK_INTERFACE interface1;
} STRUMPACK_SparseSolver;
int STRUMPACK_init(STRUMPACK_SparseSolver * S, STRUMPACK_PRECISION precision1,
STRUMPACK_INTERFACE interface1, int verbose)
{
S->precision1 = precision1;
S->interface1 = interface1;
switch (interface1)
{
case STRUMPACK_MT:
{
switch (precision1)
{
case STRUMPACK_FLOAT:
printf("srtumpack_float %d\n", verbose);
break;
case STRUMPACK_DOUBLE:
printf("srtumpack_double %d\n", verbose);
break;
default:
printf("ERROR: wrong precision!");
}
}
break;
default:
printf("ERROR: wrong interface!");
}
return 0;
}

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

Refactoring: Very similar switch cases

I have several struct declared which contain different data. I also have an enum that corresponds to those structures. There are several places in my code where I need to access information about the structures and I'm doing it via the enum. This results in few switch statements that return this information.
I've enclosed those switch statements in their own functions in order to re-use wherever possible. This resulted in three functions that look very similar.
Example psuedo-code:
#include <stdio.h>
typedef struct
{
int varA;
char varB;
} A;
typedef struct
{
int varA;
int varB;
int varC;
} B;
typedef struct
{
int varA;
short varB;
} C;
typedef enum { structA, structB, structC } STRUCT_ENUM;
int returnSize(STRUCT_ENUM structType)
{
int retVal = 0;
switch(structType)
{
case structA:
retVal = sizeof(A);
break;
case structB:
retVal = sizeof(B);
break;
case structC:
retVal = sizeof(C);
break;
default:
break;
}
return retVal;
}
void printStructName(STRUCT_ENUM structType)
{
switch(structType)
{
case structA:
printf("Struct: A\r\n");
break;
case structB:
printf("Struct: B\r\n");
break;
case structC:
printf("Struct: C\r\n");
break;
default:
break;
}
}
void createDataString(STRUCT_ENUM structType, char* output, unsigned char* input)
{
switch(structType)
{
case structA:
{
A a = *(A*)input;
sprintf(output, "data: %d, %d", a.varA, a.varB);
break;
}
case structB:
{
B b = *(B*)input;
sprintf(output, "data: %d, %d, %d", b.varA, b.varB, b.varC);
break;
}
case structC:
{
C c = *(C*)input;
sprintf(output, "data: %d, %d", c.varA, c.varB);
break;
}
default:
break;
}
}
int main(void) {
char foobar[50];
printf("Return size: %d\r\n", returnSize(structA));
printStructName(structB);
C c = { 10, 20 };
createDataString(structC, foobar, (unsigned char*) &c);
printf("Data string: %s\r\n", foobar);
return 0;
}
Those free functions basically contain the same switch with different code placed in the cases. With this setup, adding new struct and enum value results in three places in the code that needs changing.
The question is: is there a way to refactor this into something more maintainable? Additional constraint is that the code is written in C.
EDIT: online example: http://ideone.com/xhXmXu
You can always use static arrays and use STRUCT_ENUM as the index. Given the nature of your functions, I don't really know if you would consider it more maintainable, but it's an alternative I usually prefer, examples for names and sizes:
typedef enum { structA, structB, structC, STRUCT_ENUM_MAX } STRUCT_ENUM;
char *struct_name[STRUCT_ENUM_MAX] = {[structA] = "Struct A", [structB] = "Struct B", [structC] = "Struct C"};
size_t struct_size[STRUCT_ENUM_MAX] = {[structA] = sizeof(A), [structB] = sizeof(B), [structC] = sizeof(C)};
for printing content you can keep a similar array of functions receiving a void * that will print the value of this argument.
Edit:
Added designated initializers as per Jen Gustedt's comment.
You can make it into a single function and a single switch, with an additional parameter. Like so
int enumInfo(STRUCT_ENUM structType, int type) // 1 = returnSize 2 = printStructName
{
int retVal = 0;
switch(structType)
{
case structA:
If ( type == 1 ) { retVal = sizeof(A); }
else { printf("Struct: A"); }
break;
case structB:
If ( type == 1 ) { retVal = sizeof(B); }
else { printf("Struct: B"); }
break;
case structC:
If ( type == 1 ) { retVal = sizeof(C); }
else { printf("Struct: C"); }
break;
default:
break;
}
return retVal;
}

assigning struct in switch not working

In my program, I'm trying to create a new struct based od switch statement, but when I do so, the compiler returns an error:
Syntax error before '{' token on the row with the position assignment
I'm using dev-c++ 4.9.9.2 as an IDE (i think it's using MinGW as compiler). IT's for my brother's programming assignment I'm helping him with, I haven't seen C in a few years, so I'm rusty (and I wasn't a champion before either).
Here's simplified code:
typedef enum{TOP_RIGHT = 0,TOP_LEFT,BOTTOM_RIGHT,BOTTOM_LEFT} diagonal_t;
typedef struct
{
int row;
int column;
} position_t;
...
void checkDiagonal(diagonal_t diagonal_to_check)
{
...
position_t position;
switch(diagonal_to_check)
{
case TOP_RIGHT:
position = {0,0}; //here's the error, but I don't know how to repair it.
//how to create a new struct here without disrupting the
//switch?
break;
case TOP_LEFT:
position = {0,0};
break;
....
}
}
The var_of_type_struct = { init_value } syntax works only in definitions; it does not work in assignments.
Three common ways to deal with is are
Defining a function that initializes your struct
Defining a function that sets fields to parameters that you pass, and
Assigning the individual fields of your struct.
Approach 1:
void init_pos(position_t *p) {
p->row = 0;
p->column = 0;
}
...
case TOP_LEFT:
init_pos(&position);
break;
Approach 2:
void set_pos(position_t *p, int r, int c) {
p->row = r;
p->column = c;
}
...
case TOP_LEFT:
set_pos(&position, 0, 0);
break;
Approach 3:
case TOP_LEFT:
position.row = 0;
position.column = 0;
break;
You can't do that: assignment and initialization are not the same thing. You are attempting to use initializer syntax in an assignment. You'll have to set both fields manually:
case TOP_RIGHT:
position.row = 0;
position.column = 0;
/* ... */
You need to cast to struct type, like this:
position = (position_t){0, 0};

Resources