Reversing a string in C does not output the reversed line - c

I'm trying to reverse a string in C. The reverse function simply assigns the character at a given location (in a for loop) to a temp object. I cannot see any logic errors within the program, and the program compile successfully under gcc 4.7.2 with this command:
gcc -Wall -std=c99 reverse.c
To recreate the problem:
1.) Run the program and enter a string into your shell
2.) Once finished inputting, press enter/and or your EOF signal.
The problem is that neither the original string is printed, or the reversed string. This is also an exercise from K&R second edition, if you have completed this exercise, a different solution to mine would be appreciated.
I think the bug is caused by the absence of a null character, the famous printf requires a null terminated string to print input to cin. The getline function assigns a null character to the end of the array, surely the null character will be the first character in the string thereto ending the printf (and thus no character/literal is printed).
#include <stdio.h>
#define MAXLINE 1000
int geline(char s[], int lim);
void reverse(char line[], int length);
int main()
{
char s[MAXLINE];
char t[MAXLINE];
int k, len;
while ((len = getline(s, MAXLINE)) > 0) {
if (len > 1)
reverse(s, len);
}
printf("%s", s);
return 0;
}
void reverse (char input[], int length)
{
char temp[MAXLINE];
int j = length;
for (int i = 0; i < length; ++i, --j) {
temp[i] = input[i];
input[i] = input[j];
input[j] = temp;
}
}
int getline(char s[], int lim)
{
int c, i;
for (i=0; (c=getchar()) != EOF && c!='\n'; ++i)
s[i] = c;
if (c== '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}

There are two logic errors:
int j = length; should be int j = length - 1;
temp[i] = input[i] ... input[j] = temp;
There are two approaches for that last error:
Define temp as a single char: char temp; ... temp = input[i]; input[i] = input[j]; input[j] = temp;
Use the correct index in temp: temp[i] = input[i]; input[i] = input[j]; input[j] = temp[i]
Try this code:
#include <stdio.h>
#define MAXLINE 1000
int geline(char s[], int lim);
void reverse(char line[], int length);
int main () {
char s[MAXLINE];
char t[MAXLINE];
int k, len;
while ((len = getline(s, MAXLINE)) > 0) {
if (len > 1)
reverse(s, len);
}
printf("%s", s);
return 0;
}
void reverse (char input[], int length) {
char temp;
int j = length - 1;
for (int i = 0; i < j; ++i, --j) {
temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
int getline (char s[], int lim) {
int c, i;
for (i=0; (c=getchar()) != EOF && c!='\n'; ++i)
s[i] = c;
if (c== '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}

(I did my compiling with -Wall -std=c99 -O3 -g, the -g to allow use of gdb)
Here are the things I noticed and some ways of addressing them. I've tried to hew pretty closely to the style you started with (I would have converted the array decls in the prototypes to pointers, for example, but that's not necessary).
Your getline prototype was missing the t.
int getline(char s[], int lim);
In main, you don't actually need k, t[MAXLINE], and your printf should probably be in the loop so you'll see each word as it's reversed. Note that printf picks up a \n, since the getline below converts both newline and EOF-terminated lines to the same thing (without newlines):
int main()
{
char s[MAXLINE];
int len;
while ((len = getline(s, MAXLINE)) > 0) {
if (len > 0)
reverse(s, len);
printf("%s\n", s);
}
return 0;
}
In above, the getline(s, MAXLINE) could have been getline(s, sizeof(s) / sizeof(*s) - 1) although again, be careful of fencepost errors (note the - 1).
The reverse function can be greatly improved without going over to the madness of xor to skip having a variable (although Daffra's example is interesting, especially in that it correctly stops in the middle). Instead, having the sense to just index up to the halfway point is a clear win. Between that and dropping reducing the temp array to just a temporary character, your general style is retained.
void reverse (char input[], int length)
{
int max = length - 1; /* keep the final NUL in place */
for (int i = 0; i <= max / 2; ++i) {
char ch = input[i];
input[i] = input[max - i];
input[max - i] = ch;
}
}
In the above gcc -O3 can do a serious workover on the code, so there's no real reason to worry that long division is going to be performed on every loop test, etc. For example, gdb reports that i itself gets optimized out automatically, which is pretty interesting. Write good, readable code first, have some faith in your compiler, optimize later.
And last, getline benefits from testing against lim (CRITICAL!) and and converting newlines into NULs.
int getline(char s[], int lim)
{
int i, c;
for (i=0; (i <= lim) && ((c=getchar()) != EOF) && (c != '\n'); ++i)
s[i] = c;
s[i] = '\0';
return i; /* return the index to the final NUL, same as length w/o it */
}
Setting MAXLINE to 10 temporarily shows that this version handles overlong lines fairly gracefully, splitting them into two separate ones without losing any of the characters.
Be careful with strings to very clearly decide whether you want to describe them in terms of length, or in terms of the index to the NUL at the end. This affects how you phrase your loops, limits, variable names, etc, and obviously confusing them is a classic source of fencepost errors.
Hope this helps.

int j = length - 1; // Thanks to #chux
for (int i = 0; i < j; ++i, --j) { // or <= length / 2
char temp = input[i];
input[i] = input[j];
input[j] = temp;
temp is not needed, and not entirely correctly used.
You are twice swapping the values, which restores the swap on the second half of the cycling. :)
Your prototype misses a 't' (geline). Hence maybe
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
is taken?

you can use this fast function :
inline char * reverse(char *p)
{
char *save=p;
char *q = p;
while(q && *q) ++q;
for(--q; p < q; ++p, --q)
*p = *p ^ *q,
*q = *p ^ *q,
*p = *p ^ *q;
return save ;
}

Please have a look at this code:
#include <stdio.h>
#define MAXLINE 1000
int geline(char s[], int lim);
void reverse(char line[], int length);
int main()
{
char s[MAXLINE];
int len;
while ((len = geline(s, MAXLINE)) > 1) {
if (len > 1) {
reverse(s, len);
printf("%s", s);
}
}
return 0;
}
void reverse (char input[], int length)
{
char temp;
int j = length-1;
for (int i = 0; i < j; ++i, --j) {
temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
int geline(char s[], int lim)
{
int c, i;
for (i=0; (c=getchar()) != EOF && c!='\n'; ++i)
s[i] = c;
if (c== '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}

Only 2 changes needed here, and it will do the reverse fine.
Inside function reverse just do this
int j = --length;
Instead of this:
input[j] = temp; //you should use
input[j] = temp[i];

Related

Why is my program to reverse its input a line at a time not working?

I was trying to write a program that reverses its input a line at a time. I thought I had done it successfully, however it sometimes doesn't give the desirable output (this happens when I put an input with a smaller length than the one I put previously). I am new to this and I was wondering what can I do to solve this issue.
Program:
#include <stdio.h>
#define MAXLINE 1000
void reverse(char o[], char l[]);
int mgetline(char line[]);
int main(void){
int len;
char line[MAXLINE];
char rev[MAXLINE];
while((len = mgetline(line)) > 0){
reverse(rev, line);
printf("%s\n",rev);
}
return 0;
}
int mgetline(char s[])
{
int c,i;
for(i = 0; ((c=getchar())!=EOF) && (c!='\n'); ++i)
s[i] = c;
if (c == '\n')
{
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
void reverse(char revi[], char liner[])
{
int i, c, j;
for(i=0;liner[i]!='\0';++i)
;
--i;
if(liner[i]=='\n')
--i;
for(j=0; j<=i ; ++j){
c = i - j;
revi[j] = liner[c];
}
--j;
}
Since you not terminating the revi string at the end, therefore it will print the leftout characters from the previous result if the new input is smaller. Fix it by adding this
revi[j] = '\0';
at the end of the reverse function and delete that last --j;.
The function reverse does not build a string that is it does not append the terminating zero '\0' to the result string.
The second parameter of the function should have the qualifier const because it is not changed in the function.
As all standard C string functions this function should return pointer to the result string.
And it is better to name the function like reverse_copy instead of reverse because the name reverse is usually used when a container is reversed "in place".
It can look the following way
char * reverse_copy( char revi[], const char liner[] )
{
size_t n = 0;
while ( liner[n] ) ++n;
if ( n != 0 && liner[n-1] == '\n' ) --n;
size_t i = 0;
while ( n != 0 ) revi[i++] = liner[--n];
revi[i] = '\0';
return revi;
}

An unexplained segmentation fault

I'm writing code to reverse the char array in every single line I input. However, when I debugged the program, I encountered a segmentation fault and I don't know why.
Here is my code:
#include <stdio.h>
#define DEFAULT 1000
int reverse(char s[]);
main()
{
int i;
char line[DEFAULT] = {'\0'};
while ((i = getline(line, DEFAULT)) != 0) {
printf("%s", reverse(line));
}
return 0;
}
/*reverse a line*/
int reverse(char s[])
{
int i = 0, j = 0;
while (s[i] != '\n')
++i;
for (j = 0; j < i; ++j) {
s[2 * i - j] = s[j];
}
for (j = 0; j < i; ++j) {
s[j] = s[j + i + 1];
s[j + i + 1] = '\0';
}
return i;
}
/*get a line from input stream*/
int getline(char s[], int lim)
{
int i, c, j = 0;
for (i = 0; ((c = getchar()) != EOF) && (c != '\n'); ++i) {
if (i < lim - 2) {
s[j] = c; // use j to prevent index out of bounds
++j;
}
}
if (c == '\n') {
s[j] = c;
++j;
}
s[j] = '\0';
return i; // return the length of char s[]
}
When I input "abc\n" and execute reverse(s), the content in s becomes "cba\n" and i refers to 3, everything is okay. The segmentation fault happens when I step out of that function.
Here are some more details:
Complier: GCC 4.9.2 64-bit Release
System: Windows 10
Your function int reverse(char s[]) is returning an int, and then you're trying to print that resulting int as a string:
printf("%s", reverse(line));
If you want to use the function like that, you could change its signature to return a char* instead:
char* reverse(char s[]);
And then return the s that you're giving in as a parameter. Your compiler should issue a warning about giving the wrong type of parameter to printf. If not, try to set it to be more strict with warnings.

String not printing in C. Even when using '\0' character

I'm trying to reverse an input in c.
Input: Hello World
Output: dlroW olleH
Actual Output:
I've tested the code with putchar and it seems like it's placing the correct characters in the correct position, however it does not output anything. The code is below:
#include <stdio.h>
#define MAXLEN 1000
int getLine(char s[]);
void reverse(char r[], char s[], int len);
int main()
{
char string[MAXLEN];
char reversed[MAXLEN];
int len;
while((len = getLine(string)) != 0)
{
reverse(reversed, string, len);
printf("%s\n", reversed);
}
return 0;
}
int getLine(char line[])
{
int i, c;
i = 0;
while((c = getchar()) != EOF && c != '\n')
{
line[i] = c;
i++;
}
line[i + 1] = '\0';
return i;
}
void reverse(char r[], char s[], int len)
{
int i;
i = 0;
while(len >= 0)
{
r[i++] = s[len--];
}
r[i] = '\0';
}
There are 2 obvious problems at least:
As mentioned in comments, you need to use line[i] = '\0'; rather than line[i + 1] = '\0' for terminating null.
In reverse() function, you need to use r[i++] = s[--len]; rather than r[i++] = s[len--];. If you don't, you will have invalid memory access.
You have to define what getLine() means by length. Is it the number of characters inserted into the array, or the number of characters excluding the '\0' that terminates the string. This is what code comments are for:
int getLine(char line[])
{
int i = 0, c;
while ((c = getchar()) != EOF && c != '\n')
{
line[i++] = c;
}
line[i] = '\0';
return i; // return array length, not counting final \0
}
Whichever you decide, this cannot make sense:
r[i++] = s[len--];
In one case you'd be putting the string termination character '\0' as the first character of the new string, always returning an empty string, or in the other case you would be copying garbage memory into the new string. From a 0-index-based array perspective, length should mean one beyond the last index. So the last item in the array should be s[len - 1]. Rather than count down on len, I suggest you instead count up on i until it reaches len:
void reverse(char r[], const char s[], size_t length)
{
int i = 0;
while (i < length)
{
r[i] = s[length - 1 - i];
i++;
}
r[i] = '\0';
}

K&R 1.19 exercise ("reverse" func)

Here is the task:
Write a function reverse(s) that reverses the character string s . Use it to write a program that reverses its input a line at a time.
Ok, now, my performing:
#include <stdio.h>
#define LIM 40
char line[LIM];
int c;
int reverse(char line[], int lim);
int len;
int main(void) {
while ((len = reverse(line, LIM)) > 0) {
;
}
printf("\n END OF THE PROGRAM \n");
return 0;
}
********** THE REVERSE FUNCTION*********
int reverse(char s[], int lim) {
char rev[LIM];
int i;
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
int r;
for (r = 0; r < lim - 1; ++r) {
rev[r] = s[i];
--i;
}
int x;
for (x = 0; x < lim - 1; ++x) {
putchar(rev[x]);
}
printf("\n");
return r;
}
It seems to work correctly, but there are some bugs related to the output.
For example:
INPUT: hello everybody OUTPUT: ydobyreve olleh
INPUT: abc OUTPUT: cba'
INPUT: ABC OUTPUT: CBA'
INPUT: ABC ABC OUTPUT: CBA CBA
INPUT: se se OUTPUT: es es'
See? Some strange " ' " occurs in the end of output and I can't figure out any pattern why these "artifacts" get printed. It happens randomly (for me). Could you please suggest anything, what's wrong in the code?
Your reverse function has problems:
You should not store the newline into the s array as you do not want it to take part in the reverse operation.
You should stop the subsequent for loop when you reach the end of the string in s, not run all the way to the end of the buffer.
You should null terminate the rev array.
You do not need to output the rev array one character at a time, use is as a string.
Here is a corrected and simplified version:
#include <stdio.h>
#define LIM 40
int reverse(char line[], int size);
int main(void) {
char line[LIM];
int len;
while (reverse(line, LIM) > 0) {
continue;
}
printf("\n END OF THE PROGRAM \n");
return 0;
}
/* THE REVERSE FUNCTION */
int reverse(char s[], int size) {
char rev[size];
int i, r, c, len;
for (i = 0; i < size - 1 && (c = getchar()) != EOF && c != '\n'; i++) {
s[i] = c;
}
len = i;
s[i] = '\0';
for (i = 0; i < len; i++) {
rev[len - i - 1] = s[i];
}
rev[i] = '\0';
printf("%s\n", rev);
return len;
}
A bit more modular solution. I wasn't exactly sure what K&R meant by "one line at a time". But this will reverse the string until it finds a newline. Then wait for the user and repeat.
#include <stdio.h>
#define MAXLINE 1000
int get_line(char s[], int limit);
int reverse(char to[], char from[], int l);
int main() {
int size;
char line[MAXLINE];
while ((size = get_line(line, MAXLINE)) > 0) {
char revline[size];
int len = reverse(revline, line, size);
printf("%s\n", revline);
}
return 0;
}
int reverse(char to[], char from[], int l) {
int i;
int j = l - 2;
for (i = 0; i < l; i++, j--) {
to[i] = from[j];
}
to[i] = '\0';
return i;
}
// read a line into s until limit
// return length of line
int get_line(char s[], int limit) {
int c = 0;
int i = 0;
for (i = 0; i < limit-1 && (c = getchar()) != '\n'; ++i) {
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
Output:
testing one two
owt eno gnitset
three four
ruof eerht
five six
xis evif

What's wrong with my solution to K&R exercise 1-22?

Exercise 1-22 of The C Programming Language is as follow:
Write a program to "fold" long input lines into two or more shorter
lines after the last non-blank character that occurs before the n-th
column of input. Make sure your program does something intelligent
with very long lines, and if there are no blanks or tabs before the
specified column.
This is the code:
#include <ctype.h>
#include <stdio.h>
#define MAXLINE 500
#define FOLD_LENGTH 15
/* _getline: read a line into s, return length */
size_t _getline(char s[], int lim)
{
int c;
size_t i;
for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
int main()
{
int c;
char line[MAXLINE];
char temp;
unsigned last_space_idx = 0, i, offset = 0;
while (_getline(line, MAXLINE) != 0) {
for (i = 0; line[offset+i] != '\0'; i++) {
if (i == FOLD_LENGTH) {
temp = line[offset+last_space_idx];
line[offset+last_space_idx] = '\0';
printf("%s\n", line+offset);
line[offset+last_space_idx] = temp;
offset = last_space_idx;
i = 0;
continue;
}
if (isspace(line[offset+i])) {
last_space_idx = offset+i;
}
}
printf("%s\n", line+offset);
}
return 0;
}
This is the sample input I'm using:
Penny Lane is in my ears and in my eyes
There beneath
the blue suburban skies
And this is the output I get:
Penny Lane is
in my ears and in my ey
and in my eyes
eyes
eyes
eyes
What's the bug here? I really have no clue.
Lots of errors. You do this:
last_space_idx = offset+i;
But you also do this:
temp = line[offset+last_space_idx];
Which means that temp = line[(2 * offset) + last_observed_space_relative_to_offset].
You also do this:
offset = last_space_idx;
That means the offset becomes equal to the last observed space, so you'll have a preceding space on every line after the first, like this:
Penny lane is
in my ears
and in my eyes
Your _getline() method does this:
if (c == '\n') {
s[i] = c;
++i;
}
That means any line returns are preserved, so if you have There beneath\nthe blue suburban skies as the input you'll get this output:
There beneath
the blue suburban skies
Lastly, each new line you read uses the last space index and offset from the previous line. You need to reset them before the for loop starts.
Here's a fixed version. I've tidied up the style a little and replaced the printf() bodge with a string format that will print a substring.
#include <stdio.h>
#include <ctype.h>
#include <stdio.h>
#define MAXLINE 500
#define FOLD_LENGTH 15
size_t _getline(char s[], int lim);
/* _getline: read a line into s, return length */
size_t _getline(char s[], int lim) {
char c;
size_t i;
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
s[i] = c;
}
s[i] = '\0';
return i;
}
int main() {
char line[MAXLINE];
unsigned last_space_idx = 0;
unsigned i;
unsigned offset = 0;
while (_getline(line, MAXLINE) != 0) {
last_space_idx = 0;
offset = 0;
for (i = 0; line[offset+i] != '\0'; ++i) {
if (i == FOLD_LENGTH) {
printf("%.*s\n", last_space_idx, line + offset);
offset += last_space_idx + 1;
i = 0;
} else if (isspace(line[offset + i])) {
last_space_idx = i;
}
}
printf("%s\n", line + offset);
}
return 0;
}

Resources