String input using C scanf_s

38,069

Solution 1

From the specification of fscanf_s() in Annex K.3.5.3.2 of the ISO/IEC 9899:2011 standard:

The fscanf_s function is equivalent to fscanf except that the c, s, and [ conversion specifiers apply to a pair of arguments (unless assignment suppression is indicated by a *). The first of these arguments is the same as for fscanf. That argument is immediately followed in the argument list by the second argument, which has type rsize_t and gives the number of elements in the array pointed to by the first argument of the pair. If the first argument points to a scalar object, it is considered to be an array of one element.

and:

The scanf_s function is equivalent to fscanf_s with the argument stdin interposed before the arguments to scanf_s.

MSDN says similar things (scanf_s() and fscanf_s()).

Your code doesn't provide the length argument, so some other number is used. It isn't determinate what value it finds, so you get eccentric behaviour from the code. You need something more like this, where the newline helps ensure that the output is actually seen.

char name[64];
if (scanf_s("%s", name, sizeof(name)) == 1)
    printf("Your name is %s\n", name);

Solution 2

I used this very often in my university classes so this should work fine in Visual Studio (tested in VS2013):

char name[64]; // the null-terminated string to be read
scanf_s("%63s", name, 64);
// 63 = the max number of symbols EXCLUDING '\0'
// 64 = the size of the string; you can also use _countof(name) instead of that number
// calling scanf_s() that way will read up to 63 symbols (even if you write more) from the console and it will automatically set name[63] = '\0'
// if the number of the actually read symbols is < 63 then '\0' will be stored in the next free position in the string
// Please note that unlike gets(), scanf() stops reading when it reaches ' ' (interval, spacebar key) not just newline terminator (the enter key)
// Also consider calling "fflush(stdin);" before the (eventual) next scanf()

Ref: https://msdn.microsoft.com/en-us/library/w40768et.aspx

Share:
38,069
user3587529
Author by

user3587529

Updated on July 09, 2022

Comments

  • user3587529
    user3587529 almost 2 years

    I've been trying to look for answer myself, but I can't find one. I want to insert a part of the programming that reads in a string like "Hello" and stores and can display it when I want, so that printf("%s", blah); produces Hello.

    Here's the code part that's giving me trouble

    char name[64];
    scanf_s("%s", name);
    printf("Your name is %s", name);
    

    I know that printf isn't the problem; the program crashes after something is input after a prompt. Please help?

  • someone_ smiley
    someone_ smiley about 10 years
    isn't it : scanf("%s", name); (no '&')?
  • sarath
    sarath about 10 years
    '&' is not required here. The array name itself will give the address of the array.
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten about 10 years
    The & on &name does no harm, but is not needed. Leave it off because you understand the relationship between arrays and pointers. If you don't understand that relationship go read about it and then leave the extra & off.
  • wilson
    wilson about 10 years
    I changed it. Sorry, just a habit.
  • user3587529
    user3587529 about 10 years
    Still didn't work, it doesn't crash anymore, but the printf won't show the result.
  • user3587529
    user3587529 about 10 years
    Finally found the comment, still doesn't work. I used yours as a test run and the command screen just shows: Enter your name: Your name isPress any key to close
  • M-N
    M-N about 10 years
    I tried this and it worked, #include <stdio.h> int main(void) { char name[64]; scanf ("%63s", name); printf("Your name is %s", name); return 0; }
  • wilson
    wilson about 10 years
    The first line is prompting you for input, after you type in a name the program will display it back to you.
  • user3587529
    user3587529 about 10 years
    I'm using Visual Studio and it won't let me use scanf, just scanf_s, but even with the buffer, it won't work, just skips the %s in the printf afterwards.
  • M-N
    M-N about 10 years
    you started a visual c++ project?
  • user3587529
    user3587529 about 10 years
    Yes, I'm assuming that that's the problem... but what specifically?
  • M-N
    M-N about 10 years
    go to Project properties->Configuration Properties->C/C++->Preprocessor->Preprocessor Definitions click on edit and add _CRT_SECURE_NO_WARNINGS click ok, apply the settings and run the project
  • M.M
    M.M about 10 years
    @dmckee, name is correct and &name is wrong, however on common implementations both of those have the same representation, so it usually works. The %s specifier expects a char *.
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten about 10 years
    @MattMcNabb I agree that the correct for is without, but I don't think that it is by chance that it works the wrong way. I can't quote chapter and verse, but I think this is required: &name returns a pointer to a char array not a pointer to char, but scanf is variadic so all pointers are converted to void*, and a char array must be stored in a way that allows pointer indexing to work on it, so the &name form must work even though it is incorrect. Or something like that.
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten about 10 years
    Hmmm ... may be wrong about the pointer conversion. In which case a hypothetical compiler that stores pointers to arrays differently than other pointers could conceivably show different behavior.
  • Spikatrix
    Spikatrix about 9 years
    Shouldn't the third argument of scanf_s be cast to (unsigned int) as sizeof returns a value of type size_t?
  • Jonathan Leffler
    Jonathan Leffler about 9 years
    @CoolGuy: If you're going to cast it, it should be cast to rsize_t (see the quote in the answer), but K.3.3 Common definitions <stddef.h> says (in its entirety): The header <stddef.h> defines a type.The type is rsize_t which is the type size_t. and references footnote 385, which says: See the description of the RSIZE_MAX macro in <stdint.h>. The discussion of RSIZE_MAX is considerably more extensive, but the intent is that it should usually be smaller than SIZE_MAX, with SIZE_MAX >> 1 being a suggested size.
  • Jonathan Leffler
    Jonathan Leffler about 9 years
    @dmckee: The notation &name generates a char (*)[64] in the context of the question (char name[64];). The %s format expects a char *, and simply referencing name yields a char *. As you can see, the two types are quite radically different. The unfortunate curiosity is that the value of &name viewed as a void * is the same as the value of name viewed as a void *. This allows scanf("%63s", &name) to 'work', but doesn't stop it being incorrect.
  • Jonathan Leffler
    Jonathan Leffler about 9 years
    @dmckee: Also note that the compiler does not do any type conversions on the pointers passed to scanf() — only integer types shorter than int or floating type shorter than double are subject to default argument promotion when passed to variadic functions such as scanf(), and since scanf() only takes pointers (unlike scanf_s() which also takes some integers), there is no default argument promotion in valid calls to scanf().
  • Dev-iL
    Dev-iL about 7 years
    Welcome to Stack Overflow! While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. It also doesn't hurt to mention why this answer is more appropriate than others.
  • Aashishkebab
    Aashishkebab about 5 years
    Ah, yes. A code snippet with no context and doesn't answer the question. Brilliant.