K&R2 , exercise 7.6

C

Chris Dollin

arnuld said:
void print_line( const char* line )
{
while( *line != '\0')
{
puts(*line++);
}
}

================ OUTPUT =======================

[arnuld@raj C]$ gcc -ansi -pedantic -Wall -Wextra 7-6.c
7-6.c: In function `print_line':
7-6.c:96: warning: passing arg 1 of `puts' makes pointer from integer
without a cast
[arnuld@raj C]$ ./a.out 7-6.c replace-blanks.c Segmentationfault
[arnuld@raj C]$

Ask yourself: what does `puts` accept as an parameter? What is
the type of the expression `*line++`? Are C characters a kind
of integer? Do you really expect an integer to be freely and
automatically converted to an integer?

--
"It took a very long time, much longer than the most /Sector General/
generous estimates."

Hewlett-Packard Limited Cain Road, Bracknell, registered no:
registered office: Berks RG12 1HN 690597 England
 
J

Joachim Schmitz

arnuld said:
arnuld said:
Where do these point? (Hint: nowhere in particular.)


OK, here is the 2nd version:


/* K&R2, section 7.7, exercise 7.6
*
* write a program to compare 2 files, printing that first line
* where they differ.
*
* version 1.1
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum MAXSIZE { ARRSIZE=1000 };

void compare_files( FILE*, FILE* );
void print_line( const char* );


int main( int argc, char* argv[] )
{
FILE *pf1, *pf2;

if( argc !=3 )
{
fprintf( stderr, "You iDiOT, I expect 2 files as input. \n" );
exit(EXIT_FAILURE);
}


/* open files */
pf1 = fopen( *++argv, "r" );
pf2 = fopen( *++argv, "r" );

/* error check */
if( pf1 == NULL || pf2 == NULL )
{
fprintf( stderr, "error opening files\n");
exit(EXIT_FAILURE);
}
else
{
compare_files( pf1,pf2 );
}



/* don't forget to close the files */
fclose( pf1 );
fclose( pf2 );


return 0;
}



/* compare 2 files */
void compare_files( FILE* pf1, FILE* pf2 )
{
int match;
char c1, c2;
char line1[ARRSIZE];
char line2[ARRSIZE];
char *begin_line1, *begin_line2, *p1, *p2;


match = 1;

while( (fgets( line1, ARRSIZE, pf1 ) != NULL) ||
(fgets( line2, ARRSIZE, pf2 ) != NULL))
{
begin_line1 = line1;
begin_line2 = line2;

p1 = line1;
p2 = line2;
for( c1 = *p1, c2 = *p2; c1 != '\0' && c2 != '\0'; ++p1, ++p2 )
{
if ( c1 != c2 )
{
match = 0;
print_line( begin_line1 );
printf("\n-----------------------\n"); print_line( begin_line2
); }
}
}
}



void print_line( const char* line )
{
while( *line != '\0')
{
puts(*line++);
}
}

================ OUTPUT =======================

[arnuld@raj C]$ gcc -ansi -pedantic -Wall -Wextra 7-6.c
7-6.c: In function `print_line':
7-6.c:96: warning: passing arg 1 of `puts' makes pointer from integer
without a cast
What does that warning tell you?
[arnuld@raj C]$ ./a.out 7-6.c replace-blanks.c Segmentationfault
[arnuld@raj C]$
You lied to your compiler (and he spotted it and warned you about it) and he
got his revenge...

Bye, Jojo
 
R

Richard Tobin

Wrong. In C, a string is a contiguous sequence of characters, terminated
by the first null character.
[/QUOTE]
but these 2 are different:

char arr[] = "Richard";
char* pc = "Richard";

both are contiguous sequence of characters ended by '\0'.

And they are both strings.
you can never use the pc for %s in printf().

What do you mean by "the pc"? Do you mean the variable pc declared
above? If so, you can certainly print it with %s.

-- Richard
 
J

Joachim Schmitz

arnuld said:
On Mon, 21 Apr 2008 11:57:59 +0000, Richard Heathfield wrote:
Wrong. In C, a string is a contiguous sequence of characters,
terminated by the first null character.

but these 2 are different:

char arr[] = "Richard";
char* pc = "Richard";

both are contiguous sequence of characters ended by '\0'.
pc is a pointer to a (read-only) string
arr is an array containing a (modifyable) string
you can never use the pc for %s in printf().
Nonsense. In both cases printf only sees a pointer to char and prints that
up to the terminating \0.
Thats what I meant, there
are no strings, only arrays of chars. I tried to print the pc but all
I got was:


