K&R2 , exercise 7.6

B

Ben Bacarisse

arnuld said:
that was his plan but I did it with all tests inside the loop:

I think you are making this much more complex than it is -- and you are
not really doing what you think you are doing.
void compare_files( FILE* pf1, FILE* pf2 )
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char* *p1, *p2;

Eh? Does you compiler not complain about this later?
const char *begin_line1, *begin_line2;

What are these for?
int files_equal;
int file1, file2;
int eof_file1, eof_file2;

files_equal = 1;
file1 = file2 = 1;
eof_file1 = eof_file2 = 0;

You don't need five flags just to get this to work out!
while( files_equal && file1 && file2 )
{
begin_line1 = p1 = fgets( line1, ARRSIZE, pf1 );
begin_line2 = p2 = fgets( line2, ARRSIZE, pf2 );

if( !p1 && !p2 )
{
file1 = file2 = 0;
}
else if( (!p1 && p2) )
{
files_equal = 0;
eof_file1 = 1;
}
else if( !p2 && p1 )
{
files_equal = 0;
eof_file2 = 1;
}
else if( strcmp( p1, p2 ) )
{
files_equal = 0;
eof_file1 = eof_file2 = 1;
}
}


if( !files_equal )
{
if( eof_file1 )
{
puts(begin_line2);
}
else if( eof_file2 )
{
puts(begin_line1);
}
else if( eof_file1 && eof_file2 )
{

You can't ever get here. You've tied yourself in a logical knot.
puts( begin_line1 );
printf("\n-------------------------\n");
puts( begin_line2 );
}
}
}

int read_line(char *buf, size_t sz, FILE *fp) {
*buf = 0;
return fgets(buf, sz, fp) != NULL;
}

void compare_files(FILE *fp1, FILE *fp2) {
char l1[ARRSIZE], l2[ARRSIZE];

while (read_line(l1, sizeof l1, fp1) + read_line(l2, sizeof l2,
fp2)) {
if (strcmp(l1, l2)) {
fputs(l1, stdout);
puts("-------------------");
fputs(l2, stdout);
return;
}
}
}
}
The auxiliary function read_line is trivial, but it does exactly enough
to make the various tests (and subsequent output) trivial.

that is exactly what I was looking for and this is the 1st time I have
seen someone using + in a loop :)

I am quite surprised at both how a problem can be solved using 2
different approaches and that people think in very different ways when
they look at a problem.

I think it is a good problem. I am surprised that one of the
solutions on the clc wiki ignores differences in length. For what
it's worth, here is my full solution. I chose slightly different
names and the way main works is different.

/* K&R2, section 7.7, exercise 7.6 */

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

#define ARRSIZE 1000

int files_differ(FILE *, FILE *);

int main(int argc, char **argv)
{
int differ;
FILE *pf1, *pf2 = stdin;

if (argc < 2 || argc > 3) {
fprintf(stderr, "Give one or two file names as arguments.\n");
return EXIT_FAILURE;
}
if ((pf1 = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "Unable to open %s.\n", argv[1]);
return EXIT_FAILURE;
}
if (argc > 2 && (pf2 = fopen(argv[2], "r")) == NULL) {
fprintf(stderr, "Unable to open %s.\n", argv[2]);
fclose(pf1);
return EXIT_FAILURE;
}

differ = files_differ(pf1, pf2);
fclose(pf1);
fclose(pf2);
return differ ? EXIT_FAILURE : EXIT_SUCCESS;
}

int read_line(char *buf, size_t sz, FILE *fp)
{
*buf = 0;
return fgets(buf, sz, fp) != NULL;
}

int files_differ(FILE *fp1, FILE *fp2)
{
char l1[ARRSIZE], l2[ARRSIZE];

while (read_line(l1, sizeof l1, fp1) + read_line(l2, sizeof l2, fp2)) {
if (strcmp(l1, l2)) {
fputs(l1, stdout);
puts("-------------------");
fputs(l2, stdout);
return 1;
}
}
return 0;
}
 
A

arnuld

Half-right. A string is a contiguous sequence of characters terminated
by the first null character, whose length is given by the number of
characters in the string not including the null character. Thus, a
string with a null as its first character is still a string, but it has
0 length.

ah... I think I will better use this for clarity:

*buf = '\0';

for if I could be reading an array of integers at some other time.


You can do this:

size_t CalcNumElementsInChar20Array(char (*p)[20]) {
return sizeof *p / sizeof (*p)[0];
}
}
but what's the point?


The last sentence is the one I usually ask to the programmers when they
use arrays in C++ for writing Desktop Applications, all the time, or
when they use <void main()> in C.

I think I got your point.
 
