We were given an assignment in our C programming class to modify a program to make it more object oriented. Part of this was to fix the toString method. The directions were:
Modify the Student module to make it more object-oriented.
* Each Student object should have a function pointer that points to an
appropriate function for producing a string representation of the object.
* Provide a default toString method to each object when it is created.
The studentToString method should no longer be part of the Student interface
(as defined in Student.h)
However, we aren't really sure what this means and would like to know if we are on the right track with what we are suppose to do. Here is the code from the Student.c file:
#include <stdio.h>
#include <stdlib.h>
#include "Student.h"
#include "String.h"
static void error(char *s) {
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, s);
exit(1);
}
extern Student newStudent(char *name, int age, char *major) {
Student s;
if (!(s = (Student) malloc(sizeof(*s))))
error("out of memory");
s->name = name;
s->age = age;
s->major = major;
return s;
}
extern char *studentToString(Student s) {
const int size = 3;
char age[size + 1];
snprintf(age, size, "%d", s->age);
char *line = newString();
line = catString(line, "<");
line = catString(line, s->name);
line = catString(line, " ");
line = catString(line, age);
line = catString(line, " ");
line = catString(line, s->major);
line = catString(line, ">");
return line;
}
We known that the *studentToString method will be replaced by a *toString method and we think that the *toString method will have the same contents as the *studentToString method. But we don't understand how that makes it more object-oriented.
We have also determined, from the directions, that when we create a new Student object, we should have a pointer in the newStudent method that points to the new toString method.
We're not looking for anyone to do the program for us. We just want to understand what we are suppose to do as our professor has been out of town for the week. Any help would be greatly appreciated.
It sounds like he's asking you to take the Student structure and add a pointer to a function inside the structure itself so that when you have a valid pointer to a Student structure you can do something like
`myStudent->toString();`
and have it return the same value as
`studentToString(myStudent)`
would have before. This makes it more object oriented because of the fact that you're calling a toString method on an effective "instance" (for lack of better terms) of a Student structure and returning parameters related to that "instance." Just as you would in some sort of object-based programming language.
My guess is that you need to add a member to the Student struct, the type of that member would be a function pointer.
Then define that function.
Then add a parameter taking a function pointer to newStudent.
Then set that newly created member to the value of the parameter.
(this feels like an extremely abstract way to learn OO, but that's just my opinion)
Looks like your prof set you this problem so that you get an understanding of polymorphism. In this example, the idea is that every object in your system should have its own way of rendering itself as a string but you don't want to know the details; you just want to be able to call toString on any object.
E.g.
banana->toString()
apple->toString()
Related
I'm trying to code build and run the program but this error message came out :
'MAX_BOOK_NAME' undeclared (first use in this function)
Another issue I'm facing is for the program to require and implement the following:
You must use the following struct to store information about one book:
struct book {
char *title;
char *author;
char *subject;
};
You must use the following struct to store information about the library collection:
struct library {
struct book collection;
int num_books;
struct library *next;
};
The only function you are specifically required to write is a copy function, which copies the contents of one book into another book. Here is the prototype for that function:
void copybook(struct book* dest, struct book* source);
Though the rest of the function prototypes will not be given, it is expected that you follow good programming design and create several functions with well-specified tasks related to the solution of this problem. Make sure to pay very careful attention to parameter passing. In particular, in each function that needs a variable of type struct library, make sure to pass the variable by reference, as follows:
void addBook(struct library* thislib);
This will ensure that any change made to the library in the function is reflected in main. Inside a function like this one, remember to access either component, use the following expressions:
thislib->collection
thislib->num_books
Whenever you add a book to the collection, make sure you add it to the end of the collection. Do not forget to update the variable num_books in the struct library variable.
Whenever you delete a book from the collection, make sure you copy the book in the second-last slot into the vacated spot. For example, if the book to be deleted is in position 3 and the number of books in the collection before deleting is 7, then the book in position 6 (the second-last filled position) should be moved to the book in index 3. Subsequently, the number of books in the library should be updated to hold only 6.
Below is what I had done so far,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void viewBooks()
{
int found = 0;
char bookName[MAX_BOOK_NAME] = {0};
{
s_BooksInfo_addBookInfoInDataBase = {0};
FILE *fp = NULL;
int status = 0;
unsigned int countBook = 1;
headMessage("VIEW BOOKS DETAILS");
fp = fopen(FILE_NAME,"rb");
if(fp == NULL)
{
printf("File is not opened\n");
exit(1);
}
if (fseek(fp,FILE_HEADER_SIZE,SEEK_SET) != 0)
{
fclose(fp);
printf("Facing issue while reading file\n");
exit(1);
}
while (fread (&addBookInfoInDataBase, sizeof(addBookInfoInDataBase), 1, fp))
{
printf("\n\t\t\tBook Count = %d\n\n",countBook);
printf("\t\t\tBook id = %u",addBookInfoInDataBase.books_id);
printf("\n\t\t\tBook name = %s",addBookInfoInDataBase.bookName);
printf("\t\t\tBook authorName = %s",addBookInfoInDataBase.authorName);
printf("\t\t\tBook issue date(day/month/year) = (%d/%d/%d)",addBookInfoInDataBase.bookIssueDate.dd,
addBookInfoInDataBase.bookIssueDate.mm, addBookInfoInDataBase.bookIssueDate.yyyy);
found = 1;
++countBook;
}
fclose(fp);
if(!found)
{
printf("\n\t\t\tNo Record");
}
printf("\n\n\t\t\tPress any key to go to main menu.....");
fflush(stdin);
getchar();
}
return 0;
}
In the current .c file you showed us, the MAX_BOOK_NAME is not defined. In order to do that you must add the folloing line of code right bellow the #include lines but outside of any function because you want it to be visible int the whole file:
#define MAX_BOOK_NAME the_number_you_want
The problem is that the size of the bookName arrays is nowhere given it is a constant and either needed to be declared after the preprocesser directive like or inside the main function like,
#define MAX_BOOK_NAME = 10; //The number is given just for example
or
const int MAX_BOOK_NAME = 10; //The number is given just for example
The constant named MAX_BOOK_NAME is not defined in the whole piece of code you shared with us. I guess you should try defining the constant before using it. Try putting :
#define MAX_BOOK_NAME 10// or any number you need
void viewBooks()
{
int found = 0;
char bookName[MAX_BOOK_NAME] = {0};
{
or
const int MAX_BOOK_NAME=10//or any number you wish
void viewBooks()
{
int found = 0;
char bookName[MAX_BOOK_NAME] = {0};
{
if your wish was to make a dynamic array then you could try this link
if your problem was just not to define the constant then you already got many answers from others as well.
So I've been looking at structures, functions and pointers for days now. I just cant wrap my head around structures good enough to do what I want...
I was trying to write a function, which was originally going to receive user input (taken with fgets) as an argument. I have put that aside now, and just decided to give the function a single argument. That argument will be the name of a struct, and I'll use that name to access it's variables and print them the way I want.
typedef struct
{
int hp;
char *name;
} bare;
bare example;
void print_info(char *name);
int main()
{
example.hp = 5;
strcpy(example.name,"John");
print_info("example");
}
void print_info(char *name)
{
printf("The hp of %s is %d", (*name), (*name)->hp);
}
Whatever bloody thing I put there instead of char *name, it always ended up giving me the error "error: struct or union expected"! I tried struct bare **name and (*name)->hp/(*name).hp, char *name/**name and *&name.hp, *&name->hp, every possible solution I could think of..! i think they all turned out to be nonsense... I just cant wrap my head around pointers and structs enough to do this! A little help please? I searched high and low on function arguments, pointers and structs, yet couldn't find a solution/question like mine..
First, it's better to declare your struct this way:
typedef struct bare {
int hp;
char *name;
} bare;
Second, avoid global variable as much as you can. I don't see the point of declaring example in the global namespace since you are using it only inside main().
Third, this line has a problem:
strcpy(example.name, "John");
You are attempting to copy "John" to an uninitialized pointer (example.name) that points to some random memory address. You have to either allocate enough space using malloc() (and free it when you're done with it), or use a fixed-length array. Moreover, it's better to use strncpy() because it allows to specify the maximum number of characters to copy. This way you avoid the risk of buffer overflow.
Fourth, to avoid copying your entire struct to print_info() (in fact, any other struct to any other function), you should pass its address.
With all that said, here is how your code should be written:
#include <stdio.h>
#include <string.h>
typedef struct bare {
int hp;
char name[100]; // Make sure it has enough space, or use malloc() if you don't know how much it will hold initially
} bare;
void print_info(bare *name);
int main(void)
{
bare example; // Declare it inside main()
example.hp = 5;
strncpy(example.name, "John", sizeof example.name); // This works and is safe
print_info(&example);
}
void print_info(bare *name)
{
printf("The hp of %s is %d", name->name, name->hp);
}
Output:
The hp of John is 5
I think what you wish to do is this:
#include <stdio.h>
#include <string.h>
typedef struct {
int hp;
char *name;
} bare;
bare example;
void print_info(bare *name);
int main() {
example.hp = 5;
strcpy(example.name, "John");
print_info(&example);
}
void print_info(bare *name) {
printf("The hp of %s is %d", name->name, name->hp);
}
Or if you want to pass example by value:
#include <stdio.h>
#include <string.h>
typedef struct {
int hp;
char *name;
} bare;
bare example;
void print_info(bare name);
int main() {
example.hp = 5;
strcpy(example.name, "John");
print_info(example);
}
void print_info(bare name) {
printf("The hp of %s is %d", name.name, name.hp);
}
Why did your code not work?
print_info had an incorrect argument data type. What you wanted was to pass an object of bare or perhaps a pointer to an object of bare, but you were instead passing a variable of type char *.
The arrow operator is used on pointers. Maybe take a look at Arrow operator (->) usage in C.
You wanted to pass in a string typed in by the user.
I was trying to write a function, which was originally going to receive user input (taken with fgets) as an argument. I have put that aside now, and just decided to give the function a single argument.
This explains why you pass in a char * to your function. The input value was originally going to be read from fgets. In your program, you passed in the name of your variable.
bare example;
/* ... */
print_info("example");
To do a dynamic lookup on a symbol name, use dlsym.
As I suggested in comments, if you want to be able to look up the name of a variable to find the associated object, you can use dlsym so long as you are on a POSIX system (like Linux). For example:
// Need to inlcude <dlfcn.h> and link with -ldl
// Make local variables findable with -rdynamic
void print_info(char *name)
{
bare *p = dlsym(0, name);
if (p != NULL)
printf("The hp of %s is %d", p->name, p->hp);
else
printf("%s not found!\n", name);
}
So long as you include <dlfcn.h> and use -ldl when linking the program, and you make your symbol table visible (with -rdynamic on GCC), the program will find the pointer to your example variable. (Try it online!)
But you probably meant to do a lookup by name.
However, you seemed to have mixed some things up. Usually, the user will not care what names you have used for the variables in your program. You would never expect fgets to give you "example" because that is not what the user would type in.
You probably meant to search for the bare record that matches the name parameter of bare. In your case, "John".
print_info("John");
Normally, you would have a table of bares that you would look over and check for a match. However, in your simplified example, there is only one to check.
bare * find_bare(char *name)
{
if (strcmp(name, example.name) == 0) return &example;
return NULL;
}
void print_info(char *name)
{
bare *p = find_bare(name);
if (p != NULL)
printf("The hp of %s is %d", p->name, p->hp);
else
printf("%s not found!\n", name);
}
It isn't hard to create and search a table of bare.
In this case, you could probably simple create an array of bare to represent your collection that you would search over.
#define BARE_TABLE_SIZE 50
bare table_example[BARE_TABLE_SIZE];
Assuming you add the code to populate your table, you could use a simple loop to search for a matching name.
bare * find_bare(char *name)
{
for (int i = 0; i < BARE_TABLE_SIZE; ++i)
{
if (strcmp(name, table_example[i].name) == 0)
return &table_example[i];
}
return NULL;
}
Your example.name was an uninitialized pointer.
Finally, the most egregious error in your program is the attempt to call strcpy on an uninitialized pointer. One solution is to allocate new memory to hold the new name and assign the location of the new name to the pointer. POSIX systems (like Linux) supply a function called strdup that creates a copy of the input for you, in newly allocated memory.
example.name = strdup("John");
Since the memory is allocated by malloc, you would need to call free on the pointer if example is ever recycled for a new name.
I don't understand this part of the code below. I mean alloc_MY_CAR() returns some array and how does & work so that newTab->pFunFree = &free_MY_CAR sees this array which newTab->pDat returns?
I don't understand pointers well. I only know that & store address of variable and * is a pointer or a value of the variable.
Could anyone guide me on how to use it properly and how does it work? I'm a beginner, so don't be so hard on me.
Thanks in advance!
#pragma once
struct MY_CAR {
char *model;
int year;
};
void print_MY_CAR(void* pdata);
void free_MY_CAR(void *pdata);
MY_CAR* alloc_MY_CAR();
switch (typ) {
case 0:
newTab->pDat = alloc_MY_CAR();
newTab->pFunFree = &free_MY_CAR;
newTab->pFunPrint = &print_MY_CAR;
break;
}
MY_CAR* alloc_MY_CAR() {
MY_CAR* tab = (MY_CAR*)calloc(1, sizeof(MY_CAR));
if (!tab) {
exit(0);
}
else {
char model[125];
printf("Podaj model: ");
scanf("%s", model);
tab->model = (char*)calloc(strlen(model) + 1, sizeof(char));
strcpy(tab->model, model);
printf("Podaj rok: ");
scanf_s("%d", &tab->year);
}
return tab;
}
void free_MY_CAR(void *pdata) {
MY_CAR* car = (MY_CAR*)pdata;
if (!car || !car->model) return ;
free(car->model);
free(car);
}
Notice that the function free_MY_CAR has an argument of type void*,
a pointer to a "void" type
(which is a C idiom for a pointer to something without telling the type of the thing pointed to),
and the first thing it does is to reinterpret that pointer as a pointer to a MY_CAR.
So the function is probably intended to be called like this:
newTab->pFunFree(newTab->pDat);
That is, the way the functions "know" what pointer was returned by
alloc_MY_CAR() and stored in newTab->pDat
is that the programmer explicitly tells the functions what pointer
is stored in newTab->pDat.
The advantage of doing such things is that it allows some code to do some operation on a data structure without necessarily having to know what kind of data structure it will actually operate on when the program actually runs.
In the call to pFunFree above, newTab could have been initialized by the case 0 code shown in the question, but there could be another case
that initializes it with alloc_MY_JOB(), &free_MY_JOB, and &print_MY_JOB,
where the MY_JOB functions allocate/free/print a data structure that is quite different from the data structure used by
alloc_MY_CAR(), &free_MY_CAR, and &print_MY_CAR.
Then if you call
newTab->pFunPrint(newTab->pDat);
we might not be able to predict when we write the code whether it will print the contents of a data structure created by
alloc_MY_CAR() or by alloc_MY_JOB();
but we can predict that it will print the detailed information it has
about your car, or your job, or whatever was read from the data file and stored in newTab.
The property that we can make a function call that uses a data structure in a way appropriate to that data structure, without having to know when we write the code what the type of data structure will be, is called
polymorphism.
This is a cumbersome idiom and there are lots of ways to get it wrong.
One of the selling points of C++ is to enable people to write polymorphic objects more easily than this.
How can I convert a string into a variable name? E.g. I have a list of strings:
"str1", "str2", "str3", etc.
And a structure:
struct my_struct {
int str1;
int str2;
int str3;
} m = {5, 10, 15, ... etc};
Given a string "str2", I want to print the variable associated with that
name m.str2. Does C have any way to do this?
Thank you!
This is simply not possible in C.
Check out this question for more details.
How to return a variable name and assign the value of the variable returned in c
To quote Wichert, 'I suggest that you reconsider the problem you are trying to solve and check if there might not be a better method to approach it. Perhaps using an array, map or hash table might be an alternative approach that works for you.'
In C you cannot create variable or symbol names dynamically.
Here's one way.
if ( strcmp(str, "str2") == 0 )
{
// Use m.str2
}
That will be a problem with hundreds of variables. You'll have to come up with some other mechanism in that case.
I will suggest a slightly simpler, albeit maybe not as efficient solution.
This was a solution I came up with for a project after consulting with one of my professors.
Essentially, strings are just ASCII characters, and C file containing variable names can be thought of in the same way.
Thus, suppose you have a list of strings that you would like to turn into variable names for integers.
First, define your structure in a header file that can be accessed by all files in your directory, for instance 'struct.h'.
The first step is to convert the string names to their respective integers
Simply create an empty header, called variable_names.h, include struct.h, and once and for all invoke the following loop in your main file:
const char *strings[] = {"str1", "str2", ... }
fp = fopen("variable_names.h", "a");
fprintf(fp, "#ifndef FILE1_H \n");
fprintf(fp, "#define FILE1_H \n");
fprintf(fp, "extern int* m_integers = {");
int i;
for(i = 0; i < sizeof(strings) - 1; i++){ fprintf(fp, "m.%s,", strings[i]);}
fprintf(fp, "m.%s } ", strings[i+1])
fprintf(fp, "#endif");
Now you have a linear mapping between the string name and value in your structure via the m_integers array. Next is to create some mapping that takes in the string name and points it to this integer. I will use UTHASH, but there are certainly other ways.
Thus, in your main file,
#include "uthash.h"
#include "variable_names.h"
...
struct opcode_table{
char* opcode_key;
int opcodes_val;
UT_hash_handle hh;
};
struct opcode_table *mapping = NULL;
struct opcode_table* s = NULL;
for (int i = 0; i < opcode_size; i++){
s = (struct opcode_table*) malloc(sizeof(*s));
s->opcodes_key = strings[i]; // the string
s->opcode_val = m_integers[i]; // the integer
HASH_ADD(hh,mapping, opcodes_key, sizeof(int),s);
}
^ Please go easy on the code, just a rough example of what could be done. I'm sure there are some mistakes, but high level I believe this should work.
As on overview, the idea is essentially, you wrote to an external file the ascii characters "m.string1", which once is written, is interpreted as an integer via the structure definition, yielding you your desired outcome. Now, you must simply look up the string in the hash table, and you get the integer in the structure.
Also, I would appreciate any feedback if someone finds a better way or this approach is flawed. Thanks!
Here is one way, please check the sample code I have written. I have used integer pointer ptr to do this.
#include <stdio.h>
#include <string.h>
const char *s[] = {"str1", "str2", "str3", "str4", "str5", "str6", "str7", "str8", "str9", "str10"};
struct temp
{
int str1;
int str2;
int str3;
int str4;
int str5;
int str6;
int str7;
int str8;
int str9;
int str10;
}m = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
main()
{
char str[10];
int *ptr;
int i;
ptr = &m.str1;
printf("enter string: \t");
scanf("%s", str);
for(i=0;i<10;i++)
{
if(strcmp(str,s[i]) == 0)
{
break;
}
}
printf("value = %d\n", *(ptr+i));
}
Please correct me if any mistakes.
What you are wanting is what the Smalltalk Language had used in it's Interpreter in the 1980;s and what Python still uses to create Object Variables from string char's through a TTY Command Shell Program in C language.
We do these by using structs with a nested struct next. But the actual variable, names are never actually declared. They are however matched by a conditional search for their stored char name in a struct or if it does not exist it creates them.
If it does exist in the struct list it will conditionally find() it and its stored value or assign its value depending on what your program has it designed to do.
An example of this is understood best by the Token's being used for (tok),(s),(t),(e) and several others as look-aheads read in by a token stream:
if (tok == "IDENT";) {
When it finds a identifier(variable tag from lex) token as above it will look ahead and fetch the variable name, then either create it, perform a calculation of its look-ahead contains another variable after another char type definition such as "CHAR" or "FLOAT"(for a double)etc.
What it does now is to set everything conditionally as it's being read into the struct list assigned by the tok token stream.
"CHAR" "x" "EQUALS" "INT" "5" "NEWL(for \N)" "CHAR" "y" "EQUALS" "INT" "5" "NEWL" "PRNT" "CHAR" "x" "NEWL".
So basically all variables through void functions are conditionally being assigned to a struct list and likewise having their values stored by them.
And it is not the variable you need to match its stored variable name. Its only the match it requires from a single tok variable and it's look-aheads that you need--
struct Var v; //--declared in .h then--
if(tok == 'CHAR') //--and--
v = {tok2, s, t} //
"tok2" being your vars char name and "s" and "t" being look-aheads to fetch, calculate and or assign them. Basically Python uses one C variable named tok to name them all. Then matches make them both dynamic and easy for Python to use them. atoi and strtod make integer and doublke conversion on the fly also. Its advanced stuff.
This whole process is called "Scanners" and "Calculators" in early ANCI C. Study Dennis Ritchie and Tobias Shrighner. Tobias worked on several Interpreters in fact.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RECORDS 10
The function below is what I am asking for help with.
static char searchforRecordbystate(char input[3])
{
for / while /if loop
search struct array members
if a match is found
return (print) the entire struct where a match was found
return 0;
}
Main function - first time ever using pointers, (xcode is not complaining with it set to be as strict as possible) but all of you are welcome to complain, especially if I am making a huge oversight.
int main() {
typedef struct {
char *firstName[RECORDS];
char *lastName[RECORDS];
char *street[RECORDS];
char *city[RECORDS];
char *state[RECORDS];
int *zip[RECORDS];
char *phone[RECORDS];
int *accountId[RECORDS];
} Customer ;
typedef int records;
records i = 0;
array loop for data entry
Customer custArray[RECORDS];
printf("================================================================\n");
for(i = 0; i < RECORDS; ++i)
{
printf("Enter data for customer %d\n", i + 1);
printf("Enter firstname, last name, phone\n");
scanf("%s %s %s", *custArray[i].firstName, *custArray[i].lastName, *custArray[i].phone);
printf("Enter Address (Street City State ZIP)");
scanf("%s %s %s*c %d", *custArray[i].street, *custArray[i].city, *custArray[i].state, *custArray[i].zip);
break;
}
char input[3];
printf("Enter in state to search for customer a customer record:\n");
scanf("%s", input);
searchforRecordbystate(input);
}
No error checking necessary, just trying to crawl into learning c at the moment. And there will not be duplicate data in the state member. Hope that makes this easier.
how would I write a search function to look for a match in a struct
array and return (printf) the entire struct it matched?
Declare the struct datatype outside of the function so it's "visible" to the whole module.
Create a function that is able to pretty-print a struct:
void CustomerPrint(const Customer *toPrint) {
...
}
Create a search function that iterates through the array comparing given arguments:
Customer *CustomerFind(const char *name) {
...
}
Connect the two function blocks by calling CustomerFind and in case the result is not NULL call the CustomerPrint function.
Of course the interfaces are only proposal and are subject to be changed. If you've got any questions regarding the details of the proposal leave a comment, I'll explain it in great detail if you like.
Additional thoughts
While rereading my post I realized that some of my decisions I've made in above proposal need an explaination anyway:
In CustomerPrint the pointer taken is `const? because this function is not going to modify any field of the struct. Therefore we tell the compiler that we are not going to change anything.
CustomerFind is expected to have arguments for all searchable fields. (So you are encouraged to extend the signature) I'd propose to take all the "compare" values by pointer and let the caller those pointers be NULL which are not relevant for the search. (e.g. if you have name and city you can leave city NULL in order to only search for the first occurence of name.
The function itself runs through the array of records and compares the fields that are not NULL. In case it finds one, it returns the pointer to that element (return &(myRecords[n]);). If the function comes to the end of the array, it will return NULL to indicate no record matched.
There is also a concept you can introduce if you want to have "search - search next" capabilities. Let me know if you are intrested in a concept for that too.
typedef struct {
char firstName[NAMEMAX];
char lastName[NAMXMAX];
char street[STREETMAX];
char city[CITYMAX];
char state[STATEMAX];
int zip;
char phone[PHONEMAX];
int accountId;
} Customer ;
Customer Customers[RECORDS];
static int searchforRecordbystate(char input[]) {
for (int i = 0; i < RECORDS; i++) {
if (strcmp(input, Customers[i].state) == 0) {
printCustomer(Customers[i]);
return i;
}
}
return -1; // Not found
}
Writing printCustomer() is an exercise for the reader.