^A^M^D @#
They you must have done something severly wrong.

Bye, Jojo
 
B

Ben Bacarisse

arnuld said:
okay, here is the 3rd version of the program. It compiles and runs, the
only thing is semantic-bug, it always does one thing, no matter whether 2
files are same or different:
/* compare 2 files */
void compare_files( FILE* pf1, FILE* pf2 )
{
int match;
char c1, c2;
char line1[ARRSIZE];
char line2[ARRSIZE];
const char *begin_line1, *begin_line2, *p1, *p2;


match = 1;

What is this for?
while( (fgets( line1, ARRSIZE, pf1 ) != NULL) || (fgets( line2,
ARRSIZE, pf2 ) != NULL))

The logic here is wrong. || is a "a short-circuit" operator. Once
the first half gets a line the second half will not be executed. You
must read pairs of lines and I'd stop when one of the files runs out.
{
begin_line1 = line1;
begin_line2 = line2;

p1 = line1;
p2 = line2;
for( c1 = *p1, c2 = *p2; c1 != '\0' && c2 != '\0'; ++p1, ++p2 )
{
if ( c1 != c2 )

Why so many variables? What is wrong with strcmp?
{
match = 0;
print_line( begin_line1 );
printf("\n-----------------------\n");
print_line( begin_line2 );
return;
}
}
}
}



void print_line( const char* line )
{
while( *line != '\0')
{
putchar(*line++);
}
}

What is the point of this function? It is just fputs, in effect.
 
C

Chris Dollin

arnuld said:
K&R2 Appendix B, page 247:

"int puts( const char *s )

puts writes the string s and the newline to stdout. It returns EOF
if an error occurs, non-negative otherwise"

Now there are no strings in C.

What makes you think that?
We have arrays of characters. It is same
like printf() which accepts %s as an array of chars. So <line> is a single
line of input form file stored into an array of chars.

`line` needn't have come from any of the program's input files:

puts( "I do not come from from this program's input files." );
 
E

edwin ng

arnuld schreef:
The logic here is wrong. || is a "a short-circuit" operator. Once
the first half gets a line the second half will not be executed. You
must read pairs of lines and I'd stop when one of the files runs out.
[snip]


while( (fgets( line1, ARRSIZE, pf1 ) != NULL) ||
(fgets( line2, ARRSIZE, pf2 ) != NULL))

You are still not seeing it ?
If you read line1 with success you will NOT read line2.

|| means OR, so short circuit will prevent you from evaluating the second part (after the || ) if the first is true
because the answer is already known.

You could try && instead and see if that fixes it.
Good luck,
Edwin
 
B

Ben Bacarisse

arnuld said:
As per my slowly developing C knowledge :p, If one line ends before ther
other then of course, the lines are not equal. This is true when either
both or any one of them runs out.

may be I did not understand your logic here but right now this is the
limit of my thinking.