S

santosh

arnuld said:
ah... I think I will better use this for clarity:

*buf = '\0';

No. This assigns the value of the character constant '\0' to buf. This
happens to be the same as doing

*buf = 0;

and in a declaration it's the same as

char *buf = NULL;

but I don't suppose that this is what you meant. If you want to assign a
pointer to a null string, do

char *buf = "";
 
B

Ben Bacarisse

santosh said:
No. This assigns the value of the character constant '\0' to buf.

No it does not and I am absolutely sure you know that! My code used

*buf = 0;

because, at least in C, I don't think 0 is much clearer than '\0'.
(People worry about typing = instead of == but how often have you seen
'0' used by accident where '\0' was intended?)

Both mine and the suggested clearer form ensure that buf contains a
string of length zero.
This happens to be the same as doing

*buf = 0;
Yes.

and in a declaration it's the same as

char *buf = NULL;

No, not at all the same thing. Have you not had your coffee yet?
but I don't suppose that this is what you meant. If you want to assign a
pointer to a null string, do

char *buf = "";

Different again and not safe in the context (buf gets passed to fgets
on the next line). I know the context has been snipped, but even
without it

*buf = 0;
char *buf = NULL;
char *buf = "";

are all different enough to be unlikely alternatives to each other.
Arnuld's choice to prefer *buf = '\0'; to my *buf = 0; is fine and
needs no correction.
 
S

santosh

Ben said:
No it does not and I am absolutely sure you know that! My code used

*buf = 0;

because, at least in C, I don't think 0 is much clearer than '\0'.
(People worry about typing = instead of == but how often have you seen
'0' used by accident where '\0' was intended?)

Both mine and the suggested clearer form ensure that buf contains a
string of length zero.


No, not at all the same thing. Have you not had your coffee yet?

I posted without reading through the thread. My mistake was in assuming
that arnuld wanted to initialise buf to point to a empty string. Now I
see that buf already points to storage and he wants to place a '\0' at
element zero so that it would be treated as an empty string by other
code.
Different again and not safe in the context (buf gets passed to fgets
on the next line). I know the context has been snipped, but even
without it

*buf = 0;
char *buf = NULL;
char *buf = "";

are all different enough to be unlikely alternatives to each other.
Arnuld's choice to prefer *buf = '\0'; to my *buf = 0; is fine and
needs no correction.

Yes. Apologies to the OP for misreading a statement as an
initialisation.
 
K

Keith Thompson

arnuld said:
*buff = 0;

puts a zero at the place pointed by pointer.

right, and if buff is of type char*, the int value 0 is converted to
type char.
char *buffer = NULL;

initializes the pointer object ``buffer'' to a null pointer value; it
doesn't store a value in what buffer points to (it can't, since buffer
doesn't point to anything).
char *buffer = 0;

initializes the pointer object ``buffer'' to the value 0, which is
converted to a null pointer value; in other words, it does exactly
the same thing as ``char *buffer = NULL;''.
different in what context ? Both are same as of value they contain:
[...]

0 and '\0', as it happens, are both constants of type int with the
value 0. They're the same as far as the compiler is concerned, but
they're different to human readers. '\0' is a character constant, and
should be used when you specifically what a *character* value of 0.

NULL is a macro that expands to an implementation-defined null pointer
constant; it should be used only when you want a pointer value. If an
implementation happens to define NULL as 0, then you can get away with

char c = NULL;

but it's a bad idea, first because it's misleading to a human reader,
and second because it won't compile if the implementation chooses to
define NULL as ((void*)0) rather than just 0.

C has several different ways to say "zero", but as a matter of style
you should choose one that's appropriate to the type you want: '\0'
for character types, 0 for non-character integer types, 0.0 for
floating-point, NULL for pointer types. The compiler often (not
always, but often) won't care, but code typically spends more of its
time being read by humans than processed by compilers.
 
N

Nick Keighley

since NULL can be either 0 or <void*> then how come:

no! NULL can be defined as ((void*)0) (note the zero at the end).
Have you read the comp.lang.c FAQ?

<snip>
 
B

Ben Bacarisse

arnuld said:
that implicit conversion then. As a matter of style, I always make
conversions explicit (except for malloc).

No you don't. Your '\0' is of type int. It gets converted to char
int just the same way my int value 0 gets converted to char when
stored. If you want to make all conversions explicit you would have
to write:

*buf = (char)'\0';

And lets not even start on the number of different things you'll have
to put in front of NULL when you start using dynamic data structures!
I suggest you get happy with C's implicit conversions before madness
set in.
 
B

Ben Bacarisse

arnuld said:
*buff = 0;

