How to fix segmentation fault when trying to scanf a string? - c

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.

Related

Storing and retrieving strings in Multidimensional array

I have the following code which stores string-input from a user N times in a multidimensional array. And then print out the second element.
main()
{
// Array to store 10 strings, 20 characters long.
char strStorage[10][20];
printf("\nEnter how many strings: ");
scanf( "%d" , &num);
fflush(stdin);
for ( count = 0 ; count < num ; count++)
{
printf("Enter a string: ");
gets(strStorage[count]);
fflush(stdin);
}
printf("%s", strStorage[2]);
Last line prints out garbage. The user-input is not visible inside the garbage hence either my element access is wrong or my storage is wrong. Can anyone help me with regards to what is the problem?
Thanks in advance...
strStorage[2] is the third string, so if num is less than 3, you won't initialize it and it will contain garbage.
scanf("%d", &num); doesn't ensure that num contains a value. Perhaps it'd be wise to check the return value of scanf to ensure it read 1 value, like this: if (scanf("%d", &num) != 1) { puts("Error reading integer"); }
While we're on this topic, I presume num and count are declared as an int, and you've hidden the declarations from us. Tsssk! Do you want our help? If so, then make your code compilable! Do you really think int is suitable for storing indexes to arrays? It's possible that they might have negative values. I'd suggest using size_t, instead, and the %zu format specifier tells scanf to expect a size_t value from stdin.
... and what happens when that size_t contains a value greater than the number of elements in your array? I suggest researching variable length arrays.
fflush(stdin); is nonsense because fflush defines behaviour for files open for output, and stdin is a file open for input only. That's sort of like flushing the toilet and expecting the waste to come out of the bowl, rather than going down through the S-bend. Perhaps you mean to discard the remainder of a line, because you've read the data you need from the start of it. Something like for (int c = getchar(); c >= 0 && c != '\n'; c = getchar()); might work.
Don't use gets. Use fgets(strStorage[count], sizeof strStorage[count], stdin); instead, to ensure that no buffer overflows occur.
There, I think I covered just about every bit of undefined behaviour and nit-picky stuffs.

Input several ints on one line, return number of ints

I'm learning C after having learned Java (among other languages), and I'm somewhat confused about how to handle this simple problem. I need to write a program that will take input in the form of one line. For example, 5 2 75 43 68 (only ints), and I need to return the number of ints, their sum, and the number of positive and negative numbers.
The problem is that the number of inputs is, obviously, variable--there may be one int or seven, but all will be on one line. I'm not sure how to use C to handle the variable number of inputs. Can someone point me in the right direction?
TO handle variable number of inputs, you need to loop and scanf until you press (Ctrl+D)
Here is an example:
int n,sum=0,count=0;
while(scanf("%d",&n)!=EOF)
{
sum=sum+n;
count++;
}
printf("sum=%d,count=%d",sum,count);
Note: When you press Ctrl+D, scanf returns -1, and hence the process of taking inputs terminates!
Cheers!
Input a string, then search for spaces, then convert each number at that position and add it to count and sum. This way, you only need to have the upper limit on the buffer where you're inputting the line; you don't use an array, so you don't need to either limit the number of ints nor do you have to mess with dynamic allocation.
EDIT: In Kerrek SB's format, combine fgets, strchr and strtol. :P
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buffer[200];
int numbers[100];
int main() {
char *ptr;
int cnt = 0;
fgets(&buffer[0], 200, stdin); // Get the string
ptr = strtok(buffer, " "); // Split in every space
do {
printf("Number %d: %s\n", cnt, ptr);
numbers[cnt] = strtol(ptr, NULL, 0); // Grab number
cnt++;
} while((ptr = strtok(NULL, " ")));
printf("Total numbers: %d\n", cnt);
}

Am I using scanf incorrectly?

Each line of input is a line with a command followed by numbers (except in the exit case).
I cannot figure out what I am doing wrong. This segment is looking for the store command and then does the action store entails:
char command[20];
while(strcmp(command, "exit") != 0)
{
/*scans for command strings inputted*/
scanf(" %s", command);
/* handles store command*/
if(strcmp(command, "store") == 0)
{
memory[0] = 1;
scanf("%d %d %d %d %d", &startx, &starty, &finishx, &finishy, &number);
for( i = startx; i < finishx; i++)
{
for(j = starty; j < finishy; j++)
{
square[i][j] = number;
}
}
}
}
Yes, you are using it wrongly(a). The line:
scanf(" %s", command);
has no bounds checking on the input. If someone enters more than 19 characters into your program, it will overflow the char command[20] and result in undefined behaviour.
The two major problems with scanf are:
using it with an unbounded %s since there's no way to control how much data is entered. My favourite saying is that scanf is to scan formatted information and there's not much less formatted than user input.
not checking how many items were scanned - it's possible to scan less than you expected.
If you want to do it right, see here. It uses fgets to get a line, protecting from buffer overflows and detecting problems.
Once you have the line as a string, you can safely sscanf it to your heart's content, since you know the upper bound on the length and you can always return to the start of the string to re-scan (not something easy to do with an input stream).
Other problems with your code include:
The initial strcmp on an uninitialised buffer. It could actually be (arbitrarily) set to exit which would cause your loop not too start. More likely is that it's not a valid C string at all, meaning strcmp will run off the end of the buffer. That won't necessarily end well.
No checking that all your numeric items were entered correctly. You do that by checking the return value from scanf (or sscanf should you follow my advice on using the rock-solid input function linked to here) - it should be five but entering 1 hello 2 3 4 would result in a return code of one (and all but startx unchanged).
No range checking on input. Since square has limited dimensions, you should check the inputs to ensure you don't write outside of the array. That's both numbers that are too large and negative numbers.
(a) Syntactically, what you have is correct. However, from a actual semantic point of view (i.e., what you mean to happen vs. what may happen), there's a hole in your code large enough to fly an Airbus A380 through :-)

segmentation fault in C programming

I started learning C programming and in this program I am trying to get user input and then a line at a time and decide if it contains non-int characters. I've been trying this method:
scanf("%d", &n);
if (isalpha(n))
{
i = -1;
}
I googled a bit and learned the function isalpha is good way to do it. However, I'm getting a segmentation fault every time I test the fragment above with non-int characters (letters for example). Any suggestion would be appreciated.
The %d format specifier forces scanf() to only accept strings of digits. Given anything else, it will fail and leave n unfilled (and assuming you didn't initialize n before, it will be filled with garbage).
The crux of the problem is that isalpha() expects a value between 0 and 255, and has an assertion to enforce it. At least on my VC++ compiler, it causes a crash with an access violation when given an invalid value (in non-debug mode).
To solve this you just have to switch to a %c format specifier. Converting n to a char would also be advisable as that makes your intent of reading a single character clearer.
EDIT: Given your clarifications in the comments, you can leave everything as is and simply check the return value of scanf() instead of going the isalpha() route. It returns the number of values read successfully, so when it encounters a non-integer or end of file, it will return 0. E.g.:
int main() {
int n;
while (scanf("%d", &n)) {
printf("Got int: %d\n", n);
}
}
I have no idea why you're getting a seg-fault. I'd have to see more of your program.
But using "%d" for scanf will only accept integer values and you'll get "0" for n that isn't an integer and therefore isalpha(n) will always be false and i will never be set to -1.
Perhaps you aren't initializing i and therefore it is never set. If you are referencing it later, that's probably the source of your seg-fault.
Use scanf("%c", &n), like this:
int main(char** argc, int argv) {
char n = 0;
int i = 0;
scanf("%c", &n);
if (isalpha(n)) {
i = -1;
}
printf("you typed %c, i=%d", n, i);
}
Make sure you have a character buffer to store the value in. Scan it as a string, and then use isalpha():
char buffer[32];
sscanf("%32s", buffer);
// loop and check characters...
if(isalpha(buffer[i])) ....
Note the use of %32s, this is to prevent buffer overflows (32 == size of buffer)
Given that n is an integer, we can diagnose that you are reading a value into n which is not in the range 0..255 plus EOF (normally -1), so that the code for isalpha(n) is doing something like:
(_magic_array[n]&WEIRD_BITMASK)
and the value of n is causing it to access memory out of control, hence the segmentation fault.
Since scanf():
Returns the number of successful conversions, and
Stops when there is a non-integer character (not a digit or white space or sign) in the input stream,
you can use:
#include <stdio.h>
int main(void)
{
char n = 0;
while (scanf("%c", &n) == 1)
printf("you typed %d\n", n);
return 0;
}

Is there a way to read a c-string and then an int with a single scanf in C?

Hey,
I'm trying to get this function to get the following output with the listed input, the "..." is where I'm not sure what to write:
void Question8(void)
{
char sentence[100];
int grade;
scanf(….);
printf("%s %d", sentence, grade);
}
Input:
My CS Grade is 1000
Output:
My CS Grade is 100
However, the kicker is that I need the scanf to read a c-string and then an int with a single scanf command, is this even possible?
Edit:
I can only edit the code in the location with the three periods ( "..." ), I cannot use anything more. I can assume that the input listed is expected but I cannot change anything outside of the three periods.
The output does not contain typos, the purpose of this assignment is to use flags and escape sequences.
It is possible to read pre-formatted string using scanf, however the format must be strict.
This version will continue to read the input until a digit is encountered and then read an integer.
Here is your code again:
char sentence[100];
int grade;
scanf("%[^0-9] %d",sentence,&grade);
printf("%s %d\n", sentence, grade);
I'll get this over with quick:
<obligatory_rant>
stupid question, but I guess it's homework and you're
stuck with these absurd limitations
</obligatory_rant>
Then, if you need to read everything up to but excluding the first digit, then the number:
if (scanf("%100[^0-9] %3d", text, &number) == 2)
...
Notes:
100 in "%100[... should be whatever your actual buffer size is to protect against buffer overrun.
The %3d documents that at most 3 digits should partake the the numeric value, so 1000 is correctly read as 100.
[^...] means the string made up of characters not ("^") in the following set, which is then specified as 0-9 - the digits.
if (... == 2) tests whether both positional parameters were scanned / converted successfully.
If you can't add an if and error message, then simply:
scanf("%100[^0-9] %3d", text, &number)
Tested in Visual Studio 2008
#include <stdio.h>
int main()
{
char sentence[100];
int grade = 0;
scanf("%[^0-9] %d",sentence,&grade);
printf("%s %d", sentence, grade);
return 1;
}
Input :
My CS Grade is 100
Output :
My CS Grade is 100
This is a really horrible question. A correct set of scanf parameters would be "%14c%3d", sentence, &grade
Because a space is included in the printf statement the trailing space needs to not be stored in sentence. Because the input contains other spaces there is no other solution (that I can thing of) than a fixed length. The integer parsing also requires a fixed length to truncate 1000 to 100.
I can think of no reason to ever write code anything like this. The code fits the requirements but wouldn't be useful in any other circumstances. I think that this is a very poor training exercise.

Resources