OK folks, corrected

R

Richard Heathfield

santosh said:
Doesn't that constitute as just defining the interface?

No. The Standard defines the semantics (so much so that, if you provide
different semantics, the program's behaviour is undefined):

"The contents of the array are sorted in ascending order according
to a comparison function pointed to by compar , which is called with
two arguments that point to the objects being compared. The function
shall return an integer less than, equal to, or greater than zero if
the first argument is considered to be respectively less than, equal
to, or greater than the second."

Thus, the following function is non-standard:

int cmpint(const void *vp, const void *vq)
{
const int *p = vp;
const int *q = vq;
return (*p < *q) - (*p > *q);
}

....but if your program passes it to qsort, it has defined semantics which
it must not violate (or, rather, shall not violate).
 
S

santosh

Harald said:
"The function shall return an integer less than, equal to, or greater
than
zero if the first argument is considered to be respectively less
than, equal to, or greater than the second."

Unrelated to the actual topic, but a question about qsort: is the
behaviour defined if compar longjmps out of qsort when the two
arguments are considered incomparable by the application? It needs
some way to exit without returning an integer. :)

I think so, please correct me if I'm wrong; I have hardly ever used
setjmp and longjmp. However I would avoid this problem in the first
place, by perhaps ensuring that there are no incomparable elements in
the array, say something like NaN. Or I might special case the compare
function so that such "invalid" values always compare either above or
below all valid values.
 
S

santosh

Richard said:
santosh said:


No. The Standard defines the semantics (so much so that, if you
provide different semantics, the program's behaviour is undefined):

"The contents of the array are sorted in ascending order according
to a comparison function pointed to by compar , which is called with
two arguments that point to the objects being compared. The function
shall return an integer less than, equal to, or greater than zero if
the first argument is considered to be respectively less than, equal
to, or greater than the second."

Thus, the following function is non-standard:

int cmpint(const void *vp, const void *vq)
{
const int *p = vp;
const int *q = vq;
return (*p < *q) - (*p > *q);
}

...but if your program passes it to qsort, it has defined semantics
which it must not violate (or, rather, shall not violate).

Thanks. I didn't consider your careful mention of qsort, and instead
just thought that providing a prototype constitutes providing only an
interface.
 
H

Harald van Dijk

I think so, please correct me if I'm wrong; I have hardly ever used
setjmp and longjmp.

I wasn't serious, but I'm pretty sure the behaviour's meant to be
undefined for such a case the same way as for a comparison function that
unconditionally returns 1. Neither actually violates 7.20.5p4, but that's
only because of sloppy wording, and if the sloppy wording is fixed in the
most obvious possible way (removing "That is,"), both functions would fail
to meet its requiremenets.
However I would avoid this problem in the first
place, by perhaps ensuring that there are no incomparable elements in
the array, say something like NaN. Or I might special case the compare
function so that such "invalid" values always compare either above or
below all valid values.

Yes, that's really the only sensible thing to do.
 
R

Richard Heathfield

santosh said:

Thanks. I didn't consider your careful mention of qsort, and instead
just thought that providing a prototype constitutes providing only an
interface.

Moving on, then, I don't think that a mere function prototype is sufficient
as an interface description. It's certainly /necessary/ (in style terms,
that is - one can simply do: int foo(); which is not a prototype, and let
the user guess the parameters!), but in my view one also needs to specify
the constraints that the caller must observe when calling the function.
For example, char *strcpy(char *target, const char *source) does not *on
its own* tell us that target must be a pointer to sufficient memory to
store a copy of the data starting at source, which must itself be a
null-terminated string. The compiler can't tell you that this code:

#include <string.h>
int main(void)
{
char target;
char source = 'X';
strcpy(&target, &source); /* NO! */
return 0;
}

is wrong.
 
S

santosh

Richard said:
santosh said:



Moving on, then, I don't think that a mere function prototype is
sufficient as an interface description. It's certainly /necessary/ (in
style terms, that is - one can simply do: int foo(); which is not a
prototype, and let the user guess the parameters!), but in my view one
also needs to specify the constraints that the caller must observe
when calling the function. For example, char *strcpy(char *target,
const char *source) does not *on its own* tell us that target must be
a pointer to sufficient memory to store a copy of the data starting at
source, which must itself be a null-terminated string. The compiler
can't tell you that this code:

#include <string.h>
int main(void)
{
char target;
char source = 'X';
strcpy(&target, &source); /* NO! */
return 0;
}

is wrong.

