Watcom vs. gcc/icl/cl difference

P

pemo

I've just been trying out the Watcom compiler from
http://www.openwatcom.org, and almost immediately compiled some working
source that errored.

The code is

char buffer[1000];

...

if(opt & out_std)
{
fputs(&buffer, stdout);
}

Note that &buffer should [ideally] have been either &buffer[0] or just plain
buffer.

However, the same code compiles cleanly using gcc (ecven when
using -pedantic), icl, and MS's cl compilers.

In the c99 std it has the following:
6.5.3.2 Address and indirection operators

Constraints

1 The operand of the unary & operator shall be either a function designator,
the result of a

[] or unary * operator, or an lvalue that designates an object that is not a
bit-field and is

not declared with the register storage-class specifier.

I'm interpreting the beginning of this as saying that the follow are valid:

char array[1];

int n;

int * p;

&array[0];

&n;

&*p;

However, I can't find anything that says that [say] puts(&buffer) is valid.

So, any comments?
 
A

Alexei A. Frounze

pemo said:
I've just been trying out the Watcom compiler from
http://www.openwatcom.org, and almost immediately compiled some working
source that errored.

The code is

char buffer[1000];

...

if(opt & out_std)
{
fputs(&buffer, stdout);
}

Note that &buffer should [ideally] have been either &buffer[0] or just plain
buffer.

fputs() takes pointer to a char, not pointer to a buffer, hence either
buffer
or
&buffer[0]
is what you must pass to fputs().

&buffer has a different type, a pointer to a buffer of 1000 chars,
(*)[1000].

Correct example:
{
char b[100];
char (*p1)[100] = &b;
char* p2 = b;
}


Incorrect example:
{
char b[100];
char (*p1)[100] = b;
char* p2 = &b;
}

Alex
 
S

Skarmander

pemo said:
I've just been trying out the Watcom compiler from
http://www.openwatcom.org, and almost immediately compiled some working
source that errored.

The code is

char buffer[1000];

...

if(opt & out_std)
{
fputs(&buffer, stdout);
}

Note that &buffer should [ideally] have been either &buffer[0] or just plain
buffer.

> However, the same code compiles cleanly using gcc (ecven when
> using -pedantic), icl, and MS's cl compilers.
<snip>

gcc --version

gcc (GCC) 3.4.2 (mingw-special)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

foo.c:
#include <stdio.h>

void foo() {
char buffer[1000];

fputs(&buffer, stdout);
}

gcc -c test.c

foo.c: In function `foo':
foo.c:6: warning: passing arg 1 of `fputs' from incompatible pointer type

What version of gcc are you using? This isn't what I call "compiling
cleanly".

S.
 
E

Eric Sosman

pemo wrote On 10/07/05 13:18,:
I've just been trying out the Watcom compiler from
http://www.openwatcom.org, and almost immediately compiled some working
source that errored.

The code is

char buffer[1000];

...

if(opt & out_std)
{
fputs(&buffer, stdout);
}

Note that &buffer should [ideally] have been either &buffer[0] or just plain
buffer.

However, the same code compiles cleanly using gcc (ecven when
using -pedantic), icl, and MS's cl compilers.

