Avoiding segmentation fault - c

I'm trying to port a software to Linux. It works greatly on Windows.
On Linux it gives me a segmentation fault on strcmp:
int main(void) {
...
char* comando;
char istruzione[100];
scanf("%[^\n]%*c", istruzione);
comando = strtok(istruzione, " ");
if (strcmp(comando, "fput") == 0)
...
}
The issue goes away using the array notation, but I need to use the * notation because I need to use strtok, which returns a * char.

strtok can return NULL, which means you need to check for that before using it in strcmp.

As a general rule in C, always check your return values, and always check that a pointer returned by a function is not NULL before attempting to do anything with it.
There's no telling what might have produced this segmentation fault without knowing the input to your program, but I would recommend very strongly that you avoid ever using scanf, which can produce segmentation faults or much worse. Here you could safely do this instead:
if (fgets(istruzione, 100, stdin) != NULL) {
comando = strtok(istruzione, " \n");
if (comando != NULL) {
if (strcmp(comando, "fput") == 0) {
....
The scanf call you are using reads one line of text without copying the newline character. fgets reads the newline character, but you can tell strtok to just treat it as another delimiter in order to ignore it.

Assuming, istruzione is a simple char buffer, I put together the following test. One thing I observed with what you had, was that if you just hit ENTER as the input for the scanf, doing the strcmp would cause a segmentation fault. This was because strtok returns NULL since there's nothing to tokenize.
You may need to test if strtok is returning a NULL pointer before using comando. You can see how I did this in the following test code.
scanf.c
#include <stdio.h>
#include <string.h>
char istruzione[256];
int main(void) {
char* comando;
scanf("%[^\n]%*c", istruzione);
comando = strtok(istruzione, " ");
/* test for NULL here */
if (NULL != comando)
{
if (strcmp(comando, "fput") == 0)
printf("%s\n", comando);
}
return 0;
}

Related

I got Segmentation Fault error when I make cat -b function

I want to make 'cat -b' function.
This result is perfect when meet last step.
I got Segmant Fault error and I don't know how to fix it.
How can I do?
FILE *file_name = NULL;
file_name = fopen(av[2], "r");
char temp[1024], *str;
int cnt_file_number = 1;
while(!feof(file_name)){
printf("%4.d ", cnt_file_number++);
str = fgets( temp, sizeof(temp), file_name);
if(strcmp(str, "\0") == 0 ) break;
printf("%s", str);
}
fclose(file_name);
fgets returns a null pointer if it encounters an error.
You are using str right away without checking it.
A better way would be something like
while(fgets(temp, sizeof(temp), file_name) {
// your printing code but using temp instead of str
}
Since fgets returns either temp or null you don't need that extra str pointer and if it is null due to EOF the loop will terminate properly.
Also, checking feof in your loop condition won't do the job because several characters are read at once and EOF might not be the next char but among the next few chars.
Edit: oh sorry. I misread. You are actually checking if str is empty but as others pointed out in the comments there is a better way to do this. Maybe this even solves the problem.
Aside from the fact that your program does not reflect the description of what "cat -b" would do, we do not know what av[2] is; so, no telling whether that file exists or is readable. It'd be wise to check fopen() to ensure that it opens a valid file. If it is not, then it returns NULL and feof(NULL) will fail.
Also, if you read past the end of file, str == NULL and strcmp(NULL,...) will fail.

Error in string do while [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Closed 5 years ago.
char str[6];
do
{
printf("Enter the string you wanna check:");
scanf("%s", str);
}
while(str != "exit");
Why does this not work?
str will never equal "exit", because you're comparing the addresses of two different sections of memory. You probably want to compare the contents of the strings, for which there is a function strcmp().
"exit" is a char[5] generated by the compiler at some address in the data segment. This address is definitely different from the address of str, as two different objects cannot occupy the same location in memory.
The != operator between expressions of type char[] compares two pointers. These two pointers are the address of "exit" and the address of str, which, as I have already explained, will never be equal.
So, the expression str != "exit" will never evaluate to true. Which brings us to another point: your compiler should have issued a warning about this condition being always false. Which means that you are trying to program without -Wall. Don't do this, you are never going to get very far. Always use the highest warning level, and when you see warnings, always fix them.
To correct the problem, do as user3121023 suggested in a comment, and use strcmp() to compare strings.
The short answer is: it does not work because you must use strcmp(str, "exit") to compare the strings and loop for as long as the return value of strcmp() is not 0.
The long answer is: there are more problems in this little code fragment:
The array into which you read a word is very short and you do not limit the number of characters scanf() is likely to store there. Any user input longer than 5 non space characters will cause undefined behavior.
You do not check the return value of scanf(). A premature end of file, such as redirecting input from an empty file, will cause an infinite loop.
Here is how the code can be written in a safer way:
#include <stdio.h>
int main(void) {
char str[80];
for (;;) {
printf("Enter the string you wanna check:");
if (scanf("%79s", str) != 1 || strcmp(str, "exit") == 0)
break;
}
return 0;
}
As suggested above, use strcmp from the <string.h> header file.
char str[6];
do {
printf("Enter the string you wanna check:");
scanf("%s", str);
} while(!strcmp(str, "exit"));
Try :
#include <stdio.h>
#include <string.h>
int main() {
char str[6];
do
{
printf("Enter the string you wanna check:");
scanf("%s", str);
}
while(strcmp(str, "exit") != 0);
return 0;
}

Program to replace word in string using C

Ive been working on a c program that will allow the user to type in line of text until they type in the phrase "The end" on a line by itself. The program will replace every occurrence of "is" with the string "was" and count the number of changes made.
so far I've written some code but I'm getting a little lost on how to get it to work correctly, as of right now the program is giving me a buffer overflow error
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
char str[500];// ="- This, a sample string.";
char * pch;
char endTerm[5000];
printf("Enter a string to parse: ");
scanf("%[^\n]",str);
strcat(endTerm, str);
while ( (strcmp(str, "the end")) != 0 || (strcmp(str, "the end.")) != 0 )
{
scanf("%[^\n]",str);
strcat(endTerm, str);
}
printf ("Your original string was: %s\n\n",endTerm);
pch = strtok (endTerm," ,-");
while (pch != NULL)
{
if ((strcmp(pch, "is")) == 0)
{
pch = "was";
}
else if ((strcmp(pch, "is.")) == 0)
{
pch="was.";
}
printf ("%s ",pch);
pch = strtok (NULL, " ,-");
}
printf("\n\n");
return 0;
}
I can probably figure out how to end the program if the user types the end, but i really need help with replacing the word is with was.
As others have pointed out in the comments above, there are several flaws in your program.
First flaw is your usage of strcat function. If you read the documentation, you would understand that strcat treats the first argument as a destination pointer and hence expects the user to allocate sufficient memory (enough to hold the concatenated string) to the destination pointer. In your case you are passing a string of " " which can accommodate only 1 character. This is the reason, you are getting the buffer overflow or segmentation fault.
The second error in your program is in the usage of strcmp function. This function returns 0 (which is defined by false and not true in strbool.h) when two strings are equal.
The third problem in your program is in the usage of the function strtok. You need to pass NULL as the first argument from the second call onward to get the pointers to the remaining tokens.
So fix these 3 errors first and then try to think about what else needs to be corrected in order to get your desired output.

counting the number of times one word has been typed in

after numerous attempts to make my program work I turn to you guys. I want to make a program that counts the numbers of times a specific word has been typed in. That specific word has been specified as an argument and I'm trying to recall it by using argc and argv. Then I want the program to count the number of times I type in a word and to finish I want to be able to type in #EOF so it stops and shows me the result. This is what I have been trying on so far.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i=0;
char buf[1026]={'\0'};
char *p="#EOF\n";
fgets(buf, sizeof buf, stdin);
while((strcmp(buf, p) !=0) && (fgets(buf, sizeof buf, stdin) != NULL ))
{
if(strncmp(buf, argv[1], strlen(argv[1])) == 0)
{
i++;
}
}
printf("%d", i);
return 0;
}
I get no errors at all, but nothing really happens either. I did try to check where the fault lies, and I found out that when I try to display the value of I within the if statement it's counting very very very fast!
Thanks in advance!
Well, the first problem is that argv[0] is the name of your program; your parameters start at argv[1].
Also, your while loop is being invoked before you put anything into buf. You definitely need to either use a different loop or initialize buf before-hand to ensure your loop behaves properly.
Also, I recommend making sure that you know exactly what should and should not match. You are doing a substring comparison (strncmp) based on the length of the word to be searched for, but is this what you want? This will get the word in a substring, but do you want that, or the whole word? Also, will the user just type in a word on each line or a sentence in which the word may occur? Is case important?
You have an infinite loop.
fgets(buf, 1025, stdin);
In the above line of code, fgets() will take from stdin a string of characters including the newline character and store it into buf.
A simple fix to that is to add the newline character into the string you check against:
char *p="#EOF\n";
Other issues:
1)
char buf[1026]; // This should be initialized to an empty string when you start
// char buf[1026] = {'\0'}; would do it
char *p="#EOF";
while(strcmp(buf, p) !=0)
2) Because you used a while() loop, and you're checking before getting any input, you'll always enter it at least once. This might not be what you wanted to do. A do/while loop, or grabbing the input as the first step would be better.
3) if(strncmp(buf, argv[0], this is checking against the name of your program, if you wanted to check against the first command line argument you wanted argv[1]
4) If you're trying to check that the first command line argument is the same as what the user is typing you want this:
if(strncmp(buf, argv[1], strlen(argv[1])) == 0)
My version of your code that's working:
int main(int argc, char *argv[])
{
int i=0;
char buf[1026]={'\0'};
char *p="#EOF\n";
do
{
fgets(buf, 1025, stdin);
if(strncmp(buf, argv[1], strlen(argv[1])) == 0)
i++;
} while (strcmp(buf, p) != 0);
printf("%d\n", i);
return 0;
}
Your loop should be:
while( fgets(buf, sizeof buf, stdin) != NULL )
{
}
It doesn't make sense to check that the buffer is non-empty before the fgets().
Note that fgets() will include the line feed, see the documentation. Also note use of sizeof to avoid the use of magical numbers in the code.
If you want the user to really type #EOF to end, rather than ending the input by "sending" the end-of-file signal (Ctrl+D in Linux, Ctrl+Z) in Windows) which will work and cause the loop to exit, you msut of course compare against that before counting the word(s) in the line.

Learning C, would appreciate input on why this solution works

This is literally the first thing I've ever written in C, so please feel free to point out all it's flaws. :) My issue, however is this: if I write the program the way I feel is cleanest, I get a broken program:
#include <sys/queue.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Removed prototypes and non related code for brevity */
int
main()
{
char *cmd = NULL;
unsigned int acct = 0;
int amount = 0;
int done = 0;
while (done==0) {
scanf ("%s %u %i", cmd, &acct, &amount);
if (strcmp (cmd, "exit") == 0)
done = 1;
else if ((strcmp (cmd, "dep") == 0) || (strcmp (cmd, "deb") == 0))
debit (acct, amount);
else if ((strcmp (cmd, "wd") == 0) || (strcmp (cmd, "cred") == 0))
credit (acct, amount);
else if (strcmp (cmd, "fee") == 0)
service_fee(acct, amount);
else
printf("Invalid input!\n");
}
return(0);
}
void
credit(unsigned int acct, int amount)
{
}
void
debit(unsigned int acct, int amount)
{
}
void
service_fee(unsigned int acct, int amount)
{
}
As it stands, the above generates no errors at compile, but gives me a segfault when ran. I can fix this by changing the program to pass cmd by reference when calling scanf and strcmp. The segfault goes away and is replaced by warnings for each use of strcmp at compile time. Despite the warnings, the affected code works.
warning: passing arg 1 of 'strcmp' from incompatible pointer type
As an added bonus, modifying the scanf and strcmp calls allows the program to progress far enough to execute return(0), at which point the thing crashes with an Abort trap. If I swap out return(0) for exit(0) then everything works as expected.
This leaves me with two questions: why was the original program wrong? How can I fix it better than I have?
The bit about needing to use exit instead of return has me especially baffled.
This is happening because of the scanf statement.
Look how cmd is pointing to NULL. When scanf is run, it writes to the address of cmd, which is NULL, thus generating a segfault.
The solution is to create a buffer for cmd, such as:
char cmd[20];
Now, your buffer can hold 20 characters. However, you now need to worry about buffer overflows if a user enters more than 20 characters.
Welcome to C.
EDIT: Also, note that your credit, debit, and service fee functions won't work as expected as you have them written. This is because the parameters are passed by value, not by reference. This means that after the method returns, any changes will be discarded. If you want them to modify the arguments you give, try changing the methods to:
void credit(unsigned int *acct, int *amount)
And then call them like:
credit(&acct, &amt);
Doing that will pass the parameters by reference, meaning that any changes you make inside the credit function will affect the parameters, even after the function returns.
You aren't allocating memory for cmd, so it's NULL.
Try declaring it with some space:
char cmd[1000];
As others have pointed out, you haven't allocated anything for scanf to read into. But you should also test the return value of scanf:
if ( scanf ("%s %u %i", cmd, &acct, &amount) != 3 ) {
// do some error handling
}
The scanf function returns the number of succesful conversions, so if someone types in XXXX when you expect an integer you want to be able to detect and deal with it. But frankly, user interface code that uses scanf() is never really going to be proof against this sort of thing. scanf() was actually intended for reading formatted files, not random input from humans.
This :
char *cmd = NULL;
Should be:
char cmd[100];
Please note:
You should ensure that the string the user will input in cmd has length less than 100 or n
cmd is initialised to a null pointer which never points at any memory. scanf doesn't check that cmd is valid before trying to write to what cmd points to.
A preliminary solution instead creates some space for cmd to point to:
char cmd[30]; /* DANGEROUS! */
but this is a very dangerous move because you may still get segfaults if the input is longer than expected and scanf tries to write to cmd[30] and beyond.
For this reason scanf is considered unsafe and should not be used in production code. Safer alternatives include using fgets to read a line of input and sscanf to process it.
Sadly, C I/O is very difficult to get right without introducing the possibility of buffer overflows into your program. You always need to be thinking about how much memory you have available and whether it will be enough to store the longest possible input you could receive. You also need to check the return values of most I/O functions for errors.
In your example, scanf() is being passed a null pointer.
char *cmd = NULL;
scanf() won't allocate space for the string - you'll need to allocate somewhere for the string to go.
char cmd[80];
...
scanf ("%s",cmd);
Your getting a segmentation fault because scanf() is attempting to write its output to unallocated space.
Others have pointed out the error in your program, but for a better understanding of pointers, since you are just starting to learn C, look at this question at SO.
Your basic problem is that you haven't allocated memory for your string. In C, you are responsible for all memory management. If you declare variables on the stack, this is easy. With pointers, it's a little more difficult. Since you have the line char* str = NULL, when you attempt to scanf into it, you write bytes to NULL, which is illegal. What the %s specifier does is write into what str points to; it can't change str, as parameters are passed by value. This is why you have to pass &acct instead of just acct.
So how do you fix it? You need to provide memory where the read-in string can live. Something like char str[5] = "". This makes str a five-element character array, big enough to hold "exit" and its terminating zero byte. (Arrays decay into pointers at the slightest provocation, so we're fine on that front.) However, this is dangerous. If the user enters the string malicious, you're going to write "malic" into str and the bytes for "icious\0" into whatever comes after that at memory. This is a buffer overflow, and is a classic bug. The simplest way to fix it here is to require the user to enter a command of at most N letters, where N is the longest command you have; in this case, N = 4. Then you can tell scanf to read in at most four characters: scanf("%4s %u %i", cmd, &acct, &amt). The %4s says "read in at most four characters", so you can't screw up other memory. However, note that if the user enters malformed 3 4, you won't be able to find the 3 and the 4, since you'll be looking at ormed.
The reason you could do scanf("%s %u %i", &cmd, &acct, &amount) is that C is not type-safe. When you gave it &cmd, you gave it a char**; however, it was happy to treat it as a char*. Thus, it wrote bytes over cmd, so if you passed in the string exit, cmd might (if it were four bytes wide and had the appropriate endianness) be equal to 0x65786974 (0x65 = e, 0x78 = x, 0x69 = i, 0x74 = t). And then the zero byte, or any other bytes you passed in, you would start to write over random memory. If you change it at strcmp too, however, it will also treat the value of str as a string, and everything will be consistent. As for why return 0; fails but exit(0) works, I'm not sure, but I have a guess: you may have been writing over the return address of main. That's stored on the stack too, and if it happens to come after cmd in the stack layout, then you might be zeroing it or scribbling on it. Now, exit must do its cleanup manually, jumping to the right locations, etc. However, if (as I think is the case, although I'm not sure) main behaves like any other function, its return jumps to the space on the stack stored as the return address (which is probably a cleanup routine of some sort). However, since you've scribbled over that, you get an abort.
Now, there are a couple of other small improvements you could make. First, since you're treating done as a boolean, you ought to loop while (!done) { ... }. Second, the current setup requires you to write exit 1 1 to exit the program, even though the 1 1 bit shouldn't be necessary. Third, you should check to see if you have successfully read all three arguments, so you don't get errors/inconsistencies; for instance, if you don't fix this, then the input
deb 1 2
deb 3 a
Calls debit(1,2) and debit(3,2), while still leaving the a in the input to trip you up. Finally, you should exit cleanly on EOF, rather than looping forever doing the last thing you did. If we put this together, we get the following code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void credit(unsigned int acct, int amount);
void debit(unsigned int acct, int amount);
void service_fee(unsigned int acct, int amount);
int main() {
char cmd[5] = "";
unsigned int acct = 0;
int amount = 0;
int done = 0;
while (!done) {
if (feof(stdin)) {
done = 1;
} else {
if (scanf("%4s", cmd, &acct) != 1) {
fprintf(stderr, "Could not read the command!\n");
scanf(" %*s "); /* Get rid of the rest of the line */
continue;
}
if (strcmp(cmd, "exit") == 0) {
done = 1;
} else {
if (scanf(" %u %i", &acct, &amount) != 2) {
fprintf(stderr, "Could not read the arguments!\n");
scanf(" %*s "); /* Get rid of the rest of the line */
continue;
}
if ((strcmp(cmd, "dep") == 0) || (strcmp(cmd, "deb") == 0))
debit(acct, amount);
else if ((strcmp(cmd, "wd") == 0) || (strcmp(cmd, "cred") == 0))
credit(acct, amount);
else if (strcmp(cmd, "fee") == 0)
service_fee(acct, amount);
else
fprintf(stderr, "Invalid input!\n");
}
}
/* Cleanup code ... */
}
return 0;
}
/* Dummy function bodies */
void credit(unsigned int acct, int amount) {
printf("credit(%u, %d)\n", acct, amount);
}
void debit(unsigned int acct, int amount) {
printf("debit(%u, %d)\n", acct, amount);
}
void service_fee(unsigned int acct, int amount) {
printf("service_fee(%u, %d)\n", acct, amount);
}
Note that if there is no "cleanup code", you can replace all your uses of done with break and remove the declaration of done, giving the nicer loop
while (1) {
if (feof(stdin)) break;
if (scanf("%4s", cmd, &acct) != 1) {
fprintf(stderr, "Could not read the command!\n");
scanf(" %*s "); /* Get rid of the rest of the line */
continue;
}
if (strcmp(cmd, "exit") == 0) break;
if (scanf(" %u %i", &acct, &amount) != 2) {
fprintf(stderr, "Could not read the arguments!\n");
scanf(" %*s "); /* Get rid of the rest of the line */
continue;
}
if ((strcmp(cmd, "dep") == 0) || (strcmp(cmd, "deb") == 0))
debit(acct, amount);
else if ((strcmp(cmd, "wd") == 0) || (strcmp(cmd, "cred") == 0))
credit(acct, amount);
else if (strcmp(cmd, "fee") == 0)
service_fee(acct, amount);
else
fprintf(stderr, "Invalid input!\n");
}
In order to fully understand what is going on here you need to understand some basics about C pointers. I suggest you take a look here if you are really that new to C:
http://www.cprogramming.com/tutorial.html#ctutorial
The most common cause of segfaults are detailed here:
http://www.cprogramming.com/debugging/segfaults.html

Resources