Encapsulation in C functions - K&R section 1.9 - c

I am fairly new to C, and I am working through Ritchie's and Kernighan's The C programming language, and I don't understand how the following code works:
#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];
char longest[MAXLINE];
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;
}
s[i] = '\0';
return(i);
}
void copy(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
What I don't get is how does the copy function affect anything without returning anything, I am used to Python where functions can only affect outwith themselves through return values, and I was under the impression C was the same.
I have tested the code and it does work.

The statement while ((to[i] = from[i]) != '\0') is actually an assignment disguised within the comparison.
It's a shorthand code that I personally find confusing, and you're proving that it infact is.

That copy function works through the magic of pointers.
In essence, pointers are basically a memory address that you can access like an array from the same program.
On a side note, Learn C the Hard way is, in my opinion, a better book.
He explains many concepts of C, and he also points out some flaws in that very same copy function you are asking about.
UPDATE
He actually removed his section on K&R C, because he believes that C is dead.
I think he is wrong, but, to each his own.

void copy(char to[], char from[]) is actually the same like void copy(char *to, char *from). So, two pointers are passed to the copy function. The function can change the destinations of the pointers. This mechanism is known as "call by reference".
(The copy function only needs to change the destination of the to-pointer, the destination of the from-pointer needs not to be changed. This can be made clearer with void copy(char *to, const char *from).)

Related

gcc errors "conflicting types for" and "previous declaration of" during compiling

I am getting these errors despite declaring the "getline" and "copy" function prototypes before main(). This program comes straight from the code in The C Programming Language so I'm unsure what the issue is and how to fix it.
#include <stdio.h>
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int main()
{
}
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 exact errors produced by the compiler are:
string_reverser.c:4:5: error: conflicting types for 'getline'
int getline(char line[], int maxline);
^~~~~~~
In file included from string_reverser.c:1:0:
c:\mingw\include\stdio.h:650:1: note: previous declaration of 'getline' was here
getline (char ** __restrict__, size_t * __restrict__, FILE * __restrict__);
^~~~~~~
string_reverser.c:27:5: error: conflicting types for 'getline'
int getline(char s[], int lim)
^~~~~~~
In file included from string_reverser.c:1:0:
c:\mingw\include\stdio.h:650:1: note: previous declaration of 'getline' was here
getline (char ** __restrict__, size_t * __restrict__, FILE * __restrict__);
^~~~~~~
The POSIX function getline() is now a standard library function which is (already) declared in <stdio.h> (but wasn't standard when K&R was written).
Hence, you cannot re-declare the function a little differently in C language.
A workaround is to rename your getline function to something else, e.g. getline_new
The updated code is as below with this workaround, or you may want to switch to C++ that gives flexibility to have many functions with same name, but different arguments, including argument type (polymorphism concept)
#include <stdio.h>
int getline_new(char line[], int maxline);
void copy(char to[], char from[]);
int main()
{
}
int getline_new(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;
}

Does a copied string end with a '\0' (Section 1.9 The C Programming Language K&R2)

In the last line of copy function I have stored a '\0' at the end of the copied string, this was not originally in the code.
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*/
int 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;
}
/* 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;
}
/* 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;
to[i] = '\0'; /*<-----Problematic Area*/
}
My question here is, whether the copied string already contains a '\0' at the end. If it is not so then is it a good practice to include it as I have done in the code.
The statement
while ((to[i] = from[i]) != '\0')
++i;
first assigns the value of from[i] to to[i] and then compares the assigned value to \0 - if it was the null terminator that was just copied, the loop ends.
Thus
to[i] = '\0';
is unnecessary, but not otherwise incorrect here.
However, having unnecessary code around is not good style because it makes refactoring and reasoning about the other code harder. Just the presence of that last assignment could then confuse a future reader into thinking that the loop in itself is not sufficient to properly terminate the string.
Additionally, should someone come and edit the code into
while ((*to++ = *from++));
as suggested by WhozCraig, they could then mistakenly think that they do need to add the null terminator, this time potentially writing out of bounds:
*to++ = 0;

K&R Error: conflicting method definition

