0

I am trying to write a function that will allow me to put in the name, age, and salary of three people into a struct. I then want to print them out. I'm only a few weeks into my first programming class, please bear with me, and couldn't find any examples that made sense to me. I ran the loop without [i] but the data would just write over itself. (I think?) I don't want the code written for me but if anyone has any idea about the mechanics of what I'm trying to do or could point me in the correct direction I would be very grateful.(A lot of the printf's are just there for me at the moment and will be removed later.)

#include <stdio.h>
#include <conio.h>
#include <string.h>
#define SIZE 20
struct manager{
    char *name[SIZE];
    int *age[4];
    int *salary[10];
}employee;

void addInfo(struct manager* file[]);

int main(void){
    printf("\n\n");
    printf("\t***************************\n\t*Enter 1 to add employees.*\n\t*Enter 2 to search.\t  *\n\t*Enter 3 to update.\t  *\n\t*Enter 4 to delete.\t  *\n\t*Enter 0 to exit.\t  *\n\t***************************\n\n");
    addInfo(&employee);

    return 0;
}

void addInfo(struct manager* file[]){
    int i = 0;
    int counter = 1;
    int otherCounter = 1;
    for(i = 0; i < 3; i++){
        /*printf("Enter the name of employee #%d: \n"),otherCounter;
          gets(&employee.name[i]);
          printf("Enter the age of employee #%d: \n",otherCounter);
          gets(&employee.age[i]);
          printf("Enter the salary of employee #%d: \n",otherCounter);
          gets(&employee.salary[i]);
        */
        printf("Enter the name of employee #%d: \n"),otherCounter;
        scanf("%s",&employee.name[i]);
        printf("Employee name: %s\n",employee.name[i]);
        printf("Enter the age of employee #%d: \n",otherCounter);
        scanf("%d",&employee.age[i]);
        printf("Employee age: %d\n",employee.age[i]);
        printf("Enter the salary of employee #%d: \n",otherCounter);
        scanf("%d",&employee.salary[i]);
        printf("Employee salary: %d\n",employee.salary[i]);
        ++otherCounter;
    }

    for(i = 0; i < 3; i++){
        printf("\nEmployee #%d:\n",counter);
        printf("Employee name: %s\n",employee.name[i]);
        printf("Employee age: %d\n",employee.age[i]);
        printf("Employee salary: %d\n\n",employee.salary[i]);
        ++counter;
    }
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
Greystone
  • 13
  • 5
  • Why isn't the `addInfo()` function using the `file` parameter? – Barmar Sep 25 '20 at 00:07
  • `employee` is just a single structure, not an array. – Barmar Sep 25 '20 at 00:07
  • Why does the manager have 20 employees, but only 4 ages and 10 salaries for them? – Barmar Sep 25 '20 at 00:10
  • You need to allocate memory for the name before you can read the employee name into it. – Barmar Sep 25 '20 at 00:11
  • `age` and `salary` shouldn't be pointers, they should just be `int`. And you can't use `gets()` to read into integers, it's only for strings. – Barmar Sep 25 '20 at 00:11
  • Also, `gets()` should **never** be used. Use `fgets()` so you can specify the limit of how much to read, to prevent buffer overflows. – Barmar Sep 25 '20 at 00:12
  • There's so much misunderstanding here, I think you need to go back and review tutorials about arrays, pointers, and getting user input. – Barmar Sep 25 '20 at 00:13
  • I really do. I'm taking a normally 16-week course in 8 weeks and have had a total of zero lectures so far. Pretty much just the text and whatever videos/examples I can find. I just feel like I'm trying not to drown in it all right now. – Greystone Sep 25 '20 at 00:35

2 Answers2

1

It looks like you're completely confused. All your definitions look weird and the way you're using the values is wrong.

Start with something like this:

#define MAX_NAME 20
#define MAX_EMPLOYEES 100

struct Employee
{
    char name[MAX_NAME];
    int age;
    int salary;
};

struct Employee employees[MAX_EMPLOYEES];

Make your function just read a single employee. If you really want the prompts to say which employee number you're reading, you can pass that in as a parameter.

void readEmployeeInfo(struct Employee *e)
{
    printf("Enter name:\n");
    scanf("%s", e->name);
    printf("Enter age:\n");
    scanf("%d", e->age);
    printf("Enter salary:\n");
    scanf("%d", e->salary);
}

Add other functions for things like displaying an employee record. This keeps it clean, and ensures a function just does one thing.

To read some number of employees in a loop:

for (int i = 0; i < 3; i++)
{
    readEmployeeInfo(&employees[i]);
}

That should get you started.

paddy
  • 60,864
  • 6
  • 61
  • 103
  • I'd also suggest reading with a much larger buffer than `20` and then checking the length before copying to `name` -- just to ensure you won't have unread characters left in `stdin` in case the user does something unexpected -- like a 21 character name... – David C. Rankin Sep 25 '20 at 00:21
  • Yes of course. I modelled this answer from the original question and kept it intentionally simple to avoid overwhelming a complete beginner. They're likely to immediately run into the need for `fgets` if the user might enter names containing spaces. – paddy Sep 25 '20 at 00:22
  • Completely confused is an understatement! Thank you very much for your help. How would I be able to read in a second employee later and it not take up the same location as the first? – Greystone Sep 25 '20 at 00:32
  • You choose where to store it based on the index on the `employees` array. You see in the loop I'm storing records in positions 0, 1, and 2. Nothing is stopping you from using any value you like. If you're just appending employees to the list, keep a count of how many employees you have read, and use that as the index. Also range-check it to make sure you don't go past the limits of the array. – paddy Sep 25 '20 at 00:38
1

Taking it one step beyond your original approach, do not take user input with scanf() and never user scanf() without checking the return. Why? scanf() is full of pitfalls for the new C programmer because characters will remain unread in stdin just waiting to bite you again if a matching failure occurs. Further, using scanf() to read into a string without providing a field-width modifer is just as unsafe and subject to explain as using gets(). See Why gets() is so dangerous it should never be used!

Instead, use a line-oriented input function like fgets() with an adequate sized buffer to read all user-input (don't skimp on buffer size, adjust if you are programming on a micro-controller, otherwise a 1K buffer is fine) This ensures that all characters entered by the user (in all but extreme cases of the cat stepping on the keyboard) are read and nothing will remain in stdin where it can effect your next attempted input. You can also use POSIX getline() which is the second commonly used line-oriented input function.

The only caveat using line-oriented input function is that they read and include the trailing '\n' in the buffer they fill. It isn't a problem, simply use strcspn() to get the number of character before the '\n' character and then overwrite the trailing '\n' with the nul-terminating character '\0', equivalent to plain old 0. See man 3 strspn E.g. if you read into a buffer, say char buf[1024] and you need to trim the trialing newline, simply use:

    buf[strcspn (buf, "\n")] = 0;

(you can include the '\r' carriage-return as well if on windows to trim beginning with whichever occurs first, such as if a DOS line-ending mysteriously made its way into the input using "\r\n" as the delimiter)

Don't #include <conio.h> as a header. It is an anachronism from the DOS days and is 100% non-portable to anything other than windows.

Putting It To Use To Read 1-Employee At A Time

In your case, and following your comment below @paddy's answer, you don't want to write your "add" function to "add" a specific number of anything, you want to write your add function to add one employee and to perform error detection and reporting though the return so you know whether the add succeeded or failed and how it failed. To make things easier, you can use a typedef for your struct, so you can refer to employee as a type rather than having to refer to struct manager, e.g.

#define NEMP     3          /* if you need a constant, #define one (or more) */
#define MAXNM   20
#define MAXC  1024

typedef struct {            /* typedef makes working with structs convenient */
    char name[MAXNM];
    unsigned age, salary;
} employee;

Now you can declare your add function to take an array of employee as a parameter and return an int with 1 indicating success, 0 indicating an error in input (such as someone entering "five" instead of 5 as a value) or returning -1 if the user cancels input by generating a manual EOF by pressing Ctrl+d or Ctrl+z on windows. You begin your function definition by declaring an adequately sized buffer for user input and a variable to use to check the length of name before copying to your struct, e.g.

int addemployee (employee *e)
{
    char buf[MAXC];
    size_t len;

To take the input for name (no need to prompt with printf() there are no conversions, just use fputs() which provides line-end control, compared to puts() and then validate the return by checking whether it is NULL indicating the user canceled input, trim the newline from buf and then copy to your employee name, e.g.

    fputs ("\nemployee name   : ", stdout);         /* no conversion - use fputs */
    if (fgets (buf, MAXC, stdin) == NULL) {         /* read all input with fgets() */
        fputs ("(user canceled input)\n", stderr);  /* validate EVERY input */
        return -1;
    }
    
    len = strcspn (buf, "\r\n");                    /* length before either "\r\n" */
    if (len >= MAXNM) {                             /* validate length */
        fputs ("error: name too long.\n", stderr);
        return 0;
    }
    buf[len] = 0;                                   /* trim trailing '\n' */
    
    memcpy (e->name, buf, len + 1);                 /* copy to name */

(you don't need strcpy() because you already know the length of the string, so there is no need to have strcpy() scan for the end-of-string again, just use memcpy())

For age and salaray, the approach is identical. Read with fgets() and then convert and validate the conversion using sscanf(), validating all the way:

    fputs ("employee age    : ", stdout);           /* prompt for age */
    if (!fgets (buf, MAXC, stdin)) {                /* validate EVERY input */
        fputs ("(user canceled input)\n", stderr);
        return -1;
    }
    if (sscanf (buf, "%u", &e->age) != 1) {         /* validate conversion */
        fputs ("error: invalid unsigned input\n", stderr);
        return 0;
    }

(note: you do not need to trim the '\n' from buf when converting to a numeric value as the '\n' is whitespace and will be ignored by the conversion)

When you need to output each employee, now write a function that takes the array of employee and the number of employees contained and output each, e.g.

void prnemployees (employee *e, int n)
{
    for (int i = 0; i < n; i++)
        printf ("\n%s\n  age    : %u\n  salary : %u\n", 
                e[i].name, e[i].age, e[i].salary);
}

With those two functions, your main() function reduces to simply declaring an array of employee and then looping while the number of valid employees entered is less than the array size, only incrementing your employee count on a successful return from addemployee(). Print the employees when done, e.g.

int main (void) {

    int n = 0;                              /* input counter */
    employee emp[NEMP] = {{ .name = "" }};  /* array of employee, initialize all zero */
    
    while (n < NEMP) {
        int result = addemployee (&emp[n]); /* add employee VALIDATE return */
        if (result == -1)                   /* if user canceled, stop input */
            break;
        else if (result == 1)               /* if good input, increment count */
            n++;
        /* for 0 return, do nothing, error message output in funciton */
    }
    
    prnemployees (emp, n); 
}

Putting it altogether, you would have:

#include <stdio.h>
#include <string.h>

#define NEMP     3          /* if you need a constant, #define one (or more) */
#define MAXNM   20
#define MAXC  1024

typedef struct {            /* typedef makes working with structs convenient */
    char name[MAXNM];
    unsigned age, salary;
} employee;

int addemployee (employee *e)
{
    char buf[MAXC];
    size_t len;
    
    fputs ("\nemployee name   : ", stdout);         /* no conversion - use fputs */
    if (fgets (buf, MAXC, stdin) == NULL) {         /* read all input with fgets() */
        fputs ("(user canceled input)\n", stderr);  /* validate EVERY input */
        return -1;
    }
    
    len = strcspn (buf, "\r\n");                    /* length before either "\r\n" */
    if (len >= MAXNM) {                             /* validate length */
        fputs ("error: name too long.\n", stderr);
        return 0;
    }
    buf[len] = 0;                                   /* trim trailing '\n' */
    
    memcpy (e->name, buf, len + 1);                 /* copy to name */
    
    fputs ("employee age    : ", stdout);           /* prompt for age */
    if (!fgets (buf, MAXC, stdin)) {                /* validate EVERY input */
        fputs ("(user canceled input)\n", stderr);
        return -1;
    }
    if (sscanf (buf, "%u", &e->age) != 1) {         /* validate conversion */
        fputs ("error: invalid unsigned input\n", stderr);
        return 0;
    }
    
    fputs ("employee salary : ", stdout);           /* prompt for salary */
    if (!fgets (buf, MAXC, stdin)) {                /* validate EVERY input */
        fputs ("(user canceled input)\n", stderr);
        return -1;
    }
    if (sscanf (buf, "%u", &e->salary) != 1) {      /* validate conversion */
        fputs ("error: invalid unsigned input\n", stderr);
        return 0;
    }
    
    return 1;
}

void prnemployees (employee *e, int n)
{
    for (int i = 0; i < n; i++)
        printf ("\n%s\n  age    : %u\n  salary : %u\n", 
                e[i].name, e[i].age, e[i].salary);
}

int main (void) {

    int n = 0;                              /* input counter */
    employee emp[NEMP] = {{ .name = "" }};  /* array of employee, initialize all zero */
    
    while (n < NEMP) {
        int result = addemployee (&emp[n]); /* add employee VALIDATE return */
        if (result == -1)                   /* if user canceled, stop input */
            break;
        else if (result == 1)               /* if good input, increment count */
            n++;
        /* for 0 return, do nothing, error message output in funciton */
    }
    
    prnemployees (emp, n); 
}

Example Use/Output

With intentional errors in input:

$ ./bin/addemployee

employee name   : Mickey Mouse
employee age    : 92
employee salary : 200000

employee name   : Minnie Mouse
employee age    : too old
error: invalid unsigned input

employee name   : Minnie Mouse
employee age    : 92
employee salary : 250000

employee name   : Pluto (dog)
employee age    : 90
employee salary : bone
error: invalid unsigned input

employee name   : Pluto (dog)
employee age    : 90
employee salary : 10000

Mickey Mouse
  age    : 92
  salary : 200000

Minnie Mouse
  age    : 92
  salary : 250000

Pluto (dog)
  age    : 90
  salary : 10000

The point being that user-input in C isn't difficult, but new C programmers generally make it difficult by choosing the wrong tool for the job and failing to check the return of the tool they do choose -- usually scanf(). Don't get me wrong, scanf() can be used properly for user-input, but until you have read, word-for-word man 3 scanf and understand every caveat and which conversion specifiers discard whitespace and which don't -- don't use it. Use a line-oriented approach with an appropriately size buffer and trim the '\n' if you are using the buffer as a character string.

Let me know if you have any further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • I appreciate the help with scanf. I'm just trying to do this for an assignment and honestly, I'm just using what I have before just so I can first get it to even work. This is great info. Going to take me a while to digest, a lot of new stuff to cover. Thank you. – Greystone Sep 25 '20 at 02:39
  • Amen. It should take a while to digest if you are approaching C right. There is a lot to learn, but you could not have made a better choice of programming language. Learning how to work with your computer and memory at a low-level where every byte counts will make you a better programmer no matter what language you end up using. Best advice to learn C is to *slow down*, there is a lot there. Understand every function you use. Read the man page. Ask here. Learning C is a lot like eating a whale -- you do it one byte at a time... `:)` Good luck with your coding. – David C. Rankin Sep 25 '20 at 03:21
  • Also, enable compiler warnings `-Wall -Wextra -pedantic -Wshadow` on gcc/clang or `/W3` for VS. If using VS, use the *Developers Command Prompt* that will open a terminal where you can compile directly without messing with setting up a project. Just `cl /W3 /wd4996 /Ox /FeYouExeName /Tc yourCsource.c` (you can create a separate `obj` and `bin` dir so put your object files and executables in to keep your source directory clean) `/Fo` is the object file output switch, so `/Foobj/` will put the file in `obj/` and `Febin/YouExeName` will put `YourExeName.exe` in `bin`. Again, take it slow. – David C. Rankin Sep 25 '20 at 03:27
  • When you are done with your code -- however you do it -- try entering `"too old"` for `age` and `"lots of money"` for `salary`. If the errors are not handled gracefully -- find out why and fix it. Step-by-step. – David C. Rankin Sep 25 '20 at 03:30