I'm writing code to find the longest row in the input stream and print it out. However, after I defined an int called max_count = 0, I always found an overflow, which displayed max_count as 1633771873. I've initialized that variable, so I don't know where the problem is. You probably do not need to figure out all of the functions, but each of them has its comment.
Here is my code:
#include <stdio.h>
#define DEFAULT 10
int getline(char line[], int limit);
void copy(char from[], char to[]);
int enlarge(int lim, char s[]);
main()
{
int i;
int max_count = 0;
char line[DEFAULT];
char maxline[DEFAULT];
while ((i = getline(line, DEFAULT)) != 0) {
if (i > max_count) { // where weird thing happend (max_count=1633771873)
max_count = i;
copy(line, maxline);
}
}
if (max_count > 0) {
printf("maxline: %s", maxline);
} else {
printf("No maxline");
}
return 0;
}
/*get a row from input stream and return its length*/
int getline(char s[], int lim)
{
int i, c;
for (i = 0; ((c = getchar()) != EOF) && (c != '\n'); ++i) {
if (i == lim - 1) {
lim = enlarge(lim, s);
}
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
}
if (i == lim) {
enlarge(lim, s);
}
s[i] = '\0';
return i;
}
/*copy an array to another */
void copy(char from[], char to[])
{
int i = 0;
while (from[i] != '\0') {
to[i] = from[i];
++i;
}
}
/*expand an array twice as its capacity*/
int enlarge(int lim, char s[])
{
s[lim - 1] = '\0';
lim *= 2;
char temp[lim];
copy(s, temp);
s = temp;
return lim;
}
This is the console window:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
^Z
maxline:
--------------------------------
Process exited after 15.19 seconds with return value 3221225477
You have a buffer with space for 10 characters:
#define DEFAULT 10
char line[DEFAULT];
You enter 37 characters including a newline:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Your getline function tries to store them all in line (by the way, enlarge doesn't do anything useful).
The first 10 characters fit into line. The other 27 characters and the terminating '\0' overwrite other random variables which come after line in memory.
That is why max_count holds the ASCII codes for aaaa.
Your enlarge function isn't doing what you think it is.
int enlarge(int lim, char s[])
{
s[lim - 1] = '\0';
lim *= 2;
char temp[lim];
copy(s, temp);
s = temp;
return lim;
}
You're creating a new array temp within the scope of the function. You then copy the address of the start of the array to s. Since s is a parameter to the function, modifying s won't be reflected in the calling function. So after this function returns s in getline is unchanged.
Even if you were to fix this by either returning a char * or changing the function to accept a char ** and assigning temp to the dereferenced pointer, you would be returning the address of a variable local to enlarge. That variable goes out of scope when the function returns and so the pointer would be invalid.
The only way you can change the size of an array is if you first allocate it dynamically with malloc and then later use realloc to change its size.
Also, getline is the name of a function on POSIX systems. You should change the name to something else.
Related
I am studying "The C Programming Language, 2nd Ed." by Brian Kernighan and Dennis Ritchie. I was on Exercise 1-19 of the book. The question asks to define a function reverse(s) which reverses a character string a. And we have to write a program which reverses it's input one at a time.
//This program is working on online compiler but not here
#include <stdio.h>
void reverse(char s[]);
int main(void) {
int h = 0; // sort of automatic variable just to
// keep storing characters in current line
char s[200];
char c;
for (int i = 0; i < 1000; i++)
s[i] = '\0';
while ((c = getchar()) != EOF) {
if (c != '\n') {
s[h++] = c;
} else {
s[h++] = c;
h = 0;
reverse(s);
for (int i = 0; i < 1000; i++)
s[i] = '\0';
}
}
}
void reverse(char s[]) {
int i = 200;
while(i >= 0)
if(s[i--] != '\0')
putchar(s[i]);
printf("\n");
}
So when I run this code with gcc on my system, I don't get any errors while compiling, but I can't type any input for some reason. However, the program runs correctly when I use an online C compiler.
void reverse(char s[]) {
int i = 200;
while(i >= 0)
if(s[i--] != '\0')
putchar(s[i]);
printf("\n");
}
if you postdecrement your string index, you are first using value at 200 position, which is invalid (it's one position out of the array) so you are doing bad. To do it properly, you need to predecrement it, as in:
void reverse(char s[]) {
int i = 200;
while(i >= 0)
if(s[--i] != '\0')
putchar(s[i]);
printf("\n");
}
but there's still an error... as you pass a null terminated string, you cannot be sure of what there is in the array after the null.... (can be another null?) so you have to search for the null from the beginning of the string (and this is good, because the most of the time you will feed the routine short strings, and now you don't depend on the array size, which you assumed by a constant 200. One good way to do it is with the strlen() function, as in:
void reverse(char s[]) {
int i = strlen(s);
while(i >= 0) /* ??? see below */
if(s[--i] != '\0')
putchar(s[i]);
printf("\n");
}
and then, the test you do is not necessary at all (you already found the leftmost null):
void reverse(char s[]) {
int i = strlen(s);
while(i >= 0) /* still more below VVV */
putchar(s[--i]);
printf("\n");
}
... and there's still a little mistake... you have to stop when i <= 0 and not when i < 0 as you are predecrementing now:
void reverse(char s[]) {
int i = strlen(s);
while(i > 0) /* here!! */
putchar(s[--i]);
printf("\n");
}
#include <stdio.h>
#define MAXLINE 100
int get_line(char line[], int maxline);
void copy(char to[], char from[]);
/*Prints longest input line*/
int main(){
int len; /*Current line length*/
int max; /*Maximum length so far*/
char line[MAXLINE]; /*Current input line*/
char longest[MAXLINE]; /*Longest line is saved here*/
max = 0;
while ((len = get_line(line, MAXLINE)) > 0){
if (len > max){
max = len;
copy (longest, line);
}
}
if (max > 0)
printf("%s", longest);
return 0;
}
/*get_line: read a line into s, return length*/
int get_line(char s[], int lim){
int c, 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;
}
/*copy: Copy 'from' into 'to'*/
void copy (char to[], char from[]){
int i;
i = 0;
while((to[i] = from[i]) != '\0')
++i;
}
This code is from Section 1.9 of The C Programming Language, I'm trying to understand how it works. I'm not understanding how the scopes of the variables work. In the while loop the function get_line is modifying the variable line and the function copy is modifying the variable longest.
Shouldn't they be only in the scope of the function? I don't get why the functions is able to modify them in main
Array's names are actually a pointer to first element.
What [] operator does is same as *(array + n)
Edit: As pointed out in the comments, this might be confusing and misleading.
I recommend you taking a look at this question: Is an array name a pointer?
int a[10];
a[1] == *(a + 1) // true
get_line() and copy() has parameter type of char[], which is same as char*.
You passed memory address of line and longest to those functions.
Variables aren't completely inaccessible when out of scope,
you just can't reference them directly using names.
Normally function parameters are copy of original value passed to the function,
so changing them won't affect the original varaible.
But in this case you're passing memory address of a variable, and those functions are able to change value of array's elements through pointer.
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;
}
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';
}
I am just simply trying to copy entered line and display it on the screen But still, output includes some additional term.... What is wrong?
#include<stdio.h>
#define MAXLINE 1000
void copy(char to[], char from[]);
main()
{
int length;
int limit;
char saved[MAXLINE];
char len[MAXLINE];
copy(saved,len);
printf("%s", saved);
}
void copy(char to[],char from[])
{
int a,i,c;
i = 0;
a = 0;
while((c = getchar()) != EOF)
{
from[i] = c;
++i;
}
while((to[a] = from[a]) != EOF)
++a;
}
The call to printf() is expecting a null-terminated string, yet the copy() function is not providing one.
Change the first loop in copy() so that the array index i is checked, to avoid buffer overflow, and then add a null-terminator after the loop terminates:
// check array index
while(i < MAXLINE-1 && (c = getchar()) != EOF)
{
from[i] = c;
++i;
}
// add null-terminator to from[]
from[i] = '\0';
Then change the second loop so that it terminates when the null-terminator is encountered, and add a \0 character to the end of to[]:
// change loop termination condition
while((to[a] = from[a]) != '\0')
++a;
// add null-terminator to to[]
to[a] = '\0';
Better, use the saved value of i to terminate the loop, and copy the \0 from from[] to to[]:
// better, use i to terminate loop
for (a = 0; a <= i; a++)
to[a] = from[a];
In my first version of the above loop, I inadvertently used for (a = 0; a < i; a++) {}, caught by #alk. This loop fails to copy the final \0 character, since when a == i, the loop terminates without executing the body. The quick fix above, changing a < i to a <= i works, but the loop is no longer idiomatic (hence my initial trouble; I write a < i in loops by reflex). A possibly better solution would be to increment i after the \0 character is stored in from[], just as has been done for every other character in from[]. This is illustrated in the final code below.
Additionally, note that the function signature for main() should be int main(void), and since copy() is declared as returning void, there should be no value returned at the end of the function. And, to be truly correct, the types of array indices should be size_t, which is an unsigned integer type guaranteed to be able to hold any array index.
#include <stdio.h>
#define MAXLINE 1000
void copy(char to[], char from[]);
int main(void)
{
// int length;
// int limit;
char saved[MAXLINE];
char len[MAXLINE];
copy(saved,len);
printf("%s", saved);
return 0;
}
void copy(char to[],char from[])
{
size_t a,i;
int c;
i = 0;
a = 0;
// check array index
while(i < MAXLINE-1 && (c = getchar()) != EOF)
{
from[i] = c;
++i;
}
// add null-terminator to from[], increment i
from[i] = '\0';
++i;
// use i to terminate copy loop
for (a = 0; a < i; a++)
to[a] = from[a];
}