The canonical clc malloc idiom

K

Keith Thompson

The comp.lang.c-recommended way to invoke malloc() is, of course:

some_type *ptr;
ptr = malloc(count * sizeof *ptr);

But what if (in C99 only) ptr is a pointer to a VLA (variable-length
array) type? Consider this:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int n = 10;
typedef char VLA[n];
VLA *ptr = NULL;
ptr = malloc(3 * sizeof *ptr);
printf("sizeof *ptr = %d\n", (int)sizeof *ptr);
return 0;
}

As far as I can tell, this is legal in C99. The operand of sizeof in
the malloc call is *ptr, which is of a variable-length array type,
which means, according to C99 6.5.3.4p2, that it's evaluated. Since
ptr is a null pointer at that point, evaluating *ptr invokes undefined
behavior.

I'm not suggesting that we should drop the clc-approved method for the
normal case; I suspect that malloc calls involving VLA types are going
to be vanishingly rare. It's just an interesting quirk of the
language.

Incidentally, the above program compiles and executes without error
under gcc in its C99ish mode. That doesn't prove anything, since it's
one possible consequence of UB.

I think I'll post in comp.std.c asking just what C99 6.5.3.4p2 really
means and why the operand is evaluated.
 
D

Duncan Muirhead

The comp.lang.c-recommended way to invoke malloc() is, of course:

some_type *ptr;
ptr = malloc(count * sizeof *ptr);

But what if (in C99 only) ptr is a pointer to a VLA (variable-length
array) type? Consider this:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int n = 10;
typedef char VLA[n];
VLA *ptr = NULL;
ptr = malloc(3 * sizeof *ptr);
printf("sizeof *ptr = %d\n", (int)sizeof *ptr);
return 0;
}

As far as I can tell, this is legal in C99. The operand of sizeof in
the malloc call is *ptr, which is of a variable-length array type,
which means, according to C99 6.5.3.4p2, that it's evaluated. Since
ptr is a null pointer at that point, evaluating *ptr invokes undefined
behavior.

I'm not suggesting that we should drop the clc-approved method for the
normal case; I suspect that malloc calls involving VLA types are going
to be vanishingly rare. It's just an interesting quirk of the
language.

Incidentally, the above program compiles and executes without error
under gcc in its C99ish mode. That doesn't prove anything, since it's
one possible consequence of UB.

I think I'll post in comp.std.c asking just what C99 6.5.3.4p2 really
means and why the operand is evaluated.

And why the restriction to variable length array types?
Again with c99ish gcc this compiles and appears to run correctly:
#include <stdlib.h>
#include <stdio.h>
int main( int argc, char** argv)
{
struct odd
{ int b;
int a[argc];
};
struct odd* p;
printf( "sizeof odd %d\n", sizeof *p);
return EXIT_SUCCESS;
}
I'd have thought that if "evaluation" was required for your ptr, it would
be required for my p too.
 
A

Army1987

The comp.lang.c-recommended way to invoke malloc() is, of course:

some_type *ptr;
ptr = malloc(count * sizeof *ptr);

But what if (in C99 only) ptr is a pointer to a VLA (variable-length
array) type? Consider this:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int n = 10;
typedef char VLA[n];
VLA *ptr = NULL;
ptr = malloc(3 * sizeof *ptr);
printf("sizeof *ptr = %d\n", (int)sizeof *ptr);
return 0;
}

As far as I can tell, this is legal in C99. The operand of sizeof in
the malloc call is *ptr, which is of a variable-length array type,
which means, according to C99 6.5.3.4p2, that it's evaluated. Since
ptr is a null pointer at that point, evaluating *ptr invokes undefined
behavior.

I'm not suggesting that we should drop the clc-approved method for the
normal case; I suspect that malloc calls involving VLA types are going
to be vanishingly rare. It's just an interesting quirk of the
language.

Incidentally, the above program compiles and executes without error
under gcc in its C99ish mode. That doesn't prove anything, since it's
one possible consequence of UB.
But it is a strong clue that it doesn't actually read from *ptr,
since that would be likely to cause a SIGSEGV. I tried declaring
ptr as volatile VLA *, but it still doesn't crash. Whereas the UB
allows it not to actually read from there, the compiler would need
to do a serious amount of language lawyering to behave like that.
A more likely explanation is in http://gcc.gnu.org/c99status.html,
where the support for VLAs is marked as "Broken".

In the C99 rationale I can read:
Side effects in variable length array size expressions are guaranteed to be produced, except in
one context. If a size expression is part of the operand of a sizeof operator, and the result of
that sizeof operator does not depend on the value of the size expression, then it is unspecified
10 whether side effects are produced. In the following example:
{
int n = 5;
int m = 7;
size_t sz = sizeof(int (*)[n++]);
15 }
the value of the result of the sizeof operator is the same as in:
{
int n = 5;
int m = 7;
20 size_t sz = sizeof(int (*)[m++]);
}
Since the value stored in sz does not depend on the size expression, the side effect in n++ is
not guaranteed to occur. Requiring the side effect introduced a burden on some
implementations. Since side effects in this context seemed to have limited utility and are not
25 perceived to be a desired coding style, the Committee decided to make it unspecified whether
these size expressions are actually evaluated.

