Qry : Behaviour of fgets -- ?

W

Wojtek Lerch

Keith Thompson said:
The point, I think, is that the 'restrict' qualifiers allow the
compiler to assume that the char* and FILE* arguments point to
non-overlapping memory.

No they don't, except when they are in the function *definition*. In a mere
declaration, adding the restrict qualifier to parameters is as meaningless
as adding the const qualifier. The following program is strictly
conforming:

void fun( int *restrict p, int *restrict q );

int main( void ) {
int a = 0;
fun( &a, &a );
return a;
}

void fun( int *const x, int *const y ) {
*x = *y + 1;
*y = *x - 1;
}
This might permit the compiler to generate
slightly better code in some cases. At worst, it should be harmless.

Slightly better code for the function, but not for calls to the function.
 
C

CBFalconer

Charlie said:
.... snip ...

I am curious how much code does this in real life. As a code
reviewer, I would forbid using actual FILE structures for this
or any purpose.

Note that the same effet can be achieved without an actual FILE
structure, using a pointer instead of the address as the "special
value":

The standard already does this, forbidding direct access to a FILE
object. You can only pass FILE* objects back and forth to/from
system functions. However, there is a wee glitch. putc and getc
are allowed to be macros (for efficiency reasons), and thus have to
have a defined FILE object to work on. You are not supposed to
notice this, nor take any advantage of it, because it won't work on
the next system. However putc/getc macros are issued by the
implementor, and they can make suitable allowances.

Don't ask for C&V. The forbid is there, but I don't remember where.
 
K

Keith Thompson

André Gillibert said:
Keith Thompson wrote: [...]
Doing that for C would have broken too much code.

Yes, of course.
However, one of the (pre-1999) C implementation I use has a
non-conforming option "Assume no pointer aliasing". It specifically
does that. I didn't understood this option (and never enabled it)
until I read about the restrict qualifier in C99.

Interesting. For such an option to be really useful, there should be
a way to override it, perhaps a 'norestrict' or '_Norestrict'
qualifier. Otherwise, there would be no way to implement memmove().
 
K

Keith Thompson

Wojtek Lerch said:
No they don't, except when they are in the function *definition*. In
a mere declaration, adding the restrict qualifier to parameters is as
meaningless as adding the const qualifier. The following program is
strictly conforming:

void fun( int *restrict p, int *restrict q );

int main( void ) {
int a = 0;
fun( &a, &a );
return a;
}

void fun( int *const x, int *const y ) {
*x = *y + 1;
*y = *x - 1;
}

Is it? I know there's some kind of compatibility requirement for
function declarations and corresponding function definitions. For
example, using 'int' in the declaration and 'long' in the definition
would be illegal. Are differences in qualifiers allowed? Where are
the rules stated?

[...]
 
C

CBFalconer

Wojtek said:
Couldn't the macros cast their FILE* argument to a pointer to
another, complete type?

What for? The purpose of putc/getc macros is to speed up access
and avoid using a local buffer, when the system buffer will serve
adequately. To do this you have to know how FILE is built. When
combined into a few routines, virtually all i/o, especially
interactive i/o, can be handled easily.
 
W

Wojtek Lerch

Keith Thompson said:
....
Is it? I know there's some kind of compatibility requirement for
function declarations and corresponding function definitions. For
example, using 'int' in the declaration and 'long' in the definition
would be illegal. Are differences in qualifiers allowed? Where are
the rules stated?

6.7.5.3#15 "(In the determination of type compatibility and of a composite
type, each parameter declared with function or array type is taken as having
the adjusted type and each parameter declared with qualified type is taken
as having the unqualified version of its declared type.)"
 
K

Keith Thompson

Wojtek Lerch said:
6.7.5.3#15 "(In the determination of type compatibility and of a
composite type, each parameter declared with function or array type is
taken as having the adjusted type and each parameter declared with
qualified type is taken as having the unqualified version of its
declared type.)"

Thanks.

