Scope of variables in C, why is function modyfing values in main? - c

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

Related

Why does this program in Dennis Ritchie's "The C Programming Language" not work?

I'm starting to learn C and came across the following program in Dennis Ritchie's The C Programming Language (2nd edition):
#include <stdio.h>
#define MAXLINE 1000
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int main()
{
int len;
int max;
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)
printf("%s", 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;
}
When I run this program exactly as is, it does not compile because there are conflicting definitions of getline. It turns out there is a getline in stdio.h, and that's where the conflict comes from. I assume this is non-standard or was added to the library after the book was published. In any case, that error was easily fixed by simply changing the name of the function to getLine.
After making that change, the program compiles but never actually completes. What I did notice is that getLine adds both a newline character and a null terminator (\0) to the character array s, and the value it returns, while it is meant to be the length of the character array, is actually that length + 1. Modifying the function to return i - 1 instead of i fixes the issue.
My question is: why does it fix the issue? I doubt that it's a typo, but maybe that's possible? Or could it be a compiler issue? Do some compilers count a null terminator as a character (i.e. to be included in the length of the character array) while others don't?
I should also say that I'm using an M1 MacBook, so I guess it's possible that the code translates to different machine code which creates different results?
EDIT:
The following is the modified code that works for me:
#include <stdio.h>
#define MAXLINE 1000
int getLine(char line[], int maxline);
void copy(char to[], char from[]);
int main()
{
int len;
int max;
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)
printf("%s", 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 - 1;
}
void copy(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
Also, when I say that the original code never completes, I mean that I can type anything, press enter, type some more, press enter, etc. then finally just press enter without typing (the code is checking for an array of length zero, so this is where it should print out the line of max length and exit), and the program continues running.
When I run this program exactly as is, it does not compile because there are conflicting definitions of getline
There is a getline function offered as an extension in some implementations, and its prototype is conflicting with the example in the book. That's most likely what's happening with you. Awesome as K&R is, it's an old book at this point and out of date in many respects.
The easiest way to get around this is to rename your getline function to getLine or get_line or something else. Alternately you'll need to undefine _GNU_SOURCE or _POSIX_C_SOURCE before including stdio.h, either in your code or on the command line.

An unexplained overflow in C

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.

Call by reference behavior when calling by value

I am working through chapter 1.9 of the K&R C book and I don't fully understand the example code given. In it, there is a function getline(line, MAXLINE) that returns an int of the length of a line.
However, right afterwards, the 'line' variable is used. From my understanding of functions, the line variable should not be modified and C is just passing line and MAXLINE to the function and the function returns the length of a line. This looks like a pass by reference function but the code is pass by value function.
Any help would be appreciated.
I stripped away most of the original code in the K&R book to try to better understand it but it is still confusing me.
#define MAXLINE 1000
int getLine(char, int);
int main(){
char line[MAXLINE];
int len;
printf("%s\n", line); //making sure that there is nothing in line
len = getline(line, MAXLINE);
printf("length: %d\n", len);
printf("%s", line); //now there's something in line!?
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;
}
int getline(char s[], int lim) is equivalent to int getline(char *s, int lim).
What that means is that s is a pointer, pointing to the location in memory where char line[MAXLINE] is stored, so by modifying the contents of s, you actually modify the line array declared in main.
Also you have a small bug in the code in the question. I believe that the forward declaration int getLine(char, int); should be int getline(char[], int); (note the [] and the lowercase l);

What exactly breaks the while loop in main?

What exactly breaks the while loop in this code? This is code from the book The C Programming Language from the C creators. It is a code from section 1.9. I guess int len will always be bigger than 0, but somehow when I compile this code the while loop breaks when I press Ctrl+Z (which is EOF for Windows).
#include <stdio.h>
#define MAXLINE 1000 /* maximum input line length */
int mgetline(char line[], int maxline);
void copy(char to[], char from[]);
/* print the 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 = mgetline(line, MAXLINE)) > 0)
if (len > max) {
max = len;
if (max == len)
copy(longest, line);
}
if (max > 0) /* there was a line */
printf("%s", longest);
return 0;
}
/* mgetline: read a line into s, return length */
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;
}
/* 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;
}
Loop 1: (In copy)
Strings in C are NUL-terminated by convention. NUL is a special char value with the value 0.
The value of the expression to[i] = from[i] is the new value of to[i].
That is 0 when NUL is reached, and the loop exits.
Loop 2: (In main)
Similarly the value of len = mgetline(line, MAXLINE) is the new value of len. That is 0 if mgetline returns 0 which is does when no characters are read. So that loop exits.

what is wrong with this code? Trying to display copied line of character array

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

Resources