If a correct prototype for fputs() is in scope, a
diagnostic is required. Since the code uses `stdout'
as an identifier, it is 99.44% likely that <stdio.h>
has been included (and 0.56% likely that the code is
utterly, hopelessly bogus). If <stdio.h> was included,
a correct prototype for fputs() should be in scope.
Therefore, it is 99.44% likely that all the compilers
(or the headers) are broken.

... or is it just faintly possible that the code you
show isn't the code you fed to the compilers? Since what
you've shown won't compile on any C compiler I have access
to or have ever heard of, we cannot rule out that faint
possibility, can we? Would you consider posting *real*
code instead of a half-baked hacked-up paraphrase?
 
P

pemo

gcc (GCC) 3.4.2 (mingw-special)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

foo.c:
#include <stdio.h>

void foo() {
char buffer[1000];

fputs(&buffer, stdout);
}

gcc -c test.c

foo.c: In function `foo':
foo.c:6: warning: passing arg 1 of `fputs' from incompatible pointer type

What version of gcc are you using? This isn't what I call "compiling
cleanly".

Interesting! I'm using Dev-C++ and, according to a gcc -v in \dev-cpp\bin
it's also 3.4.2.

Ahhhhh - very red faced!

In an attempt to show a clean example, I [um] edited out a cast!

fputs((const char *)&buffer, echoFP);

Ok lesson learned - like I can now see why that wasn't such a great idea!

However - I say to myself "well, in that case, that line of code was surely
never executed!". So, I took out the newly inserted [0], and put it back to
its original and inserted an extra puts():

puts("yup");
fputs((const char *)&buffer, echoFP);

The code runs fine.

Here's the whole function [probably going to regret this; although it's not
my code!] - used as a kind of printf/fprint etc.


/* Used to output stuff to the console and, optionally, to a file.
*/
static void out(int opt, const char * format, ...)
{
/* Potential for buffer overrun here: but *unlikely*. Maybe work a
decent malloc in here in the future though!
*/
auto char buffer[1000];

auto va_list ap;

va_start(ap, format);


/* Output to buffer according to what's passed in format and ap (no
matter what).
*/
vsprintf(&buffer[0], format, ap);

/* stdout output.
*/
if(opt & out_std)
{
puts("here");
fputs((const char *)&buffer, stdout);
}

/* File output if fp points to something.
*/
if(opt & out_fle)
{
if(echoFP != NULL)
{
fputs(&buffer[0], echoFP);
fflush(echoFP);
}
else
{
out(out_err, "function 'out' called with out_fle, but file
pointer is invalid!");
}
}

/* stderr output.
*/
if(opt & out_err)
{
fputs((const char *)&buffer[0], stderr);
}

/* Invalid flags given? This is not a totally exhaustive test as
opt_??? are
static const int out_std = 1;
static const int out_err = 2;
static const int out_fle = 4;
static const int out_reg = out_std | out_fle;
static const int opt_all = out_err | out_std | out_fle;
*/
if(opt < 1 || opt > opt_all)
{
/* Better report this!
*/
out(out_err, "function 'out' called using opt of %d: which is
invalid", opt);
}

fflush(stdout);
fflush(stderr);

va_end(ap);
}
 
P

pemo

Eric Sosman said:
pemo wrote On 10/07/05 13:18,:
I've just been trying out the Watcom compiler from
http://www.openwatcom.org, and almost immediately compiled some working
source that errored.

The code is

char buffer[1000];

...

if(opt & out_std)
{
fputs(&buffer, stdout);
}

Note that &buffer should [ideally] have been either &buffer[0] or just
plain
buffer.

However, the same code compiles cleanly using gcc (ecven when
using -pedantic), icl, and MS's cl compilers.

If a correct prototype for fputs() is in scope, a
diagnostic is required. Since the code uses `stdout'
as an identifier, it is 99.44% likely that <stdio.h>
has been included (and 0.56% likely that the code is
utterly, hopelessly bogus). If <stdio.h> was included,
a correct prototype for fputs() should be in scope.
Therefore, it is 99.44% likely that all the compilers
(or the headers) are broken.

... or is it just faintly possible that the code you
show isn't the code you fed to the compilers? Since what
you've shown won't compile on any C compiler I have access
to or have ever heard of, we cannot rule out that faint
possibility, can we? Would you consider posting *real*
code instead of a half-baked hacked-up paraphrase?

stdio in scope -
Would you consider posting *real*
code instead

full function code shown in other post
 
S

Skarmander

pemo said:
gcc (GCC) 3.4.2 (mingw-special)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

foo.c:
#include <stdio.h>

void foo() {
char buffer[1000];

fputs(&buffer, stdout);
}

gcc -c test.c

foo.c: In function `foo':
foo.c:6: warning: passing arg 1 of `fputs' from incompatible pointer type

What version of gcc are you using? This isn't what I call "compiling
cleanly".


Interesting! I'm using Dev-C++ and, according to a gcc -v in \dev-cpp\bin
it's also 3.4.2.

Ahhhhh - very red faced!

In an attempt to show a clean example, I [um] edited out a cast!

fputs((const char *)&buffer, echoFP);
*rolls eyes*
Ok lesson learned - like I can now see why that wasn't such a great idea!

However - I say to myself "well, in that case, that line of code was surely
never executed!". So, I took out the newly inserted [0], and put it back to
its original and inserted an extra puts():

puts("yup");
fputs((const char *)&buffer, echoFP);

The code runs fine.
Does it now? Is (opt & out_fle) true when the function is called? What
file is being written to? Open it and see if it contains what you expect
or garbage.

My money's on the latter.

&buffer points to valid memory. It just doesn't point to what it should
point: the contents of buffer. Instead it points to the variable buffer
itself, which is probably somewhere on the stack. fputs() will try to
interpret this as a string, which has a good chance of succeeding, since
the stack will probably contain a NUL somewhere and end the output
before things can get too bad.
Here's the whole function [probably going to regret this; although it's not
my code!] - used as a kind of printf/fprint etc.


