I was teaching the C programming language to a friend and we came up with something I could not explain. This is the code we wrote:
#include <stdio.h>
int main(void)
{
char num1;
char num2;
printf("%s", "Enter the first number: ");
scanf("%d", &num1);
printf("%s%d\n", "The number entered is:", num1);
printf("%s", "Enter the second number: ");
scanf("%d", &num2);
printf("%s%d\n", "The number entered is:", num2);
printf("%s%d\n", "The first number entered was:", num1); /* This was done for testing */
printf("%s%d\n", "The sum is:", num1+num2);
return 0;
}
The weird thing is that we tried to do 5 + 6 and we expected to get 11 but instead got 6, I added a line to see what's going on with the first number and it becomes 0 after the second number is read.
I am aware that the variables should be an int (in fact the original code was like that and worked) but my understanding is that a char is a small integer so I thought it would be 'safe' to use if we were adding small numbers.
The code was tested and compiled on a Linux machine with cc and on a Windows machine with cl. The output was the same. On the Windows machine the program throw an error after the addition.
I would like an explanation on why this code is not working as I expected. Thanks beforehand.
You cannot pass a pointer to a different datatype to scanf. scanf will write to memory assuming you gave it a pointer to what it expected (e.g. int for %d), and will exhibit wonderful undefined behaviour if you give it a pointer to a different datatype.
Here, what is most likely happening is that scanf is overwriting e.g. 4 bytes on your stack when your chars only take up 1 byte, so scanf will just be happily writing right over some other variable on your stack.
a char is a small integer so I thought it would be 'safe' to use it if we were adding small numbers.
That is correct, char is a small integral type , and it's OK to use it in integer arithmetic(although char may be signed or unsigned which may causes the result unexpected).
But the problem is, a pointer to char can NOT be used in a place where a pointer to int is expected. And this is the case for scanf("%d", &num1);, the second parameter is expected to a of type int *.
Related
I'm trying to make a program that writes to a file every combination of letters formed from a given phone number. I'm fairly positive it's giving the segfault where I scanf the name of the file the user wants to write to (in main) because I've put in printf tests and they don't print. For some reason, before adding in my recursive functions it doesn't give me a segfault but after adding them it does.
const char letters[8][5] = {"ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ"};
int main()
{
int userNum[7];
char userFile[25];
printf("Please enter the phone number you want to process with no added
characters or spaces: ");
scanf("%d", &userNum);
printf("Enter the file name (including extension and less than 25
characters) that you would like to write to.\n");
scanf("%s", userFile); //This is where I think the seg fault is happening
printf("Test"); //Because this doesn't print
FILE* file_ptr;
char fileName[17];
sprintf(fileName, "%s", userFile);
file_ptr = fopen(fileName, "w");
findWordsHelper(userNum, 7);
}
//Adding this function and the one after is what made the program start
//giving said seg fault
void findWords(int userNum[], int digit, char words[], int n)
{
printf("Test. n = %d", n);
int i;
if(digit == n)
{
printf("%s ", words);
return;
}
for(i = 0; i < strlen(letters[userNum[digit]]); i++)
{
words[digit] = letters[userNum[digit]][i];
findWords(userNum, digit+1, words, n);
if(userNum[digit] == 0 || userNum[digit] == 1)
{
return;
}
}
}
void findWordsHelper(int userNum[], int n)
{
printf("test");
char result[n+1];
result[n] = '\0';
findWords(userNum, 0, result, n);
}
I'm not in an environment that I can test but I see a few things. First off, your printf test isn't printing because if has no new line character. If you're not going to put one in, call fflush. E.g
printf("test");
fflush(stdout);
Second, your use of scanf to read in the phone number shows a bit of misunderstanding of how scanf will treat input as an integer. You don't need an array of 7 ints for this because you've simply instructed for the input to be no extra characters. So, a phone number like 345-6789 is input as 3456789 which will be read as a single int: i.e 3 million, 4 hundred 56 thousand, 7 hundred 89. This will be read into a single integer. I know you want to treat them as separate numbers, but scanf will treat them as 1 number when not separated by whitespace. To read into a single int, this would suffice:
...
int phoneNumber;
scanf("%d", &phoneNumber); // <--- notice the & operator
EDIT
I was reading over the manual page for scanf() and when the %s specifier is used, it should insert a null-character into the string. So, the part directing to check the return and ensure a '\0' character is placed there is not applicable.
EDIT 2
I had a moment this morning and had to figure this out. I think I've got it. Something in the function findWords() looked a bit suspect and compiling, seg-faulting, and looking at the core file showed it to be the case. It's this line in that function
for(i = 0; i < strlen(letters[userNum[digit]]); i++)
Specifically, it is the call to strlen() with the result of using userNum[] to index into letters[]. As myself, and others, have indicated, scanf() isn't going to read a "phone number" such as 3456789 (input as such) into 7 different integer values. It is read as a single int and it's going to be read into userNum[0] (and guess what? digit = 0 when the segfault occurs). This is no surprise, the letters array does not contain 3 million, 4 hundred 56 thousand, 7 hundred 89 indecies. (Assuming the number entered is what I wrote.)
As mentioned by Jasen (I think), ints don't really work well for phone numbers. At the very least, you'll have to develop a different manner of breaking apart the phone number to use as an index.
First of all, lack of printf output does not indicate that the program did not execute past that point, as the output is probably buffered.
You can either do fflush(stdout) immediately after a printf call, or use setvbuf with a null argument to force unbuffering.
Second, in your recursive function, each call results in calling itself up to the number of iterations in your for loop. I suspect there are logic errors there and the number of recursive calls may just explode.
Third, you definitely have a logic problem here:
int userNum[7];
...
scanf("%d", &userNum);
Looks like you are expecting that if a user enters "1234567", that each userNum[i] contains one of the digit, but that's not what's happening.
scanf would treat the argument as a pointer to an int, e.g. on a 32-bit CPU, it would put the integer value 1234567 into the first 4 bytes of "userNum". In fact, this could cause a segfault right there if the CPU cannot write to an integer (whether 16 or 32 bits) to an unaligned address. As userNum is actually an array, it may or may not be aligned suitable for an integer.
int main() {
int userNum[7];
char userFile[25];
printf("Please enter the phone number you want to process with no added"
" characters or spaces: ");
printf("Enter the file name (including extension and less than 25 "
" characters) that you would like to write to.\n");
scanf("%s", userFile); //This is where I think the seg fault is happening
should be :
scanf("%24s",userfile);
that puts a limit of 24 chars on what is read.
char fileName[17];
sprintf(fileName, "%s", userFile);
However userfile could be 24 long... and that can only fit 16, so it should be.
sprintf(fileName, "%.16s", userFile);
which chops off any overflow.
Suppose,"5181 2710 9900 0012"- is a string of digits.I need to take one single digit at a time as input from the string of number without space to make arithmatic operations . So, i write that,
int a[20];
for(int i=0;i<16;i++)
{
scanf("%d",&a[i]);
}
but it didn't give me expected result. But when i use "%1d"instead of "%d",it gave me the expected result. so, how it works?
Since scanf is the inverse of printf, you could verify this by printing any number with the modifier (just a little tip).*
In general, the number before the format is a 'width' modifier. In this case it means you're only reading one byte into a number. If you specify %d, it may be a number of arbitrary length.
Example:
#include <stdio.h>
int main() {
int a;
sscanf("1234", "%d", &a);
printf("%d\n", a); // prints 1234
sscanf("1234", "%1d", &a);
printf("%d\n"m a); // prints 1
}
*) this appears to be false for this particular case. Makes sense that numbers are not truncated when specifiying a %d format, though, since that would change the meaning of the number. However, for many cases you could try what printf would do to predict scanf's behavior. But of course, reading the manual or docs on it is always the more helpful approach :)
Suppose,"5181 2710 9900 0012"- is a string of digits.I need to take one single digit at a time as input from the string of number without space to make arithmatic operations . So, i write that,
int a[20];
for(int i=0;i<16;i++)
{
scanf("%d",&a[i]);
}
but it didn't give me expected result. But when i use "%1d"instead of "%d",it gave me the expected result. so, how it works?
Since scanf is the inverse of printf, you could verify this by printing any number with the modifier (just a little tip).*
In general, the number before the format is a 'width' modifier. In this case it means you're only reading one byte into a number. If you specify %d, it may be a number of arbitrary length.
Example:
#include <stdio.h>
int main() {
int a;
sscanf("1234", "%d", &a);
printf("%d\n", a); // prints 1234
sscanf("1234", "%1d", &a);
printf("%d\n"m a); // prints 1
}
*) this appears to be false for this particular case. Makes sense that numbers are not truncated when specifiying a %d format, though, since that would change the meaning of the number. However, for many cases you could try what printf would do to predict scanf's behavior. But of course, reading the manual or docs on it is always the more helpful approach :)
My C program is giving the number "32767" when I enter a letter, but when I enter an integer it tells me the number that I entered.
why will my program not tell me what letters I entered? why is it giving me the number "32767"?
#include <stdio.h>
main()
{
int number;
printf("Enter an integer\n");
scanf("%d",&number);
printf("Integer entered by you is %d\n", number);
return 0;
}
If scanf doesn't find what it's looking for (in this case, an int), it will simply return without modifying whatever gets passed in. In other words, scanf won't change number, so it'll have it's old value, which, in this case, is undefined (since it's not initialized).
What you are seeing is "undefined behaviour", which pretty much means "anything can happen". The value in number, in particular, can have any value, because it has not been initialized. If you initialize it int number = 42; it will (probably) print 42, but I'm not sure that's guaranteed.
If you want printf() to display characters and scanf() to get that data, you must point that it's a character, using "char" instead of "int" and use "%c" instead of "%d".
Something like this (I still used the "number" variable and the description in the printf() about the " integer":
#include <stdio.h>
main()
{
char number;
printf("Enter an integer\n");
scanf("%c",&number);
printf("Integer entered by you is %c\n", number);
return 0;
}
I created a very simple progam whith a menu,
that take a value, then memorize it into the
local variable value, and finally with the
second option the progam prints the value.
my question is:
Why does the program work only if I add an "h"
to the scanf parameter?
In other words: what kind of relation there is
between scanf() and my local int value variable?
thanks!
p.S. (I used Dev-C++ (GCC) to compile it.
With Visual Studio it works)
#include <stdio.h>
main () {
int value = 0;
short choice = 0;
do {
printf("\nYour Choice ---> ");
scanf("%d", &choice); /* replace with "%hd" and it works */
switch (choice) {
case 1:
printf("\nEnter a volue to store ");
scanf("%d", &value);
getchar();
printf("\nValue: %d", value);
break;
case 2:
printf("\nValue: %d", value);
break;
}
} while (choice < 3);
getchar();
}
With scanf, the "h" modifier indicates that it's reading a short integer, which your variable choice just happens to be. So the "%hd" is necessary to write only two bytes (on most machines) instead of the 4 bytes that "%d" writes.
For more info, see this reference page on scanf
The variable choice is of type short so that's why you need the %h specifier in scanf to read into it (in fact you don't need the d here). The int type just requires %d. See the notes on conversions here
You're reading into a short. The h is necessary because %d is the size of an int by default. See this reference page on scanf.
It looks like your problem is that choice is a short, which is (generally) 2 bytes long, while %d expects an integer, which is (generally) 4 bytes long… So the scanf clobbers whatever comes after choice on the stack.
choice is a short and %d specifies an int.
When you specify %d, scanf has to assume that the associated argument is a pointer to an int sized block of memory, and will write an int to it. When that happens it will likely be writing to data adjacent to but not part of choice and the results are undefined and probably not good! If it works in one compiler and not another that is simply the nature of undefined behaviour!
In GCC -Wformat should give you a warning when you make this error.
From the comp.lang.c FAQ:
Why doesn't the code short int s; scanf("%d", &s); work?
Someone told me it was wrong to use %lf with printf. How can printf use %f for type double, if scanf requires %lf?
%d is for reading an int, not a short. Your code never really "worked" -- it just appears that in this case you didn't notice any difference between what you wanted and the undefined behavior you got.
The modifier for scanf to input a variable of type short is %hd. Hence you need to specify the correct modifier.
scanf("%d",&integer); // For integer type
scanf("%hd",&short_int); // For short type
Hence it doesnt work.
Depending upon numeric padding, endian-ness, and other such issues, you may be storing either the upper or lower part of the input value into choice; you are storing the rest of the input value into memory that may or may not be being used for anything else.