puts a zero at the place pointed by pointer.

char *buffer = NULL;
char *buffer = 0;

different in what context ?

For the record, I never suggested they were different. I gave three
examples which *are* all different, but 'char *buf = 0;' was not one
of them. If you are asking a new question, you've had a very full
answer from Keith Thompson.
 
A

arnuld

*buf = 0;
char *buf = NULL;
char *buf = "";

are all different enough to be unlikely alternatives to each other.

*buff = 0;

puts a zero at the place pointed by pointer.

char *buffer = NULL;
char *buffer = 0;


different in what context ? Both are same as of value they contain:



#include <stdio.h>


int main(void) {

char *buffer = 0;
char *buffer2 = NULL;

printf("%s\n%s\n", buffer, buffer2);

return 0;
}

============ OUTPUT ===============
/home/arnuld/programs/C $ gcc -ansi -pedantic -Wall -Wextra test.c
/home/arnuld/programs/C $ ./a.out
(null)
(null)
/home/arnuld/programs/C $



Arnuld's choice to prefer *buf = '\0'; to my *buf = 0; is fine and
needs no correction.

:)
 
A

arnuld

right, and if buff is of type char*, the int value 0 is converted to
type char.

that implicit conversion then. As a matter of style, I always make
conversions explicit (except for malloc).


... SNIP......
but it's a bad idea, first because it's misleading to a human reader,
and second because it won't compile if the implementation chooses to
define NULL as ((void*)0) rather than just 0.

since NULL can be either 0 or <void*> then how come:


char *buffer = NULL;
char *buffer = 0;

are equivalent ?


if <NULL == 0> then they are equivalent but if <NULL == void*> then they
are different assignments .

C has several different ways to say "zero", but as a matter of style you
should choose one that's appropriate to the type you want: '\0' for
character types, 0 for non-character integer types, 0.0 for
floating-point, NULL for pointer types. The compiler often (not always,
but often) won't care, but code typically spends more of its time being
read by humans than processed by compilers.



facts I did not know. Thanks :)
 
E

Eligiusz Narutowicz

arnuld said:
I did read the FAQ but the concept of NULL, NUL, '\0', ((void*) 0) and 0
is too complex and confusing to be grasped by oneself by just reading the
FAQs

Most of answers are in the FAQ, but I dont know why someone answers
so. Why have a newsgroup if the people to help are asking this?
 
S

santosh

arnuld said:
I did read the FAQ but the concept of NULL, NUL, '\0', ((void*) 0) and
0 is too complex and confusing to be grasped by oneself by just
reading the FAQs

WRT to null pointers maybe these historical c.l.c threads can shed some
light for you...

<http://groups.google.com/group/comp.lang.c/browse_thread/thread/47ccaa72c414a0e>
<http://groups.google.com/group/comp.lang.c/browse_thread/thread/9a06a227e2ca1460>

NULL vs. 0 has been been debated often in c.l.c. You might want to do
your own search too.
 
K

Keith Thompson

arnuld said:
well, the programmer knows that he *intentionally* casted the type.

It's still a bad idea. Why? Because implicit conversions are more
tightly restricted than casts (explicit conversions).

The language allows implicit conversions between closely related
types, and most of the time the conversion will be exactly the one you
really want. The language allows explicit conversions (casts) between
more distantly related types. If you use a cast, it's entirely up to
you, the programmer, to get the type right. If you get the type
wrong, you'll introduce a bug which the compiler will be unable to
diagnose for you.

In the example from a previous post, suppose you write this:

int n = some_value;
*buff = (char)n;

Is this correct? You don't know unless you know the type of buff. If
buff is a char*, then it's just fine -- and the cast is unnecessary;
you could have written
*buff = n;
and let the compiler generate the conversion for you. But if buff is
unsigned char*, or short*, or double*, then you could lose
information.

If you get the type right, the cast is harmless and completely
unnecessary. If you get the type wrong (perhaps because you changed
the declaration of buff two years after you wrote the original code),
then the cast causes a bug that could have been avoided by omitting
the cast in the first place.

Now explain to me again why the cast is a good idea.

Note that a cast can sometimes be necessary when you're passing an
argument to a variadic function such as printf. In that case, the
compiler *doesn't* know the target type, so it can't generate the
implicit conversion for you. In that one case, you just have to be
very careful to get the type right, because the compiler most likely
won't tell you if you get it wrong. An example:

printf("sizeof(double) = %d\n", (int)sizeof(double));

The result of sizeof(double) is of type size_t, but printf with "%d"
requires an int argument.
from FAQ, I can conclude that, use of NULL is ok where I need a <void*>
and for everything else I can use a 0 (zero).

No.

