I've been told that this is not valid C because, in the case that there
are no more fields (commas), strchr() returns NULL; and NULL + 1 is not
valid.
Comments, anyone?
David Newall Phone: +61 8 343 3160
Unix Systems Programmer Fax: +61 8 349 6939
Academic Computing Service E-mail: cc...@levels.sait.oz.au
SA Institute of Technology Post: The Levels, South Australia, 5095
NULL, in this case, is just a pointer that has the value 0. NULL + 1 is a valid
operations, however *(NULL+1) is not.
I wouldn't code the loop as you have displayed because one has to
spend time thinking about what the will is trying to do. A "better" method
would be something like:
do
...;
s = strchr(s,',');
while ( s++ != NULL );
You were told right. You're not allowed to perform pointer arithmetic
involving null pointers.
No!
In my experience, NULL is always defined using the preprocessor line
"#define NULL 0" (or 0L). Since the while construct is relying on the
fact NULL is, in fact, 0, doing NULL + 1 - 1 is ok. I certainly wouldn't
recommend using it as a reference to memory. But, unless NULL is a
reserved word to your compiler, the compiler sees 0 + 1 - 1 and that is
ok.
Bill Rust (w...@ftp.com)
That's not always true, but anyway it's irrelevant...
-Since the while construct is relying on the fact NULL is, in fact, 0,
-doing NULL + 1 - 1 is ok.
The code example was adding 1 to the return value from strchr().
strchr() does not return a preprocessor macro; it returns a null macro
(when it doesn't return a pointer to a valid char object). You are not
allowed to add 1 to a null pointer. If you happen to get away with it,
you're just lucky; it's not correct code.
In any event, if you rely on NULL being defined (for example in <stdio.h>)
as the source character string "0", then you're asking for trouble, since
it can be defined as any valid form of null pointer constant, including
for example "((void*)0)". Indeed, it's rather expected that standard-
conforming implementations are more likely to choose the latter form.
Your program may suddenly stop working when a new release of the compiler
is installed, or when you port it to another environment.
That was supposed to say:
strchr() does not return a preprocessor macro; it returns a null pointer ...
Our stupid news system software wouldn't let me cancel the article so
that I could send a corrected version. I hope that this slip-up didn't
cause anybody too much confusion.
strchr(/*...*/)+1 is WRONG when strchr() returns a null pointer.
NULL may correctly (by the pANS) be defined as `(void *)0'.
>Since the while construct is relying on the fact NULL is, in fact, 0,
>doing NULL + 1 - 1 is ok.
It is *if* two conditions hold:
0. NULL is `#define'd as an integral constant zero
rather than (void *)0, and
1. the loop actually reads `while (NULL + 1 - 1)'.
The latter did not hold in the original example, which was
do ... while ((s = index(s, ',') + 1) - 1);
The result of
<expression yeilding non nil character pointer> + 1
is a pointer to the character `beyond the one returned', so that
s = index("foo, bar", ',') + 1
winds up making s point to the space in "foo, bar"; but the
result of
<expression yeilding nil character pointer> + 1
is not defined.% On many machines it `just happens' to give the
address of byte number 1 in the machine; loading this into a machine
pointer register (e.g., for assignment to s) may cause a runtime trap.
In any case, its being undefined gives the system license to do
arbitrarily annoying things at this point. The `-1' after this
is thus irrelevant: like Humpty Dumpty, once a pointer is broken,
not all the King's horses nor all the King's persons%% can put it
back together again.
-----
% So *that* is how you get a butterfly! :-)
%% non-sexist noun :-) [too bad about `King']
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain: ch...@mimsy.umd.edu Path: uunet!mimsy!chris
Bad assumption. In most decent implementations, NULL is indeed
defined as either 0 or 0L. But this can't be, and isn't, true in all
implementations, which immediately prohibts use of it, if for nothing else
than portability reasons. In many current implmentations, NULL is often
defined as ((char *)0) since it is the only "safe" thing to do--since many
programmers are not safe.
As defined by K&R2, NULL is an expression "with value 0, or such
cast to type void *." (A6.6, p.198) This allows implementations to define NULL
as (void *)0, which would cause your NULL +1 -1 to fail.
In spite of all that, why the hell would you want to use something
designed to designate a _nil pointer_ as an integer expression?! All of the
above is moot; NULL should not be used as an integer in an integer expression!
If using a symbolic is that important to you, do the ASCII thing:
#define NUL (0)
Or perhaps something a little more readable:
#define ZERO (0)
(Don't you feel sorry for those you don't know what a "0" means when they see
it inside code? I know I sure do.)
--
D. Chadwick Gibbons, ch...@lakesys.lakesys.com, ...!uunet!marque!lakesys!chad
> do
> ...
> while ((s = strchr(s, ',') + 1) - 1);
>I've been told that this is not valid C because, in the case that there
>are no more fields (commas), strchr() returns NULL; and NULL + 1 is not
>valid.
Why should NULL + 1 not be valid ??? NULL is a pointer with the value 0
and you can add the integer 1 to it ( but you cannot access *s in the case
strchr is NULL of course ).
Wolfgang.
I have to disagree with Bill here. The NULL being
returned was from a string manipulation function...ie
not just a NULL but a (char *) NULL....I believe it is
illegal (or at least unportable) to add (char *)NULL + 1.
--
===========================================================
Name: Bob Earl Phone: (202) 872-6018 (wk)
UUCP: ...!uunet!wash08!rae98
BITNET: ...rae98@CAS (At least, that is what I'm told)
Right.
>But this can't be, and isn't, true in all implementations,
No and yes: it could be, but it is not.
>... In many current implmentations, NULL is often defined as ((char *)0)
>since it is the only "safe" thing to do [meaning `the only way the vendor
>can keep the authors of bad code happy'].
This is both unsafe and wrong, even if it does keep such authors happy.
Consider: If we write
char *cp;
int *ip;
ip = cp;
the compiler must issue some kind of diagnostic (it says so in the
proposed ANSI C specification, and it says in K&R-1 that this operation
is machine-dependent, and all quality compilers do indeed generate a
warning). This situation does not change if we write
ip = (char *)ip;
It does change if we write instead
ip = (int *)(char *)ip;
which puts the value in ip through two transformations (from
pointer-to-int to pointer-to-char, then from pointer-to-char to
pointer-to-int), and these two together are required to reproduce
the original value (this is something of a special case).
So: consider what happens if some implementer has wrongly put the line
#define NULL ((char *)0)
in <stdio.h> and <stdarg.h> and so forth, and we write
ip = NULL;
The compiler sees
ip = ((char *)0);
which, as far as the type system is concerned, is identical to
ip = cp;
---that is, it is machine dependent, and requires a warning. We can
(probably) eliminate the warning% by adding a cast:
ip = (int *)NULL;
which expands to
ip = (int *)((char *)0);
On *most* machines, this `just happens' to work. But if we look very
closely at the language definition, we find that it is not *required*
to work. The version of this that is required to work is instead
ip = (int *)(char *)(int *)0;
We are not allowed (outside of machine-dependent code) to change a
pointer-to-char into a pointer-to-int unless the pointer-to-char itself
came into existence as the result of a cast from a pointer-to-int. The
only way to *create* a nil-pointer-to-int in the first place is to
write (int *)0, or (in the proposed ANSI C) (int *)(void *)0.
Of course, the actual definition of NULL in <stdio.h> and <stdarg.h>
and so on is provided per machine, so if
int *ip = (int *)(char *)0;
`just happens' to work on that machine, the vendor could get away
with it. But
int *ip = NULL;
is guaranteed to work *without* generating warnings on any machine
where NULL is correctly defined, and one should not have to write
int *ip = (int *)NULL;
just to avoid getting warnings---nor should the compiler be silent
about code like
int *ip; char *cp; ip = cp;
The rest of <9...@lakesys.UUCP> is correct.
-----
% The (probably) in eliminating warnings refers to the fact that
a compiler can warn about anything it pleases:
% cc -o foo foo.c
cc: Warning: relative humidity and barometer pressure
indicate that thunderstorms are likely
cc: Warning: your shoelace is untied
cc: Warning: this code looks ugly
cc: Warning: your mother wears army boots
cc: Warning: Hey! Keep away from me with that axe!
cc: Warning: Ack! No, wait, I di(*&1to01llk
In article <8...@ruso.UUCP> wolf...@ruso.UUCP (Wolfgang Deifel) writes:
>Why should NULL + 1 not be valid ??? NULL is a pointer with the value 0
>and you can add the integer 1 to it ....
NULL is not a pointer with the value 0, and 1 is not being added to
NULL here, but rather to a nil-pointer-to-char in the case in question.
NULL is a preprocessor macro; it expands to either an integral constant
zero (whose type is one of the integral types, e.g., int or short or long,
and whose value is zero) or to such a value cast to pointer-to-void
(whose type is pointer-to-void and whose value is unknowable).
A nil-pointer-to-char has type pointer-to-char and an ineffable value.
There is no way to talk about its value other than to say `it is a
nil pointer to char'. In particular, you cannot say what happens
when you add one to it.
The pANS does guarantee that, for example,
0 == (void *)(int *)(char *)0
[3.2.2.3: "Two null pointers, converted through possibly different sequences
of casts to pointer types, shall compare equal."]
Therefore, I interpret the pANS as requiring
(int *)(char *)0
to have the same value as
(int *)0
(the nil-pointer-to-int, in your terminology)--not `just happening' to work.
Dave Prosser ...not an official X3J11 answer...
I've seen this one (actually I think it was "expression too complex")
> cc: Warning: your mother wears army boots
You must be using our compiler..
was not portable.
In article <14...@cbnewsl.ATT.COM> d...@cbnewsl.ATT.COM (david.f.prosser) writes:
>The pANS does guarantee that, for example,
> 0 == (void *)(int *)(char *)0
>[3.2.2.3: "Two null pointers, converted through possibly different sequences
>of casts to pointer types, shall compare equal."]
>
>Therefore, I interpret the pANS as requiring
> (int *)(char *)0
>to have the same value as
> (int *)0
>(the nil-pointer-to-int, in your terminology)--not `just happening' to work.
It does seem to say this. I missed this clause earlier. I find it
somewhat astonishing, actually. It makes carrying types around with
each nil pointer value rather difficult. (It would be nice for, e.g.,
a debugger to be able to say
location arr[300] contains (struct foo *)0
but the only way to do this is to have a different value for every kind
of nil, and this clause would then constrain pointer comparisons and/or
casts to `look past' the difference. Being able to tell what kind of
object---nil pointer or otherwise---resides in some memory location
would be of some use in tracking down stray-pointer bugs. Then again,
to do this right requires type tags everywhere, not just for nil pointers.)
I got this one the other day:
cc: Warning: this is a riot; posting to comp.lang.c
--Blair
"Y'see, I was trying to
add two NULL pointers
together, and..."
[ No replies or followups please :-) ]
--
Bjorn Engsig, ORACLE Europe \ / "Hofstadter's Law: It always takes
Path: mcvax!orcenl!bengsig X longer than you expect, even if you
Domain: ben...@oracle.nl / \ take into account Hofstadter's Law"
> while ((s = strchr(s, ',') + 1) - 1);
>I've been told that this is not valid C because, in the case that there
>are no more fields (commas), strchr() returns NULL; and NULL + 1 is not
>valid.
I think it's a difference if you write " NULL + 1 " ( which is non-
portable C, NULL is a machine dependent macro ) and " strchr(...) + 1 ".
strchr() is a function that returns always a legal value. If strchr fails
it will return (char*)0 ( regardless of the machine or the compiler ),
and here it's legal to add '1' ( the result is (char*)1 ).
----------------------------------------------------------------------------
Wolfgang Deifel
Dr. Ruff Software GmbH, 5100 Aachen, Juelicherstr. 65-67, W-Germany
uucp: ...!uunet{!mcvax}!unido!rwthinf!ruso!wolfgang - phone : +49 241 156038
Well, _I_ can. :-)
--Blair
"But not in this dump."
What is this, a time-shift phenomenon? We keep getting a sprinkling
of articles making this incorrect claim. It is NOT legal to perform
arithmetic on a null pointer.
There is a difference. However:
>strchr() is a function that returns always a legal value. If strchr fails
>it will return (char*)0 ( regardless of the machine or the compiler ),
>and here it's legal to add '1' ( the result is (char*)1 ).
this is what we just got finished saying is false: it is NOT legal to
add 1 to (char *)0; indeed, it is not legal to add 1 to any of the
various infinite varieties of nil pointer.
I suppose this is machine-dependent because of alignment: char-pointers can
point to just about anywhere, but int-pointers on many machines have to be
aligned properly. My question is: can I make sure in my program, that
though generally non-portable this IS portable? I tried this once in the
following way:
The char-pointer gets its value from malloc, which the manual says gives
pointers properly aligned for any type. I never change that char-pointer
other than by adding multiples of sizeof(int) to it.
Is a "ip = cp" guaranteed safe under these conditions, so can I ignore
the compiler-warning?
Thanks, everyone, for your opinions. I'll remember the rule in future:
(Offsets to NULL are non-portable, and should never be used).
In article <5...@targon.UUCP> ru...@targon.UUCP (Ruud Harmsen) writes:
>I suppose this is machine-dependent because of alignment: char-pointers can
>point to just about anywhere, but int-pointers on many machines have to be
>aligned properly.
This is at least one reason, if not the only reason. I know of no other
reason, and the (proposed) standard makes various constraints that may
eventually prove to eliminate other objections to such pointer conversions.
>My question is: can I make sure in my program, that though generally
>non-portable this IS portable? I tried this once in the following way:
>The char-pointer gets its value from malloc, which the manual says gives
>pointers properly aligned for any type. I never change that char-pointer
>other than by adding multiples of sizeof(int) to it.
>Is a "ip = cp" guaranteed safe under these conditions, so can I ignore
>the compiler-warning?
I believe it is safe, which is not the same as its being safe. (It is
much harder, given some property P, to prove based on the proposed
standard that, for all x, P(x) holds. In general, it is easier to come
up with some y for which P(y) does not hold. Here I cannot think of
any such y. Coming up with y's [or maybe they are y-nought's :-) ]
is good exercise for those who propose adding features to the standard.)
Almost. Strictly speaking, malloc must return a pointer to an object that
can be accessed by a type commensurate with its size in bytes. For example,
``malloc(1)'' need not return a pointer that is appropriately aligned for a
pointer-to-int.
Moreover, it may well be possible to argue that unless the requested size
is a multiple of the size of an int, the returned pointer need not be
aligned appropriately for an int. For example, ``malloc(5)''.
However, the rest of your conditions are sufficient for the guarantee of
correct behavior.
If you use a cast it is.
I hope not. This would break the variable-sized structure trick
(discussed here at length not long ago):
struct string
{
int length;
char text[1]; /* actually text[length] */
};
where we allocate a "string" with something like:
(struct string *)malloc(sizeof(struct string)-1+stringlen)
Henry Spencer says that Dennis Ritchie calls this "unwarranted
chumminess with the compiler," but it's widely used.
(Note that (struct string *)malloc(sizeof(struct string)) _would_
work, since sizeof(struct string) will typically be 2*sizeof(int).)
Steve Summit
s...@adam.pika.mit.edu
No. It is not safe. If you ever want to run your program on a Data General
MV, among others, you should use "ip = (int *) cp".
Since pointers to anything except char are word aligned on MV machines,
they decided that they could drop the last bit of the address and shift it.
A char pointer pointing to the second byte of memory is represented with
0x2. A word pointer to the same location is represented by 0x1.
This gave problems when porting programs. Most programmers write
"newp = (type *) malloc (sizeof type)"
but many forget the cast to char with free:
"free(oldp)" instead of "free((char *) oldp)"
This works fine in most cases, but not on machines that shift pointers
when casting.
--cd
Casper H.S. Dik VCP/HIP: +31205922022
University of Amsterdam | cas...@fwi.uva.nl
The Netherlands | casper%fwi.u...@hp4nl.nluug.nl
You're right, of course. As a matter of fact, I did use the cast in my
program. Sorry I didn't mention that in the original article.
Ruud Harmsen