can anyone tell me what mgetline is doing in ? i cant understand - c

i could not understand what mgetline does in this code.
anyone can help me?
int mgetline(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;
}

The function basically reads characters one-by-one from the the standard input stream stdin until you enter a \n (newline) or the array limit of s, lim, is reached. The characters are stored in the char s[] and the length of what was read is returned.
It's hard to answer with more detail since it's a little unclear what it is you don't understand, but I've tried to annotate the code to make it somewhat clearer.
This is the same code, only reformatted to fit my comments.
int mgetline(char s[], int lim) {
int c, i;
for(i = 0; // init-statement, start with `i` at zero
i < lim - 1 // condition, `i` must be less than `lim - 1`
&& // condition, logical AND
(c = getchar()) !=EOF // (function call, assignment) condition, `c` must not be EOF
&& // condition, logical AND
c != '\n'; // condition, `c` must not be `\n` (newline)
++i) // iteration_expression, increase i by one
{
s[i] = c; // store the value of `c` in `s[i]`
}
if(c == '\n') { // if a newline was the last character read
s[i] = c; // store it
++i; // and increase i by one
}
s[i] = '\0'; // store a null terminator last
return i; // return the length of the string stored in `s`
}
In the condition part of the for loop you have 3 conditions that must all be true for the loop to enter the statement for(...;...;...) statement. I've made that statement into a code block to make it easier to see the scope. EOF is a special value that is returned by getchar() if the input stream (stdin) is closed.
Note: If you pass an array of one char (lim == 1) this function will cause undefined behavior. Any program reading uninitialized variables has undefined behavior - and that's a bad thing. In this case, if lim == 1, you will read c after the loop and c will then still be uninitialized.
Either initialize it:
int mgetline(char s[], int lim) {
int c = 0, i;
or bail out of the function:
int mgetline(char s[], int lim) {
if(lim < 2) {
if(lim == 1) s[0] = '\0';
return 0;
}
int c, i;

Related

fuggy concept on how function and character array work in function parameter

** I understood how the function getline is working, it simply assigning value in each s[] array address which gets stored into the char line[] array because function argument has local scope there is no conflict due to the use of different array names unless it shares the same data type, But my concern is that why checker function has no effect on an actual string. correct me if the above understanding is wrong and why the checker function not working.**
the expected result was, getting string without trailing blanks and tabs like (hello World) but instead of that actual input that I typed in is printed which is ('\s'hello World'\t').
#define MAXLINE 50
int getline(char line[], int max);
int checker(char line[]);
int main(){
char line[MAXLINE];
while( getline(line, MAXLINE) > 0 )
if( checker(line) > 0 )
printf("%s",line);
return 0;
}
int getline(char s[],int lim){
int c,i,j;
j=0;
for(i=0; (c=getchar()) != EOF && c != '\n';i++){
if(i < lim-1){
s[j]=c;
++j;
}
}
if(c == '\n'){
s[j] = c;
++j;
++i;
}
s[j] = '\0';
return i;
}
int checker(char s[]){
int i;
i=0;
while(s[i] != '\n' )
++i;
--i;
while(i >= 0 && (s[i] == ' ' || s[i] == '\t') )
i++;
if( i >= 0){
++i;
s[i] = '\n';
++i;
s[i] = '\0';
}
return i;
}
If you are trying to trim trailing blanks and tabs from your string, try changing the contents of the second while loop in checker() to contain i-- rather than i++.
Since checker() is intended to change the string, perhaps a different name would be better. The word check does not usually imply modification. A well chosen name is a great help to the next person who encounters your code.
The bug seems to be here:
while(i >= 0 && (s[i] == ' ' || s[i] == '\t') )
i++;
^^^^
This shall probably be i--;
That said... Your function isn't secure. It lacks some checks to prevent access outside the char array.
For instance:
What happens if the input string has no '\n' ?
What happens if the input string is a space followed by '\n' ?
Also the getline function has a problem. If the input is longer than lim, the code will do s[lim] = '\0'; which is out of bounds.

function to read a line on c programming language

On the C book, (second edition page 29), I read the following content:
/* getline: read a line into s, return length */
int getline(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;
}
}
My question is: why it is i<lim-1 but not i<lim in the for condition test? (ps: lim is the maximum length of a line)
Question 2: On C, is EOF counted as a character?
Space needs to be reserved for the null-terminator \0 that is appended to the string at the end of the loop. (This is how strings are modelled in C).
EOF is a special value that denotes the end of a file. Note how getchar() returns an int: this is chiefly so the value of EOF doesn't have to be within the range of a char.

K&R - Section 1.9 - Character Arrays (concerning the getline function)

My question is concerning some code from section 1.9-character arrays in the Kernighan and Ritchie book. The code is as follows:
#include <stdio.h>
#define MAXLINE 1000 /* maximum input line size */
int getline(char line[], int maxline);
void copy(char to[], char from[]);
/* print longest input line */
main()
{
int len; /* current line length */
int max; /* maximum length seen so far */
char line[MAXLINE]; /* current input line */
char longest[MAXLINE]; /* longest line saved here */
max = 0;
while ((len = getline(line, MAXLINE)) > 0)
if (len > max) {
max = len;
copy(longest, line);
}
if (max > 0) /* there was a line */
printf("%s", longest);
return 0;
}
/* get line: read a line into s, return length */
int getline(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'; assume to is big enough */
void copy(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
My question is about the getline function. Now looking at this following output from my command line as a reference:
me#laptop
$ characterarray.exe
aaaaa
a
aaaaaa
^Z
aaaaaa
me#laptop
$
When I type in the first character which is 'a', does the character 'a' go through the for loop, in the getline function, and initialize s[0] - s[998] = a ? And my second part of the question is once the program leaves the for loop and goes to the
s[i] = '\0'
return i;
wouldn't it be initialized s[998] = '\0' and the return integer is 998? I've spent over an hour staring at this problem and I can't seem to grasp what is going on.
Character 'a' does not go through the for loop. Look here:
for (i=0; i<lim-1 && ((c=getchar()) != EOF) && c != '\n' ; ++i)
c = getchar() is where your program stops and waits for user input -- not just once, but in a loop! This is a condition of continuing the for loop, and each time it is checked, a getchar() is called. The loop goes only one char at a time. Each time a char is added to the current end of an array (indicated by i) and the loop breaks when a newline symbol is entered or when a limit size is reached. This is very compact code but it is kind of oldschool C coding which is not very readable these relaxed days. It can be broken into following parts:
i = 0;
while(i < lim - 1) {
c = getchar();
if (c == EOF || c == '\n') break;
s[i] = c;
++i;
}
s[i] = '\0';
return i;
(also I don't think a newline symbol is needed to be included in the string, so I omitted the
if (c == '\n') {
s[i] = c;
++i;
}
part.)
Here is how the input is working in the for-loop.
Before the for-loop, the array s is empty(ish. It's full of whatever garbage is in that memory). If you type in a single a and hit enter, the for-loop goes through 2 characters 'a' and '\n'. For 'a', the variable c becomes 'a' from getchar() in the for-loop parameters, and that gets saved in the i spot (which is 0) of s. So, the array s is now
s[0] = 'a' and the rest of s has random garbage in it.
Then c becomes '\n' from getchar(). This stops the for-loop because of the c != '\n' check. The if-statement has s[i], where i is 1, become '\n' and i bumps up to 2.
Now s is
s[0] = 'a'
s[1] = '\n'
getline finishes up by making s[2] be '\0', which is the end of string character, and returns i.
Your end result is
s[0] = 'a'
s[1] = '\n'
s[2] = '\0'
and i, your length, is 2.

Broken code on K&R?

At page29 of 'The C programming language' (Second edition) by K&R I've read a procedure I think is broken. Since I'm a beginner I would expect I'm wrong though I cannot explain why.
Here's the code:
#include <stdio.h>
#define MAXLINE 1000 // Maximum input line size
int get1line(char line[], int maxline);
void copy(char to[], char from[]);
// Print longest input line
int
main()
{
int len; // Current line lenght
int max; // Maximum lenght seen so far
char line[MAXLINE]; // Current input line
char longest[MAXLINE]; // Longest line saved here
max = 0;
while ((len = get1line(line, MAXLINE)) > 0)
if (len > max) {
max = len;
copy(longest, line);
}
if (max > 0) // There was a line to read
printf("Longest string read is: %s", longest);
return 0;
}
// `get1line()` : save a line from stdin into `s`, return `lenght`
int
get1line(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`; assuming
// `to` is big enough.
void
copy(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
My perplexity is: we're using the function get1line and suppose that at the end of the for-loop i is set at lim -1. Then the following if-statement will update i at lim, causing the next instruction (the one which puts the NULL character at the end of the string) to corrupting the stack (since s[lim] is not allocated, in that case).
Is the code broken?
Summary: It's not possible to exit the loop with both i == lim-1 and c == '\n', so the case you are worried about never arises.
In detail: We can rewrite the for loop (while preserving its meaning) to make the order of events clear.
i = 0;
for (;;) {
if (i >= lim-1) break; /* (1) */
c = getchar();
if (c == EOF) break; /* (2) */
if (c == '\n') break; /* (3) */
s[i] = c;
++i;
}
At loop exit (1) it can't be the case that c == '\n' because if that were the case then the loop would have exited at (3) the previous time around.*
At loop exits (2) and (3) it can't be the case that i == lim-1 because if that were the case then the loop would have exited at (1).
* This depends on lim being at least 2, so that there was in fact a previous time around the loop. The program only ever calls get1line with lim equal to MAXLINE, so this is always the case.**
** You could make make the function safe when lim is less than 2 by initializing c to a value other than '\n' before the loop begins. But if you are concerned about this possibility, then you might also want to be concerned about the possibility that lim is INT_MIN, so that lim-1 results in undefined behaviour due to integer overflow.
The code is wrong if lim == 0 because it uses c uninitialised and adds the \0.
It is also wrong if lim == 1 because it uses c uninitialised. Calling the function with lim < 2 is not very useful, but it should not fail like this.
If lim > 1 then the function is OK
for (i = 0; i < lim -1 && (c = getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
The loop exits either if i == lim-1 or if c == EOF or if c == '\n'.
If the first condition is true (i == lim-1), then the last condition is definitely not true (unless lim < 2, as noted above).
If the first condition is false (i < lim-1), then even if the loop exits with c == \n, we know that there is space in the buffer because we know that i < lim-1.

Embracing/or not ++i in one part of a code. K&R longest line example

int getline(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;
}
This example is from K&R book on C, chapter 1.9 on arrays. What I do not understand is why do we have to embrace ++i inside if statement? Writing it outside should do the same work.
if(c=='\n')
s[i] = c;
++i;
s[i] = '\0'
return 0;
}
In case of embracing i program works as intended, but on the second case(which in my opinion should do the same work and this is why I edited that part) it doesn't. I ran it through debugger and watched i which in both cases was correctly calculated and returned. But program still won't work without embracing ++i. I don't get my print from printf statement, and Ctrl+D just won't work in terminal or XTerm(thorough CodeBlocks) I can't figure out why. Any hint please? Am I missing some logical step? Here is a complete code:
//Program that reads lines and prints the longest
/*----------------------------------------------------------------------------*/
#include <stdio.h>
#define MAXLINE 1000
int getline(char currentline[], int maxlinelenght);
void copy(char saveto[], char copyfrom[]);
/*----------------------------------------------------------------------------*/
int main(void)
{
int len, max;
char line[MAXLINE], longest[MAXLINE];
max = 0;
while( (len = getline(line, MAXLINE)) > 0 )
if(len > max){
max = len;
copy(longest, line);
}
if(max > 0)
printf("StrLength:%d\nString:%s", max, longest);
return 0;
}
/*----------------------------------------------------------------------------*/
int getline(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;
}
/*----------------------------------------------------------------------------*/
void copy(char to[], char from[])
{
int i;
i = 0;
while( (to[i]=from[i]) != '\0')
++i;
}
/*----------------------------------------------------------------------------*/
The line
if(c == '\n')
is equivalent to
if(c != EOF)
Does that help explain why the embracing occurs?
There is a logic there:
if(c=='\n'){
s[i] = c;
++i;
}
It means only if you read an additional newline, you need to increment i one more in order to keep space for the \0 character. If you put ++i outside the if block. it means that it will always increase i by 1 even there is no newline input, in this case, since i is already incremented in the for loop , there is already space for \0, therefore, ++i again will be wrong. You can print the value of i and see how it works.
The index specified by i is the location where the terminating null should be placed when there is no more input for the line. The location just before the index i contains the last valid character in the string.
Keep in mind that the loop that reads data from stdin can terminate for reasons other than reading a \n character.
If you had this construct:
if(c=='\n')
s[i] = c;
++i;
then if the last character read from stdin wasn't a newline you would increment the index by one without writing anything into the location specified by the pre-incremented value of i. You would be effectively adding an unspecified character to the result.
Worse(?), if the for loop terminated because of the i<lim-1 condition you would end up writing the terminating null character after the specified end of the array, resulting in undefined behavior (memory corruption).
The ++i is inside the if statement because we do not want to increment i if we are not placing the \n character in the current index; that would result in leaving an index in between the last character of the input and the \0 at the end of the character string.
For loop can exit due to 3 conditions
1. reading char limit reached or EOF encountered
2. New Line encountered
For first Case We need to store Null into string s , as i points to next position to last valid character read so no need to increment i.
But for second case , as i points to next position to last valid character read , we now store newline at that position then increment i for storing NULL character.
Thats why we need to increment i in 2nd case not in 1st case.
if(c=='\n'){
s[i] = c;
++i;
}

Resources