7
\$\begingroup\$

This is my solution for exercise 1-19 of K&R:

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.1

It works, but I'm trying to write good programs. I think the fact that I can't use the length of the vector when already got the line in function getlinea makes me think I could do better. The function reverse(s) uses 3 for sentences, which might be a little ugly. I know it doesn't mean it's not good but having to "read" the line 4 times is kind of a little stupid to me. I still can't think of a different solution.

/*
 * main.c
 *
 *  Created on: 19/3/2015
 *      Author: utnso
 */

#include <stdio.h>

#define MAXLINE 1000

void reverse (char s [] );
int getlinea (char s [] , int lim);

int main ()
{
    int len;
    char palabra [MAXLINE];
    while ( ( len = getlinea ( palabra, MAXLINE ) )  >= 0 )
        if (len > 0)
            printf ("%s\n", palabra);
    return 0;

}

int getlinea ( char s [], int lim )
{
    int i, c;
    i = 0;
    while ( ( c = getchar() ) != EOF && c != '\n' && i < lim-1 )
    {
        s [i] = c ;
        ++ i;
    }
    s [i] = '\0';
    if ( i > 0 )
        reverse ( s ) ;
    else
        if ( c == EOF )
            return -1;
    return i ;
}

void reverse ( char turnaround [] )
{
    int i, j;
    int h = 0;
    char aux [MAXLINE];
    for ( i = 0; turnaround [i] != '\0' ; ++i )
        ;
    for ( j = i-1 ; j >= 0 ; --j)
    {
        aux [h] = turnaround [j];
        ++h;
    }
    for ( i = 0 ; turnaround [i] != '\0' ; ++i )
        turnaround [i] = aux [i];
}

This is the output for

$ ./1-19 < main.c

(it also deletes white lines)

*/
c.niam * 
* 
5102/3/91 :no detaerC  * 
osntu :rohtuA      * 
/* 
>h.oidts< edulcni#
0001 ENILXAM enifed#
;) ][ s rahc( esrever diov
;)mil tni , ][ s rahc( aenilteg tni
)( niam tni
{
;nel tni  
;]ENILXAM[ arbalap rahc   
) 0 =>  ) ) ENILXAM ,arbalap ( aenilteg = nel ( ( elihw   
)0 > nel( fi      
;)arbalap ,"n\s%"( ftnirp         
;0 nruter 
}
) mil tni ,][ s rahc ( aenilteg tni
{
;c ,i tni 
;0 = i    
) 1-mil < i && 'n\' =! c && FOE =! ) )(rahcteg = c ( ( elihw  
{ 
; c = ]i[ s       
;i ++     
} 
) 0 > i ( fi  
{ 
;'0\' = ]i[ s     
; ) s ( esrever       
} 
esle  
) FOE == c ( fi       
;1- nruter            
; i nruter    
}
) ][ dnuoranrut rahc ( esrever diov
{
;j ,i tni 
;0 = h tni    
;]ENILXAM[ xua rahc   
) i++ ; '0\' =! ]i[ dnuoranrut ;0 = i ( rof   
;     
)j-- ; 0 => j ; 1-i = j ( rof 
{ 
;]j[ dnuoranrut = ]h[ xua     
;h++      
} 
) i++ ; '0\' =! ]i[ dnuoranrut ; 0 = i ( rof  
;]i[ xua = ]i[ dnuoranrut     
}

1http://cs.indstate.edu/~cbasavaraj/cs559/the_c_programming_language_2.pdf - Page 31

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Just another side thought: did you consider reversing the string in place, without auxiliary buffer? Just exchange the first & last letter, second and next to last,.... up to the middle. \$\endgroup\$
    – orion
    Commented Mar 20, 2015 at 7:49
  • \$\begingroup\$ Please do not add, remove, or edit code in a question after you've received an answer. The site policy is explained in What to do when someone answers. \$\endgroup\$ Commented Mar 21, 2015 at 6:37
  • \$\begingroup\$ @orion Consider posting your comment as an answer. \$\endgroup\$ Commented Mar 21, 2015 at 6:39
  • \$\begingroup\$ understood, @200_success , ty. won't happen again. \$\endgroup\$ Commented Mar 21, 2015 at 14:03
  • \$\begingroup\$ i could also use reverse after every getline in main function and pass to it only the vector and having len as a global variable, so it wouldn't be needed to have more than one loop in reverse function. How about using github to show code is there any policy about that? like... this is my code and i'm optimizing it on this github url so you can check it out \$\endgroup\$ Commented Mar 22, 2015 at 23:18

2 Answers 2

8
\$\begingroup\$
  • getlinea does too much: it reads the line and reverses it. A Single Responsibility Principle mandates restructuring the code:

    while((len = getlinea(...)) > 0) {
        reverse();
        print();
    }
    
  • Avoid naked loops. Each loop represent an important action, and deserves a name. The code:

    reverse(char turnaround[])
    {
        char aux[...];
        int len = length_of_string(turnaround);
        copy_backwards(aux, turnaround, len);
        copy(turnaround, aux, len);
    }
    

    is much easier to reason about.

  • The length of the input is known to the caller. Consider passing it as parameter:

    reverse(char turnaround[], int len)
    {
        char aux[...];
        copy_backwards(aux, turnaround, len);
        copy(turnaround, aux, len);
    }
    
  • Finally, a string can be reversed in place. There is no need for aux. I hope you can figure a solution yourself.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Thanks for the advices! very apriciated i was trying to use only the tools that the book has taught me so far, besides the function reverse only accepts string character s, i was really tempted to pass length, but i don't think that's what the authors of the book want readers to do. ill consider: avoiding naked loops, trying that functions don't do much more than what the names say they do. \$\endgroup\$ Commented Mar 20, 2015 at 2:52
  • \$\begingroup\$ good naming is more than half the victory in code poetry. +1 for clothing your loops and functions \$\endgroup\$
    – sova
    Commented Mar 20, 2015 at 9:13
  • \$\begingroup\$ The code which reads the string indicates the offset where it stuck a zero byte, but does not indicate whether that is the first zero byte read. In the event that the input file contains null bytes, the original code will output the portion prior to the first null byte, reversed, while a version which did its reversal using the number of bytes prior to the newline would return the portion after the last null byte, reversed. Of course, the proper thing for a utility to do would probably be to ignore null bytes altogether, but that would require replacing the printf with a fwrite. \$\endgroup\$
    – supercat
    Commented Mar 24, 2015 at 22:41
3
\$\begingroup\$

While the answer is using concepts not covered in chapter 1 (and for that reason, is probably a better solution in real situations), here's my take on the exercise using only the covered material:

void reverse(char s[]) {
    int i;
    for (i = 0; s[i] != '\0' && s[i] != '\n'; ++i) { }
    for (i = i-1; i >= 0; --i) {
        putchar(s[i]);
    }
    putchar('\n');
}

The requirement of the exercise is actually to reverse its input one line at a time, so no character array is actually needed. This function gets the job done without allocating extra memory.

The function could actually perform better if another parameter len could be given, so you won't even need the first loop. I didn't add the second parameter only because the exercise seems to want a function taking only one argument s.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.