Segmentation fault in C program with apparently perfect code - c

I recently recommended K&R to a friend who wanted to learn C. He came across an exercise in the first chapter that gave him various errors. I compiled it on my Ubuntu installation, alternating between the C90 option and the defaults. I've looked at every angle but it seems to be perfect code...yet it consistently gives me a segmentation fault each time I run it. I'm not the sharpest programmer in the shed but this has me pretty frustrated.
What on earth is causing such an error?
Here is the code:
#include <stdio.h>
#define MAXLINE 1000
void reverse(char s[]);
/* A program that reverses its input a line at a time */
main()
{
int c, i;
char line[MAXLINE];
for (i = 0; (c = getchar()) != EOF; ++i) {
line[i] = c;
if (c == '\n') { /* Upon encountering a newline */
line[i] = '\0'; /* replace newline with null terminator */
i = 0;
reverse(line);
printf("\n%s\n", line);
}
}
return 0;
}
/* A function that reverses the character string */
void reverse(char s[])
{
int a, z;
char x;
for (z = 0; s[z]; ++z) /* Figure out where null terminator is */
;
--z;
for (a = 0; a != z; ++a) { /* Reverse array usinng x as placeholder */
x = s[a];
s[a] = s[z];
s[z] = x;
--z;
}
}

You're missing a semi-colon here:
for (z = 0; s[z]; ++z); /* Figure out where null terminator is */
// ^
This loop is supposed to run until it finds the null terminator. If you leave off this semi-colon, then on each iteration it does both ++z and --z, which means it just loops forever. You want the --z to happen after this loop has completed, as that will set z equal to the last character before the string's null terminator.
For an even-length string, a and z will cross each other (they'll never be equal) in the second loop. For example, if z=5 and a=4, then on the next iteration a=5 and z=4. If you check for a<z instead of a!=z then you avoid that problem. Since you're checking for != instead of <, this would cause the loop to run pretty much infinitely. However, you'll end up with a SEGFAULT as a grows too large and z grows too small, since they'll both be used to index into memory outside your buffer.
for (a = 0; a != z; ++a) { /* Reverse array usinng x as placeholder */
// ^ should be <, not !=
Finally, there's also a bug in main. When you find a newline, you set i=0. This works fine when you print the string, but when i is incremented at the end of the loop, you end up with i=1 as you start reading the next string. This means you'll have an extra character at the beginning of your string. You need to do something to properly reset i to make sure that doesn't happen.
K&R has a string reverse function in Section 3.5. In my copy it's on page 62. It looks like your friend decided to iterate over the string rather than calling strlen (which is really all the function call does anyway), and thought that a<z should be equivalent to a!=z.

Does this loop over run?
for (z = 0; s[z]; ++z) /* Figure out where null terminator is */
--z;

for (z = 0; s[z]; ++z) /* Figure out where null terminator is */
--z;
Just after the first iteration, z becomes -1 -- so trying to access s[z] results in segmentation fault.

Related

How the while statement is executed in C or how this array-referenced pointers work?

I started learning C and I had this exercise from the book "Prentice Hall - The C Programming Language".
Chapter 5 Exercise 3:
Write a pointer version of the fuction strcat that we showed in Chapter 2. strcat(s, t) copies the string t to the end of s.
I did the exercise but the first method that came up to my mind was:
void stringcat(char *s, char *t){
int i,j;
i = j = 0;
while(*(s+i) != '\0'){
printf("%d", i);
i++;
}
while ( (*(t+j)) != '\0'){
*(s+i) = *(t+j);
i++;
j++;
}
}
In main I had:
int main(){
char s[] = "Hola";
char t[] = "lala";
stringcat(s,t);
printf("%s\n", s);
}
At first sight I thought it was right but the actual output was Holalalaa.
Of course it was not the output that I expected, but then I coded this:
void stringcat(char *s, char *t){
int i,j;
i = j = 0;
while(*(s+i) != '\0'){
printf("%d", i);
i++;
}
while((*(s+i) = *(t+j)) != '\0'){
i++;
j++;
}
}
And the output was right.
But then I was thinking a lot about the first code because it's very similar to the second one but why the first output was wrong?. Is it something related with the while statement? or something with pointers?. I found it really hard to understand because you can't see what's happening in the array.
Thanks a lot.
Your code has more than the one problem that you found, but let's start with it.
Actually you are asking why
/* ... */
while ((*(t+j)) != '\0') {
*(s+i) = *(t+j);
/* ... */
works differently than
/* ... */
while ((*(s+i) = *(t+j)) != '\0') {
/* ... */
I hope you see it already, now that both cases stand side by side, actually vertically ;-). In the first case the value of t[j] is compared before it is copied to s[i]. In the second case the comparison is done after the copy. That's why the second case copies the terminating '\0' to the target string, and the first case does not.
The output you get works accidentally, it is Undefined Behavior, since you are writing beyond the border of the target array. Fortunately for you, both strings are laying in sequence in the memory, and you are overwriting the source string with its own characters.
Because your first case does not copy the '\0', the final printf() outputs more characters until a '\0' is encountered. By chance this is the last 'a'.
As others commented, the target string has not enough space for the concatenated string. Provide some more space like this:
char s[10] = "Hola"; /* 10 is enough for both strings and the terminating '\0'. */
However, if you had done this already, the error would have not been revealed, because the last 6 characters of s are initialized with '\0'. Not copying the terminating '\0' makes no difference. You can see this if you use
char s[10] = "Hola\0xxxx";
I don't think that your solution is the expected one. Instead of s[i] you are using *(s + i), which is essentially the same, accessing an array. Consider changing s (and in the course, t) in the function and use just *s.
Side note: The printf() in the function is most probably a leftover from debugging. But I'm sure you know.

A question regarding the syntax of loop in C

I saw this little problem in the practice book, which is used to print whatever is typed in backward. And here, I just can't understand this loop for (; i >= 0; i--), why doesn't it assign the i to a variable in the first, is this a normal syntax as well? Thanks!
#include <stdio.h>
#include <string.h>
main() {
int i;
char msg[25];
printf("Type up to 25 characters and then press Enter...\n");
for (i = 0; i < 25; i++) {
msg[i] = getchar(); //Outputs a single character
if (msg[i] == '\n') {
i--;
break;
}
}
putchar('\n'); // One line break after the loop is done.
for (; i >= 0; i--) { // <--- this is the line
putchar(msg[i]);
}
putchar('\n');
return (0);
}
why doesn't it assign the i to a variable in the first
Because i already has the value we want to start at, from prior logic.
is this a normal syntax as well?
Yes. Any of the three parts of the for intro may be empty.
The three expressions which appear between the parentheses in a for() loop are all optional, and theoretically arbitrary.
Theoretically, you could write
for(a = b; c < d; e++)
printf("%d\n", f);
This is probably meaningless and useless, but as far as the C language is concerned, there's nothing wrong with it, and I wouldn't expect a compiler to emit any error messages or even warnings about it.
Now, conventionally, the first expression initializes something, the second expression tests whether we should make another trip through the loop, and the third expression increments. But that's all by convention, and again, all three expressions are optional.
For example, you could write
int i = 5;
for(; i < 10; i++)
printf("%d\n", i);
In this case, i gets its initial value when it's declared. This looks weird, to be sure, and it's arguably bad style, but it's not illegal.
The other expressions are optional, too. For example, I could write it like this:
int i = 5;
for(; i < 10; )
printf("%d\n", i++);
Now i gets incremented within the printf call, so there's no need to increment it in the for loop header. But by now, we've basically got a while loop:
int i = 5;
while(i < 10)
printf("%d\n", i++);
Anyway, this explains the loop you saw. The variable i already had the value it needed, from code up above, so there was no reason to set it in the for loop header.
The initial clause of the for loop can be empty: in the case you mention, i already contains the number of characters stored into the array, 25 or less if the user typed fewer characters before the newline. Yet the program has a bug because i should be decremented to avoid accessing msg beyond the last character stored in the array. Decrementing i if the character typed was '\n' is inconsistent and starting the output at msg[i] is incorrect too.
Your practice book is obsolete and confusing. Consider discarding it and using a more up to date book or online tutorial.
Any of the clauses of the for loop can be empty:
the initial clause may not be needed
the test clause can be empty, causing an infinite loop that ends either via a break or a return statement, or via a direct or indirect call to exit().
the update clause can contain any update expression, or nothing if the code already updates the required variables in the body of the loop, if any update is needed at all.
The first for loop is a very idiomatic example: for (i = 0; i < 25; i++) { ... }
Here is another classic for loop: for (;;) { /* repeat some action for ever */ }
Note the following problems in the posted code:
main must be defined with a return type int. The code uses an obsolete syntax where the return type was implicitly int. This syntax is invalid in C99.
the first for loop stores up to 25 bytes to the array, if the user typed more than 24 characters before the newline or signalled an end of file before the newline.
the program has undefined behavior in this latter case because msg[i] points beyond the end of msg if the user typed 25 characters before the newline.
the contents of the array is printed backwards, with potentially some funny characters such as ÿ at the beginning if indeed the input was ended prematurely.
there is no need to include <string.h>
the parentheses around the return value (0) are useless.
Here is a modified version:
#include <stdio.h>
int main() {
int i, c;
char msg[25];
printf("Type up to 25 characters and then press Enter...\n");
for (i = 0; i < 25; i++) {
c = getchar();
if (c == EOF || c == '\n')
break;
msg[i] = c;
}
putchar('\n'); // One line break after the loop is done.
while (--i >= 0) { // equivalent to for (; --i >= 0;)
putchar(msg[i]);
}
putchar('\n');
return 0;
}

C—Infinite loop, I think?

I'm having an issue with a program in C and I think that a for loop is the culprit, but I'm not certain. The function is meant to take a char[] that has already been passed through a reverse function, and write it into a different char[] with all trailing white space characters removed. That is to say, any ' ' or '\t' characters that lie between a '\n' and any other character shouldn't be part of the output.
It works perfectly if there are no trailing white space characters, as in re-writing an exact duplicate of the input char[]. However, if there are any, there is no output at all.
The program is as follows:
#include<stdio.h>
#define MAXLINE 1000
void trim(char output[], char input[], int len);
void reverse(char output[], char input[], int len);
main()
{
int i, c;
int len;
char block[MAXLINE];
char blockrev[MAXLINE];
char blockout[MAXLINE];
char blockprint[MAXLINE];
i = 0;
while ((c = getchar()) != EOF)
{
block[i] = c;
++i;
}
printf("%s", block); // for debugging purposes
reverse(blockrev, block, i); // reverses block for trim function
trim(blockout, blockrev, i);
reverse(blockprint, blockout, i); // reverses it back to normal
// i also have a sneaking suspicion that i don't need this many arrays?
printf("%s", blockprint);
}
void trim(char output[], char input[], int len)
{
int i, j;
i = 0;
j = 0;
while (i <= len)
{
if (input[i] == ' ' || input[i] == '\t')
{
if (i > 0 && input[i-1] == '\n')
for (; input[i] == ' ' || input[i] == '\t'; ++i)
{
}
else
{
output[j] = input[i];
++i;
++j;
}
}
else
{
output[j] = input[i];
++i;
++j;
}
}
}
void reverse(char output[], char input[], int len)
{
int i;
for (i = 0; len - i >= 0; ++i)
{
output[i] = input[len - i];
}
}
I should note that this is a class assignment that doesn't allow the use of string functions, hence why it's so roundabout.
Change
for (i; input[i] == ' ' || input[i] == '\t'; ++i);
to
for (; i <= len && (input[i] == ' ' || input[i] == '\t'); ++i);
With the first method, if the whitespace is at the end, the loop will iterate indefinitely. Not sure how you didn't get an out of bounds access exception, but that's C/C++ for you.
Edit As Arkku brought up in the comments, make sure your character array is still NUL-terminated (the \0 character), and you can check on that case instead. Make sure you're not trimming the NUL character from the end either.
Declaring your main() function simply as main() is an obsolete style that should not be used. The function must be declared either as int main(void) or as int main(int argc, char *argv[]).
Your input process does not null-terminate your input. This means that what you're working with is not a "string", because a C string, by definition, is an array of char that the last element is a null character ('\0'). Instead, what you've got are simple arrays of char. This wouldn't be a problem as long as you're expecting that, and indeed your code is passing array lengths about, but you're also trying to print it with printf(), which requires C strings, not simple arrays of char.
Your reverse() function has an off-by-one error, because you aren't accounting for the fact that C arrays are zero-indexed, so what you're reversing is always one byte longer than your actual input.
What this means is that if you call reverse(output, input, 10), your code will start by assigning the value at input[10] to output[0], but input[10] is one past the end of your data, and since you didn't initialize your arrays before starting to fill them, that's an indeterminate value. In my testing, that indeterminate value happens, coincidentally, to have zero values much of the time, which means that output[0] gets filled with a null ('\0').
You need to be subtracting one more from the index into the input than you actually are. The loop-termination condition in the reverse() function is also wrong, in compensation, that condition should be len - i > 0, not len - i >= 0.
Your trim() function is unnecessarily complex. Additionally, it too has an incorrect loop condition to compensate for the off-by-one error in reverse(). The loop should be while ( i < len ), not while ( i <= len ).
Additionally, the trim() function has the ability to reduce the size of your data, but you don't provide a way to retain that information. (I see in the comments of Arkku's answer that you've corrected for this already. Good.)
Once you've fixed the issue with not keeping track of your data's size changes, and the off-by-one error which is copying indeterminate data (which happens, coincidentally, to be a null) from the end of the blockout array to the beginning of the blockprint array when you do the second reverse(), and you fix the incorrect <= condition in trim() and the incorrect >= condition in reverse(), and you null-terminate your byte array before passing it to printf(), your program will work.
(Moved from comments to an answer)
My guess is that the problem is outside this function, and is caused by the fact that in the described problem cases the output is shorter than the input. Since you are passing the length of the string as an argument, you need to calculate the length of the string after trim, because it may have changed...
For instance, passing an incorrect length to reverse can cause the terminating NUL character (and possibly some leftover whitespace) to end up at the beginning of the string, thus making the output appear empty.
edit: After seeing the edited question with the code of reverse included, in addition to the above problem, your reverse puts the terminating NUL as the first character of the reversed string, which causes it to be the empty string (in some cases your second reverse puts it back, so you don't see it without printing the output of the first reverse). Note that input[len] contains the '\0', not the last character of the string itself.
edit 2: Furthermore, you are not actually terminating the string in block before using it. It may be the case that the uninitialised array often happens to contain zeroes that serve to terminate the string, but for the program to be correct you absolutely need to terminate it with block[i] = '\0'; immediately after the input loop. Similarly you need ensure NUL-termination of the outputs of reverse and trim (in case of trim it seems to me that this already happens as a side-effect of having the loop condition i <= len instead of i < len, but it's not a sign of good code that it's hard to tell).

Infinite loop when getting a line with getchar()

I was trying an exercise from K&R (ex 1-17), and I came up with my own solution.
The problem is that my program appears to hang, perhaps in an infinite loop. I omitted the NUL ('\0') character insertion as I find C generally automatically attaches it to the end of a string (Doesn't it?).
Can somebody please help me find out what's wrong?
I'm using the GCC compiler with Cygwin on win8(x64), if that helps..
Question - Print all input lines that are longer than 80 characters
#include<stdio.h>
#define MINLEN 80
#define MAXLEN 1000
/* getlin : inputs the string and returns its length */
int getlin(char line[])
{
int c,index;
for(index = 0 ; (c != '\n') && ((c = getchar()) != EOF) && (index < MAXLEN) ; index++)
line[index] = c;
return (index); // Returns length of the input string
}
main()
{
int len;
char chArr[MAXLEN];
while((len = getlin(chArr))>0)
{
/* A printf here,(which I had originally inserted for debugging purposes) Miraculously solves the problem!!*/
if(len>=MINLEN)
printf("\n%s",chArr);
}
return 0;
}
And I omitted the null('\0') character insertion as I find C generally automatically attaches it to the end of a string (Doesn't it?).
No, it doesn't. You're using getchar() to read input characters one at a time. If you put the chars in an array yourself, you'll have to terminate it yourself.
The C functions that return a string will generally terminate it, but that's not what you're doing here.
Your input loop is a little weird. The logical AND operator only executes the right-hand-side if the left-hand-side evaluates to false (it's called "short-circuiting"). Rearranging the order of the tests in the loop should help.
for(index = 0 ; (index < MAXLEN) && ((c = getchar()) != EOF) && (c != '\n'); index++)
line[index] = c;
This way, c receives a value from getchar() before you perform tests on its contents.
I'm not positive about what's wrong, but you don't provide the input to the program so I'm guessing.
My guess is that in getlin your variable c gets set to '\n' and at that point it never gets another character. It just keeps returning and looping.
You never SET c to anything inside your getlin function before you test it, is the problem.
C does not insert a NUL terminator at the end of strings automatically. Some functions might do so (e.g. snprintf). Consult your documentation. Additionally, take care to initialize all your variables, like c in getlin().
1) C doesn't add a final \0 to your string. You are responsible for using an array of at least 81 chars and puting the final \0 after the last character you write in it.
2) You're testing the value of c before reading it
3) Your program doesn't print anything because printf uses a buffer for I/O which is flushed when you send \n. Modify this statement to print a final \n:
printf("\n%s",chArr);
to become:
printf("%s\n",chArr);
4) To send an EOF to your program you should do a Ctrl+D under unix and I don't know if it's possible for windows. This may be the reason why the program never ends.

Taking a string as input and storing them in a character array in C [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions must demonstrate a minimal understanding of the problem being solved. Tell us what you've tried to do, why it didn't work, and how it should work. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I am stumped on how to store strings in an array in C, with each character kept separately. As an example, if the user inputs hellop, I want to store it in a given array, say userText, with userText[0] = h, userText[1] = e, userText[2] = l, and so on. I know this is easy stuff, but I'm still new. So if anyone could help, it would be great. Please explain how to do this using pointers.
#include<stdio.h>
void main()
{
char a[10],c;
int i=0;
while((c=getchar())!='\n')
{
scanf("%c",&a[i++]);
c=getchar();
}
for(i=0;i<11;i++)
printf("%c",a[i]);
}
The program outputs some garbage value (eoeoeoeo\363) when I type in hellop.
To read input I recommend using the fgets function. It's a nice, safe alternative to scanf.
First let's declare a buffer like so:
char user_input[20];
Then we can get user input from the command line in the following manner:
fgets(user_input, 20, stdin);
This will store a maximum of 20 characters into the string from the standard input and it will ensure it is null-terminated. The fact that we've limited the input to the size of the array declared earlier ensures that there's no possibility of buffer overruns.
Then let's clear the pesky newline that's been entered into the string using strlen:
user_input[strlen(user_input) -1] = '\0';
As strlen returns the size of the string up to the null terminator but without it, we can be sure at that position lies the newline character (\n). We replace it with a null-terminator(\0) so that the string ends there.
Finally, let's print it using printf:
printf("The user has entered '%s'\n", user_input);
To use fgets and printf you will need to declare the following header:
#include <stdio.h>
For strlen we need another header, namely:
#include <string.h>
Job done.
P.S. If I may address the code you've added to your question.
main is normally declared as int main rather than void main which also requires that main returns a value of some sort. For small apps normally return 0; is put just before the closing brace. This return is used to indicate to the OS if the program executed successfully (0 means everything was OK, non-zero means there was a problem).
You are not null-terminating your string which means that if you were to read in any other way other than with a careful loop, you will have problems.
You take input from the user twice - once with getchar and then with scanf.
If you insist on using your code I've modified it a bit:
#include<stdio.h>
int main()
{
char a[10];
int i=0;
while( (a[i++]=getchar()) != '\n' && i < 10) /* take input from user until it's a newline or equal to 10 */
;
a[i] = '\0'; /* null-terminate the string */
i = 0;
while(a[i] != '\0') /* print until we've hit \0 */
printf("%c",a[i++]);
return 0;
}
It should now work.
To read a string into char array:
char *a = NULL;
int read;
size_t len;
read = getline(&a, &len, stdin);
//free memory
free(a);
Your code is this (except I've added a bunch of spaces to improve its readability):
1 #include <stdio.h>
2 void main()
3 {
4 char a[10], c;
5 int i = 0;
6 while ((c = getchar()) != '\n')
7 {
8 scanf("%c", &a[i++]);
9 c = getchar();
10 }
11 for (i = 0; i < 11; i++)
12 printf("%c", a[i]);
13 }
Line-by-line analysis:
OK (now I've added the space between #include and <stdio.h>).
The main() function returns an int.
OK (it is hard to get an open brace wrong).
Since the return value of getchar() is an int, you need to declare c separately as an int.
OK.
Needs to account for EOF; should be while ((c = getchar()) != EOF && c != '\n'). You're still very open to buffer overflow, though.
OK.
Not OK. This reads another character from standard input, and doesn't check for EOF.
Not OK. This too reads another character from standard input. But when you go back to the top of the loop, you read another character. So, as things stand, if you type abcdefg at the program, c is assigned 'a' in the loop control, then a[0] is assigned 'b', then c is assigned 'c', then the loop repeats with a[1] getting 'e'. If I'd typed 6 characters plus newline, the loop would terminate cleanly. Because I claimed I typed 7 characters, the third iteration assigns 'g' to c, which is not newline, so a[2] gets the newline, and the program waits for more input with the c = getchar(); statement at the end of the loop.
OK (ditto close braces).
Not OK. You don't take into account early termination of the loop, and you unconditionally access a non-existent element a[10] of the array a (which only has elements 0..9 — C is not BASIC!).
OK.
You probably need to output a newline after the for loop. You should return 0; at the end of main().
Because your input buffer is so short, it will be best to code a length check. If you'd used char a[4096];, I'd probably not have bothered you about it (though even then, there is a small risk of buffer overflow with potentially undesirable consequences). All of this leads to:
#include <stdio.h>
int main(void)
{
char a[10];
int c;
int i;
int n;
for (i = 0; i < sizeof(a) && ((c=getchar()) != EOF && c != '\n')
a[i++] = c;
n = i;
for (i = 0; i < n; i++)
printf("%c", a[i]);
putchar('\n');
return 0;
}
Note that neither the original nor the revised code null terminates the string. For the given usage, that is OK. For general use, it is not.
The final for loop in the revised code and the following putchar() could be replaced (safely) by:
printf("%.*s\n", n, a);
This is safe because the length is specified so printf() won't go beyond the initialized data. To create a null terminated string, the input code needs to leave enough space for it:
for (i = 0; i < sizeof(a)-1 && ((c=getchar()) != EOF && c != '\n')
a[i++] = c;
a[i] = '\0';
(Note the sizeof(a)-1!)

Resources