My program needs to take people's input and calculate salary and withholds and print all of it out. I have the print outs but I'm having trouble storing it in the array.
#include <stdio.h>
#include <stdlib.h>
typedef char *string;
#define MaxEmployees 100
typedef struct {
string name;
string title;
string ssnum;
double salary;
int withholding;
} employeeRecordT;
typedef struct {
string name;
string title;
string ssnum;
double salary;
int withholding;
} *employeeT;
Type: payrollT
This type represents an entire collection of employees. The type definition uses a dynamic array of employeeT values to ensure that there is no maximum bound imposed by the type. The cost of this design is that the programmer must explicitly allocate the storage for the array using NewArray.
typedef struct {
int nEmployees;
employeeT *employees;
} *payrollT;
Global variables
staff -- Array of employees
nEmployees -- Number of employees
manager -- Used to produce a figure for the code
static employeeT staff[MaxEmployees];
static int nEmployees;
static employeeRecordT manager = {
"Ebenezer Scrooge", "Partner", "271-82-8183", 250.00, 1
};
Private function declarations:
static void InitEmployeeTable(void);
static payrollT CreatePayroll(employeeT staff[], int nEmployees);
static void ListEmployees(payrollT payroll);
static double AverageSalary(payrollT payroll);
static void WeeklyPayroll(payrollT payroll);
//static void GetPayroll(void);
static double ssnum(payrollT payroll);
Main program:
int main(void)
{
payrollT payroll;
//GetPayroll(payroll);
InitEmployeeTable();
payroll = CreatePayroll(staff, nEmployees);
ListEmployees(payroll);
WeeklyPayroll(payroll);
}
static void InitEmployeeTable(void)
{
employeeT empRec;
int condition = 1;
int emp_id = 2;
empRec = (employeeT)malloc(sizeof (employeeT));
empRec->name = "Ebenezer Scrooge";
empRec->title = "Partner";
empRec->ssnum = "271-82-8183";
empRec->salary = 250.00;
empRec->withholding = 1;
staff[0] = empRec;
empRec->name = "Bob Cratchit";
empRec->title = "Clerk";
empRec->ssnum = "314-15-9265";
empRec->salary = 15.00;
empRec->withholding = 7;
staff[1] = empRec;
nEmployees = 2;
do {
//malloc(sizeof ());
char name;
char title;
char ssnum;
float salary;
double withholding;
printf("enter name or input stop to quit!\n");
printf("enter first and last name\n");
scanf("%s", empRec->name);
//empRec->name = name;
printf("\nenter title\n");
scanf("%s", empRec->title);
//empRec->title = title;
printf("\nenter social xxx-xx-xxxx\n");
scanf("%s", empRec->ssnum);
//empRec->ssnum = ssnum;
printf("\nenter salary xx.xx\n");
scanf("%lf", &empRec->salary);
//empRec->salary = salary;
printf("\nenter withhodling x\n");
scanf("%d", &empRec->withholding);
//empRec.withholding = withholding;
printf("printed %d", emp_id++);
staff[emp_id] = empRec;
emp_id++;
if (strcmp(empRec->name,"stop") == 1) {
condition = 0;
break;
}
//staff[emp_id]=empRec;
//emp_id++;
} while (condition = 1);
return 0;
}
You aren't allocating storage for any of your strings (name, title, ssnum [in your records, and now that I looked, in your do-while loop too--you can't use char name;, you need e.g. char name[100]; or pointer with malloc]). Using your typedef for string is somewhat hiding that fact. The only time you can assign to a string variable and have the allocation taken care of for you is at declaration time, since the size is known at compile-time e.g.:
char *str = "Hello";
char *str2 = { 'w', 'o', 'r', 'l', 'd', '\0' };
In your case, the assignment of the strings for manager is fine since they are in the initializer, but you still do need to give explicit sizes for the character arrays in your employeeRecordT.
What you need to do is either give explicit sizes for these character arrays or explicitly allocate storage for them (malloc etc.); and instead of assigning, you must use strcpy() (<string.h>) (or strncpy() in cases where overflow is a possibility).
One thing you could do for something like your title variable to save space is to declare a statically allocated list of strings and then just have title be a pointer to the appropriate element in the list, e.g. (the consts are optional, but just makes sure this array doesn't get messed up in any way):
const char const *titles[] = { "Partner", "Clerk", "etc." };
typedef struct {
const char *title;
// ...
} *employeeT;
// ...
empRec->title = titles[0]; // Points title to "Partner"
You could even use an enum as well to help with the index (all of the rest of the code would be the same):
enum { PARTNER, CLERK, ETC };
// ...
empRec->title = titles[CLERK]; // Points title to "Clerk"
Another issue with your code is that you are only allocating storage for a single employeeT record; you can't reuse empRec without calling malloc() again. What is going to happen is every time you change empRec, it's going to change all of the previous staff elements because they all point to the same location (i.e., staff[0] will contain exactly what you set staff[1] to).
Another issue is that you are taking the sizeof (employeeT) but this is wrong since that is only giving you the size of a pointer to your struct. This is one reason why typedefs can make things more confusing. You should create your struct (and typedef to it if you want), but when you need a pointer to it, you should honestly just use the '*' right there in the code (optionally making a second typedef, i.e. typedef struct { // ... } node; typedef node *node_ptr; or even at the same time: typedef struct { // ... } node, *node_ptr;). You have two options, you can either call malloc() again for empRec before you start assigning its members the second (and subsequent) times, or you can simply discard the empRec variable and use staff directly, e.g.:
staff[0] = malloc(sizeof *staff[0]);
// Note I couldn't use employeeT above since you need struct size, not ptr.
// (`sizeof (employeeRecordT)` could have been used though)
strcpy(staff[0]->name, "Ebenezer Scrooge");
// ...
staff[0]->salary = 250.00;
// ...
Be sure to free() every element in staff[] later on.
Furthermore, I don't see why you have this payrollT type which is essentially a copy of your staff array? If you want payrollT you can just have a pointer to an employee record (which by the way, you probably didn't want employeeT *employees;, you probably wanted either employeeT employees; (since it is already a pointer type) or employeeRecordT *employees). I think this is really the only other thing I see wrong with your program snippet.
If you wanted to, you could get rid of your condition variable, and instead of using a do-while loop, just use a while (1) and your break in your if will exit it for you. This way you can be sure it is still evaluated at least once.
By the way, you don't have to cast the return value of malloc(). It's unnecessary since void pointers are safely and automatically promoted to any other type (and vise versa). See here for more info. Also, I see you mention NewArray but never reference it anywhere.
Related
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 was working on the following as an example to see the differences between passing an object directly and then passing a pointer to it:
#include "stdio.h"
// Car object
typedef struct Car {
char* name;
unsigned int price;
} Car;
void print_car(Car car) {
printf("<Car: %s, Price: $%d>", car.name, car.price);
};
void print_car2(Car *car) {
printf("<Car: %s, Price: $%d>", car->name, car->price);
};
int main(int argc, char* argv[]) {
Car chevy = {chevy.name = "Chevy", chevy.price = 45000};
print_car(chevy);
Car mazda = {chevy.name = "Mazda", chevy.price = 30000};
print_car2(&mazda);
return 1;
}
Other than the first approach being much more readable and easier to understand for me, what are the differences between the two? When would passing a pointer be the only option, and why do both work in the above case?
There are two reasons to use the second approach:
If you want to avoid copying the whole struct. If the struct is big, this can affect performance.
If you want to modify struct that you're passing.
In general (not only for structs) passing a variable to a function will make a copy of this variable so if you want to alter this variable you ll have to return the value of the altered copy but you may want to alter the variable and return something else, in this case you have no other choice of passing a pointer as argument exemple :
first exemple with passing a variable
int useless_func(int nb) /*nb is actually a copy of the variable passed as argument */
{
nb++; /* the copy was incremented, not the real variable */
return nb; /* the new value is returned */
}
int main()
{
int nb = 1;
useless_func(nb); /* here nb is still = 1 cause it wasn't altered by the function */
nb = useless_func(nb); /* here nb is = 2 cause it took the return value of the func */
}
now a second stupid exemple with pointer :
char *useless_func(int *nb) /* nb is now a pointer to the variable */
{
*nb++; /* the derefencement of the pointer (so the variable value) was incremented */
return strdup("This is a useless return"); /* we return some other stupid stuff */
}
int main()
{
int nb = 1;
char *str = useless_func(&nb); /* now nb is = 2 and str is an allocated useless string woohoo */
}
When a function is called, the arguments in a function can be passed by value or passed by reference.
void print_car(Car car)
In here you are directly passing an object to the function, that means it will be copied into the functions stack and destroyed after function call ends. This method should be avoided because copying is expensive and unnecessary. Also if your objects are quite big, this operation will take a lot of time
void print_car2(Car *car) {
In this situation you are passing a pointer to the object which is called pass by reference, that means you are working with the original object and changes you make will directly affect it. It's a lot faster because you are not moving your object, but you should be careful about alter of original data
I have a headerfile which declares these arrays:
int stVal_On[] = {2};
int stVal_Off[] ={1};
int subVal_On[]={1};
int subMss[]={1};
int subVal_Off[]={0};
The dereferenced arrays are then used in structs that are declared:
Definition of WriteData struct:
/* Write structure used in loop for Read- and Write Tests */
typedef struct WriteData {
char* name; // MMS object name
const VOID* data; // Data to write
const SINT32 localFormat; // SVI type (on server)
const SINT32 dataLength; // length of data to write/read
const SINT32 NbrofElmnts; // Number of elements to write/read
char* description; // SVI type as String (on server)
char* SVI_Name; // SVI address of the SVI mapped on server
UINT32 svi_Length; // length of SVI variable on server (used for readback)
} WriteData;
What is the purpose of this int arr[] = {1}; idiom? Why use arrays at all if only one value is assigned?
Well, one reason I can think of has to do with code organization. If you write your code in a tabular form:
struct {
char const *file_name;
uint16_t flags;
// Other data
} table [] = {
{ .file_name = "/usr/bin/foo", .flags = 0x0 },
};
for (size_t i = 0; i < sizeof(table)/sizeof(table[0]); ++i) {
// do something meaningful with a table row table[i]
}
While it's just an array of 1 now, if you need to add more cases, your code is already written well for it. You'd just need to add another "row' to the table initializer.
All the definitions create arrays with only one element, true. The actual use case may vary.
One commonly used scenario is, this variable can be passed as a function argument and it will be possible to change the content of the array (the only element value) from the called function, which in case of a non-array variable is not possible. This maybe not the very smart way, but still, it's possible and someone chose to use it.
Also, as always, array size determined by the supplied initializer leaves the room for expansion without requiring major code changes.
I am working on an assignment and ran into challenging problem. As far as I'm concerned and from what I've learnt the code that follows should be correct however it does not work. Basically what I am trying to is copy a string value into the variable member of a structure the is part of an array passed into a method as a pointer. What am I missing?
typedef struct
{
char * name; //variable in struct I am trying to access
} Struct;
void foo(Struct * arr) //array of Structs passed into function as a pointer
{
int i = 0;
while(i++ < 2)
{
arr[i].name = malloc(sizeof(char *)); //assigning memory to variable in each Struct
arr[i].name = strdup("name"); //copying "name" to variable in each Struct
printf("C - %s\n", arr[i].name); //printing out name variable in each Struct
}
}
main()
{
Struct * arr; //defining pointer
arr = calloc(2, sizeof(Struct)); //allocating memory so pointer can hold 2 Structs
foo(arr); //calling function foo passing pointer into function
return 0;
}
This code compiles and runs however it does not do what it is designed to do. Forgive me if it is something trivial. I am new to the language C
Two issues:
while(i++ < 2) This line changes the value of i as soon as it checks it, so your loop body will not be the same as it was checked.
arr[i].name = strdup("name"); overwrites the value of the .name pointer, causing a memory leak of the memory you malloc()'ed earlier.
Extending on 2 pointed out correctly already,
arr[i].name = strdup("name");
Even if you use following instead of above,
strcpy(array[i].name, "name");
you haven't allocated enough bytes to store the string i.e. this is wrong
arr[i].name = malloc(sizeof(char *));
// even if pointer is 8 byte here, concept isn't right
Should be something like
arr[i].name = malloc(strlen("name")+1);
// or MAX_SIZE where it is greater than the possible "name".
Or better yet, remove the malloc at all, strdup takes care of allocation itself
This is not answering your question directly, but addresses an issue to big to put into a comment...
Additional issue: You probably did not intend to allocate only a (char *) worth of memory to a variable intended to hold at least "name". Change;
arr[i].name = malloc(sizeof(char *));
to:
arr[i].name = malloc(sizeof(char)*strlen("name")+1); //+1 for '\0'
or better yet, use char *name="name";, then:
arr[i].name = malloc(sizeof(char)*strlen(name)+1);
Even more general (and better):
char *name;
name = malloc(strlen(someInputString)+1);
//do stuff with name...
free(name);
Now, you can allocate name to any length needed based on the length of someInputString.
[EDIT]
Etienz, I wanted to address one more thing, alluded to by #H2CO3 above, but not really explained, that I think might be useful to you:
Regarding your desire to have room for two structs, because you typedef'd your struct, you can simply do something like this: (but I am going to change the name you used from Struct to NAME :) The whole point being that when a struct is created as an array, you do not need to use calloc or malloc to create space for them, it is done as shown below...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
char *name;
}NAME;
//use new variable type NAME to create global variables:
NAME n[2], *pN; //2 copies AND pointer created here
//prototype func
int func(NAME *a);
int main()
{
pN = &n[0]; //pointer initialized here
func(pN); //pointer used here (no malloc or calloc)
printf("name1 is %s\nname 2 is %s", pN[0].name, pN[1].name);
return 0;
}
int func(NAME *a)
{
char namme1[]="andrew";
char namme2[]="billebong";
//You DO have to allocate the members though
a[0].name = malloc(strlen(namme1)+1);
a[1].name = malloc(strlen(namme2)+1);
strcpy(a[0].name, namme1);
strcpy(a[1].name, namme2);
return 0;
}
For an assignment at school, we have to use structs to make matrices that can store a infinite amount of points for an infinite amount of matrices. (theoretical infinite)
For the assignment I decided to use calloc and realloc. How the sizes for the matrix go is: It doubles in size every time its limit is hit for its points (so it starts at 1, then goes to 2, then 4 and so on). It also doubles in size every time a matrix is added as well.
This is where my issue lies. After the initial matrix is added, and it goes to add the second matrix name and points, it gives me the following:
B???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
B is the portion of it that I want (as I use strcmp later on), but the ? marks are not supposed to be there. (obviously)
I am not sure why it is exactly doing this. Since the code is modular it isn't very easy to get portions of it to show exactly how it is going about this.
Note: I can access the points of the matrix via its method of: MyMatrix[1].points[0].x_cord; (this is just an example)
Sample code that produces problem:
STRUCTS:
struct matrice {
char M_name[256];
int num_points[128];
int set_points[128];
int hasValues[1];
struct matrice_points * points;
} * MyMatrix;
struct matrice_points {
int set[1];
double cord_x;
double cord_y;
};
Setup Matrix Function:
void setupMatrix(){
MyMatrix = calloc(1, sizeof(*MyMatrix));
numMatrix = 1;
}
Grow Matrix Function:
void growMatrix(){
MyMatrix = realloc(MyMatrix, numMatrix * 2 * sizeof(*MyMatrix));
numMatrix = numMatrix * 2;
}
Add Matrix Function which outputs this problem after growing the matrix once.
void addMatrix(char Name, int Location){
int exists = 0;
int existsLocation = 0;
for (int i = 0; i < numMatrix; i++){
if (strcmp(MyMatrix[i].M_name, &Name) == 0){
exists = 1;
existsLocation = i;
}
}
*MyMatrix[Location].M_name = Name;
printf("Stored Name: %s\n", MyMatrix[Location].M_name);
*MyMatrix[Location].num_points = 1;
*MyMatrix[Location].set_points = 0;
*MyMatrix[Location].hasValues = 1;
MyMatrix[Location].points = calloc(1, sizeof(*MyMatrix[Location].points));
}
void addMatrix(char Name, int Location)
char Name represents a single char, i.e. a integer-type quantity. char is just a number, it's not a string at all.
When you do this:
strcmp(..., &Name)
you're assuming that the location where that one character is stored represents a valid C string. This is wrong, there is no reason why this should be the case. If you want to pass a C string to this function, you will need to declare it like this:
void addMatrix(char *Name, int Location)
Then you need to copy that C string into the appropriate place in your matrix structure. It should look like:
strncpy(... .M_name, Name, max_number_of_chars_you_can_store_in_M_Name);
Also these field definitions are strange in your struct:
int num_points[128];
int set_points[128];
int hasValues[1];
This means that your struct will contain an array of 128 ints called num_points, another array of 128 ints calls set_points, and an array of one int (strange) called hasValues. If you only need to store the count of total points and set points, and a flag indicating whether values are stored, the definition should be:
int num_points;
int set_points;
int hasValues;
and correct the assignments in your addMatrix function.
If you do need those arrays, then your assignments as they are are wrong also.
Please turn on all warnings in your compiler.
Try adding '\0' to the end of your data.
*MyMatrix[Location].M_name = Name;
You're copying a single character here, not a string. If you want a string, Name should be defined as char *, and you should be using strcpy.