0
#include <stdio.h>
int main(){
    int age;
    char name[] ="";
    printf("enter your age: ");
    scanf("%d", &age);
    printf("enter your name: ");
    scanf("%s", &name);
    printf("your name is %s and you are %d years old.",name, age);

    return 0;
}

If i for example set the age to "20" and the name to "name", it outputs the following:

your name is name and you are 6647137 years old.

Why does it say "6647137" years instead of 20?

enter image description here

user438383
  • 5,716
  • 8
  • 28
  • 43
Uzzlius
  • 13
  • 3

3 Answers3

1
char name[] ="";

You do not define name correctly.

#define MAX_NAME_LENGTH 100
char name[MAX_NAME_LENGTH+1]; 

Defining it incomplete and completing it afterwards you make it point to some region where there may be other variables aound the array defining the string literal, or the string literal can be even in RO memory, making it impossible to write. It is undefined behavior trying to write at the pointer of a string literal (6.4.5.p6 String literals, page 63).

alinsoar
  • 15,386
  • 4
  • 57
  • 74
  • thanks, this solved the problem, but how can "name" effect "age"? – Uzzlius Oct 03 '21 at 15:16
  • @Uzzlius: When you write past the end of the memory buffer `name`, then you are possibly writing into something else that is important. In this case, it seems to be `age`. – Andreas Wenzel Oct 03 '21 at 15:17
  • @Uzzlius If the compiler puts the location of `age` into a zone next to the string literal "", trying to write to the location of "", this will overwrite the age. – alinsoar Oct 03 '21 at 15:17
  • 1
    @Uzzlius: note that you will probably get a different result if you declare ```name``` before ```age```. (This is **not** a fix - it's still undefined behavior, it's just .... different undefined behavior). – sj95126 Oct 03 '21 at 15:21
  • Limiting `scanf("%99s"...)` to prevent buffer over-run is a good idea, too. – Neil Oct 03 '21 at 15:24
  • 1
    @Neil 100, as I put `LEN+1`. – alinsoar Oct 03 '21 at 15:36
  • That's not exactly what's happening, [see this](https://stackoverflow.com/q/9460260/2472827), but practically, this is a good answer. Also, this allows you to do `#define XQUOTE(name) #name ... #define QUOTE(name) XQUOTE(name) ... scanf("%" QUOTE(MAX_NAME_LENGTH) "s", name);`, which is more versatile. – Neil Oct 03 '21 at 15:59
  • @Neil What means "not what's happening"? It's an initialization of incomplete array to be the same as a string literal and that initialization completes it to the LEN(str+1). What is not clear? If you call scanf to point to a string literal's pointer location as input, this is undefined. So we define it as complete uninitialized array. – alinsoar Oct 03 '21 at 16:15
  • Pedantic: `char *str = "";` declares a read-only string literal, which should be `const`; `char str[] = "";` declares a fixed-size one-character modifiable array containing `'\0'`, which is the only valid C string of one byte; it is a buffer over-run. – Neil Oct 03 '21 at 16:30
  • @Neil yes, in the C language this is called an incomplete array completed by string literal initialization. You pass to scanf the address of this string literal. – alinsoar Oct 03 '21 at 17:18
  • @Neil practically, what happens is to give a name to the string literal array. – alinsoar Oct 03 '21 at 17:20
  • @Neil in the example that you quoted there is a pointer variable that is initialised to point to the string literal. this is totally different. – alinsoar Oct 03 '21 at 17:22
  • @alinsoar I was referring to ISO/IEC 9899:6.7.8, "`char s[] = "abc";` The contents of the array are modifiable. On the other hand, the declaration `char *p = "abc";` defines `p` with type "pointer to char" and initializes it to point to an object with type "array of char" with length 4 whose elements are initialized with a character string literal. If an attempt is made to use `p` to modify the contents of the array, the behaviour is undefined." I think this falls under the former, (though this is only peripherally related to your answer.) – Neil Oct 03 '21 at 20:33
  • @Neil 6.4.5.p6 , page 63. It says explicitly that trying to modify it is undefined. This means, if the compiler stores it in a RW segment, it will work, but if it is stored in a RO segment, it will fail. – alinsoar Oct 03 '21 at 21:30
1

You overwrite beyond the much too short array of chars for name.
It currently is of size 1, holding exactly only the teminating '\0'.
Your weird age value can in many environment be explained by the integer being the first victim.

Make sure to use an array of sufficient size.
Also it is very much recommended to use advanced features of scanf() to avoid buffer overrun.

Please read the documentation:
https://en.cppreference.com/w/c/io/fscanf

And this article might be very helpful:
http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
1

Learn this soon and learn this well: C does not have a first-class "string" type!

When you wrote

char name[] = "";

you did not declare a string variable, that initially contained an empty string, but that could and would automatically expand to contain any string you tried to assign to it.

No, what you got was an array of char of size exactly 1, initially containing the empty string, consisting of exactly (and only) the string-terminating character '\0'. This array can't be used for much of anything: the only thing it's ever going to be able to contain is the empty string, because it doesn't (and will never) have room for anything more.

In C, it is generally your responsibility to know how big your strings are going to be, and to explicitly allocate each variable to refer to enough memory for any string it might contain.

For example, you could write

char name[11] = "";

Now what you're saying is, "Give me an array of characters, sufficient to contain strings up to 10 characters long (plus 1 for the terminating \0 character), initially containing the empty string."

Now you can safely say

scanf("%s", name);

But there are two more points to make.

First, you'll notice that I have left out the &. You might have gotten the impression that you always need the & on your variables when you call scanf. And that's a real rule, but it has an exception: it turns out that you do not need the & when you're using %s to read a string into an array. Sometimes the error is innocuous (the code will happen to work anyway), but sometimes it will cause problems (such as when you use %s to read into an array pointed to by a pointer variable). My compiler warns me warning: format specifies type 'char *' but the argument has type 'char (*)[1]' when I do something like this.

But second, if we've declared

char name[11] = "";

, then how do we actually enforce that? How do we arrange that we, or a function like scanf over which we have less control, won't accidentally try to write more than 10 characters to our name array?

As we've already seen, if you just call

scanf("%s", name);

you don't get any protection. scanf will read as many characters as the user types, until it sees a newline or other whitespace, and it will write all those characters to the variable you provided, and if that's more characters than the variable can hold, boom, something bad happens.

One way of protecting against this is to give scanf a limit on the number of characters it can read:

scanf("%10s", name);

Now, by putting that 10 in there, you're telling scanf not to read a string longer than 10 characters, so it won't overflow your name array.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103