I am going through K&R (2 ed.) to learn C as I've been trying to get a basis in lower-level languages to help my programming and also because I want to know C. The book is absolutely fantastic; however, a program they provided on pp. 29 (Sec 1.9 Character Arrays) does not compile. This is the code
#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;
}
/* getline: read a line int 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;
}
When I run cc longest_line.c (that's what I called it) I get an error that getline has conflicting definitions as it was already defined in stdio.h. My question is, how do I resolve this/can I resolve this without giving getline a different name?
As #Olaf commented, getline() is standard POSIX,
K&R second edition is ansi 89 standard.
Just compile in ansi mode, to prevent the collision with POSIX libraries:
gcc -ansi source.c
Edit:
GodBot Link
This approach avoids your problem by not including the problematic header. Rather, it copies all the declarations needed in your implementation locally.
This is a very bad idea because the declarations of these functions may change without your knowledge in which case your code will pass or receive bad inputs
#define MAXLINE 1000 /* maximum input line size */
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int printf(const char *format, ...);
int getchar(void);
/* print longest input line */
int 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;
}
/* getline: read a line int s, return length */
int getline(char s[], int lim)
{
int c, i;
for (i = 0; i<lim-1 && (c=getchar())!=-1 && 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;
}
~

Old C code not compiling

I am trying this old example from "The C Programming Language". I am getting a conflicting types error with the getline function. I also tried placing the function before main. I am using gcc 4.6.3 as my compiler.
#include <stdio.h>
#define MAXLINE 1000
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int main(void)
{
int len;
int max;
char line[MAXLINE];
char longest[MAXLINE];
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;
}
getline is a standard POSIX function nowadays, with a different signature than the function in this K&R example. Rename getline to something else and it should work.
Alternatively, compile with gcc -std=c89 (or -std=c99) to get pure ISO C mode without POSIX or GNU extensions.
Your getline function is clashing with getline as provided by the standard library. It'd be best to rename your function.

Printing a C string in reverse without using pointers?

Is there a way to print a string of fixed size in reverse without using pointers?
#include<stdio.h>
main()
{
char buffer[10];
scanf("%s", buffer);
// need to print buffer in reverse without using pointers??
}
A lovely K&R function to reverse your string in-place before printing it, perhaps?
#include <stdio.h>
#include <string.h>
void strrev(char *s) {
int tmp, i, j;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}
int main(int argc, const char *argv[]) {
char buffer[10];
scanf("%s", buffer);
strrev(buffer);
printf("%s\n", buffer);
return 0;
}
#include<stdio.h>
main()
{
char buffer[10];
int n = scanf("%s", buffer);
// print the number of chars written to buffer
if (n != EOF) {
int len = strlen(buffer);
if (len <= 10) {
int i;
for (i = len - 1; i >= 0; i--)
printf("%c", buffer[i]);
}
}
}
Since [] is just syntactic sugar for pointers, here's a version that works completely without pointers, arrays or anything else, just one single int. You didn't say that the string has to be stored somehow. :) (Note that I use fgetc instead of a buffer and scanf).
[jkramer/sgi5k:.../c]# cat rev.c
#include <stdio.h>
#include <stdlib.h>
void read_print();
int main(void) {
fputs("Enter your string, yo! ", stdout);
read_print();
fputs("\nDone!\n", stdout);
return EXIT_SUCCESS;
}
void read_print() {
int c = fgetc(stdin);
if(c != EOF && c != '\n') {
read_print();
fputc(c, stdout);
}
}
[jkramer/sgi5k:.../c]# gcc -o rev rev.c -Wall -W -Os
[jkramer/sgi5k:.../c]# ./rev
Enter your string, yo! foobar
raboof
Done!
Here's a recursive way of doing it; technically, this is using a pointer, but I wouldn't go into language-lawyer mode with such simple tasks.
#include <stdio.h>
/* If you want it printed forward, or backward, or think of another way.. */
typedef enum {
FRONT = 1,
BACK,
} direction;
/* Technically still using a pointer...don't nitpick. */
void echo_string(char buffer[], size_t buflen, direction from)
{
/* An index into the buffer to echo, which will preserve
* its value across subsequent recursive calls.
*/
static size_t index = 0;
/* According to the specified direction, print from the front
* or the back of the buffer. Advance the index (a misnomer, I guess).
*/
if(from == FRONT) {
printf("%c", buffer[index++]);
}
else {
printf("%c", buffer[buflen - ++index]);
}
/* Are there any more characters to echo? Yes? Awesome! */
if(index != buflen) {
echo_string(buffer, buflen, from);
}
}
int main(int argc, char **argv)
{
char buffer[10];
scanf("%s", buffer);
/* Better strlen() than sizeof() here,
* but BEWARE! scanf() is DANGEROUS!
*/
echo_string(buffer, strlen(buffer), BACK);
return(0);
}
reverse(char c[], int len)
{
if( ! (len / 2))
return;
char t = c[0];
c[0] = c[len--];
c[len] = t;
reverse(c, len-1);
}
The error(s) is left as an exercise to the student.
As caf pointed out, we're still using pointers..!
Here's an other way to solve the problem (of reversing a string).
This code snippet (and probably most others) don't respect stuff like utf8. I think signines post demonstrating the K&R way was quite close to mine (:D) so I adapted mine to fit that example (and corrected some things..)
#include <stdio.h>
#include <string.h>
void strrev(char *s) {
size_t len = strlen(s) + 1;
size_t i, j;
for(i = 0; i < len / 2; i++) {
j = len-1 - i-1;
char tmp = s[j];
s[j] = s[i];
s[i] = tmp;
}
}
int main(int argc, const char *argv[]) {
char buffer[10];
scanf("%s", buffer); // Look out for an overflow ;)
strrev(buffer);
puts(buffer);
return(0);
}
You can use strrev to reverse a string.
#include <stdio.h>
#include <string.h>
main()
{
char buffer[10];
scanf("%s", buffer);
strrev(buffer);
printf("%s", buffer);
}
void outstrreverse(const char s[])
{
size_t l=strlen(s);
while( l && s!=&s[--l] )
putchar(s[l]);
if(s[0])
putchar(s[0]);
}
Because of the relationship between C strings, arrays, and pointers the exercise is rather shotty IMHO - the most idiomatic description of a "String" in C is represented by the char*, which is not an array. Your (the OPs) title and post differ in their definitions between string and char[fixed length].
The OP should read and understand this FAQ entry, and between that and the posts here: easily figure out a solution—as well as defend it to the teacher/judge if need be.
I'll comment on this: never use scanf("%s", buffer) to populate a fixed length string. If you must use scanf() to do it, please use a field width specifier: e.g. scanf("%9s", buffer); if buffer is an [10], you want a specifier of 9 because of how scanf fills the buffer: otherwise you must beware the dragons! You could also scanf by character and evade the issue with a loops bounds, but that would likely be less efficient.
#include <stdio.h>
#include <conio.h>
void reverse(char a[], int s, int sc );
void reverse(char a[], int s, int sc ){
if ((sc-s)<(s-1))
{
a[sc-s]^=a[s-1];
a[s-1]^=a[sc-s];
a[sc-s]^=a[s-1];
reverse (a, s-1, sc) ;
}
}
void main (){
char a[]="ABCDEFG";
reverse(a, 7, 7);
printf("%d",a);
getch(); //i just use it to freeze the screen
}

Resources