/* Used to output stuff to the console and, optionally, to a file.
*/
static void out(int opt, const char * format, ...)
{
/* Potential for buffer overrun here: but *unlikely*. Maybe work a
decent malloc in here in the future though!
*/
auto char buffer[1000];
auto? Now there's something you don't see every day. The comment is, of
course, lame. "In the future" means "never", or "when disaster has
struck". :)
auto va_list ap;

va_start(ap, format);

/* Output to buffer according to what's passed in format and ap (no
matter what).
*/
vsprintf(&buffer[0], format, ap);
Should be
vsnprintf(buffer, sizeof buffer, format, ap);
to prevent overflow.

va_end should go here. See later.
/* stdout output.
*/
if(opt & out_std)
{
puts("here");
fputs((const char *)&buffer, stdout);

Wrong. Bad. Evil. Should indeed be buffer or &buffer[0] if you insist.
Since this was done right not a few lines ago I have no clue why this
was messed up. Sounds like a case of "compiler complains, let me insert
a cast rather than looking at the solution".
}

/* File output if fp points to something.
*/
if(opt & out_fle)
{
if(echoFP != NULL)
{
fputs(&buffer[0], echoFP);
fflush(echoFP);
}
else
{
out(out_err, "function 'out' called with out_fle, but file
pointer is invalid!");

Function calls itself which is mildly dangerous. If ever a mistake is
made in invoking the function for reporting an error in the function,
the recursion will never end. This is not as far-fetched as it sounds.

Function should be split up. More maintainable and more versatile.
}
}

/* stderr output.
*/
if(opt & out_err)
{
fputs((const char *)&buffer[0], stderr);

Cast should be removed.
}

/* Invalid flags given? This is not a totally exhaustive test as
opt_??? are
static const int out_std = 1;
static const int out_err = 2;
static const int out_fle = 4;
static const int out_reg = out_std | out_fle;
static const int opt_all = out_err | out_std | out_fle;
*/
if(opt < 1 || opt > opt_all)
{
/* Better report this!
*/
out(out_err, "function 'out' called using opt of %d: which is
invalid", opt);
}

fflush(stdout);
fflush(stderr);
Silly. Why is the file flushed in the if-branch in case of (opt &
out_file), but not stdout and stderr? Lines should be moved to the
relevant places.
va_end(ap);

va_end should appear as near to va_start as possible, to prevent
mistakes. There's no need to defer it to the end of the function.

S.
 
K

Keith Thompson

pemo said:
gcc (GCC) 3.4.2 (mingw-special)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

foo.c:
#include <stdio.h>

void foo() {
char buffer[1000];

fputs(&buffer, stdout);
}

gcc -c test.c

foo.c: In function `foo':
foo.c:6: warning: passing arg 1 of `fputs' from incompatible pointer type

What version of gcc are you using? This isn't what I call "compiling
cleanly".

Interesting! I'm using Dev-C++ and, according to a gcc -v in \dev-cpp\bin
it's also 3.4.2.

Ahhhhh - very red faced!

In an attempt to show a clean example, I [um] edited out a cast!

fputs((const char *)&buffer, echoFP);

Ok lesson learned - like I can now see why that wasn't such a great idea!

Yes, that was a bad idea -- but let's look into why it seems to work.
(You might already know this.)

The expression "buffer" (without the quotes, of course) is an array
name, which is automatically converted to a pointer to the array's
first element. In this case, it's of type char*, which is exactly
what you need for the first argument to fputs. So what you really
want is
fputs(buffer, stdout);
or
fputs(buffer, echoFP);

An array name is *not* converted to a pointer to its first element
if it's the operand of a unary "&" or "sizeof" operator. So &buffer
is a pointer to the array, not to its first element; the type is
char(*)[1000], or pointer-to-array-of-1000-char. You then cast
this pointer to "const char *", which almost certainly gives you
a pointer to the first element of the array. The expressions
"buffer" and "&buffer" are of different types (pointer-to-char
vs. pointer-to-array), but they both (loosely speaking) have the same
value, the address of the first byte of the object "buffer".

Incidentally, the "const" is superfluous; the formal parameter is
declared const, but the actual argument doesn't need to be.

There might be cases where the conversion from pointer-to-array
to pointer-to-char might not give you the value you want. I'm too
lazy to analyze the wording of the standard to determine just what's
guaranteed and what isn't -- and you probably should be as well.
Just use "buffer"; it's both simpler and safer.

Also, the first argument to fputs is expected to be a pointer to
a string (i.e., it needs to be terminated with a '\0' character).
I'm too lazy to go through the code you posted to confirm that buffer
will always be properly terminated -- but you shouldn't be.

One more thing: the "auto" keyword is a relic of ancient C history.
There never any need to use it.
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top