For the purposes of an exercise, I was given a snippet of code and told to find the bug. I removed a bunch of noise and the part that is tripping me up is the following:
int main() {
char *p;
char n;
scanf("%i", n);
if (n < get_int()) {
p = malloc(n);
}
}
Here, if I enter a number for n, I get a seg fault. If I enter a character, n is set to 0. What is scanf doing that makes this so?
Edit: the exercise I'm trying to figure out is Exercise 2 from this page
It is simply UB.
C does not specify any specific behavior here. "%i" expect a int *, not an uninitialized char converted to an int.
"What is scanf doing that makes this so?" implies defined behavior. There is no specified UB.
"If I enter a character, n is set to 0. " --> scanf() does not attempt to change n, it uses a copy of n (passed by value).
The usual scanf() usages is like the below where the address of nn is passed, not nn itself.
int nn;
if (scanf("%i", &nn) == 1) Success();
else Failure();
You aren't just passing the wrong kind of variable to scanf, you are also passing it's value instead of the pointer to it. The scanf have no way of knowing this value isn't an actual pointer to store the scanned data into, so thats exactly what its going to try and do, scan the input and place it into whatever memory address the n, treated as pointer value, happened to point to. In the absolute most of the cases this will attempt to access unmapped/protected/etc memory, and cause segfault/access violation.
Entering a character simply terminates the scan prematurely, avoiding the segfault, and leaving the n intact. Bit since the value of n isn't initialized, it can happen to be just about anything, any junk that happened to be on the stack at that point of time.
Related
After compiling my program of Dice Roll, I got this error. What is wrong with the code?
Also before I was using gets() instead of scanf() command, but because of that I got this error - passing argument 1 of 'gets' makes pointer from integer without a cast
So I removed the gets() command and used scanf and then there was no error regarding scanf().
What is the reason for getting these two errors?
Ok, so as per the answer I got to know how I should have used the gets() command and why I shouldn't use it instead should use scanf(). So, I made the changes.
Though I have encountered two new errors, this time it's related to the delay() command that I used.
Errors: undefined reference to delay
|error: ld returned 1 exit status|
OK so I solved my last errors by using Sleep() command from windows.h library instead of Delay() command. The programs was compiled.
But still there is a runtime error in the program, it works well till getting the roll1 but then it just print the next two statement and terminated the programs without taking a input for the guess.
It skips all the code after printf("Will it be Higher/Lower or the same? (press H/L/S)\n"); and directly terminates the program.
Ok So I solved above problem adding a whitespace before the "%c" in scanf(" %c", &nextGuess); statement. (Little things xD)
Now only problem is that my toupper() command is not working.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <windows.h>
int main()
{
int i, roll1=0, roll2=0, NumberOfRolls, RandomNUM1[50], RandomNUM2[50];
char nextGuess;
puts("Welcome to the Dice Roll Game");
puts("How many times do you want to roll a dice?");
scanf("%d", &NumberOfRolls);
for( i=1; i<=NumberOfRolls; i++ ) {
RandomNUM1[i] = ( rand()%6 ) + 1;
roll1 += RandomNUM1[i];
}
printf("\nYou Got %d in your first roll!\n", roll1);
Sleep(3000);
printf("\nLet's see if you can guess the value of next roll.\n");
printf("Will it be Higher/Lower or the same? (press H/L/S)\n");
scanf(" %c", &nextGuess);
toupper(nextGuess);
for( i=1; i<=NumberOfRolls; i++ ) {
RandomNUM2[i] = ( rand()%6 ) + 1;
roll2 += RandomNUM2[i];
}
if(nextGuess=='H'){
if(roll1<roll2){
printf("You are such a player, you guessed it right! It's %d", roll2);
}
else if(roll1>roll2){
printf("Uh-Oh! Bad Luck! First roll was higher, It's %d", roll2);
}
else if(roll1==roll2){
printf("Uh-Oh! Bad Luck! Both the rolls are same, It's %d", roll2);
}
}
if(nextGuess=='L'){
if(roll1>roll2){
printf("You are such a player, you guessed it right! It's %d", roll2);
}
else if(roll1<roll2){
printf("Uh-Oh! Bad Luck! First roll was lower, It's %d", roll2);
}
else if(roll1==roll2){
printf("Uh-Oh! Bad Luck! Both the rolls are same, It's %d", roll2);
}
}
if(nextGuess=='S'){
if(roll1==roll2){
printf("You are such a player, you guessed it right! It's %d", roll2);
}
else if(roll1>roll2){
printf("Uh-Oh! Bad Luck! First roll was higher, It's %d", roll2);
}
else if(roll1<roll2){
printf("Uh-Oh! Bad Luck! Second roll is higher, It's %d", roll2);
}
}
return 0;
}
You have a stray ,
At the 2nd line of your main, you declare char nextGuess, instead of char nextGuess;
The compiler tells you it expects specifiers or ... after , so either you add these, or you end the line properly with;.
And for the other problem you mention:
passing argument 1 of 'gets' makes pointer from integer without a cast
Since gets argument should be char *str and you didn't provide it.
You can fix that by, for example:
char tmp_NumberOfRolls[10];
gets(tmp_NumberOfRolls);
NumberOfRolls = atoi(tmp_NumberOfRolls);
but I prefer the scanf solution
PS: (in a now edited version of the code) ***//Error Line*** is not a comment (at least, not all of it) since the *** is still counted as part of the code and will cause an error. Either move the // a bit to the left or enclose that whole part with /* ... */
The gets function reads a string (till the next newline character) from stdin, therefore it asks for a char pointer (char*) to an area of memory where it can put all the characters read including string terminator. The mistake you made is to pass to that function a pointer to int, therefore not only you get a compiler error because you tried to pass an int pointer to gets (for which there are none implicit conversions), but even if it compiled, it would not have worked as you expected because it would put all the characters (which have a size of one byte - most of the times) in that area of memory encoded as chars. That means that when you try to dereference one using a pointer to int, the characters are "read like they were an int"!
There is an example (supposing 1-byte chars, 4-bytes int and a little endian machine and that there is an implicit cast from int* to char* which does not exist and hopefully will never exist):
int num
gets(&num);
if I input 123 in stdin, the memory area pointer by num is large enough to contain the string, but it would contain (exadecimal representation):
0x31|0x32|0x33|0x00
because the string is 3 characters long, ASCII code for '1' is 0x31, for '2' is 0x32, and for '3' is 0x33 and terminates with '\0'!!
Then, when you try to dereference it you get this int (binary representation - supposing a little endian machine):
00000000|00110001|00110010|00110011
which is not the int value 123 but instead 3224115. Which is not what you wanted to get.
When you use the scanf function, you pass to that function a format string which tells it how to interpret the next arguments passed to it and performs the appropriate conversions between the string read to the right type you specified. That means that you should still pay attention to what you tell to the scanf function while you write the format string (if you tell it to read a string and pass to it a pointer to int, the program will probably crash), but it performs the appropriate conversions for you, if you write the format string correctly.
That's why with scanf everything works perfectly (you're reading an int and you specified "%d" as format string), whereas with gets it does not compile to avoid serious mistakes.
I also would like to remark some points of this answer:
The example is just for didactic purposes and the code provided does not work indeeed. The fact that I supposed that it compiles is for didactic purposes; obviously, the code provided in that example does not compile
If, in that example, we input a string larger than 3 characters (which are four if we include the null terminator) or the int (char) type contains less (more) than 4 (1) byte, the program would have crashed because we corrupted other areas of memory
The reference is more expert than me in techincal stuff, so here are the links to the gets function and the scanf function: gets, scanf (and the ASCII Table is useful too)
You could use the gets function together with the atoi function to parse the string read from gets (using a char pointer to a free area of memory large enough to contain the string, which is pretty hard to allocate (*)) to an int, but scanf is the best approach.
(*) Remember: if you allocate an area of memory that contains 20 chars, the user will input 21 chars. The atoi function fails, but the worst thing is that you have a buffer overflow (and can be a high security issue if your program runs under root permissions).
I am trying to use sscanf (on a string that I know is well formed and not malicious) to write a value to a specific part of an array.
#include <stdio.h>
int main(void){
int arr[10], i;
sscanf("5 5", "%d %d", &i, arr + i);
}
When I run this in valgrind, I am told that the sscanf line reads an uninitialized value. However, arr is initialized as a pointer to an automatically allocated array and i is initialized because there is a sequence point associated with every format specifier (this is not the case for general functions which do not have sequence points between arguments). I might be inclined to believe that this is only happening because valgrind does not know about that format specifier sequence point rule, but in my full program the line causes an actual segmentation fault. Why is this happening? Am I mistaken about format specifiers creating sequence points?
The problem is that the sequence point of the function call means that i is definitely accessed for the argument computation of arr + i before it is set by sscanf. So you are accessing it before it is initialized.
You'll need two sccanf calls; something like:
int len, i;
if (sscanf(buf, "%d%n", &i, &len) >= 1 && sscanf(buf+len, "%d", arr + i) >= 1) {
// successful sscanf
Note that your should ALWAYS be checking the return value of the sscanf call to see if it worked.
Easy code down below.
Mac OS X 10.10.5, Xcode 7.2, C-file.
If I input 1, and afterwards qwert, I get 0 and qwert back.
1 and qwer gives 1 and qwer.
1 and e.g. qwerty gives 121 and qwerty.
What have I missed - why can I write more than 4 chars (+null) to a 5 char variable?
Why is the integer affected?
#include <stdio.h>
int main() {
int userInput;
char q[5];
printf("Hello\n");
scanf("%d", &userInput);
printf("%d\nAnd\n", userInput);
scanf("%s", q);
printf("\n");
printf("%d\n%s", userInput, q);
return 0;
}
What have I missed - why can I write more than 4 chars (+null) to a 5 char variable?
There is nothing stopping you from accessing out of bounds portions of an array in c. This will compile:
char a[2];
a[10000] = 10;
Why is the integer affected?
What you are causing is undefined behavior and is likely the reason that your int is affected. You can learn more about this by reading about c arrays. This is happening because you are putting a 5 character string plus a null terminating character ( ie 6 chars) into a space only meant for 5. You are going outside the bounds of your array.
As a further note, scanf("%s" offers no method of protecting against this behavior. If a user puts in a string that is too long then too bad. That is why you should protect your input by using something like a format string of "%4s" or use fgets:
fgets(q, sizeof q, stdin);
Which are both ways you can protect your input from entering more than 4 characters.
[Edit] User/code can try to "write more than 4 chars (+null) to a 5 char variable". C does not specify what should happen when code does not prevent such an event. C is coding without the safety net/training wheels.
scanf("%s", q); reads and saves the 5 characters of "qwert" and it also appends a null character '\0'. #Weather Vane
Since q[] has only room to 5 characters, undefined behavior occurs (UB). In OP's case, it appear to have over-written userInput.
To avoid, use a width limit on "%s" such as below. It will not consume more than 4 non-white-space from the user. Unfortunately, extra text will remain in stdin.
char q[5];
scanf("%4s", q);
Or better, review fgets() for reading user input.
The reason that the int userInput is affected is that you are writing past the end of the char array (q). Since both of these are stack variables, the compiler you're using seems to be allocating memory on the stack for the local variables in "reverse order", it, they are being "pushed" in the order defined, so the first local variable listed is lower on the stack. So, in your case, when you write past the end of q, you are writing in the memory space allocated for userInput, which is why it is affected.
I came across the following code :
int i;
for(; scanf("%s", &i);)
printf("hello");
As per my understanding, if we provide integer input scanf would be unsuccessful in reading and therefore return 0, thus the loop should not run even once. However, it runs infinitely by accepting all types of inputs as successful reads.
Would someone kindly explain this behaviour?
That is the incorrect format specifier for an int: should be "%d".
It is attempting to read a string into an int variable, probably overwriting memory. As "%s" is specified, all inputs will be read thus scanf() returns a value greater than zero.
(Edit: I don't think this answer should have been accepted. Upvoted maybe, but not accepted. It doesn't explain the infinite loop at all, #hmjd does that.)
(This doesn't actually answer the question, the other answers do that, but it's interesting and good to know.)
As hmjd says, using scanf like this will overwrite memory ("smash the stack"), as it starts writing to i in memory, and then keeps going, even outside the 4 bytes of memory that i takes up (or 8 bytes, on a 64-bit platform).
To illustrate, consider the following bit of code:
#include<stdio.h>
int main() {
char str_above[8] = "ABCDEFG";
int i;
char str_below[8] = "ABCDEFG";
scanf("%s", &i);
printf("i = %d\n", i);
printf("str_above = %s\nstr_below = %s\n", str_above, str_below);
return 0;
}
Compiling and running it, and entering 1234567890 produces the following output:
i = 875770417
str_above = 567890
str_below = ABCDEFG
Some points:
i has little correspondence to the integer 1234567890 (it is related to the values of the characters '1',...,'4' and the endianness of the system).
str_above has been modified by scanf: the characters '5',...,'0','\0' have overrun the end of the block of memory reserved for i and have been written to the memory reserved for str_above.
The stack has been smashed "upwards", i.e. str_above is stored later in memory than i and str_below is stored earlier in memory. (To put it another way &str_above > &i and &str_below < &i.)
This is the basis for "buffer overrun attacks", where values on the stack are modified by writing too much data to an array. And it is why gets is dangerous (and should never be used) and using scanf with a generic %s format specifier should also never be done.
#include<stdio.h>
main()
{
int i;
char c;
for (i=0;i<5;i++){
scanf("%d",&c);
printf("%d",i);
}
printf("\n");
}
I thought it will print 0 1 2 3 4 but it didn't.
What's the reason of the strange output?
Undefined Behaviour.
You're attempting to read an int (the "%d" in the scanf call) into an object of type char (the c). Don't do that!
This program exhibits undefined behavior: The type of &c (char *) does not correspond to the type of the scanf arg (%d wants a signed int *).
What is probably happening is that the scanf is writing 4 bytes to the memory location starting at the address of c. Which is only 1 byte long, so the other 3 bytes overwrite the first 3 bytes of i's value. On a little-endian system, that would effectively set i to whatever integer value you enter shifted right by 8 bits.
But, of course, the behavior is undefined. Next time you compile this code, it could do something completely different. A different compiler, or the same compiler with different options, could keep i in a register (where scanf cannot overwrite it) (but it might instead smash the return address on the stack, causing a crash when the program ends), or it could put the values on the stack in the opposite order (same deal), or it could leave 4 bytes on the stack for c (causing no unexpected behavior), or it could detect the situation and abort with an error, or it could even make demons fly out of your nose.
You are reading into c but printing i, so you will see 01234. You probably mean to print c. But that's not enough. c should be declared int because you are reading with format "%d", which expects an int. Either use format hhd or change c to type int
scanf("%d",&c);
should be
scanf("%c",&c);
btw. It will ask you to input value for c in every iteration and then it will print one value of i. What are you getting?
What's the reason of the strange output?
It's because you havn't used scanf properly. When you use "%d", you have to give scanf a pointer to an int, but you gave it a pointer to a char.
Change your char c to int c
(You should also check functions for errors. e.g. scanf will return EOF on end of input. It will also return the count of assigned values. Since you have given it 1 value, &c , you should check that scanf returns 1. If it does not, something bad might have happened)