What is the purpose of allowing the qualifiers to differ? Is it
specifically to allow qualifiers to be given for a function definition
(where they can affect the body of the function), but not in a
function declaration (where they're largely irrelevant)?
 
W

Wojtek Lerch

Keith Thompson said:
What is the purpose of allowing the qualifiers to differ? Is it
specifically to allow qualifiers to be given for a function definition
(where they can affect the body of the function), but not in a
function declaration (where they're largely irrelevant)?

When the only qualifiers were "const" and "volatile", it made sense:

extern int fun( int x );

int fun( const int x ) { ... }
 
C

Charlie Gordon

Kenneth Brody said:
Well, you can't realloc() the array, as that would invalidate all
existing FILE* values. (Unless it returned the same address, which
is certainly not guaranteed, or even very likely in most scenarios.)

Of course! I was not suggesting using realloc.
You can, however, allocate chunks of fixed-sized arrays which are then
put on a linked list. Not as efficient as a single array, but a lot
more efficient than individual allocations.

Yes, but the complexity is still linear: the number of comparisons against
array bounds is proportional to the total number of FILE objects allocated
(2 * N / chunksize).
The trick is to allocate chunks of increasing sizes for instance by doubling
the chunk size each time you run out of objects: the number of chunks will
be Log_base_2(N / first_chunk_size), a much smaller value for large values
of N. The chunks would be linked in a list and fclosed FILEs linked in a
free list. Alternately, one can store pointers to the chunks in a small
array to make the test more cache efficient.

You can use that method for any type of fixed size structure that a program
allocates, and add debug time asserts to verify pointer validity. There are
2 constraints: any 2 object pointers (converted to char *) must be
comparable (not guaranteed by the Standard) and you need a way to tell that
a freed or unallocated structure is valid (either a dedicated flag or some
other test of field values). This is not 100% foolproof, but can help debug
allocation problems when more general tools are not available.

There are other efficient methods for pointer validity checking, using hash
tables, trees or similar ad hoc structures, usually with more complexity.
 
R

Richard Bos

Keith Thompson said:
Making it incomplete would make it more difficult for programs to
abuse it by declaring FILE objects. We can't prevent perverse
programmers from examining the expansion of the getc() macro and
messing around with the internals of _FILE< but we can make it less
convenient. Or we could, if FILE were permitted to be incomplete.

This isn't a huge deal (i.e., I don't consider it to be a bug in the
standard), but IMHO it would make sense for a future standard to allow
FILE to be an incomplete type. It wouldn't break any existing code
that doesn't deserve to be broken.

Oh, I can certainly agree with that.

Richard
 
C

Charlie Gordon

Wojtek Lerch said:
No they don't, except when they are in the function *definition*. In a
mere declaration, adding the restrict qualifier to parameters is as
meaningless as adding the const qualifier. The following program is
strictly conforming:

void fun( int *restrict p, int *restrict q );

int main( void ) {
int a = 0;
fun( &a, &a );
return a;
}

void fun( int *const x, int *const y ) {
*x = *y + 1;
*y = *x - 1;
}


Slightly better code for the function, but not for calls to the function.

That's exactly my point.

There are even simpler examples to prove the compiler cannot issue
appropriate warnings from the restrict keyword alone in the function
declaration:

char buf[100];
int n;

memcpy(buf, buf, 0) // OK
memcpy(buf + 10, buf, 10); // OK
memcpy(buf + 10, buf, 20); // not OK
memcpy(buf + 10, buf, n); // cannot tell at compile time!
memmove(buf + 10, buf, n); // cannot tell at compile time either.

restrict qualifiers in function declarations convey no useful information to
the compiler.
the information conveyed to the programmer is minimal and vastly
misunderstood.

While it may not necessaarily be considered a defect, it was not a necessary
addition either.

In particular, I do not see the point of adorning the parameters of fopen
with restrict qualifiers: it is a useless constraint, meaningless for two
const char * . C99 6.7.3.1p10 example 3 clearly shows that unmodified
objects can be aliased through multiple restricted pointers. In this case,
I call for removal.
 
A

André Gillibert

Keith said:
Interesting. For such an option to be really useful, there should be
a way to override it, perhaps a 'norestrict' or '_Norestrict'
qualifier.

There was non _Norestrict or equivalent keyword.
However, the option could be overriden in individual translation units.
Otherwise, there would be no way to implement memmove().

Indeed, in a TU using this option it wasn't possible to deal with
aliasing, except with manual equality pointer tests, but that wouldn't
work with memmove.

An equivalent of memmove() would have to be implemented in a different TU.

That's why C99 restrict keyword is much more practical.
 
J

Jean-Marc Bourguet

Douglas A. Gwyn said:
Kenneth said:
Does alignment matter if the pointer is never dereferenced?

It might; it depends on the architecture.
Also, the C implementation might rely on the alignment
assumption when generating code for pointer conversion.
The following will not work properly on many word-addressed
platforms:
union t { int i; char c[2]; } u, *x;
char *p = &u.c[1]; *q;
x = (union t *)p; // might be allowed
q = (char *)x; // byte selector is lost
assert(p == q); // likely to fail!

I'm interested, do you know one implementation on a word adressed platform
which used a byte selector which didn't use otherwise unused bits in other
pointers?

My perception was that the current word-addressed platforms used
sizeof(char)==sizeof(word) and so had no byte selector: breaking this too
common assumption doesn't bring anything on these targets not aimed at
caracter handling.

On vintage computers, the only word addressable platform I know which had C
compilers with sizeof(char) != sizeof(word) is the PDP-10. But there other
reasons (some specific to the platform, see
(e-mail address removed)) made the implementation I used
choose a unique pointer size for everything. And these technical reasons
apply to the other vintage word-addressed architectures I know; but I don't
know them all.


Yours,
 
R

Rainer Weikusat

CBFalconer said:
My compilers exercise a random generator on each ++ (or --) call.
If two of them on the same object occur between sequence points it
uses the last random value to select a function to call. This can
be any function in any process, live or not (and requires system
specific code). This produces an amazing density of nasal demons.
Someday it may well launch WWIII. It is standard conforming.

This text would fit into a newsgroup whose topic is production of
mildly creative absurd fiction or one discussing quirks of a
particular implementation of C BUT NOT into a discussion of C, because
it is only insofar related to it as it talks about something the
C-standard does not talk about. And 'C' is the set of things the
C-standard DOES talk about.

One would assume that this cannot be that complicated for someone
trying to understand it, ie someone whose motivation is not the very
production of mildly creative absurd fiction.
 
R

Richard Tobin

Couldn't the macros cast their FILE* argument to a pointer to
another, complete type?
[/QUOTE]
What for?

So that they can work wothout FILE being a complete type. You said it
had to be one so that getc() and putc() could be macros; but that's not true
because the definition of getc() could cast the FILE * to, say, _FILE *,
where _FILE is the complete type.

-- Richard
 
C

Chris Dollin

Harald said:
I didn't say "the laws of physics as discovered (so far)", I copied Bart van
Ingen Schenau's unqualified "the laws of physics", which do exist even if
we don't fully know them yet.

I don't see that that's necessarily the case.

What's more, if there /are/ actual laws, we don't (and, I think, can't)
know what they are. So it doesn't matter; all we can work with is the
"laws" of physics, ie, our best-tested conjectures.
 
R

Richard Tobin

Keith Thompson said:
Making it incomplete would make it more difficult for programs to
abuse it by declaring FILE objects. We can't prevent perverse
programmers from examining the expansion of the getc() macro and
messing around with the internals of _FILE< but we can make it less
convenient. Or we could, if FILE were permitted to be incomplete.

You can't prevent perverse programmers from doing anything. They're
too determined. If getc() can see the internals, so can anyone with
sufficiently poor taste.

I think the advantage of making FILE an incomplete type would be more
to prevent inexperienced programmers from making mistakes about what
is legal, such as trying to copy a FILE object (which may or may not
work depending on the implementation).

-- Richard
 
W

Wojtek Lerch

What for?

So that they can work wothout FILE being a complete type. You said it
had to be one so that getc() and putc() could be macros; but that's not
true
because the definition of getc() could cast the FILE * to, say, _FILE *,
where _FILE is the complete type.[/QUOTE]

That's exactly what my reply was going to be. Thanks.
 
A

Army1987

No, they don't. Nature has no laws. Nature has behaviour. We observe
that behaviour, and formulate "laws" which explain it. Nature, however,
is under no obligation to observe those "laws" (and indeed frequently
breaks them). For example, the vast majority of gases don't observe the
gas laws.

<topicality level="LDBL_MIN">
They are approximate laws. There are laws which instead are
believed to be exact (e.g. the conservation of electrical charge).
So even if the Standard doesn't forbid

char *str = "Hey!";
size_t len = strlen(str);
struct flexible {
size_t size;
char data[1];
} *word = malloc(sizeof *word + len);
if (word != NULL) {
word->size = len;
strcpy(word->data, str);
puts(word->data);
}

from causing all electrons in my body to turn into positrons, I
am quite confident that this won't happen.
</topicality>
 

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,796
Messages
2,569,645
Members
45,371
Latest member
TroyHursey

Latest Threads

Top