IOW, "don't do that".
 
K

Keith Thompson

Duncan Muirhead said:
I think I'll post in comp.std.c asking just what C99 6.5.3.4p2 really
means and why the operand is evaluated.

And why the restriction to variable length array types?
Again with c99ish gcc this compiles and appears to run correctly:
#include <stdlib.h>
#include <stdio.h>
int main( int argc, char** argv)
{
struct odd
{ int b;
int a[argc];
};
struct odd* p;
printf( "sizeof odd %d\n", sizeof *p);
return EXIT_SUCCESS;
}
I'd have thought that if "evaluation" was required for your ptr, it would
be required for my p too.

Yes, that looks like another flaw. I'll post to comp.std.c again.
 
?

=?iso-2022-kr?q?Harald_van_D=0E=29=26=0Fk?=

Duncan Muirhead said:
I think I'll post in comp.std.c asking just what C99 6.5.3.4p2 really
means and why the operand is evaluated.

And why the restriction to variable length array types? Again with
c99ish gcc this compiles and appears to run correctly: #include
<stdlib.h>
#include <stdio.h>
int main( int argc, char** argv)
{
struct odd
{ int b;
int a[argc];
};
struct odd* p;
printf( "sizeof odd %d\n", sizeof *p); return EXIT_SUCCESS;
}
I'd have thought that if "evaluation" was required for your ptr, it
would be required for my p too.

Yes, that looks like another flaw. I'll post to comp.std.c again.

You're not allowed to have VLA structure members, so there doesn't have
to be any way for sizeof to deal with VLA structure members. If an
implementation supports them as an extension, the implementation is also
allowed to evaluate them as an operand to sizeof, because it won't affect
any correct program.
 
K

Keith Thompson

Army1987 said:
In the C99 rationale I can read:
Side effects in variable length array size expressions are
guaranteed to be produced, except in one context. If a size
expression is part of the operand of a sizeof operator, and the
result of that sizeof operator does not depend on the value of
the size expression, then it is unspecified 10 whether side
effects are produced. In the following example:
{
int n = 5;
int m = 7;
size_t sz = sizeof(int (*)[n++]); 15 }
the value of the result of the sizeof operator is the same as in:
{
int n = 5;
int m = 7; 20 size_t sz = sizeof(int (*)[m++]);
}
Since the value stored in sz does not depend on the size
expression, the side effect in n++ is not guaranteed to
occur. Requiring the side effect introduced a burden on some
implementations. Since side effects in this context seemed to have
limited utility and are not 25 perceived to be a desired coding
style, the Committee decided to make it unspecified whether these
size expressions are actually evaluated.

IOW, "don't do that".

I think this is an inconsistency between the Rationale and the
Standard. According to the standard, it's *not*
implementation-defined whether the side effect occurs. C99 6.5.3.4p2:

If the type of the operand is a variable length array type, the
operand is evaluated; otherwise, the operand is not evaluated and
the result is an integer constant.

In the case of

sizeof(int (*)[m++])

the operand is a pointer type, not a VLA type, so it's not evaluated
(assuming that evaluating a type makes sense).

I don't see anything in the standard that makes evaluation of a sizeof
operand implementation-defined; either it's evaluated or it isn't.
 
K

Keith Thompson

Duncan Muirhead said:
And why the restriction to variable length array types?
Again with c99ish gcc this compiles and appears to run correctly:
#include <stdlib.h>
#include <stdio.h>
int main( int argc, char** argv)
{
struct odd
{ int b;
int a[argc];
};
struct odd* p;
printf( "sizeof odd %d\n", sizeof *p);
return EXIT_SUCCESS;
}
I'd have thought that if "evaluation" was required for your ptr, it would
be required for my p too.

Your program contains a constraint violation that gcc is failing to
diagnose.

C99 6.7.2.1p8:

A member of a structure or union may have any object type other
than a variably modified type.

(I was almost finished with my comp.std.c post before I noticed this.)
 
F

Flash Gordon

Keith Thompson wrote, On 20/09/07 20:12:
Army1987 said:
In the C99 rationale I can read:
Side effects in variable length array size expressions are
guaranteed to be produced, except in one context. If a size
expression is part of the operand of a sizeof operator, and the
result of that sizeof operator does not depend on the value of
the size expression, then it is unspecified 10 whether side
effects are produced. In the following example:
{
int n = 5;
int m = 7;
size_t sz = sizeof(int (*)[n++]); 15 }
the value of the result of the sizeof operator is the same as in:
{
int n = 5;
int m = 7; 20 size_t sz = sizeof(int (*)[m++]);
}
Since the value stored in sz does not depend on the size
expression, the side effect in n++ is not guaranteed to
occur. Requiring the side effect introduced a burden on some
implementations. Since side effects in this context seemed to have
limited utility and are not 25 perceived to be a desired coding
style, the Committee decided to make it unspecified whether these
size expressions are actually evaluated.

