What do you think?
--
comp.lang.c.moderated - moderation address: cl...@plethora.net -- you must
have an appropriate newsgroups line in your header for your mail to be seen,
or the newsgroup name in square brackets in the subject line. Sorry.
and what happens if a null pointer gets passed as an argument to a
non-null pointer parameter?
angel_tsankov wrote:
> It's been some time since I started wondering why the C/C++ languages
> do not have a non-null pointer (something like a copyable C++
> reference)?
Because it wouldn't work. Just because a pointer can't be NULL doesn't
mean it's safer to use. It could still be invalid in any number of
other ways.
> -provide compilers with further semantic information, thus enabling
> them to perform more optimizations;
There's no optimization you could do with a non-null pointer that you
would be forbidden to do with a NULL pointer --- optimizations are
already completely free to pick among various different kinds of
undefined behaviour. C code never has to check a pointer for NULL
before using it.
So what's supposed to happen when the restriction is violated? And
are YOU willing to be aboard a plane running this code in the
automatic pilot when the restriction is violated? I can imagine
that it's really, really easy to have the function *definition*
declare the pointer arguments non-null but the declaration at the
point of use doesn't. Guess what the undefined behavior will do?
>-allows restrictive programming techniques to be apllied at a lower
>cost (e.g. if a pointer passed to a function may not be null, there
>would be no need for a null-check before dereferencing it);
Yes, there really is a need for a null-check. The question is what
to do (at runtime) when the check fails.
>-allow for improvements in programming practices (having two types of
>pointers will bring to attention the difference between them, namely
>the possibility that "traditional" pointers can be null, hopefully
>thus making it more obvious that "traditional" pointers should be
>checked for null before thay are dereferenced);
>-allow for more reliable code (having a "traditional" pointer where a
>non-null pointer would do increases the risk of undefined behavior; on
>the other hand, using a non-null pointer instead would be safer);
Is it really going to be more reliable to count on all the header files
containing the declarations to be fixed?
>-provide compilers with further semantic information, thus enabling
>them to perform more optimizations;
>-make source code more concise (as a result of having fewer null-tests
>when replacing "traditional" pointers with non-null ones).
In other words, attempting to completely hide the error-handling.
>What do you think?
fgets() returns a possibly-null pointer. fputs() takes a non-null
pointer for the string to output. Write the obvious copy loop to
copy a file to stdout. The file open for the input has already
been done:
int display_file_to_stdout(FILE no_steenking_null *input);
Returns 0 on success, -1 on error.
It's undefined behaviour, but the compiler may be able to detect it and
issue a diagnostic. And in a debug mode, it could also insert opcodes to
test for null at runtime.
C99 actually has this kind of "non-null pointers" already, although they can
only be used for function parameters:
void fun( int ptr[ static 1 ] );
...
fun( NULL ); // undefined, easy to diagnose
If these "non-null pointers" were actual types, compilers could also detect
when a regular pointer whose value is not known to be non-null is converted
to a "non-null pointer" type by assignment or other operations, similarly to
how some compilers issue a warning when an integer is converted to a
narrower type and its value is not known to be in the range. This would not
be a huge improvement to the safety of the language, but that doesn't
necessarily mean that it would be completely worthless.
If the compiler knows that the pointer isn't supposed to be null, it can
help you detect some cases where you attempt to set it to null by mistake.
That makes it safer to use. Of course, your code can still be incorrect in
any number of other ways, but I don't think anybody has suggested that these
"non-null pointers" would be a panacea for all programming errors.
>> -provide compilers with further semantic information, thus enabling
>> them to perform more optimizations;
>
> There's no optimization you could do with a non-null pointer that you
> would be forbidden to do with a NULL pointer --- optimizations are already
> completely free to pick among various different kinds of undefined
> behaviour. C code never has to check a pointer for NULL before using it.
No, but there may be optimization opportunities that are impossible to
detect without knowing that a pointer cannot possibly be null, and quite
often a compiler has no way to know that, especially if pointers are passed
around between separately compiled translation units.
I think at least part of what you seek is already
present in C99:
void func(char string[static 1]) {
assert (string != NULL);
...
}
See Section 6.7.5.3, paragraphs 7 and 21 (example 5).
> and what happens if a null pointer gets passed as an argument to a
> non-null pointer parameter?
It's undefined behaviour, but the compiler may be able to detect it and
issue a diagnostic. And in a debug mode, it could also insert opcodes to
test for null at runtime.
C99 actually has this kind of "non-null pointers" already, although they can
only be used for function parameters:
void fun( int ptr[ static 1 ] );
...
fun( NULL ); // undefined, easy to diagnose
If these "non-null pointers" were actual types, compilers could also detect
when a regular pointer whose value is not known to be non-null is converted
to a "non-null pointer" type by assignment or other operations, similarly to
how some compilers issue a warning when an integer is converted to a
narrower type and its value is not known to be in the range. This would not
be a huge improvement to the safety of the language, but that doesn't
necessarily mean that it would be completely worthless.
Sure, there would be optimizations. For instance, you can safely skip
the null checks. The standard library could do this also, e.g. in
functinos where this matters (free for example).
That would not be possible! A non-null pointer would be convertible to
a null one, but not vice versa.
If the compiler knows that the pointer isn't supposed to be null, it can
help you detect some cases where you attempt to set it to null by mistake.
That makes it safer to use. Of course, your code can still be incorrect in
any number of other ways, but I don't think anybody has suggested that these
"non-null pointers" would be a panacea for all programming errors.
>> -provide compilers with further semantic information, thus enabling
>> them to perform more optimizations;
>
> There's no optimization you could do with a non-null pointer that you
> would be forbidden to do with a NULL pointer --- optimizations are already
> completely free to pick among various different kinds of undefined
> behaviour.
No, but there may be optimization opportunities that are impossible to
detect without knowing that a pointer cannot possibly be null, and quite
often a compiler has no way to know that, especially if pointers are passed
around between separately compiled translation units.
First of all, I when I wrote 'restrictive programming' I meant
'protective programming', which I think is a nice 'thing'.
Secondly, I should say that a non-null pointer would probably be a new
(i.e. diferent) type and it would not be possible to convert a
'traditional' pointer to a non-null one. Right on the contrary, a non-
null pointer would be convertible to a 'traditional' one.
And last, a new operator might be needed - one that takes a single
argument and returns a non-null pointer to it. An implicit conversion
from an object to a non-null pointer would also do. Another
alternative is to change the semantics of the traditional address-of
(&) operator to return a non-null pointer (instead of a 'traditional'
one) and have an implicit conversion from a non-null pointer to a
'traditional' one (in order not to break existing code that uses the
address-of operator with 'traditional' pointers).
That's all for now. Hope this clarifies the idea somewhat.
P.S. A very short example usage (assuming that # designates a non-null
pointer and & returns a non-null pointer that is implicitly
convertible to a 'tarditional' one):
struct s
{
int i;
};
// Traditional way to pass a structure to a function
void f(struct s* arg)
{
if (arg != NULL)
{
arg->i = 3;
}
}
void g(struct s# arg)
{
arg.i = 3; // The member access operator should be different from "-
>" to avoid the impression that arg is a 'tarditional' pointer; the .
operator seems to fit this purpose well
}
int main(void)
{
s S1;
s S2;
struct s# nnp1 = &S1;
struct s# nnp2 = &S2;
f(&S1); // Same as 'g((s*)&S1);'
g(&S1);
// In machine language non-null pointers would probably be
represented just like 'traditional' pointers and would be copyable and
assignable:
g(nnp1);
nnp2 = nnp1;
I think you have a terminology problem here. I suspect that you are
using "null pointer" to refer to a pointer type that can hold a null
value, and "non-null pointer" to refer to a pointer type that is not
permitted to hold a null value. Unfortunately, the standard has
already pre-empted the term "null pointer" to refer to a type of
pointer value (not a pointer type). it doesn't explicitly define what
a non-null pointer is, but it does use the term several times. A
native speaker of English would naturally expect "non-null pointer" to
refer to a pointer value (not a type), which is not null. In terms of
the standard, "converting a non-null pointer to a null pointer" is
gibberish. It wouldn't be a conversion, it would be a replacement.
I'll use "nullable" as a term for pointers like the ones currently
defined by the C standard, and "unnullable" as a possible new keyword
that can be used to specify a new type that is identical to an
ordinary pointer, except that any code construct that could give it a
null value is, in some sense, "prohibited". In those terms, I think
what you meant to say was that "An value with an unnullable pointer
type would be convertible to a nullable pointer type, but not vice
versa".
What you need to do is be more specific about what precisely is
prohibited. I gather that you want it to be prohibited at compile
time, so that code with behavior that is otherwise well-defined and
which compiles without diagnostics is guaranteed to never generate a
null value for an unnullable pointer. In standardese, that means that
any problematic code must be either a syntax error or a constraint
violation, and it doesn't seem feasible to me to make these syntax
errors.
From what you've just said, conversion of a null pointer constant or
the value of an expression with a nullable pointer type to an
unnullable pointer type would have to be a constraint violation. I
presume the same would be true for conversion from an integer type?
That's easy. It would also have to be constraint violation for an
object with an unnullable pointer type to be left uninitialized. As a
result, I don't think it would be feasible to allow the space
containing an unnullable pointer object to be allocated using the
malloc() family.
The address-of operator (&) would have to have a value which is
neither nullable nor unnullable, but usable for either purpose; the
initializer for an object with an unnullable pointer type would have
to be the address of an lvalue expression. You'd need a special case
to disallow
unnullable char* p = &*(char*)0;
Pointer arithmetic on an unnullable pointer would also have to be a
constraint violation.
By the time you've put on enough restrictions to allow a guaratee that
the pointer is unnullable, it doesn't look much like a pointer
anymore.
In that case, I think you just threw all the "safety" arguments out
the window. If you're going to claim "safety" benefits, the compiler
needs to *CHECK*. Of course, the issue is still there about what
to do when the check fails.
In order to be useful, there has to be a way to convert a plain,
old, ordinary, unsafe pointer to a non-null pointer, provided, of
course that it passes the check. Here's a suggestion, but I'm sure
someone will think the syntax is horrible. ifnotnull is not a
function, it's a syntax construct, sorta like if. The assignment
in ifnotnull(nnp = np) is only done if the value is not null.
The syntax is:
ifnotnull ( <no_steenking_null pointer variable> =
<dangerous pointer-valued expression )
statement else statement;
Just for the heck of it, I'll make the else clause mandatory.
#include <stdlib.h>
char *p;
char no_steenking_null *nnp;
p = malloc(...whatever...);
ifnotnull (nnp = p) {
... do something with nnp ...
... like passing nnp to a function that takes ...
... no_steenking_null pointers ...
} else {
... pointer is null, handle it ...
}
which is equivalent to:
#include <stdlib.h>
char *p;
char no_steenking_null *nnp;
p = malloc(...whatever...);
if (p != NULL) {
nnp = (char no_steenking_null *) p;
... do something with nnp ...
... like passing nnp to a function that takes ...
... no_steenking_null pointers ...
} else {
... pointer is null, handle it ...
}
>And in a debug mode, it could also insert opcodes to
>test for null at runtime.
I'll even go so far as to say that in debug mode, it would be a
good idea that converting *one* dangerous pointer to a not-null
pointer should, ideally, involve checking *all* not-null pointers
for being not null, to the extent possible. This would at least
include all of the not-null pointers with declarations in scope.
Should we also forbid uninitialized not-null pointers?
> and it would not be possible to convert a
> 'traditional' pointer to a non-null one.
This is impractical; you don't seem to have much experience in
programming in a language that offers both references and pointers (such
as C++). Even in C++, you generally can convert pointers to references
(and vice-versa), and thankfully so (although of course converting
pointers that do not point to an object of the reference's type yields
undefined behaviour.)
Not *all* the safety arguments, just some of them. Just because the
behaviour is officially undefined doesn't mean that a good compiler can't
insert opcodes to check for null and abort the program with an error
message -- or, even better, have a switch to let you choose whether you want
the check or not.
I wouldn't expect this new feature to add more safety (or more syntactic
complexity) to C than adding the "const" qualifier once did. Despite the
fact that C doesn't make it impossible to attempt to modify a
const-qualified object by casting away the constness of a pointer, I don't
think anybody would claim that "const" has absolutely no safety benefits.
> In order to be useful, there has to be a way to convert a plain,
> old, ordinary, unsafe pointer to a non-null pointer, provided, of
> course that it passes the check.
There's a difference between "useful" and "perfectly safe". A feature that
makes programming a little safer can be useful, even if it doesn't make it
perfectly safe.
> Here's a suggestion, but I'm sure
> someone will think the syntax is horrible. ifnotnull is not a
> function, it's a syntax construct, sorta like if. The assignment
> in ifnotnull(nnp = np) is only done if the value is not null.
Indeed, it does look pretty horrible. But what feels worse to me is that if
you decide to go in that direction, you should also invent non-zeroable
arithmetic types and similar syntax to protect division, and of course a
non-indeterminate qualifier that could apply to any type to indicate that an
object cannot possibly contain an indeterminate value (and therefore, for
instance, must be initialized if it's automatic). Good luck with that.
...
>>And in a debug mode, it could also insert opcodes to
>>test for null at runtime.
>
> I'll even go so far as to say that in debug mode, it would be a
> good idea that converting *one* dangerous pointer to a not-null
> pointer should, ideally, involve checking *all* not-null pointers
> for being not null, to the extent possible. This would at least
> include all of the not-null pointers with declarations in scope.
The C standard has no concept of "debug mode". Any debug facilities are a
quality-of-implementation issue.
> Should we also forbid uninitialized not-null pointers?
Exactly, and what about pointers that point to an object that has gone out
of scope?
What about pointers inside a union?
My vote: if you're looking for a language with absolute safety, C is one of
the worst possible choices. It will never make it impossible for you to
shoot youself in the foot if you try; the best that can be done is to make
it more helpful in avoiding shooting yourself by accident.
C code had better check that the return value from malloc() et al is not
null before using it. C code had better check that the return value
from fopen() et al is not null before using it. C code had better check
that the return value from fgets() is not null before using it. (C code
had better not be using gets(), so there's no need to check its return
value!)
> Sure, there would be optimizations. For instance, you can safely skip
> the null checks. The standard library could do this also, e.g. in
> functinos where this matters (free for example).
Since free(0) is defined behaviour - nothing happens - it isn't clear
that there is any optimization available.
--
Jonathan Leffler #include <disclaimer.h>
Email: jlef...@earthlink.net, jlef...@us.ibm.com
Guardian of DBD::Informix v2008.0229 -- http://dbi.perl.org/
publictimestamp.org/ptb/PTB-3317 ripemd160 2008-05-24 03:00:05
851B441D3F34BE2EBCF36BD250B03C07F300D8D7
Yes, I totally agree with you.
> I'll use "nullable" as a term for pointers like the ones currently
> defined by the C standard, and "unnullable" as a possible new keyword
> that can be used to specify a new type that is identical to an
> ordinary pointer, except that any code construct that could give it a
> null value is, in some sense, "prohibited". In those terms, I think
> what you meant to say was that "An value with an unnullable pointer
> type would be convertible to a nullable pointer type, but not vice
> versa".
"Nullable" and "unnullable" sound fine. Thanks for your suggestion!
> What you need to do is be more specific about what precisely is
> prohibited. I gather that you want it to be prohibited at compile
> time, so that code with behavior that is otherwise well-defined and
> which compiles without diagnostics is guaranteed to never generate a
> null value for an unnullable pointer. In standardese, that means that
> any problematic code must be either a syntax error or a constraint
> violation, and it doesn't seem feasible to me to make these syntax
> errors.
Why? Could you support your point with some more details or examples?
> From what you've just said, conversion of a null pointer constant or
> the value of an expression with a nullable pointer type to an
> unnullable pointer type would have to be a constraint violation. I
> presume the same would be true for conversion from an integer type?
> That's easy.
Roughly speaking, yes. More precisely, the nullable to unnullable
conversion should be prohibited. The integer to unnullable conversion
could also be prohibited - I've not considered this case yet, so it's
kind of an open question. It could also be prohibited, since the idea
behind unnullable pointer is to have copyable C++ references (see
below).
> It would also have to be constraint violation for an
> object with an unnullable pointer type to be left uninitialized. As a
> result, I don't think it would be feasible to allow the space
> containing an unnullable pointer object to be allocated using the
> malloc() family.
One could have malloc2(), taking an unnullable pointer that would
serve as initilizer for the malloc'ed unnullable pointer.
> The address-of operator (&) would have to have a value which is
> neither nullable nor unnullable, but usable for either purpose; the
> initializer for an object with an unnullable pointer type would have
> to be the address of an lvalue expression. You'd need a special case
> to disallow
>
> unnullable char* p = &*(char*)0;
This code is undefined behaviour - *(char*)0.
> Pointer arithmetic on an unnullable pointer would also have to be a
> constraint violation.
The basic idea behind unnullable pointers is to have a C++ reference
with copy semantics in C and in C++.
> By the time you've put on enough restrictions to allow a guaratee that
> the pointer is unnullable, it doesn't look much like a pointer
> anymore.
We could call it "copyable reference" instead...
> --
> comp.lang.c.moderated - moderation address: c...@plethora.net -- you must
> have an appropriate newsgroups line in your header for your mail to be seen,
> or the newsgroup name in square brackets in the subject line. Sorry.--
Simplest example:
void func(char *p)
{
unnullable char *q=p;
// Code which uses q, relying upon it not being null.
}
Code like this would have to be prohibited, since 'p' might be null.
However, it's very difficult to write a rule that prohibits such code by
making it a syntax error. If you think otherwise, please provide grammar
rules that express that idea. On the other hand, it's easy to express
such a prohibition as a violation of a constraint, one that requires
that the initializer for an unnullable pointer be an expression with a
nullable pointer type.
>> It would also have to be constraint violation for an
>> object with an unnullable pointer type to be left uninitialized. As a
>> result, I don't think it would be feasible to allow the space
>> containing an unnullable pointer object to be allocated using the
>> malloc() family.
>
> One could have malloc2(), taking an unnullable pointer that would
> serve as initilizer for the malloc'ed unnullable pointer.
The C standard currently allows for the possibility that pointers to
different types can have different representations, and even different
sizes. There are a number of good reasons why many real-world
implementations actually use two or more different pointer
representations, depending upon the type of the object being pointed at.
How could such an implementation implement malloc2() so it would know
which representation to use? It would be relatively straightforward to
handle that case by making malloc2() a C++ template function, but I
don't see any easy way to do it in C.
Would it be prohibited for a structure to contain a member of unnullable
pointer type? If not, how do you tell malloc2() where to insert the
pointer that it takes as an argument, when allocating space for such a
structure?
Could you allocate an array of unnullable pointers? Does that mean that
malloc2() would have to be prepared to accept an unlimited number of
pointer arguments as initializers, presumably by using a <varargs.h>
interface? Or would it initialize all of them to point at the same
object, and let them be reassigned later? Unlike the previous issues,
this one has at least two different perfectly feasible solutions; but
you'll have to choose one.
>> The address-of operator (&) would have to have a value which is
>> neither nullable nor unnullable, but usable for either purpose; the
>> initializer for an object with an unnullable pointer type would have
>> to be the address of an lvalue expression. You'd need a special case
>> to disallow
>>
>> unnullable char* p = &*(char*)0;
>
> This code is undefined behaviour - *(char*)0.
No, it does not. The expression "*(char*)0", if it were to occur in most
other contexts, would have undefined behavior. However, section
6.5.3.2p3, describing the '&' operator, says "If the operand is the
result of a unary * operator, neither that operator nor the & operator
is evaluated and the result is as if both were omitted,". In other
words, &*(char*)0 is equivalent to (char*)0, which has perfectly
well-defined behavior, even though *(char*)0 would have undefined
behavior in any other context.
--
Sorry - during editing, I incompletely converted a prohibition into a
requirement. That last "nullable" should have been 'unnullable".
Yes, implicit conversion from a nullable pointer to a unnullable would
be prohibited. However, there might be a reason for explicit
conversion:
void func(char *p)
{
if (p != NULL)
{
unnullable char* r = (unnullable char*)q;
// ...
}
}
> However, it's very difficult to write a rule that prohibits such code by
> making it a syntax error. If you think otherwise, please provide grammar
> rules that express that idea. On the other hand, it's easy to express
> such a prohibition as a violation of a constraint, one that requires
> that the initializer for an unnullablepointerbe an expression with a
> nullablepointertype.
>
> >> It would also have to be constraint violation for an
> >> object with an unnullablepointertype to be left uninitialized. As a
> >> result, I don't think it would be feasible to allow the space
> >> containing an unnullablepointerobject to be allocated using the
> >> malloc() family.
>
> > One could have malloc2(), taking an unnullablepointerthat would
> > serve as initilizer for the malloc'ed unnullablepointer.
>
> The C standard currently allows for the possibility that pointers to
> different types can have different representations, and even different
> sizes. There are a number of good reasons why many real-world
> implementations actually use two or more differentpointer
> representations, depending upon the type of the object being pointed at.
> How could such an implementation implement malloc2() so it would know
> which representation to use? It would be relatively straightforward to
> handle that case by making malloc2() a C++ template function, but I
> don't see any easy way to do it in C.
I do not really get your point here... As far as I know malloc does
not care about types - it only cares about allocating memory of the
specified amount.
> Would it be prohibited for a structure to contain a member of unnullablepointertype? If not, how do you tell malloc2() where to insert thepointerthat it takes as an argument, when allocating space for such a
> structure?
> Could you allocate an array of unnullable pointers? Does that mean that
> malloc2() would have to be prepared to accept an unlimited number ofpointerarguments as initializers, presumably by using a <varargs.h>
> interface? Or would it initialize all of them to point at the same
> object, and let them be reassigned later? Unlike the previous issues,
> this one has at least two different perfectly feasible solutions; but
> you'll have to choose one.
Automatic arrays would either be randomly initialized or prohibited.
> >> The address-of operator (&) would have to have a value which is
> >> neither nullable nor unnullable, but usable for either purpose; the
> >> initializer for an object with an unnullablepointertype would have
> >> to be the address of an lvalue expression. You'd need a special case
> >> to disallow
>
> >> unnullable char* p = &*(char*)0;
>
> > This code is undefined behaviour - *(char*)0.
>
> No, it does not. The expression "*(char*)0", if it were to occur in most
> other contexts, would have undefined behavior. However, section
> 6.5.3.2p3, describing the '&' operator, says "If the operand is the
> result of a unary * operator, neither that operator nor the & operator
> is evaluated and the result is as if both were omitted,". In other
> words, &*(char*)0 is equivalent to (char*)0, which has perfectly
> well-defined behavior, even though *(char*)0 would have undefined
> behavior in any other context.
Ooops, sorry. I think initialization with constant expressions should
be prohibited, too. In fact, it would only be possible to initialize
an unnullable pointer fron an object, just like references in C++.
This is a bare minimum; it could be extended, if needed.
> --
> comp.lang.c.moderated - moderation address: c...@plethora.net -- you must
You are perfectly correct about malloc(); but we weren't talking about
malloc(), we were talking about malloc2(). As described above, it takes
an unnullable pointer argument that is used to initialize the memory
allocated by malloc2(). Since unnullable pointers could, presumably,
have many different types, with correspondingly different sizes and
different representations, malloc2() could not work without being type
aware. Here's code for a hypothetical language with C++ - style
templates and an 'unnullable' keyword:
template <type T> unnullable T** malloc2(unnullable T *t, size_t size)
{
T** temp = malloc(size);
if(temp)
temp[0] = (T*)t; // Must be type-aware
return (unnullable T**) temp;
}
The marked line has to be type-aware, because sizeof(T*) can depend upon
what T is.
>> Would it be prohibited for a structure to contain a member of unnullablepointertype? If not, how do you tell malloc2() where to insert thepointerthat it takes as an argument, when allocating space for such a
>> structure?
Have you figured out an answer to that question?
>> Could you allocate an array of unnullable pointers? Does that mean that
>> malloc2() would have to be prepared to accept an unlimited number ofpointerarguments as initializers, presumably by using a <varargs.h>
>> interface? Or would it initialize all of them to point at the same
>> object, and let them be reassigned later? Unlike the previous issues,
>> this one has at least two different perfectly feasible solutions; but
>> you'll have to choose one.
>
> Automatic arrays would either be randomly initialized or prohibited.
I thought that the point of unnullable pointers was to ensure that they
could not be null? If they can, under certain circumstances, be randomly
initialized, doesn't that defeat the purpose?
>It's been some time since I started wondering why the C/C++ languages
>do not have a non-null pointer (something like a copyable C++
>reference)? In my opinion, using non-null pointers instead of the
>"traditional" ones (wherever appropriate) would provide the following
>benefits: ...
This is neither C nor C++, but look at Cyclone for a language
featuring such pointers:
http://cyclone.thelanguage.org/
http://cyclone.thelanguage.org/wiki/Pointers
http://cyclone.thelanguage.org/wiki/Pointers%20with%20Restricted%20Aliasing
http://cyclone.thelanguage.org/wiki/Cyclone%20for%20C%20Programmers#Pointers
--
Roberto Waltman
[ Please reply to the group,
return address is invalid ]
Why not:
unnullable void** malloc2(unnullable void const* initial_value, size_t
size)
{
void** new_ptr = malloc(size);
if(new_ptr != NULL)
{
*new_ptr = initial_value;
}
return (unnullable void**)new_ptr;
}
If think the above sample implementation of malloc2 should do,
provided that a conversion from T* to void*, and back to T*, always
yields the initial value for any type T (which I think is guaranteed
by the standard).
> The marked line has to be type-aware, because sizeof(T*) can depend upon
> what T is.
>
> >> Would it be prohibited for a structure to contain a member of unnullablepointertype? If not, how do you tell malloc2() where to insert thepointerthat it takes as an argument, when allocating space for such a
> >> structure?
>
> Have you figured out an answer to that question?
>
> >> Could you allocate an array of unnullable pointers? Does that mean that
> >> malloc2() would have to be prepared to accept an unlimited number ofpointerarguments as initializers, presumably by using a <varargs.h>
> >> interface? Or would it initialize all of them to point at the same
> >> object, and let them be reassigned later? Unlike the previous issues,
> >> this one has at least two different perfectly feasible solutions; but
> >> you'll have to choose one.
>
> > Automatic arrays would either be randomly initialized or prohibited.
>
> I thought that the point of unnullable pointers was to ensure that they
> could not be null? If they can, under certain circumstances, be randomly
> initialized, doesn't that defeat the purpose?
> --
> comp.lang.c.moderated - moderation address: c...@plethora.net -- you must
> have an appropriate newsgroups line in your header for your mail to be seen,
> or the newsgroup name in square brackets in the subject line. Sorry.- Скриване на цитирания текст -
>
> - Показване на цитирания текст -
In several of my replies I have mentioned, that I suggest the addition
of A COPYABLE C++ REFERENCE to the C language under the name
"unnullable pointer":
"[...]the idea behind unnullable pointer is to have copyable C++
references"
"[...]it would only be possible to initialize an unnullable pointer
fron an object, just like references in C++. This is a bare minimum;
it could be extended, if needed."
So, an unnullable pointer would be just like a C++ reference:
-it MUST be initialized (implementations shall ensure that this
restriction is met);
-if we allow struct's to have unnullable pointers as members, those
members MUST be initialized (again, implementations shall ensure that
this is met);
-as to malloc2, if we cannot have it, then we won't have it (C++
references cannot be allocated on the heap either);
-if we allow arrays of unnullable pointers, then ALL members of such
an array MUST are initialized (again, implementations shall ensure
this).
Any questions left unanswered?
Yes, but that implementation only allows the initialization of
unnullable void* pointers; it doesn't allow the initialization of any
other pointer type. Therefore, if malloc2() is your only proposed
mechanism for allowing the initialization of unnullable pointers in
dynamically allocated memory, then no other unnullable pointer types can
reside in such memory. With references in C++, there's no corresponding
problem, because the constructor for any object type containing a
reference is responsible for initializing it, and the 'new' operator
causes the constructor to be called on the newly allocated memory.
>>>> Would it be prohibited for a structure to contain a member of unnullablepointertype? If not, how do you tell malloc2() where to insert thepointerthat it takes as an argument, when allocating space for such a
>>>> structure?
>> Have you figured out an answer to that question?
I gather that you have not, since you didn't bother to answer it. You
know, "I don't know" is a perfectly acceptable answer.
Your solution, like mine, still suffers from only be able to initialize
a pointer that occupies the initial bytes of the allocated memory.
...
> -as to malloc2, if we cannot have it, then we won't have it (C++
> references cannot be allocated on the heap either);
Not directly, but it can be done indirectly:
#include <cstdlib>
#include <memory>
struct ReferenceHolder
{
int &ri;
ReferenceHolder(int i):ri(i){}
};
int main()
{
int j;
ReferenceHolder *rj;
ReferenceHolder *rk;
rj = new ReferenceHolder(j);
rk = static_cast<ReferenceHolder*>(std::malloc(sizeof *rk));
new(rk) ReferenceHolder(j); // inplace construction
int retval = (&(rj->ri) == &(rk->ri)) ? EXIT_SUCCESS : EXIT_FAILURE;
std::free(rk);
delete(rj);
return retval;
}
This relies upon the existence of constructors in C++. I've no idea how
you could do something similar in C+unnullable.
[oops, a bit late. here we go with the fixed one, then - mod]
Yes, but that implementation only allows the initialization of
unnullable void* pointers; it doesn't allow the initialization of any
other pointer type. Therefore, if malloc2() is your only proposed
mechanism for allowing the initialization of unnullable pointers in
dynamically allocated memory, then no other unnullable pointer types can
reside in such memory. With references in C++, there's no corresponding
problem, because the constructor for any object type containing a
reference is responsible for initializing it, and the 'new' operator
causes the constructor to be called on the newly allocated memory.
>>>> Would it be prohibited for a structure to contain a member of unnullablepointertype? If not, how do you tell malloc2() where to insert thepointerthat it takes as an argument, when allocating space for such a
>>>> structure?
>> Have you figured out an answer to that question?
I gather that you have not, since you didn't bother to answer it. You
know, "I don't know" is a perfectly acceptable answer.
Your solution, like mine, still suffers from only be able to initialize
a pointer that occupies the initial bytes of the allocated memory.
...
> -as to malloc2, if we cannot have it, then we won't have it (C++
> references cannot be allocated on the heap either);
Not directly, but it can be done indirectly:
#include <cstdlib>
#include <memory>
struct ReferenceHolder
{
int &ri;
ReferenceHolder(int &i):ri(i){}
};
int main()
{
int j;
ReferenceHolder *rj;
ReferenceHolder *rk;
rj = new ReferenceHolder(j);
rk = static_cast<ReferenceHolder*>(std::malloc(sizeof *rk));
new(rk) ReferenceHolder(j); // inplace construction
int retval = (&(rj->ri) == &(rk->ri)) ? EXIT_SUCCESS : EXIT_FAILURE;
std::free(rk);
delete(rj);
return retval;
}
This relies upon the existence of constructors in C++. I've no idea how
you could do something similar in C+unnullable.
I guess that means such a struct can't be allocated on the heap. To
prevent that, you have to disallow converting any other pointer
(e.g., a void pointer) to a pointer to such a struct.
> -as to malloc2, if we cannot have it, then we won't have it (C++
> references cannot be allocated on the heap either);
I allocate references on the heap all the time in C++ (inside structs).
You can't allocate a reference directly, however. (I wonder why not.)
I actually sort of like the idea of unnullable pointers, but I don't
think the idea is viable without constructors.
It seems that I do not have any "correct" answer as to how the C
language (and the C library) can quarantee that a dynamically
allocated unnullable pointer is properly initialized.
So, for the time being we could restrict the use of "unnullable" to
qualify pointers of static storage duration and pointers which are
pointed to. This means that a pointer whose storage duration is not
known at the point of their declaration (e.g. member pointers) may not
be unnullable (unless it specifies a pointer which is pointed to:
struct s
{
unnullable**p; // This is OK
};
--End example).
However, even with these restrictions one can still have dynamically
allocated unnullable pointers (of which I cannot see any benefit):
unnullable some_type** malloc_some_type(unnullable some_type*
existing_ptr)
{
some_type** new_ptr = malloc(sizeof(unnullable some_type*));
// If malloc has succeeded, new_ptr is not of type unnullable
some_type**, since
// the pointer that new_ptr points to may be NULL
if (new_ptr != NULL)
{
*new_ptr = existing_ptr;
}
return (unnullable some_type**)new_ptr;
}
> >>>> Would it be prohibited for a structure to contain a member of unnullablepointertype? If not, how do you tell malloc2() where to insert thepointerthat it takes as an argument, when allocating space for such a
> >>>> structure?
> >> Have you figured out an answer to that question?
>
> I gather that you have not, since you didn't bother to answer it. You
> know, "I don't know" is a perfectly acceptable answer.
> Your solution, like mine, still suffers from only be able to initialize
> apointerthat occupies the initial bytes of the allocated memory.
Yes, I do not have any answer to this question.
> ...
>
> > -as to malloc2, if we cannot have it, then we won't have it (C++
> > references cannot be allocated on the heap either);
>
> Not directly, but it can be done indirectly:
>
> #include <cstdlib>
> #include <memory>
>
> struct ReferenceHolder
> {
> int &ri;
> ReferenceHolder(int i):ri(i){}
>
> };
>
> int main()
> {
> int j;
> ReferenceHolder *rj;
> ReferenceHolder *rk;
> rj = new ReferenceHolder(j);
>
> rk = static_cast<ReferenceHolder*>(std::malloc(sizeof *rk));
> new(rk) ReferenceHolder(j); // inplace construction
>
> int retval = (&(rj->ri) == &(rk->ri)) ? EXIT_SUCCESS : EXIT_FAILURE;
>
> std::free(rk);
> delete(rj);
> return retval;
>
> }
>
> This relies upon the existence of constructors in C++. I've no idea how
> you could do something similar in C+unnullable.
> --
> comp.lang.c.moderated - moderation address: c...@plethora.net -- you must
> have an appropriate newsgroups line in your header for your mail to be seen,
> or the newsgroup name in square brackets in the subject line. Sorry.- Скриване на цитирания текст -
>
> - Показване на цитирания текст -
I meant to write "guarantee", not "quarantee". Sorry for this typo.
> So, for the time being we could restrict the use of "unnullable" to
> qualify pointers of static storage duration and pointers which are
> pointed to. This means that a pointer whose storage duration is not
> known at the point of their declaration (e.g. member pointers) may not
> be unnullable (unless it specifies a pointer which is pointed to:
> struct s
> {
> unnullable**p; // This is OK
> };
And here I meant "unnullable some_type** p; // This is OK". Sorry
again.
> --End example).
>
> However, even with these restrictions one can still have dynamically
> allocated unnullable pointers (of which I cannot see any benefit):
>
> unnullable some_type** malloc_some_type(unnullable some_type*
> existing_ptr)
> {
> some_type** new_ptr = malloc(sizeof(unnullable some_type*));
> // If malloc has succeeded, new_ptr is not of type unnullable
> some_type**, since
> // thepointerthat new_ptr points to may be NULL
Pointers of automatic storage duration can also be declared
unnullable.
> This means that apointerwhose storage duration is not
> known at the point of their declaration (e.g. member pointers) may not
> be unnullable (unless it specifies apointerwhich is pointed to:
> struct s
> {
> unnullable**p; // This is OK};
>
> --End example).
>
> However, even with these restrictions one can still have dynamically
> allocated unnullable pointers (of which I cannot see any benefit):
>
> unnullable some_type** malloc_some_type(unnullable some_type*
> existing_ptr)
> {
> some_type** new_ptr = malloc(sizeof(unnullable some_type*));
> // If malloc has succeeded, new_ptr is not of type unnullable
> some_type**, since
> // thepointerthat new_ptr points to may beNULL
>
> if (new_ptr !=NULL)
> {
> *new_ptr = existing_ptr;
> }
>
> return (unnullable some_type**)new_ptr;
>
> }
So, there are no more questions about "unnullable"? If so, how can I
propose the addition of unnullable to the C standard?
I still claim that if there is no way to convert a nullable pointer
to an unnullable pointer (which includes a check for NULL and the
possibility of failure), you've missed about 99% of the problem
that unnullable pointers are supposed to solve (dealing with dynamic
memory allocation). So far, I've seen mostly proposals to prohibit
trying to do such a conversion.
Also, if you expect "unnullable" to do any good, it needs documented
behavior should a NULL manage to slip in where it's not wanted.
Declaring it to be "undefined behavior" just removed any benefit
it might have had.
>
> So, there are no more questions about "unnullable"? If so, how can I
> propose the addition of unnullable to the C standard?
By writing a paper making the proposal in detail including if possible
detailed wording changes to the C Standard. However you would almost
certainly need to either attend WG14 meetings or find someone who does
who would promote your proposal.
Personally, I think you would be wasting your time, but it is yours to
waste if you want to and others might disagree with me.
--
Note that robinton.demon.co.uk addresses are no longer valid.
How about this way (posted by me on this thread on June, 1st):
void do_something()
{
char* p = get_pointer_from_somewhere();
if (p == NULL)
{
// There's nothing to do with a null pointer here, so just
return
return;
}
unnullable char* r = (unnullable char*)p;
// Go ahead and work with r
}
> So far, I've seen mostly proposals to prohibit
> trying to do such a conversion.
>
Why prohibiting such a conversion?!
> Also, if you expect "unnullable" to do any good, it needs documented
> behavior should a NULL manage to slip in where it's not wanted.
Why do you expect that a NULL may slip in an unnullable pointer?! How do you
image this happening?
Ok, describe (preferably in standardese) under what circumstances
the compiler *MUST* determine that p is not NULL at the declaration
of r. If that one is too obvious, I can construct a maze of if and
switch statements that guarantee that p is not NULL when it arrives
at the declaration of r, but the compiler cannot prove that.
Also, the compiler needs to throw an error if the line "if (p ==
NULL)" gets changed to "if (q == NULL)" where q is declared elsewhere
but r is still initialized from p.
>> So far, I've seen mostly proposals to prohibit
>> trying to do such a conversion.
>>
>
>Why prohibiting such a conversion?!
Obviously you want to prohibit:
unnullable char *r = NULL;
Some people apparently thought forcing a check was too messy, so
they want to prohibit converting from anything that *might* be null.
Some proposals wanted to only permit unnullable pointers to be
initialized from things that CANNOT be null (such as taking the
address of a variable). That, IMHO, limits the use of it so much
that it's worthless.
You want to be able to take pointers from real-world functions that
can fail (e.g. malloc() and fopen()), *CHECK* them (no wimping out that
letting a null sneak in is "undefined behavior"), and then assign it to
an unnullable pointer. Much of the problem of dereferencing NULL pointers
comes from the failure to include such checks.
>> Also, if you expect "unnullable" to do any good, it needs documented
>> behavior should a NULL manage to slip in where it's not wanted.
>
>Why do you expect that a NULL may slip in an unnullable pointer?! How do you
>image this happening?
If you initialize an unnullable from the return value of a function
that can return NULL, and forget the NULL check, and don't mandate
that the compiler perform an actual check (not calling it "undefined
behavior"), a NULL could sneak in. So you want to make sure that at
the time of the conversion, there IS such a check. Preferably enforced
by syntax, so it won't even compile until you put in the check.
No existing code has unnullable pointers, so if you're modifying
the code to use them, it's easy to miss something. That's a large
part of the reason for wanting them: existing code tends to forget
the checks. Also, there's the myth that "we've got plenty of memory,
malloc() will never fail".
I suggest "unnullable" mainly for convenience and to avoid re-doing the same
check multiple times (i.e. to avoid redundancy and increase efficiency).
What other problems do you think I'm trying to solve by suggesting to have
unnullable pointers in C?
>>
>>How about this way (posted by me on this thread on June, 1st):
>>
>>void do_something()
>>{
>> char* p = get_pointer_from_somewhere();
>>
>> if (p == NULL)
>> {
>> // There's nothing to do with a null pointer here, so just
>>return
>> return;
>> }
>>
>> unnullable char* r = (unnullable char*)p;
>> // Go ahead and work with r
>>}
>
> Ok, describe (preferably in standardese) under what circumstances
> the compiler *MUST* determine that p is not NULL at the declaration
> of r.
The compiler does not need to determine that p is not NULL; it is the
programmer's responsibility to do this. Please note that casting away
nullability is similar to casting away constness:
int const c = 0;
int const* pc = &c;
// The compiler does not need to determine anything here:
int* p = (int*)pc;
int* p = NULL;
// Nor does it need to determine anything here:
unnullable int* up = (unnullable int*)p;
I admit that given this the compiler could hardly make any use of the
"unnullable" keyword -- this keyword is for programmer's convenience mainly.
Note also, that this is also the case with the const keyword -- the
programmer can modify a const object, though they should not do it.
Similarly, the programmer should not assign a null pointer to an unnullable
one, though they can do this.
What is the return type of the function? Nullable or an unnullable pointer?
If it is nullable, then it is the programmer's responsibility to check if
the returned pointer is null. If it is unnullable, then the compiler will
reject to compile the assignment since the implicit cast from a nullable to
an unnullable pointer would be prohibited. So, this leaves a single choice:
assign the return value to a nullable pointer, check if it is non-null and,
if so, cast it to an unnullable pointer.
> and forget the NULL check, and don't mandate
> that the compiler perform an actual check (not calling it "undefined
> behavior"), a NULL could sneak in. So you want to make sure that at
> the time of the conversion, there IS such a check. Preferably enforced
> by syntax, so it won't even compile until you put in the check.
>
> No existing code has unnullable pointers, so if you're modifying
> the code to use them, it's easy to miss something. That's a large
> part of the reason for wanting them: existing code tends to forget
> the checks. Also, there's the myth that "we've got plenty of memory,
> malloc() will never fail".
It is the responsibility of programmers to write code that works and this
includes writing checks wherever necessary.
In that case, instead of unnullable, try /* unnullable */. It's
just as useless and doesn't affect programs that currently use
unnullable as a variable name.
>What other problems do you think I'm trying to solve by suggesting to have
>unnullable pointers in C?
To actually *ENFORCE* checks where practical (and this would include
pointer assignment, and argument passing and return). Compilers
do this now with balanced parentheses and braces. I'm not expecting
the compiler to check for out-of-bounds memset()s that happen to hit
pointers.
>>>
>>>How about this way (posted by me on this thread on June, 1st):
>>>
>>>void do_something()
>>>{
>>> char* p = get_pointer_from_somewhere();
>>>
>>> if (p == NULL)
>>> {
>>> // There's nothing to do with a null pointer here, so just
>>>return
>>> return;
>>> }
>>>
>>> unnullable char* r = (unnullable char*)p;
>>> // Go ahead and work with r
>>>}
>>
>> Ok, describe (preferably in standardese) under what circumstances
>> the compiler *MUST* determine that p is not NULL at the declaration
>> of r.
>
>The compiler does not need to determine that p is not NULL; it is the
>programmer's responsibility to do this.
For unnullable to be useful, it should be the compiler's responsibility
to check this, just like type-checking function arguments and
checking for balanced parentheses.
>Please note that casting away
>nullability is similar to casting away constness:
>
>int const c = 0;
>int const* pc = &c;
>// The compiler does not need to determine anything here:
>int* p = (int*)pc;
>
>int* p = NULL;
>// Nor does it need to determine anything here:
>unnullable int* up = (unnullable int*)p;
>
>I admit that given this the compiler could hardly make any use of the
>"unnullable" keyword -- this keyword is for programmer's convenience mainly.
Using unnullable for the programmer's convenience only is using it
as a comment, so it should BE a comment.
>Note also, that this is also the case with the const keyword -- the
>programmer can modify a const object, though they should not do it.
>Similarly, the programmer should not assign a null pointer to an unnullable
>one, though they can do this.
Although it might be necessary to allow a cast to bypass a check,
it should probably require a 3200-point flashing red font with siren
in the source code to do it.
Currently the compiler checks for all sorts of things:
Balanced parentheses.
Balanced braces.
Function argument type checking.
Expression type checking.
Not using undeclared variables.
and if you're going to include unnullable pointers, the compiler
should have enough checks in there to prevent NULL from getting
assigned to unnullable pointers by accidentally forgetting a check.
If you cast T const* to T*, pass the result to a function that takes T*, and
that function changes the pointed-to value, do you get undefined behaviour?
If your answer is "no", then please tell us what should happen, possibly
citing the standard.
If your answer is "yes", then why do you bother so much about NULL sneaking
into an unnullable pointer and causing undefined behaviour, too (which
should very, very, really very rarely happen as the explicit cast from a
nullable pointer to an unnullable one would remind you that you have to
check befoure you cast)?
*p=2; // this gives undefined behavior
> int* p = NULL;
> // Nor does it need to determine anything here:
> unnullable int* up = (unnullable int*)p;
so this should too?
So using unnullable you could define a function like malloc but returning
"unnullable void *" and code it so that it would never return null (eg by
having it call exit(1) when malloc would return null) and then the compiler
could keep track of this fact through the use of the unnullable attribute?
> I admit that given this the compiler could hardly make any use of the
> "unnullable" keyword -- this keyword is for programmer's convenience mainly.
> Note also, that this is also the case with the const keyword -- the
> programmer can modify a const object,
C is not turbo-pascal. (TP used const to provide initialised structs)
The programmer can only attempt to modify a const object.
On some architectures consts go into ROM, and so, writes have no effect,
on others they go into memory that's flagged such that write
attempts abort the program, on others the write will succeed, but the
compiler may have put other consts with the same value into the same
memory location which could give unexpected results, The compiler/linker
could even have found an op-code that looks like the value of the const
in the code of the program and mapped the const to that address....
This is why the standard says writing to a const gives undefined
behavior. absolutely anything could happen. on the second example even
writing it with the value it already holds would trigger the trap
> though they should not do it.
> Similarly, the programmer should not assign a null pointer to an unnullable
> one, though they can do this.
yeah, and I see that failing to check the return from malloc for
null can have similar results to the 'writing to a const' scenario
above.
The main difference I see between const and the proposed unnullible is that
using const has an implementation defined effect on compiler output, whereas
your proposed unnullable pointers are exactly the same as ordinary pointers
once the code is compiled.
Bye.
Jasen
That depends upon whether or not the object pointed at was defined as
const. The definition of the pointer is irrelevant, only the definition
of the pointed-at object matters.
const int i=42;
void zeroint(int *pi)
{
*pi = 0;
}
void const_zeroint(const int *cpi)
{
zeroint((int*)cpi);
}
int main(void)
{
int i=42;
const int ci=6;
const_zeroint(&i); // defined behavior
const_zeroint(&ci); // undefined behavior
return 0;
By the way, this means that there would be binary compatibility between new
code (written with unnullable) and old code (written without unnullable),
i.e. a new program (one that uses unnullable pointers) can use an old
library (that has been compiled without unnullable). The other way around is
also possible (old source code re-compiled with an old compiler and linked
to a library that uses unnullable and has been compiled with a newer
compiler), provided that the library interface does not contain unnullable
pointers.
Regards,
Angel Tsankov
> That depends upon whether or not the object pointed at was defined as
> const. The definition of the pointer is irrelevant, only the
> definition of the pointed-at object matters.
[...]
What I meant is passing a pointer to const object to a function that expects
a pointer to non-const one and changing the object via the passed-in
pointer. Taking into account this clarification, I would take your answer as
"yes". This leaves one of my previous questions unanswered:
> If your answer is "yes", then why do you bother so much about NULL
> sneaking into an unnullable pointer and causing undefined behaviour, too
> (which should very, very, really very rarely happen as the explicit cast
> from a nullable pointer to an unnullable one would remind you that you
> have to check befoure you cast)?
I left that one unanswered, because the "you" it refers to appears to
have been Gordon Burditt (it was harder to figure that out than it
should have been,because you didn't use any attribution lines in that
message).
I think that if you had wanted to import the C++ concept of references,
that would have had some advantages, possibly enough to make it a good
idea. A C++ reference can be implemented in a way that makes it seem
just like syntactic sugar for a const pointer. However, a consequence of
that syntactic sugar, combined the special rules that apply to
references, is that code which might otherwise appear capable to making
that pointer null is either a syntax error or a constraint violation.
The underlying pointer (or equivalent) could be invalid, if the object
it originally referred to having ended it's lifetime, but it can't be null.
However, the rules you've chosen for your unnullable pointers are too
lax, and make it possible for it to be nulled. I find that concept much
less useful.
What few advantages your concept of unnullable pointers have, are
advantages that are shared, and improved upon, by C++ references.
>> that would have had some advantages, possibly enough to
>> make it a good idea.
>>
> Do you suggest that C++ references are not a good idea?!
No, exactly the opposite. I think that they can be useful syntactic
suger, though they would be less useful as an addition to C than they
are as part of C++.
>> A C++ reference can be implemented in a way that
>> makes it seem just like syntactic sugar for a const pointer.
>>
> I guess you meant 'unnullable pointer' rather that 'const pointer'.
No, I meant what I said.
>> [...] The underlying pointer (or equivalent) could be invalid, if the
>> object
>> it originally referred to having ended it's lifetime, but it can't be
>> null.
> This is possible with C++ references, as well, so if your point is to prefer
> C++ references to unnullable poitners, then this argument does not count.
I was describing C++ references, so of course what I describe is
possible with C++ references.
>> However, the rules you've chosen for your unnullable pointers are too
>> lax, and make it possible for it to be nulled.
> I do not see any problem with the possibility to make an unnullable pointer
> null. If you do, please let me know what it is. After all, if we can modify
> a const object, then why not make an unnullable pointer null, as well?
I'd say that the only thing that could make your concept useful would be
the assurance that it cannot be null. My point is that without such a
guarantee, I don't see that it has any great usefulness.
Well, if it has to point to something that is not null, it could point to a
Singleton object ("Design Patterns" book) of the correct type. That way, you
have a place to tuck away all your dangling, non-null pointers, or point to
at the end of linked-lists. Null With Benefits. ;)
<whimsy>Perhaps an array of Singletons! That sounds nicer! Each non-null can
rest in their bunks when they are not out doing their duty Iterating!
Pointer races during lag time, of course... Bragging: "I parsed the Kessel
Portfolio in 6.7 gigabits!" [Lucas shout-out!]</whimsy>
--
Mabden
-- --
What are those fumes ...? ... "Arrrggghhhh...." --
> I'd say that the only thing that could make your concept useful would
> be the assurance that it cannot be null. My point is that without
> such a guarantee, I don't see that it has any great usefulness.
>
Here are the reasons why I want unnullable pointers in C and in C++:
Sometimes I need a "reference" type whose instances are copy assignable and
(for the sake of security and performance) do not have any null value. One
application for such objects is to make them members of struct's which, too,
need to be copy assignable. And since C++ references cannot be used for this
purpose, I thought pointers would be a good alternative; besides, people
know pointers well and are used to them (at least, I hope this is so).
However, in order to use unnullable pointers with "old" code we must be able
to cast nullable pointers to unnullable ones and hence the "weak"
unnullability guarantee, i.e. the possibility to assign a null pointer to an
unnullable one. (Currently, I do not see any other reason for unnullable
pointers not to provide a stronger unnullability guarantee.)
Finally, don't you think that, if C and C++ target high performance, the
addition of unnullable pointers to these languages would provide programmers
with more mechanisms to produce effective code?
Regards,
Angel Tsankov
One use of them could be for functions which modify a pointed-to value and
always expect a non-NULL pointer, but a C++ reference does the job. The
only other significant place to use one would be in a structure member,
but what's the gain over a normal pointer there? I'm not finding any
compelling examples. Just some 10-20 line code that speaks on its own.
One can come up with all sorts of attributes that could be added to object
types, but there's a diminishing return on catching bugs, and an increase
in language complexity.
One of the primary supposed advantages of your non-null pointers was
> -allows restrictive programming techniques to be apllied at a lower
> cost (e.g. if a pointer passed to a function may not be null, there
> would be no need for a null-check before dereferencing it);
However, since you've defined your unnullable pointers in such a way
that they can, in fact, be null, that advantage disappears. You allow
for them to be null as a result of being uninitialized, as result of
an explicit conversion, and as the undefined behavior that results
from passing a null pointer value as an argument to a function where
the corresponding parameter has an unnullable pointer type. As a
result, any of my code that used non-null pointers for function
arguments would still have to check them for null, so they give me no
advantage over ordinary pointers.
This is the key advantage that C++ references have over your
unnullable pointers; code which might otherwise result in the
corresponding problems in C++, constitutes either a syntax error or a
constraint violation, allowing it to be caught at compile time, rather
than run time. I would have a use for C++ references, because they do
save me the need for null checks. However, that fact depends in part
on the fact that structs containing members which are references have
to have those members initialized by the constructor. I'd have a lot
more use for reseatable references than for your unnullable pointers.
You've also suggested that this would allow optimization of standard
library functions, because they would no longer have to check for null
values, either. However, this is no change from the current situation,
because there are only a few standard library functions that are
required to check for null pointers, and in every case the arguments
they are checking are ones for which a null value is legal, so they
are not candidates for "non-null" pointers. Implementations that
currently perform null checks even though they are not required to do
so, would probably continue to do so, for precisely the same reason
that my code would contain checks for non-null pointers with null
values.
I tend to agree. C++ references are well-understood, and fit well
with C. Passing a struct by ref results in briefer references to
the parameter (a.b instead of a->b) and makes it clear that the
structure must be present and that NULL is not an option.
John Nagle
void f(int* p)
{
int* unnullable p2 = &*p;
}
but the dereference operator and the address-of operator (applied one after
the other in this order) have no effect even if the operand pointer is null.
Regards,
Angel Tsankov