3

I am new to C programming, and I don't know what I'm doing wrong. I have a main method and I have made a struct.

I need to make an array in main of that struct and then assign the values via scanf in the for loop.

My program compiles without an error, but after entering anything in the console, it throws exceptions that I do not understand. The exceptions that I get are:

Warning C4477   'scanf_s' : format string '%s' requires an argument of type 'char *', but variadic argument 1 has type 'char **'

Warning C4473   'scanf_s' : not enough arguments passed for format string   

Here is how my code looks like:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

struct Employee {
    char *ccFirstName;
    char *ccLastName;
    int iAge;
};

int main(void) {
    struct Employee sExampleEmployee[3];

    for (int i = 0; i < 3; i++) {
        printf("Geben Sie den Namen :");
        sExampleEmployee[i].ccFirstName = scanf_s("%s", &sExampleEmployee->ccFirstName);
        sExampleEmployee[i].ccLastName = scanf_s("%s", &sExampleEmployee->ccLastName);
        sExampleEmployee[i].iAge = scanf_s("%d", &sExampleEmployee->iAge);
    }

    for (int i = 0; i < 3; i++) {
        printf("First name of the employee: %s", sExampleEmployee[i].ccFirstName);
        printf("Last name of the employee : %s", sExampleEmployee[i].ccLastName);
        printf("Age of the employee: %d", sExampleEmployee[i].iAge);
    }
    return EXIT_SUCCESS;
}
2
  • Honestly, what you're doing wrong is that you are using scanf. It is a source of much confusion and pitfalls. Avoid it until you understand the language very well. This is a good read: sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html Commented May 1, 2020 at 20:50
  • You should add a trailing newline \n at the end of each of the printf format strings. Commented May 1, 2020 at 21:37

2 Answers 2

2

You have several 'points of confusion', it would seem!

First, the return value of the scanf_s function is not the actual value read, but rather the number of items successfully scanned. So, your assignments like sExampleEmployee[i].ccFirstName = scanf_s(... won't do anything like what you appear to think they will.

Second, your sExampleEmployee-> usage won't get the structure field of any structure but the first - as an array name used without an [] index will, effectively, be a pointer to the first member of the array. (See next point on arrays!)

Third (and very important), you have not assigned any memory to the ccFirstName and ccLastName structure members - they are just uninitialized pointers. You would be better off declaring them as arrays of fixed length: long enough to hold any possible input plus the required terminating nul character.

Finally (a more subtle problem), when you have a string argument (corresponding to the %s format specifier) in the scanf_s function, you need to add an extra argument (immediately after the string) specifying the size of that string buffer (to prevent reading in too many characters).

So, with these points in mind, you should re-define your structure, and re-write your input loop, to something along these lines:

#define MAXNAMELEN 100 // Use whatever value you want - this will allow 99 letters
struct Employee
{
   char ccFirstName[MAXNAMELEN];
   char ccLastName[MAXNAMELEN];
   int iAge;
};
//...
   for (int i = 0; i < 3; i++)
   {
      printf("Geben Sie den Namen :");
      scanf_s("%s", sExampleEmployee[i].ccFirstName, MAXNAMELEN); // Arrays (strings) are automatically...
      scanf_s("%s", sExampleEmployee[i].ccLastName, MAXNAMELEN);  // ... pointers!
      scanf_s("%d", &(ExampleEmployee[i].iAge)); // But integers need the "&"
   }

EDIT: For more information on the scanf_s function, see here. The important part (for your case) is this:

Unlike scanf and wscanf, scanf_s and wscanf_s require you to specify buffer sizes for some parameters. Specify the sizes for all c, C, s, S, or string control set [] parameters. The buffer size in characters is passed as an additional parameter. It immediately follows the pointer to the buffer or variable.

NOTE: as can be seen in the MS documentation, the size argument after the pointer for %s has type unsigned for Microsoft's implementation of scanf_s, whereas it must have type rsize_t as per the C Standard Annex K. Type rsize_t is specified there as being the same as size_t which may have a different size from unsigned int, and indeed it does on 64-bit architectures for both Linux and Windows, so the MS doc specifies that The size parameter is of type unsigned, not size_t. Use a static cast to convert a size_t value to unsigned for 64-bit build configurations, A very peculiar approach to portability.

For this and other reasons, scanf_s should not be used in portable code.

Sign up to request clarification or add additional context in comments.

6 Comments

Note too that the return values from these scanf_s calls should be tested to detect premature end of file and conversion failures. The contents of the some of the structs would be uninitialized in such cases.
@chqrlie In an ideal world, yes of course. But that would make the answer considerably more complex, and could lead to more confusion in this case. (I did mention what the return value is, though.)
A simple approach: combine the 3 scanf_s into a single call with "%s%s%d" and check the return value, produce an error message and break from the loop. Alternately, for easier error recovery, read the input with fgets() and parse it with sscanf_s.
@chqrlie Again, I can't and won't disagree. However, a problem I have here (and quite often, when posting answers on SO) is trying to find the right balance between "Repository of Information" and being helpful to the question asker. I suppose I should lean more towards the former, but I'm inherently more of a teacher than a standard-setter.
@chqrlie Well, if the comments linger, that will add to the 'lesson'. (I see no reason why most of them can't be kept.) And, yes, scanf is a time-bomb! I have developed a far deeper understanding of it's subtleties since I've been answering on SO.
|
0

I think you want to store info for 3 employees when you use the array sExampleEmployee[3];.

You have to locate the memory for ccFirstName et ccLastName in each struct element.

for (int i = 0; i < 3; i++) {
   sExampleEmployee[i].ccFirstName = malloc(sizeof(char)*30); // max length of name is up to 29.
   if(!sExampleEmployee[i].ccFirstName) {
      // handle the error;
   }
   sExampleEmployee[i].ccLastName = malloc(sizeof(char)*30);
   if(!sExampleEmployee[i].ccLastName) {
      // handle the error;
   }
}
sExampleEmployee[i].ccFirstName = scanf_s("%s", &sExampleEmployee->ccFirstName);
sExampleEmployee[i].ccLastName = scanf_s("%s", &sExampleEmployee->ccLastName);
sExampleEmployee[i].iAge = scanf_s("%d", &sExampleEmployee->iAge);

should become (remove the ampersand & when you want to input the string):

scanf_s("%s", sExampleEmployee[i].ccFirstName);
scanf_s("%s", sExampleEmployee[i].ccLastName);
scanf_s("%d", &sExampleEmployee[i].iAge);

2 Comments

sorry, i forget !. We can use &sExampleEmployee[i].iAge, it's similar to &(sExampleEmployee[i].iAge)
Yes - but the %s format in scanf_s needs an extra (size) argument. (It''s not the same as scanf.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.