K&R 1.19 exercise ("reverse" func) - c

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

Related

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.

counting occurrence of character and printing them out

I've managed to get the frequency of the alphabet from my input. I also found the largest occurred character. But i dont know how to print the actual character. Right now my program displays A-Z and display the occurrence for each alphabet. I want to be able on the next line print out the largest occurred letter and how many times it occurred.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX 200
int readLine(char string[]);
void find_frequency(char string[], int count[]);
int maxArray(int a[]);
int main(void)
{
char array[MAX];
int freq[MAX];
int nrOfChar;
int i;
char c;
int max;
printf("Command Line Tool\n");
printf("Please enter text here: ");
nrOfChar = readLine(array);
for(c = 'A'; c<= 'Z'; c++)
{
printf("%c ", c);
}
find_frequency(array, freq);
printf("\n");
for(i=0;i<26;i++)
{
printf("%d ", freq[i]);
}
printf("\n");
max=maxArray(freq);
printf("Print letter and how many occurrence.\n");
printf("Finished excuting.\n");
return 0;
}
int readLine(char string[])
{
int ch;
int i=0;
while (isspace(ch = getchar()))
;
while (ch != '\n' && ch != EOF)
{
if (i < MAX)
{
string[i++] = ch;
ch = getchar();
}
}
string[i] = '\0';
return i;
}
void find_frequency(char string[], int count[])
{
int i;
for(i = 0; string[i] != '\0'; i++)
{
if (string[i] >= 'A' && string[i] <= 'Z' )
{
count[string[i]-'A']++;
}
}
}
int maxArray(int a[])
{
int i, max=0;
for (i=0; i<26; i++)
{
if (a[i]>max)
{
max=a[i];
}
}
return max;
}
Below I've incorporated various folks' suggestions from the comments as well as some other fixes and style changes for you to consider:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define ALPHABET_LENGTH 26
#define MAXIMUM_CHARACTERS 200
int readLine(char string[], int maximum);
void findFrequency(char string[], int count[]);
int findMaximumIndex(int array[], int length);
int main(void)
{
char characters[MAXIMUM_CHARACTERS];
int frequencies[ALPHABET_LENGTH];
printf("Command Line Tool\n");
printf("Please enter text here: ");
(void) readLine(characters, MAXIMUM_CHARACTERS);
for (char c = 'A'; c <= 'Z'; c++)
{
printf("%c ", c);
}
printf("\n");
findFrequency(characters, frequencies);
for (int i = 0; i < ALPHABET_LENGTH; i++)
{
printf("%d ", frequencies[i]);
}
printf("\n");
int maximum = findMaximumIndex(frequencies, ALPHABET_LENGTH);
printf("%c occurred most often (%d times)\n", maximum + 'A', frequencies[maximum]);
printf("Finished excuting.\n");
return 0;
}
int readLine(char string[], int maximum)
{
int c, count = 0;
while (isspace(c = getchar()))
;
while (c != EOF && c != '\n')
{
if (count < maximum - 1)
{
string[count++] = c;
c = getchar();
}
}
string[count] = '\0';
return count;
}
void findFrequency(char string[], int count[])
{
for (int i = 0; string[i] != '\0'; i++)
{
char c = string[i];
if (c >= 'A' && c <= 'Z' )
{
count[c - 'A']++;
}
}
}
int findMaximumIndex(int array[], int length)
{
int index = 0;
for (int i = 1; i < length; i++)
{
if (array[i] > array[index])
{
index = i;
}
}
return index;
}
EXAMPLE OUTPUT
> ./a.out
Command Line Tool
Please enter text here: IN TEACHING OTHERS WE TEACH OURSELVES
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
2 0 2 0 6 0 1 3 2 0 0 1 0 2 2 0 0 2 3 3 1 1 1 0 0 0
'E' occurred most often (6 times)
Finished excuting.
>
Things yet to ponder: your output won't look as nice if any character occurs more than 9 times -- how can you fix that; you might want to deal with mixed case as an option in your readLine() routine to uppercase the input; there may be a (multi-way) tie for the most frequently occuring letter, can you deal with that; your readLine() returns a count of characters that you don't do anything with -- how might it be useful (possibly in error checking)?

Question mark at the end of the output

Trying to implement detab function which is described in K&R book http://clc-wiki.net/wiki/K%26R2_solutions:Chapter_1:Exercise_20 an faced a problem: when outputting a replaced string there's a question mark in the end of the output. Why?
#include <stdio.h>
#define MAXLINE 9999
void get_text(char output[]);
void detab(char input[], char output[], int tab_size);
int main()
{
char input[MAXLINE];
char final[MAXLINE];
get_text(input);
detab(input, final, 4);
for (int i = 0; final[i] != '\0'; ++i)
{
putchar(final[i]);
}
putchar('\n');
return 0;
}
void get_text(char output[])
{
int c;
int i = 0;
for (i = 0; i < MAXLINE && (c = getchar()) != EOF; ++i)
{
output[i] = c;
}
output[i + 1] = '\0';
}
void detab(char input[], char output[], int tab_size)
{
int c = 0;
int r = 0;
for (int i = 0; input[i] != '\0'; ++i)
{
c = input[i];
if(c == '\t')
{
for (int t = 0; t < tab_size; ++t)
{
output[r] = '.';
r++;
}
}
else
{
output[r] = c;
r++;
}
}
output[r] = '\0';
}
And here is the output when I'm passing the file with following content 'asdasdads tasdasdasdasdasd sadasdasd':
asdasdads....tasdasdasdasdasd....sadasdasd? (? at the end).
Why there is a question mark at the end?
output[i + 1] = '\0';
You don't need to add 1 here, it was already done in the loop. (First i is incremented and then i < MAXLINE && (c = getchar()) != EOF is tested, so i is one higher than in the last loop iteration already)
I'd guess that's in place of an unprintable character. The characters in final aren't initialized to anything in particular, and you're leaving a gap at the end of the detabbed string when you say:
output[i + 1] = '\0';
output[i], one past the output text, is still uninitialized.
You want:
output[i] = '\0';

Reversing a string in C does not output the reversed line

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];

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