Something wrong in my program

  • Thread starter Dominique =?ISO-8859-1?Q?L=E9ger?=
  • Start date
P

Peter Nilsson

Mark McIntyre said:
... void* converts implicitly to any other pointer type.

That's not true in the case of function pointers. Apart from null pointers,
the conversion in such cases is undefined. [C99 J.5.7 notwithstanding.]
 
P

Peter Nilsson

Randy Howard said:
I agree. VLA's don't really provide anything you can't have otherwise
with a lot less downside risk of demon invoking.

I don't see risk management issue.

Over-allocation via VLA is no different to over-allocation via any other
automatic object.

Many development tools can diagnose the level of stack usage by a program,
and most programmers will specify the appropriate stack level settings
within the final executable.

Something as simple as...

void blah(size_t parameter)
{
if (parameter <= 20000)
{
char buffer[parameter];
/* ... */
}
else
/* fail */
}

....is probably safer and more robust than...

void blah(size_t parameter)
{
char buffer[20000];
/* ... */
}

Even with malloc, you're still faced with the issue of what to do if the
allocation fails. And worse, on some systems, a non-void malloc return need
not indicate successful allocation of resources!

You're screwed either way. You might as well use the cleaner faster syntax
of VLA's than cumbersome malloc/free pointers, if it's convenient to do so!
 
P

pete

Richard said:
It is certainly undefined behaviour
(I wrongly assumed that this would be
obvious from the fact that a loss of information is involved - a
rather drastic thing where a function address is concerned!).

Since some pointers are guaranteed to have
the same representation and some are not,
I assume that there may be other information
besides address information, in some pointers.
But I can't find a constraint that it violates,
but that doesn't mean there isn't one. Do you have a reference?