Yes, good point. There is a continuum between the interface and the
semantics. For C functions the prototype with a description of
constraints on the arguments and the return value would constitute the
interface. The semantics might be a more deeper explanation of what the
function does, why it does it that way, and other similar details. A
good example, I suppose, is strtok.

But this is all just splitting hairs anyway.
 
R

Richard Heathfield

santosh said:

There is a continuum between the interface and the
semantics. For C functions the prototype with a description of
constraints on the arguments and the return value would constitute the
interface. The semantics might be a more deeper explanation of what the
function does, why it does it that way, and other similar details. A
good example, I suppose, is strtok.

But this is all just splitting hairs anyway.

Actually, I think it is a genuinely useful discussion (especially if it
prompts readers to think more carefully about how - or whether! - they
write their interface documentation).
 
K

Keith Thompson

Richard Heathfield said:
santosh said:


Actually, I think it is a genuinely useful discussion (especially if it
prompts readers to think more carefully about how - or whether! - they
write their interface documentation).

I note that the "restrict" keyword makes it possible to squeeze
slightly more interface information into a prototype than you can
without it. A hypothetical new keyword that says a pointer must be
non-null might be something similar. But you can't completely
describe the interface in the language. A full-blown mechanism for
describing preconditions in code could come a lot closer, but I think
some natural-language documentation will always be necessary.
 
J

jacob navia

Keith said:
I note that the "restrict" keyword makes it possible to squeeze
slightly more interface information into a prototype than you can
without it. A hypothetical new keyword that says a pointer must be
non-null might be something similar.

In standard C you can say that a pointer is non null with

int function(int tab[static 1]);

tab must point to at least one element, hence it can't be NULL.
 
H

Harald van Dijk

Keith said:
I note that the "restrict" keyword makes it possible to squeeze
slightly more interface information into a prototype than you can
without it. A hypothetical new keyword that says a pointer must be
non-null might be something similar.

In standard C you can say that a pointer is non null with

int function(int tab[static 1]);

tab must point to at least one element, hence it can't be NULL.

True, but this also disallows other valid non-null pointer values from
being passed to that function, such as a pointer to the end of an array.
 
J

jacob navia

Harald said:
Keith said:
I note that the "restrict" keyword makes it possible to squeeze
slightly more interface information into a prototype than you can
without it. A hypothetical new keyword that says a pointer must be
non-null might be something similar.
In standard C you can say that a pointer is non null with

int function(int tab[static 1]);

tab must point to at least one element, hence it can't be NULL.

True, but this also disallows other valid non-null pointer values from
being passed to that function, such as a pointer to the end of an array.

No, since this tells the compiler that this pointer can be
1 or more elements, nothing more. This means that a pointer to the end
of the array is valid if the called function decrements it
before using it.
 
H

Harald van Dijk

Harald said:
In standard C you can say that a pointer is non null with

int function(int tab[static 1]);

tab must point to at least one element, hence it can't be NULL.

True, but this also disallows other valid non-null pointer values from
being passed to that function, such as a pointer to the end of an
array.

No, since this tells the compiler that this pointer can be 1 or more
elements, nothing more.

So when it's really 0 elements, you have a problem.
 
J

jacob navia

Harald said:
Harald said:
On Sun, 09 Mar 2008 01:14:38 +0100, jacob navia wrote:
In standard C you can say that a pointer is non null with

int function(int tab[static 1]);

tab must point to at least one element, hence it can't be NULL.
True, but this also disallows other valid non-null pointer values from
being passed to that function, such as a pointer to the end of an
array.
No, since this tells the compiler that this pointer can be 1 or more
elements, nothing more.

So when it's really 0 elements, you have a problem.

Yes of course. Every day I pass arrays of zero elements
(that can't be null) to functions. I have only problems.

As you can test any day too

int tab[0];

is illegal C.

And if you pass a pointer to 1 past the end of the array,
you can't access it until you decrement it. Since the
compiler will NOT verify the address anyway, the syntax
I proposed will work.

But you want to polemic without end. Go ahead.
 
H

Harald van Dijk

Harald said:
Harald van Dijk wrote:
On Sun, 09 Mar 2008 01:14:38 +0100, jacob navia wrote:
In standard C you can say that a pointer is non null with

int function(int tab[static 1]);

tab must point to at least one element, hence it can't be NULL.
True, but this also disallows other valid non-null pointer values
from being passed to that function, such as a pointer to the end of
an array.
No, since this tells the compiler that this pointer can be 1 or more
elements, nothing more.

So when it's really 0 elements, you have a problem.

Yes of course. Every day I pass arrays of zero elements (that can't be
null) to functions. I have only problems.

Consider a simple function to zero out a block of memory. You might give
it a size parameter, but another equally legitimate declaration is

void clearmem(void *start, void *end);

where neither start nor end is allowed to be a null pointer, _but_ end
will usually not point to anything.

Let's try your suggestion.

void clearmem(void start[static 1], void end[static 1]);

This is not valid, since you can't have arrays of incomplete type. (By
itself, this is another argument for a separate indicator that an argument
must not be null.) Okay, let's try giving it unsigned char *, because we
happen to know that in this program, it will only be used with byte arrays
anyway, and it's possible to cast if required.