Here is the 4th revision of the program. As usual output is not what I
wanted :(

while( (fgets( line1, ARRSIZE, pf1 ) != NULL) ||
(fgets( line2, ARRSIZE, pf2 ) != NULL))
{

This does not do what you think it does. In C, the following is
perfectly correct:

if (x == 0 || y/x > 1)

y/x will never be evaluated when x == 0 because the second part of the
'||' is not evaluated when the first part is found to be true
(non-zero). In your case, the second file is never read until first
file read *fails*. This is, absolutely, not what you wanted to
happen.
 
A

arnuld

PURPOSE :: see statement in comments

GOT: Segmentation Fault

I guess the segfault is sourced in the compile-time warning but I am
giving a char* to the function already.




/* K&R2, section 7.7, exercise 7.6
*
* write a program to compare 2 files, printing that first line
* where they differ.
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum MAXSIZE { ARRSIZE=1000 };

void compare_files( FILE*, FILE* );
void print_line( char* );


int main( int argc, char* argv[] )
{
FILE *pf1, *pf2;

if( argc !=3 )
{
fprintf( stderr, "You iDiOT, I expect 2 files as input. \n" );
exit(EXIT_FAILURE);
}


/* open files */
pf1 = fopen( *++argv, "r" );
pf2 = fopen( *++argv, "r" );

/* error check */
if( pf1 == NULL || pf2 == NULL )
{
fprintf( stderr, "error opening files\n");
exit(EXIT_FAILURE);
}
else
{
compare_files( pf1,pf2 );
}



/* don't forget to close the files */
fclose( pf1 );
fclose( pf2 );


return 0;
}



/* compare 2 files */
void compare_files( FILE* pf1, FILE* pf2 )
{
int c1, c2, match;
char *line1, *line2;
char *begin_line1, *begin_line2;

match = 1;

while( ((line1 = fgets( line1, ARRSIZE, pf1 )) != NULL) ||
((line2 = fgets( line2, ARRSIZE, pf2 )) != NULL))
{
begin_line1 = line1;
begin_line2 = line2;

for( c1 = *line1, c2 = *line2; c1 != '\0' && c2 != '\0'; ++line1,
++line2 )
{
if ( c1 != c2 )
{
match = 0;
print_line( begin_line1 );
printf("\n-----------------------\n"); print_line( begin_line2 );
}
}
}
}



void print_line( char* line )
{
printf("%s\n", *line++);
}

================== OUTPUT ======================
[arnuld@raj C]$ gcc -ansi -pedantic -Wall -Wextra 7-6.c
7-6.c: In function `print_line':
7-6.c:88: warning: format argument is not a pointer (arg 2)

[arnuld@raj C]$ ./a.out
You iDiOT, I expect 2 files as input.

[arnuld@raj C]$ ./a.out 7-6.c 5-4.c
Segmentation fault
 
S

santosh

arnuld said:
I don't know whether you noticed, but I gave you a working version of
print_line - and you appear to have broken it again.

yes. I fixed it again but I am using putchar() now.
Wrong. In C, a string is a contiguous sequence of characters,
terminated by the first null character.

but these 2 are different:

char arr[] = "Richard";
char* pc = "Richard";

Yes. The first declaration creates an array (named arr) of the length
needed to store the string literal "Richard" which is of eight
characters including the terminating newline. Note that the elements of
arr are subsequently modifiable, as you have not const qualified it.
It's identical to any other char array, merely initialised in a
slightly special manner.

The second declaration says that pc is a pointer to type char and
initialises it to the address of the start of the string "Richard",
which is placed somewhere in memory according to the compiler's
convinience. The main difference with the first declaration is that
here pc is simply a char * and points to a string literal which cannot
be modified without invoking undefined behaviour. However pc can always
be set to point elsewhere in which case you'll lose access to the
string literal "Richard" unless you preserve it's address elsewhere.
both are contiguous sequence of characters ended by '\0'.

The first is a char array into which a string has been _copied_ . The
latter is a char * which points to a string literal somewhere in
memory.

I hope the difference is clear...
you can never use the pc for %s in printf(). Thats what I meant, there
are no strings, only arrays of chars. I tried to print the pc but all
I got was:


^A^M^D @#

You can perfectly print both the declarations you have given above using
the %s printf specifier. The %s format specifier looks for a char *
argument and prints all characters upto the first null character. You
can also use a length specifier to control the number of characters
printed.
 
A

arnuld

arnuld said:
Where do these point? (Hint: nowhere in particular.)


OK, here is the 2nd version:


/* K&R2, section 7.7, exercise 7.6
*
* write a program to compare 2 files, printing that first line
* where they differ.
*
* version 1.1
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum MAXSIZE { ARRSIZE=1000 };

void compare_files( FILE*, FILE* );
void print_line( const char* );


int main( int argc, char* argv[] )
{
FILE *pf1, *pf2;

if( argc !=3 )
{
fprintf( stderr, "You iDiOT, I expect 2 files as input. \n" );
exit(EXIT_FAILURE);
}


/* open files */
pf1 = fopen( *++argv, "r" );
pf2 = fopen( *++argv, "r" );

/* error check */
if( pf1 == NULL || pf2 == NULL )
{
fprintf( stderr, "error opening files\n");
exit(EXIT_FAILURE);
}
else
{
compare_files( pf1,pf2 );
}



/* don't forget to close the files */
fclose( pf1 );
fclose( pf2 );


return 0;
}



/* compare 2 files */
void compare_files( FILE* pf1, FILE* pf2 )
{
int match;
char c1, c2;
char line1[ARRSIZE];
char line2[ARRSIZE];
char *begin_line1, *begin_line2, *p1, *p2;


match = 1;

while( (fgets( line1, ARRSIZE, pf1 ) != NULL) ||
(fgets( line2, ARRSIZE, pf2 ) != NULL))
{
begin_line1 = line1;
begin_line2 = line2;

p1 = line1;
p2 = line2;
for( c1 = *p1, c2 = *p2; c1 != '\0' && c2 != '\0'; ++p1, ++p2 )
{
if ( c1 != c2 )
{
match = 0;
print_line( begin_line1 );
printf("\n-----------------------\n"); print_line( begin_line2 );
}
}
}
}



void print_line( const char* line )
{
while( *line != '\0')
{
puts(*line++);
}
}

================ OUTPUT =======================

[arnuld@raj C]$ gcc -ansi -pedantic -Wall -Wextra 7-6.c
7-6.c: In function `print_line':
7-6.c:96: warning: passing arg 1 of `puts' makes pointer from integer
without a cast
[arnuld@raj C]$ ./a.out 7-6.c replace-blanks.c Segmentationfault
[arnuld@raj C]$
 
K

Keith Thompson

santosh said:
arnuld wrote: [...]
char arr[] = "Richard";
char* pc = "Richard";

Yes. The first declaration creates an array (named arr) of the length
needed to store the string literal "Richard" which is of eight
characters including the terminating newline. Note that the elements of
arr are subsequently modifiable, as you have not const qualified it.
It's identical to any other char array, merely initialised in a
slightly special manner.

You mean the terminating null character ('\0'), not newline.
 
S

santosh

Keith said:
santosh said:
arnuld wrote: [...]
char arr[] = "Richard";
char* pc = "Richard";

Yes. The first declaration creates an array (named arr) of the length
needed to store the string literal "Richard" which is of eight
characters including the terminating newline. Note that the elements
of arr are subsequently modifiable, as you have not const qualified
it. It's identical to any other char array, merely initialised in a
slightly special manner.

You mean the terminating null character ('\0'), not newline.

Oops yes. Thanks for the correction.
 
K

Keith Thompson

arnuld said:
void print_line( char* line )
{
printf("%s\n", *line++);
}

================== OUTPUT ======================
[arnuld@raj C]$ gcc -ansi -pedantic -Wall -Wextra 7-6.c
7-6.c: In function `print_line':
7-6.c:88: warning: format argument is not a pointer (arg 2)
[...]

You've modified your print_line function, but I'm not sure you
understand what was wrong with the original one.

printf with a "%s" format requires a char* argument; the argument
needs to point to a string (i.e., to the first element of an array of
characters terminated by '\0').

The expression ``line'' is of type char*, so this:

printf("%s\n", line);

would have been ok. But the expression ``*line'' or ``*line++'' is of
type char, so your printf call:

printf("%s\n", *line++);

is invalid. Your compiler was even kind enough to warn you about it.

It's as if you had written:

char c = 'x';
printf("%s\n", c);

What probably happens is that the printf function tries to grab the
pointer value that it assumes you passed it; it instead grabs a
character value and perhaps some adjacent garbage, and treats it as a
pointer. That's just one possibility; the behavior is completely
undefined. Kaboom.
 
A

arnuld

Ask yourself: what does `puts` accept as an parameter? What is
the type of the expression `*line++`?


K&R2 Appendix B, page 247:

"int puts( const char *s )

puts writes the string s and the newline to stdout. It returns EOF
if an error occurs, non-negative otherwise"


Now there are no strings in C. We have arrays of characters. It is same
like printf() which accepts %s as an array of chars. So <line> is a single
line of input form file stored into an array of chars.

Are C characters a kind
of integer? Do you really expect an integer to be freely and
automatically converted to an integer?

that was my mistake, my typo(s), I think I am getting sleepy. I just
recently shifted from North-India to South-India and I had to change my
food-habits. I am not able to get any food I used to eat from last 27
years (since I was born). So I have lots of difficulties in living a
normal daily routine here.
 
A

arnuld

I don't know whether you noticed, but I gave you a working version of
print_line - and you appear to have broken it again.


yes. I fixed it again but I am using putchar() now.


Wrong. In C, a string is a contiguous sequence of characters, terminated
by the first null character.

but these 2 are different:

char arr[] = "Richard";
char* pc = "Richard";

both are contiguous sequence of characters ended by '\0'.

Wrong. printf interprets %s as meaning "the parameter matching this
format specifier is a ##pointer## to the first character in a contiguous
sequence of characters that is terminated by a null character" - in
other words, %s means "I'm giving you a string".


you can never use the pc for %s in printf(). Thats what I meant, there
are no strings, only arrays of chars. I tried to print the pc but all I
got was:


^A^M^D @#
 
A

arnuld

Where do these point? (Hint: nowhere in particular.)


okay, here is the 3rd version of the program. It compiles and runs, the
only thing is semantic-bug, it always does one thing, no matter whether 2
files are same or different:



/* K&R2, section 7.7, exercise 7.6
*
* write a program to compare 2 files, printing that first line
* where they differ.
*
* version 1.2
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum MAXSIZE { ARRSIZE=1000 };

void compare_files( FILE*, FILE* );
void print_line( const char* );


int main( int argc, char* argv[] )
{
FILE *pf1, *pf2;

if( argc !=3 )
{
fprintf( stderr, "You iDiOT, I expect 2 files as input. \n" );
exit(EXIT_FAILURE);
}


/* open files */
pf1 = fopen( *++argv, "r" );
pf2 = fopen( *++argv, "r" );

/* error check */
if( pf1 == NULL || pf2 == NULL )
{
fprintf( stderr, "error opening files\n");
exit(EXIT_FAILURE);
}
else
{
compare_files( pf1,pf2 );
}



/* don't forget to close the files */
fclose( pf1 );
fclose( pf2 );


return 0;
}