N869
6.5.16 Assignment operators
6.5.16.1 Simple assignment
Constraints
[#1] One of the following shall hold:

-- the left operand has qualified or unqualified
arithmetic type and the right has arithmetic type;
-- the left operand has a qualified or unqualified version
of a structure or union type compatible with the type
of the right;
-- both operands are pointers to qualified or unqualified
versions of compatible types, and the type pointed to
by the left has all the qualifiers of the type pointed
to by the right;
-- one operand is a pointer to an object or incomplete
type and the other is a pointer to a qualified or
unqualified version of void, and the type pointed to by
the left has all the qualifiers of the type pointed to
by the right; or
-- the left operand is a pointer and the right is a null
pointer constant.
-- the left operand has type _Bool and the right is a
pointer.
 
M

Mark McIntyre

That's not true in the case of function pointers

Correct. However its rare that a newby works with function pointers,
and by the time they do, the meaning of void* should hopefully be well
embedded.
Also the OP talks about types.
 
B

Barry Schwarz

Even with malloc, you're still faced with the issue of what to do if the
allocation fails. And worse, on some systems, a non-void malloc return need
not indicate successful allocation of resources!

Could you expand please. Since malloc always returns a void*, did you
mean non-NULL? Is there a situation where a non-NULL return could
result when the memory requested is not allocated?


<<Remove the del for email>>
 
P

pete

Mark said:
Correct. However its rare that a newby works with function pointers,
and by the time they do, the meaning of void* should hopefully be well
embedded.
Also the OP talks about types.

Types apply to functions as well as to objects.
 
C

CBFalconer

Barry said:
Could you expand please. Since malloc always returns a void*,
did you mean non-NULL? Is there a situation where a non-NULL
return could result when the memory requested is not allocated?

I think he did. In theory a non-NULL return always means the
memory is allocated and usable, however some virtual memory
systems have the annoying habit of allocating virtual memory using
copy on write paging techniques, which means that unexpected
failures can (not will) occur later on use. This is an OS
problem, not a C language problem. Besides which, running into
such an occurence is rare.
 
K

Keith Thompson

Barry Schwarz said:
Could you expand please. Since malloc always returns a void*, did you
mean non-NULL? Is there a situation where a non-NULL return could
result when the memory requested is not allocated?

I think some malloc() implementations may return a pointer to virtual
memory that isn't actually allocated until it's referenced. If there
isn't actually enough memory (virtual or otherwise) to satisfy the
request, the program can die when it tries to access the memory that
it thinks is already allocated.

Such implementations are probaby non-conforming.
 
K

Keith Thompson

CBFalconer said:
I think he did. In theory a non-NULL return always means the
memory is allocated and usable, however some virtual memory
systems have the annoying habit of allocating virtual memory using
copy on write paging techniques, which means that unexpected
failures can (not will) occur later on use. This is an OS
problem, not a C language problem. Besides which, running into
such an occurence is rare.

Strictly speaking, I'd say it's a problem that the C implementation
should deal with. There's nothing wrong with the OS providing a
memory allocator that doesn't actually allocate the memory, but if
it's not compatible with the defined semantics for malloc(), a
conforming C library needs to do whatever is necessary to work around
it. For example, malloc() might call the OS-level allocator, then
read a byte in each block of the supposedly allocated memory; if any
of the reads fail, it can deallocate and return NULL.

The library might also provide a direct interface to the OS routine,
to be used by non-portable code at the user's risk.
 
M

Michael Wojcik

I think some malloc() implementations may return a pointer to virtual
memory that isn't actually allocated until it's referenced.

In my experience lazy allocators and overcommitment of virtual memory
are relatively common OS features. Lazy allocators are useful for
allocating large sparse arrays and the like. They can simplify memory
management, since programs can allocate generous-size buffers rather
than tweaking areas with realloc. That, in turn, can reduce program
overhead (fewer library calls) and heap fragmentation (assuming the
standard library's memory allocation functions are implemented using
a heap, and the heap is more likely to be fragmented by many small
allocations than by fewer larger ones, etc).

A program can portably defeat a lazy allocator simply by touching
the entire allocated area immediately after allocation, with memset.
It's much harder to coax lazy allocation out of a strict allocator...
Such implementations are probaby non-conforming.

I don't offhand see anything in the standard that makes an implementation
which relies on a lazy allocator non-conforming. A non-null return from
malloc indicates that a certain amount of space has been allocated; it
does not guarantee that the program will later successfully be able to
modify that space. Program failure due to overcommitment of virtual
memory strikes me as not essentially different from program failure due
to any other external cause, such as hardware failure or forcible
termination by the OS. The possibility of those failures doesn't
render an implementation non-conforming.

It's arguably a QOI issue, but I for one would vote in favor of the
lazy allocator in many cases.
 
D

Dan Pop

In said:
One thing that raises my eyebrows once in a while when following the
traffic here, is that the "standards thumpers" usually quote
chapter-and-verse from the C99 standard (this seems to be the consensus
on the 'de jure' standard), while code snippets using new C99 features
usually elicit snide comments - C89 is preferred as the 'de facto'
standard.

The reason is fairly obvious: the final version of C89/C90 has never been
available in a *usable* electronic format[*], so quoting from it
effectively requires typing by hand the relevant text. The alternatives
are:

- Quoting from the last public draft of C89. Requires a good
understanding of the differences between this draft and the final
standard and such knowledge is rather on the esoteric side.

- Quoting from the C99 standard, which is available in a (sort of) usable
electronic format. Requires a good understanding of the differences
between C89 and C99, but these differences are partly documented
documented in C99.

Another reason for preferring to quote from C99 is that, for issues that
have not changed between the two versions, the C99 wording is often
improved, in terms of both clarity and details. And yet another (minor)
reason is that the chapter and verse numbering is different between
C89 and C90, even if the normative text is the same.

However, when the two standards differ on the issue at hand, I would
always point out the difference in my posts, to avoid confusing other
people. Quoting from C99 and deliberately ignoring the differences from
C89 would be intellectually dishonest. Ditto for posting code using
C99 features without an explicit warning.


[*] ANSI sold, at one time, a PDF version of C90. Apart from being
very expensive, it actually contained the output of a scanner that
scanned the printed document, page by page. Not only ugly, but not
usable for copy and paste, unless someone took the additional trouble
of passing it through some OCR software.

Dan
 
D

Dan Pop

In said:
But not without possible loss of information when used with function pointer
types. Caution is indicated.

void * doesn't convert (implicitly or ortherwise) to function pointers.
The attempt to do it results in undefined behaviour.

Dan
 
D

Dan Pop

In said:
The other method of conversion, a cast to (void*),
may only operate on scaler types.

But applying it to a pointer to function doesn't violate any constraint.
So, an explicit conversion of pointer to function to pointer to void (or
vice versa) is undefined behaviour (by lack of explicit definition).

Dan
 
J

Joona I Palaste

Arthur J. O'Dwyer said:
I think pete is right. I suggest the use of the term 'object pointer',
as contrasted with 'function pointer' -- a void* MAY safely be converted
to any other OBJECT POINTER type, as far as I'm aware.

What about pointers to pointers to functions? AFAIK a pointer to
anything is an object, and as void* is compatible with any object
pointer type, it's compatible with pointers to pointers to functions.
Am I right?
 
C

Chris Torek

[Background: on some systems, malloc() can return a non-NULL pointer
that eventually fails in some way because of "overcommitment".]

In my experience lazy allocators and overcommitment of virtual memory
are relatively common OS features. [various positive aspects snipped]
I don't offhand see anything in the standard that makes an implementation
which relies on a lazy allocator non-conforming.

Here, by "relies on" I suspect you mean "uses".

We should, I think, note that these systems may (and do) allow
programs to start up with large data arrays, then cause accesses
in those arrays to fail in the same way that the non-NULL malloc()
return value fails.

In other words:

#include <stdio.h>

#define SIZE 32767 /* for now */

char mem[SIZE];
...
int main(void) {
printf("got here\n");
fflush(stdout);
mem[number_less_than_SIZE()] = 42;
printf("done\n");
return 0;
}

can, when run on such a system, fail after printing "got here" and
before "done". Such failures are more *likely* with malloc()
because "real world" programs on such systems tend to allocate much
more space via malloc(), of course -- but the same "lazy allocation"
and overcommittment that make it possible to have enormous three
dimensional arrays (e.g., REAL A(10000,10000,10000) in Fortran --
this array typically needs just under 4 terabytes of RAM, i.e.,
not at all out of line on 64-bit machines) that are only sparsely
used at runtime will cause problems for programs that actually use
every array element (unless, of course, you have 4 terabytes of
swap space -- also hardly out of line these days, since a terabyte
of disk space costs less than $1000).
... Program failure due to overcommitment of virtual
memory strikes me as not essentially different from program failure due
to any other external cause, such as hardware failure or forcible
termination by the OS. The possibility of those failures doesn't
render an implementation non-conforming.

It's arguably a QOI issue, but I for one would vote in favor of the
lazy allocator in many cases.

I would say it is definitely tied to quality-of-implementation, and
that it should work by default under any sort of "strict ANSI mode".
At the same time, as with many compilers, the default might well be
"non-strict non-ANSI mode". :)
 
P

pete

Dan said:
But applying it to a pointer to function doesn't
violate any constraint.
So, an explicit conversion of pointer to function to pointer to void
(or
vice versa) is undefined behaviour (by lack of explicit definition).

I got a little mixed up there.
Thank you.
 
D

Dan Pop

In said:
What about pointers to pointers to functions? AFAIK a pointer to
anything is an object, and as void* is compatible with any object
pointer type, it's compatible with pointers to pointers to functions.
Am I right?

Again, you have a terminology problem. Type compatibility is a concept
defined by the standard in a different way than you're using it here.

Pointers to void can be converted both explicitly and implicitly to and
from pointers to pointers to functions, for the reason you have mentioned
above.

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top