void clearmem(unsigned char start[static 1], unsigned char end[static 1]);
As you can test any day too

int tab[0];

is illegal C.
Obviously.

And if you pass a pointer to 1 past the end of the array, you can't
access it until you decrement it. Since the compiler will NOT verify the
address anyway, the syntax I proposed will work.

The behaviour will be undefined, and the compiler will be permitted to
make the function access *end. This will be allowed even if the function
does not actually access *end.

Suppose the compiler can tell that for your processor, it would be more
efficient to save *end, then clear from *start to *end (inclusive), and
finally restore *end. You told the compiler that *end exists, so this is a
perfectly legitimate optimisation. Now suppose that the access to *end
aborts your program.

The result is a visible bug in the program. This bug will need to be
fixed. This bug can be fixed by dropping the misuse of '[static 1]' as
meaning 'nonnull'.
 
J

jacob navia

Harald said:
Suppose the compiler can tell that for your processor, it would be more
efficient to save *end, then clear from *start to *end (inclusive), and
finally restore *end. You told the compiler that *end exists, so this is a
perfectly legitimate optimisation. Now suppose that the access to *end
aborts your program.

This is not possible, there is no compiler that will do this.

1) The compiler can't know you are going to clear memory

2) Supposing that a future, intelligent compiler deduces that
you are clearing memory. It will discover also that you
first decrement the pointer, before using it.

It is amazing how the regulars can spew completely fantasy
situations for trying to make their arguments a little less silly.

I would understand an argument like

"What an horrible syntax. I would propose this one".

etc.

But this contrived fantasy situation?

I have often argued for an attribute syntax, and Microsoft/gcc have done
their syntax. I haven't followed either, since I find it
too ugly.

I proposed this ugly syntax because it *is* the current language and it
is maybe important to know.

And it is not misuse of anything.
 
H

Harald van Dijk

This is not possible, there is no compiler that will do this.

It's possible you're correct for current compilers. I admit I don't have
a concrete example that fails now (in part because I didn't bother to
look for one). However, making use of assumptions such as this is exactly
what static was designed to do, so I don't expect your hacks to continue
to work. You're wrong. I've explained why you're wrong. If you disagree,
show me why. Don't insult me, explain it to me.
 
K

Kenny McCormack

Harald van Dijk said:
what static was designed to do, so I don't expect your hacks to continue
to work. You're wrong. I've explained why you're wrong. If you disagree,
show me why. Don't insult me, explain it to me.

Oh. The. Irony.
 
H

Harald van Dijk

Oh. The. Irony.

I suppose the same goes for you. If you feel I've insulted jacob, give me
a message where I've done so. I've re-read my messages in this thread,
and I don't believe I have. The closest I've come is using the word
"hacks" as you quote it. (Yes, I'm aware of your reputation.)
 
G

George Peter Staplin

jacob said:
Not easily. I know the size of the VLA expression because I store
it in a variable.

With alloca however, I would have to modfy the
signature of alloca to make a new alloca

void *new_alloca(size_t siz, size_t *pVar);

and alloca would store in pVar the size of the variable
it allocates, adding it in case there are two or more
allocations.

This would imply that I monitor all calls and see if they
are a call to "alloca", and if they are, I pass the
address of that variable (that must be block specific)

Obviously, I would spare myself all the adds to esp
and I could do it in a single instruction like
gcc:
move %esi,%esp

BUT

in the x86 is quite expensive.

In x86-64 this is the way to go since there are 8 more
registers, and dedicating one is much less expensive anyway.

In the power pc lcc-win this is not even a problem with 32 registers!

Are you sure it's expensive? There are many more *internal* registers
on a modern x86. A register renaming algorithm runs automatically on
the modern x86. You should probably be more concerned now about data
stalls, and dependencies than trying to smash everything into a few
registers.

In some cases it's also not inefficient to store a small struct
pointer in a temporary register, so that you have a bit more space at
the cost of some indirection.

I also don't see why you can't use the frame pointer (%ebp), and leave,
ret to cleanup. Keep it simple. :)


George
 

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,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top