/* compare 2 files */
void compare_files( FILE* pf1, FILE* pf2 )
{
int match;
char c1, c2;
char line1[ARRSIZE];
char line2[ARRSIZE];
const char *begin_line1, *begin_line2, *p1, *p2;


match = 1;

while( (fgets( line1, ARRSIZE, pf1 ) != NULL) || (fgets( line2, ARRSIZE, pf2 ) != NULL))
{
begin_line1 = line1;
begin_line2 = line2;

p1 = line1;
p2 = line2;
for( c1 = *p1, c2 = *p2; c1 != '\0' && c2 != '\0'; ++p1, ++p2 )
{
if ( c1 != c2 )
{
match = 0;
print_line( begin_line1 );
printf("\n-----------------------\n");
print_line( begin_line2 );
return;
}
}
}
}



void print_line( const char* line )
{
while( *line != '\0')
{
putchar(*line++);
}
}
 
A

arnuld

What is this for?

:-\


The logic here is wrong. || is a "a short-circuit" operator. Once
the first half gets a line the second half will not be executed. You
must read pairs of lines and I'd stop when one of the files runs out.

As per my slowly developing C knowledge :p, If one line ends before ther
other then of course, the lines are not equal. This is true when either
both or any one of them runs out.

may be I did not understand your logic here but right now this is the
limit of my thinking.