IOW, "don't do that".

I think this is an inconsistency between the Rationale and the
Standard. According to the standard, it's *not*
implementation-defined whether the side effect occurs. C99 6.5.3.4p2:

If the type of the operand is a variable length array type, the
operand is evaluated; otherwise, the operand is not evaluated and
the result is an integer constant.

In the case of

sizeof(int (*)[m++])

the operand is a pointer type, not a VLA type, so it's not evaluated
(assuming that evaluating a type makes sense).

I don't see anything in the standard that makes evaluation of a sizeof
operand implementation-defined; either it's evaluated or it isn't.

In N1124 and N1256 you have in 6.7.5.2 para 5:
If the size is an expression that is not an integer constant
expression: if it occurs in a declaration at function prototype
scope, it is treated as if it were replaced by *; otherwise,
each time it is evaluated it shall have a value greater than zero.
The size of each instance of a variable length array type does not
change during its lifetime. Where a size expression is part of the
operand of a sizeof operator and changing the value of the size
expression would not affect the result of the operator, it is
unspecified whether or not the size expression is evaluated.

which I believe is inconsistent with the paragraph you quoted. So I
think it is a defect in the standard.

Since my comment is about the standard itself I've cross-posted to
comp.std.c
 
J

Jack Klein

The comp.lang.c-recommended way to invoke malloc() is, of course:

some_type *ptr;
ptr = malloc(count * sizeof *ptr);

But what if (in C99 only) ptr is a pointer to a VLA (variable-length
array) type? Consider this:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int n = 10;
typedef char VLA[n];
VLA *ptr = NULL;
ptr = malloc(3 * sizeof *ptr);
printf("sizeof *ptr = %d\n", (int)sizeof *ptr);
return 0;
}

As far as I can tell, this is legal in C99. The operand of sizeof in
the malloc call is *ptr, which is of a variable-length array type,
which means, according to C99 6.5.3.4p2, that it's evaluated. Since
ptr is a null pointer at that point, evaluating *ptr invokes undefined
behavior.

I'm not suggesting that we should drop the clc-approved method for the
normal case; I suspect that malloc calls involving VLA types are going
to be vanishingly rare. It's just an interesting quirk of the
language.

Incidentally, the above program compiles and executes without error
under gcc in its C99ish mode. That doesn't prove anything, since it's
one possible consequence of UB.

I think I'll post in comp.std.c asking just what C99 6.5.3.4p2 really
means and why the operand is evaluated.

And why the restriction to variable length array types?
Again with c99ish gcc this compiles and appears to run correctly:
#include <stdlib.h>
#include <stdio.h>
int main( int argc, char** argv)
{
struct odd
{ int b;
int a[argc];
};

It is not legal for a structure or union to contain a VLA:

6.7.5 Declarators P3 "A full declarator is a declarator that is not
part of another declarator. The end of a full declarator is a sequence
point. If the nested sequence of declarators in a full declarator
contains a variable length array type, the type specified by the full
declarator is said to be variably modified."

6.7.5.2 Array declarators P2 "Only an ordinary identifier (as defined
in 6.2.3) with both block scope or function prototype scope and no
linkage shall have a variably modified type. If an identifier is
declared to be an object with static storage duration, it shall not
have a variable length array type."

6.2.3 Name spaces of identifiers P1 "If more than one declaration of
a particular identifier is visible at any point in a translation unit,
the syntactic context disambiguates uses that refer to different
entities.
Thus, there are separate name spaces for various categories of
identifiers, as follows:

— label names (disambiguated by the syntax of the label declaration
and use);

— the tags of structures, unions, and enumerations (disambiguated by
following any of the keywords struct, union, or enum);

— the members of structures or unions; each structure or union has a
separate name space for its members (disambiguated by the type of the
expression used to access the member via the . or -> operator);

— all other identifiers, called ordinary identifiers (declared in
ordinary declarators or as enumeration constants)."

So a structure member, is specifically not allowed to be a VLA,
because it is not an ordinary identifier.

Also there is 6.7.2.1 Structure and union specifiers P8 "A member of
a structure or union may have any object type other than a variably
102) modified type. In addition, a member may be declared to consist
of a specified number of bits (including a sign bit, if any). Such a
member is called a bit-field; its width is preceded by a colon."

I left the reference to footnote 102 in because that adds "A structure
or union can not contain a member with a variably modified type
because member names are not ordinary identifiers as de?ned in 6.2.3."
struct odd* p;
printf( "sizeof odd %d\n", sizeof *p);
return EXIT_SUCCESS;
}
I'd have thought that if "evaluation" was required for your ptr, it would
be required for my p too.

Unfortunately, you can't draw any conclusions from your code because
defining a structure with a VLA member produces undefined behavior.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top