What do you mean by <void*>? What are the angle brackets supposed to
indicate?

NULL is a macro that expands to a null pointer constant. You should
use it when you want a null pointer of *any* pointer type. (void* is
just one of many pointer types.)

There are a number of ways that NULL can be defined. If you use it
properly, you don't have to care just how it's defined; your code will
work correctly whether it expands to 0, ((void*)0), 0x00000000, or
__MAGIC_NULL_POINTER_CONSTANT__.

That's why
char *buffer = NULL;
and
char *buffer = 0;
are equivalent.

Note that 0 and NULL are *not* equivalent in all contexts; they're
equivalent only in contexts that require a pointer value. NULL should
*only* be used in contexts that require a pointer value. The point of
using NULL rather than 0 is to make it clear to the human reader that
you have a null pointer constant, not just an integer value of 0.

[snip]>> int m = 6;
compiler will initialize <m> to a vale of 6. Compiler will get there
differently in both expressions but the end result will be <m> will be
equal 6.

Right ?

Of course.
 
K

Keith Thompson

Eligiusz Narutowicz said:
Most of answers are in the FAQ, but I dont know why someone answers
so. Why have a newsgroup if the people to help are asking this?

There are plenty of questions whose answers aren't in the FAQ. And
even for those that are, sometimes more explanation is required.

Steve Summit and his contributors expended a great deal of time and
effort composing the FAQ. As a result, it contains excellent answers
to a many of the questions that are asked here. If someone is having
a problem with arrayd and pointers, I *could* spend 10 minutes
composing a lengthy response that explains how they relate to each
other (with a significant risk of getting something wrong and
misleading the questioner). Or I can say "Read section 6 of the
comp.lang.c FAQ", saving myself considerable time and giving the the
questioner a better answer than I could have written myself.

The FAQ hasn't caused this newsgroup to suffer from a lack of things
to talk about.
 
K

Keith Thompson

arnuld said:
with some hard-thought I came to understand the distinction between NULL
and 0:


1.) In case of pointers, use NULL.
2 .) In cases where integral zero is requires, use 0.

except these 2 points, to me, every other discussion about NULL and 0 does
not seem important to me from programming in C perspective.

That's not the whole picture, but yes, it's basically correct.

(Except that it's best to use '\0' when you want a character with
value 0, and 0.0 when you want a floating-point zero.)
 
K

Keith Thompson

arnuld said:
I did read the FAQ but the concept of NULL, NUL, '\0', ((void*) 0) and 0
is too complex and confusing to be grasped by oneself by just reading the
FAQs

NULL is a macro that expands to a null pointer constant. Don't worry
about just what it expands to. You can use NULL in any context where
you need a null pointer of *any* pointer type. (But see my caveat in
another article in this thread about variadic functions like printf.)

NUL is not defined in C. It's sometimes used as a name for the
character with value 0, but you can and should just use a literal '\0'
instead.

'\0' is a character constant with value 0, also known as the null
character (*not* to be confused with a null pointer). This is the
value used to terminate a string. (Due to a quirk of the language,
character constants are actually of type int, not char. Don't worry
about that; due to implicit conversions, it rarely matters.)

((void*)0) and 0 are two possible expansions of the NULL macro.
They're both null pointer constants. Don't worry about using either
of them; just use NULL.
 
D

Default User

Eligiusz Narutowicz wrote:

Most of answers are in the FAQ, but I dont know why someone answers
so. Why have a newsgroup if the people to help are asking this?


What is the reason for having a newsgroup Frequently Asked Question
list?




Brian
 
E

Eligiusz Narutowicz

Default User said:
Eligiusz Narutowicz wrote:




What is the reason for having a newsgroup Frequently Asked Question
list?




Brian

I think that is very obvious. I don't know your point? So you will only
point people to the FAQ? Why do you post here then?
 
U

user923005

I think that is very obvious. I don't know your point? So you will only
point people to the FAQ?

If a well prepared and correct answer is found in the FAQ, wouldn't it
be a nice place to point people?
Why do you post here then?

Brian posts here to be helpful. On those occasions when something is
not contained in the FAQ, he will post pertinant information regarding
the issue at hand.

Pretty much all usenet groups have FAQs and it is simple common
courtesy to read them before posting.
http://www.uiowa.edu/~writingc/handouts/netiquette.htm
http://www.albion.com/bookNetiquette/0963702513p32.html
http://www.walthowe.com/navnet/faq/guidelines.html
http://en.wikipedia.org/wiki/FAQ

HTH
 

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

No members online now.

Forum statistics

Threads
473,800
Messages
2,569,657
Members
45,417
Latest member
BonitaNile
Top