What is the point of this function? It is just fputs, in effect.


K&R2 never talked about using it. So I never came across it.



Here is the 4th revision of the program. As usual output is not what I
wanted :(



/* K&R2, section 7.7, exercise 7.6
*
* write a program to compare 2 files, printing that first line
* where they differ.
*
* version 1.3
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum MAXSIZE { ARRSIZE=1000 };

void compare_files( FILE*, FILE* );


int main( int argc, char* argv[] )
{
FILE *pf1, *pf2;

if( argc !=3 )
{
fprintf( stderr, "You iDiOT, I expect 2 files as input. \n" );
exit(EXIT_FAILURE);
}


/* open files */
pf1 = fopen( *++argv, "r" );
pf2 = fopen( *++argv, "r" );

/* error check */
if( pf1 == NULL || pf2 == NULL )
{
fprintf( stderr, "error opening files\n");
exit(EXIT_FAILURE);
}
else
{
compare_files( pf1,pf2 );
}



/* don't forget to close the files */
fclose( pf1 );
fclose( pf2 );


return 0;
}



/* compare 2 files */
void compare_files( FILE* pf1, FILE* pf2 )
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char *begin_line1, *begin_line2, *p1, *p2;


while( (fgets( line1, ARRSIZE, pf1 ) != NULL) ||
(fgets( line2, ARRSIZE, pf2 ) != NULL))
{
begin_line1 = p1 = line1;
begin_line2 = p2 = line2;

if( !strcmp( p1, p2 ) )
{
puts( begin_line1 );
printf("-------------- Lines differ here ---------------\n");
puts( begin_line2 );
return;
}
}
}
============= OUTPUT =================

[arnuld@raj C]$ gcc -ansi -pedantic -Wall -Wextra test.c
[arnuld@raj C]$ ./a.out file1.txt file2.txt




-------------- Lines differ here ---------------




[arnuld@raj C]$
 
B

Ben Bacarisse

arnuld said:
:\

Now I see it

I just changed the "while" condition to:

while( fgets( line1, ARRSIZE, pf1 ) && fgets( line2, ARRSIZE, pf2 ) )


and "if" condition to:

if( strcmp( p1, p2 ) )


it works fine now. Thanks

Really? What if one file is a prefix of the other? I'd expect that
you'd have to change more than you say you changed.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,226
Latest member
KristanTal

Latest Threads

Top