Review of my answer

  • Thread starter Vijay Kumar R Zanvar
  • Start date
V

Vijay Kumar R Zanvar

Hi clc.

Following is my reply to a question posted in a newsgroup,
in which, a person said my advice was wrong without
saying where and why. I turn to clc.

[BEGINS]
Hi friends,
I cam across these questions and seem baffled by these
Can anyone tell me answers to this?

This code may have bugs!. If any suggest remedy
to this else give output!

#1.c
#include<stdio.h>

You should include said:
main()
{
void *pointer;
void *vector;
void *address;
void *location;

The prototype of malloc is:

void *malloc ( size_t );

where,
size_t is an unsigned integer typedefe in said:
pointer=malloc(-1);

Read: http://www.geocities.com/vijoeyz/faq/c/unsigned2.txt
to know about how a negative integer is assigned to an unsigned
int.
vector=malloc(0);

Each compiler is free to define the behaviour of malloc () when
the size is 0. Usually, the compiler documents how it does.
Two possibilies are:

When the requested size is zero
* return a NULL pointer.
* Assume some non-zero size

In either case, using the return value malloc() can cause undefined
behaviour.
address=malloc(1);

This is OK.
location=NULL;

free(pointer);
free(vector);
free(address);

If the corresponding mallocs were successful, then this is OK.
free(location);

This is also legal, but no action occurs for free ( NULL ).
[ENDS]
 
R

Richard Bos

Vijay Kumar R Zanvar said:
Following is my reply to a question posted in a newsgroup,
in which, a person said my advice was wrong without
saying where and why. I turn to clc.


Each compiler is free to define the behaviour of malloc () when
the size is 0. Usually, the compiler documents how it does.
Two possibilies are:

When the requested size is zero
* return a NULL pointer.

This is correct.
* Assume some non-zero size

This is not; or rather, it is misleading. malloc(0) is allowed to return
(in C89) just any unique pointer; in C99, it is allowed to behave as if
it were some non-zero size _except that the pointer must not be
dereferenced_. That restriction is not present when you _just_ assume
some non-zero size.
In either case, using the return value malloc() can cause undefined
behaviour.

Well, no. Not quite. Using it _as if it pointed to some usable object_
causes undefined behaviour.
In C89, if you get a non-null result, I'm not sure whether, e.g.,
comparing it for equality with any other same-type or null pointer
invokes UB; I can't determine whether it is supposed to be a _valid_
unique pointer, and if it isn't, even the comparison is UB. If it is,
the comparison must work.
In C99, if you get a non-null result, that result must be a valid
pointer, and can be treated as any pointer _except_ that it cannot be
dereferenced. For example, you can compare it for equality, you can pass
it to a function (which may then not deref it, but...), you can assign
it to a pointer variable. This is also true if you get a null pointer,
under either Standard.
This is OK.

Albeit useless, of course, but I presume that was not the point of the
code.
If the corresponding mallocs were successful, then this is OK.

Even if not, this is OK. An unsuccessful malloc() returns a null
pointer, and free(0) is legal (and a no-op) under both Standards.

Richard
 
V

Vijay Kumar R Zanvar

Richard Bos said:
This is correct.


This is not; or rather, it is misleading. malloc(0) is allowed to return
(in C89) just any unique pointer; in C99, it is allowed to behave as if
it were some non-zero size _except that the pointer must not be
dereferenced_. That restriction is not present when you _just_ assume
some non-zero size.


Well, no. Not quite. Using it _as if it pointed to some usable object_
causes undefined behaviour.
In C89, if you get a non-null result, I'm not sure whether, e.g.,
comparing it for equality with any other same-type or null pointer
invokes UB; I can't determine whether it is supposed to be a _valid_
unique pointer, and if it isn't, even the comparison is UB. If it is,
the comparison must work.
In C99, if you get a non-null result, that result must be a valid
pointer, and can be treated as any pointer _except_ that it cannot be
dereferenced. For example, you can compare it for equality, you can pass
it to a function (which may then not deref it, but...), you can assign
it to a pointer variable. This is also true if you get a null pointer,
under either Standard.


Albeit useless, of course, but I presume that was not the point of the
code.


Even if not, this is OK. An unsuccessful malloc() returns a null
pointer, and free(0) is legal (and a no-op) under both Standards.

Richard

Thank you very much. This clears my doubt.

Vijay
 
C

Christopher Benson-Manica

Vijay Kumar R Zanvar <[email protected]> spoke thus:

You should include <stdlib.h> for malloc.
Correct.

The return type of main (int) must be specified in C99, and it's a
good idea to explicitly enumerate its parameters (either void or int,
char**) anyway.
The prototype of malloc is:
void *malloc ( size_t );
where,
size_t is an unsigned integer typedefe in <stdlib.h>.

I believe this is correct.
When the requested size is zero
* return a NULL pointer.
* Assume some non-zero size

Part of 7.20.3 (I believe C89 and C99 say essentially the same thing
here) reads thus:

"If the size of the space requested is zero, the behavior is
implementation-defined: either a null pointer is returned, or the
behavior is as if the size were some nonzero value, except that the returned
pointer shall not be used to access an object."
If the corresponding mallocs were successful, then this is OK.

They are OK regardless; if the mallocs failed these pointers are NULL,
and as you note free( NULL ); is entirely legal. This assumes, of
course, that the portion of 7.20.3 I quoted above does not apply here.
 
J

Jack Klein

Hi clc.

Following is my reply to a question posted in a newsgroup,
in which, a person said my advice was wrong without
saying where and why. I turn to clc.

[BEGINS]
Hi friends,
I cam across these questions and seem baffled by these
Can anyone tell me answers to this?

This code may have bugs!. If any suggest remedy
to this else give output!

#1.c
#include<stdio.h>

You should include said:
main()
{
void *pointer;
void *vector;
void *address;
void *location;

The prototype of malloc is:

void *malloc ( size_t );

where,
size_t is an unsigned integer typedefe in said:
pointer=malloc(-1);

Read: http://www.geocities.com/vijoeyz/faq/c/unsigned2.txt
to know about how a negative integer is assigned to an unsigned
int.
vector=malloc(0);

Each compiler is free to define the behaviour of malloc () when
the size is 0. Usually, the compiler documents how it does.

Both versions of the C standard state that the result of calling
malloc() with a parameter of 0 is implementation-defined, which is a
term with a very specific definition in the standard.

A compiler is "free" to select among the behaviors specifically
allowed by the standard, not any behavior. And it must document its
choice.
Two possibilies are:

In fact, the only two possibilities...
When the requested size is zero
* return a NULL pointer.
* Assume some non-zero size

Richard seems to think there is something vague about the "unique
pointer" phrase used by c89/90. I think the meaning is rather simple.
It merely means that a non-NULL pointer returned by malloc(0) must be
different every time you make that call in a program. It must also be
different from every pointer returned by a successful non-zero
allocation call, and the address of every non-dynamic object existing
in a program.

In other words, it must not compare equal to any other valid pointer
value in a program.
 
R

Richard Bos

Jack Klein said:
Richard seems to think there is something vague about the "unique
pointer" phrase used by c89/90. I think the meaning is rather simple.
It merely means that a non-NULL pointer returned by malloc(0) must be
different every time you make that call in a program. It must also be
different from every pointer returned by a successful non-zero
allocation call, and the address of every non-dynamic object existing
in a program.

Yes; I do not think _this_ part is vague at all.
In other words, it must not compare equal to any other valid pointer
value in a program.

This, however, I'm not sure about. If the general consensus is that "a
unique pointer" means "a _valid_ unique pointer", fine by me. However,
if you want to be nasty, "a unique pointer" can also be read as "a
unique, possibly not even valid, pointer". I agree that this would be
against the spirit of the Standard; I'm less certain that it goes
against the letter.

Richard
 
A

Alex

Richard Bos said:
Jack Klein said:
Richard seems to think there is something vague about the "unique
pointer" phrase used by c89/90. [snip]
In other words, it must not compare equal to any other valid
pointer value in a program.

This, however, I'm not sure about. If the general consensus is that
"a unique pointer" means "a _valid_ unique pointer", fine by me.
However, if you want to be nasty, "a unique pointer" can also be
read as "a unique, possibly not even valid, pointer". I agree that
this would be against the spirit of the Standard; I'm less certain
that it goes against the letter.

How would you define "valid" in this context? Seems to me that it doesn't
matter; it would be UB to dereference the pointer, so what difference does
it make?
 
R

Richard Bos

Alex said:
How would you define "valid" in this context? Seems to me that it doesn't
matter; it would be UB to dereference the pointer, so what difference does
it make?

An invalid pointer value could cause a segfault even when it's being
compared for equality to another pointer. This is not allowed for a
valid pointer, no matter whether that pointer points at a trap value or
otherwise illegal object or not.
For example,

int i;
int *valid_p=&i;
int *invalid_p;
int *null_p=NULL;

if (valid_p==null_p) /* This is a legal comparison, even though you
; cannot dereference null_p. */

if (valid_p==invalid_p) /* This is an illegal comparison; invalid_p
; has not been initialised, and the pointer
itself may be a trap value -> UB. */

Richard
 
S

Stephen Sprunk

Christopher Benson-Manica said:
Part of 7.20.3 (I believe C89 and C99 say essentially the same thing
here) reads thus:

"If the size of the space requested is zero, the behavior is
implementation-defined: either a null pointer is returned, or the
behavior is as if the size were some nonzero value, except that the returned
pointer shall not be used to access an object."



They are OK regardless; if the mallocs failed these pointers are NULL,
and as you note free( NULL ); is entirely legal. This assumes, of
course, that the portion of 7.20.3 I quoted above does not apply here.

Why would 7.20.3 matter? Either the pointers are NULL, in which case free()
is a no-op, or the pointer is some value which could have resulted from a
non-zero-sized malloc, in which case the value should be a legal argument to
free().

I'm assuming though that "shall not be used to access an object" is
equivalent to "shall not be dereferenced", which free() wouldn't be doing.

S
 
S

Stephen Sprunk

Richard Bos said:
This, however, I'm not sure about. If the general consensus is that "a
unique pointer" means "a _valid_ unique pointer", fine by me. However,
if you want to be nasty, "a unique pointer" can also be read as "a
unique, possibly not even valid, pointer". I agree that this would be
against the spirit of the Standard; I'm less certain that it goes
against the letter.

I don't see how one can read 7.20.3 as allowing malloc(0) to return an
invalid pointer, even if unique. If the implementation chooses for
malloc(0) to behave "as if the size were some nonzero value", then the
returned pointer MUST be valid or NULL because those are the only possible
returns if malloc() were called with a nonzero size.

It also seems easier for an implementation to return unique pointers if
they're valid, since it can just fall through to the code for allocating a
minimum-sized object.

S
 
S

Stephen Sprunk

Richard Bos said:
An invalid pointer value could cause a segfault even when it's being
compared for equality to another pointer. This is not allowed for a
valid pointer, no matter whether that pointer points at a trap value or
otherwise illegal object or not.

Where does it say that operations on an invalid pointer (other than
dereferencing) invoke undefined behavior? And on what systems is that
likely/possible such that the standard allows for it?

S
 
R

Régis Troadec

"Stephen Sprunk" <[email protected]> a écrit dans le message de

Hi,
Where does it say that operations on an invalid pointer (other than
dereferencing) invoke undefined behavior?

It's said in the standard. It's in the sixth point of 6.5.9 concerning
equality operators, in fact a footnote of the paragraph:

"Two pointers compare equal if and only if both are null pointers, both are
pointers to the same object (including a pointer to an object and a
subobject at its beginning) or function, both are pointers to one past the
last element of the same array object, or one is a pointer to one past the
end of one array object and the other is a pointer to the start of a
different array object that happens to immediately follow the first array
object in the address space [91]"

91 : "Two objects may be adjacent in memory because they are adjacent
elements of a larger array or adjacent members of a structure with no
padding between them, or because the implementation chose to place them so,
even though they are unrelated. If prior invalid pointer operations (such
accesses outside array bounds) produced undefined behavior, subsequent
comparisons also produce undefined behavior."

Regards.

Regis
 
S

Stephen Sprunk

Rgis Troadec said:
Stephen Sprunk said:
Where does it say that operations on an invalid pointer (other than
dereferencing) invoke undefined behavior?

It's said in the standard. It's in the sixth point of 6.5.9 concerning
equality operators, in fact a footnote of the paragraph:

"Two pointers compare equal if and only if both are null pointers, both are
pointers to the same object (including a pointer to an object and a
subobject at its beginning) or function, both are pointers to one past the
last element of the same array object, or one is a pointer to one past the
end of one array object and the other is a pointer to the start of a
different array object that happens to immediately follow the first array
object in the address space [91]"

91 : "Two objects may be adjacent in memory because they are adjacent
elements of a larger array or adjacent members of a structure with no
padding between them, or because the implementation chose to place them so,
even though they are unrelated. If prior invalid pointer operations (such
accesses outside array bounds) produced undefined behavior, subsequent
comparisons also produce undefined behavior."

I don't see anything in there that says the following code produces
undefined behavior:

void *a, *b;
a = malloc(1);
free(a);
b = a;

Of course, a and b are both useless at the end, but why would assigning an
invalid pointer be undefined?

S
 
C

Chris Torek

I don't see anything in there that says the following code produces
undefined behavior:

void *a, *b;
a = malloc(1);
free(a);
b = a;

In my C99 draft, it is in section 7.14.3:

... The value of a pointer that refers to freed space is indeterminate.

combined with 3.18:

3.18 Undefined behavior

[#1] Behavior, upon use of [various cases deleted], or of
indeterminately valued objects, for which this International
Standard imposes no requirements. ...
Of course, a and b are both useless at the end, but why would assigning an
invalid pointer be undefined?

Why are there separate "." and "->" operators, when the type of
the left-hand side would disambiguate? Why are there += and &=
operators, but no &&= operator?

The answer to this kind of question -- "why is it designed the way
it is" -- ultimately boils down to "because Dennis and/or the
ANSI/ISO C committee folks decided". If they were to say that such
pointers did *not* have undefined behavior, implementors would have
to do whatever is required to achieve that. If they said that
integer overflow had to trap at runtime, implementors would have
to do that. If they said that the result of the rand() function
was always 42, implementors would have to do that, too.

The art of language design (and standards-writing) lies in defining
enough to be useful, without defining so much as to cause more
problems than you solve. As you yourself note, "a and b are both
useless at the end" -- so what problems would additional defining
solve? The cost of such defining might be small, but what benefit
would it buy?
 
K

Keith Thompson

Stephen Sprunk said:
I don't see anything in there that says the following code produces
undefined behavior:

void *a, *b;
a = malloc(1);
free(a);
b = a;

Of course, a and b are both useless at the end, but why would assigning an
invalid pointer be undefined?

Quick answer: because the standard says so (chapter and verse have
been cited elsewhere in this thread).

Longer answer: for much the same reason that the following produces
undefined behavior:

void *a, *b;
b = a;

An uninitialized variable has an indeterminate value; referring to
that value invokes undefined behavior. A pointer whose value has been
passed to free() also has an indeterminate value.

Referring to such a value almost certainly indicates a programming
error; there's nothing useful you can do with the value anyway. Part
of the rationale for making such references undefined behavior, I
think, is to allow for implementations that might be able to detect
and diagnose such errors. Since such detection is impractical for
most implementations, it wasn't practical to require it. Calling it
undefined behavior was the only real way to allow detection without
imposing an undue burden on implementors.
 
O

Old Wolf

Stephen Sprunk said:
I don't see anything in there that says the following code produces
undefined behavior:

void *a, *b;
a = malloc(1);
free(a);
b = a;

Of course, a and b are both useless at the end, but why would assigning an
invalid pointer be undefined?

These days, systems have hardware-based memory protection. In PCs this
takes the form that a hardware exception occurs if you try and write
outside your process's address space. But there are also other systems
where the mere act of loading a register designed for memory access
with a value outside the process's address space, causes a hardware
exception. After the free() call, 'a' might now be pointing to a
different process's address space, hence the possibility for UB
when it is loaded to registers for the copy to b.
 
S

Stephen Sprunk

Old Wolf said:
These days, systems have hardware-based memory protection. In PCs this
takes the form that a hardware exception occurs if you try and write
outside your process's address space. But there are also other systems
where the mere act of loading a register designed for memory access
with a value outside the process's address space, causes a hardware
exception. After the free() call, 'a' might now be pointing to a
different process's address space, hence the possibility for UB
when it is loaded to registers for the copy to b.

Ah, I wasn't aware that such systems existed. That explains it.

S
 
G

Guillaume

where the mere act of loading a register designed for memory access
with a value outside the process's address space, causes a hardware
exception. After the free() call, 'a' might now be pointing to a
different process's address space, hence the possibility for UB
when it is loaded to registers for the copy to b.

Oh? I have yet to see this on any existing system. But why not.
 

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
474,141
Messages
2,570,813
Members
47,357
Latest member
sitele8746

Latest Threads

Top