473,473 Members | 2,074 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

multi dimensional arrays as one dimension array

The subject might be misleading.
Regardless, is this code valid:

#include <stdio.h>

void f(double *p, size_t size) { while(size--) printf("%f\n", *p++); }
int main(void) {
double array[2][1] = { { 3.14 }, { 42.6 } };
f((double *)array, sizeof array / sizeof **array);
return 0;
}

Assuming casting double [2][1] to double * is implementation defined
or undefined behavior, replace the cast with (void *).

Since arrays are not allowed to have padding bytes in between of
elements, it seems valid to me.
Aug 29 '08
152 9715
Harald van Dijk <tr*****@gmail.comwrites:
On Tue, 02 Sep 2008 06:26:22 +0000, Richard Heathfield wrote:
>Now show me a string literal that doesn't contain *any* strings.

#if 0
"Hello"
#endif
Note that that string literal is a preprocessing token, not a token;
by the time it would have been converted into a token (in phase 7),
it's been eliminated (in phase 4).

But yes, a string-literal is a preprocessing-token (C99 6.4, Lexical
elements). Nicely done.

But I would argue that the string literal "Hello" doesn't contain any
strings even if it isn't eliminated during preprocessing. The object
specified by the string literal certainly does contain a string, but
that object exists only during execution; the string literal itself
exists in a C source file, where no objects of any kind yet exist.

The anonymous static array object whose existence is specified by a
string literal is not the string literal itself, though we often refer
to it as a string literal as a convenient verbal shorthand.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 2 '08 #101
Barry Schwarz wrote:
On Tue, 02 Sep 2008 11:09:37 -0400, Eric Sosman <Er*********@sun.com>
wrote:
>Barry Schwarz wrote:
>>On Tue, 02 Sep 2008 10:14:45 -0400, Eric Sosman <Er*********@sun.com>
wrote:

Richard Heathfield wrote:
vi******@gmail.com said:
>On Sep 2, 8:57 am, Richard Heathfield <r...@see.sig.invalidwrote:
>>vipps...@gmail.com said:
>>>On Sep 2, 8:23 am, Keith Thompson <ks...@mib.orgwrote:
<snip>
>
>>>>Perhaps, but in C string has a very specific meaning.
>>>And "string literal" has a completely different one.
>>No, it doesn't. It is a specification, that's all. A string literal /is/
>>a string, /and/ it's a literal. Hence, string literal.
>A string literal needs not to be a string, for example "hello\0world"
>is not a string.
Right. It's several strings. (I count twelve.) I should have said "a string
literal contains at least one string".
>
Now show me a string literal that doesn't contain *any* strings.
char hello[5] = "Hello";
hello is not a literal.
Agreed. Neither is char, [, 5, ], =, ;, or the white space.
Everything else in the source line is a string literal.
>> The string literal used to initialize it, if
it does exist in the object module (it need not), will certainly
contain the terminating '\0'.
Chapter and verse?

6.4.5-5 seems to fit. 6.4.5-6 adds confirmation.
>> The code that initializes hello with
the literal will not copy the '\0'.
It certainly cannot "copy the '\0'," just as it cannot copy
a three-kilogram slab of luminiferous ether. As far as I can tell,
the Standard says the same thing about the existence of the former
and the latter, to wit, nothing at all.

So you think the initialization of an automatic array of char does not
involve any code to copy the initial value into the array?
No, but I think the initialization of this automatic array
(if it is in fact automatic) does not involve any code to copy
a '\0' as part of the initial value.

The Standard citations you and Richard Heathfield offer do
appear to make your case: A character string literal always ends
with a '\0', whether it is needed or not, whether it is observable
or not. That seems to me a defect in the Standard, but since it's
survived many editorial inspections I guess I'll just have to accept
it. (Margaret Fuller: "I accept the Universe." Thomas Carlyle:
"Gad! She'd better!")

Here's another string literal whose stringiness is of no
importance:

size_t twelve = 2 * sizeof "Hello";

I can think of no C program that could test whether the six
characters generated by this string literal do or do not actually
exist, and it seems a shame that the Standard promulgates an
untestable requirement. Maybe "as if" saves the day.

--
Er*********@sun.com
Sep 2 '08 #102
Antoninus Twink wrote:
On 1 Sep 2008 at 23:01, Keith Thompson wrote:
>pete <pf*****@mindspring.comwrites:
>>Keith Thompson wrote:
memcpy, for example, is
a function that doesn't have anything directly to do with strings.
Has it occurred to you that "library functions" are called that,
because they are part of the library,
and not because of what they do?
Of course.

So you're saying that memcpy is a string function because it's
declared in <string.h>? The same reasoning implies that size_t is a
string type, and NULL is a string macro.

FFS... it must be, like, 6 months or something since we last went
through this completely absurdly argument that excites such passion in
the breasts of the clc pedants club.
They love to discuss this stuff over and over.
Like void main, or casting the result of malloc.

There we go. Another exciting discussion in perspective

--
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32
Sep 2 '08 #103
jacob navia wrote:
Antoninus Twink wrote:
>[...]

They love to discuss this stuff over and over.
Like void main, or casting the result of malloc.

There we go. Another exciting discussion in perspective
Jacob, please stop feeding the troll.

--
Er*********@sun.com
Sep 2 '08 #104
Keith Thompson wrote:
So you're saying that memcpy is a string function because it's
declared in <string.h>? The same reasoning implies that size_t is a
string type, and NULL is a string macro.
No.
The same reasoning implies that size_t is a standard type
and that NULL is a standard macro.
All conforming C implementations define those in <stddef.h>.

Hosted implementations also define those
in various headers.

--
pete
Sep 2 '08 #105
pete <pf*****@mindspring.comwrites:
Keith Thompson wrote:
>So you're saying that memcpy is a string function because it's
declared in <string.h>? The same reasoning implies that size_t is a
string type, and NULL is a string macro.

No.
The same reasoning implies that size_t is a standard type
and that NULL is a standard macro.
All conforming C implementations define those in <stddef.h>.
Of course, but they're *also* defined in <string.h>.

So if any function declared in <string.his a "string function" (I'm
still not sure that's what you meant), wouldn't it follow that any
macro defined in <string.his a "string macro"?

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 3 '08 #106
Keith Thompson wrote:
pete <pf*****@mindspring.comwrites:
>Keith Thompson wrote:
>>So you're saying that memcpy is a string function because it's
declared in <string.h>? The same reasoning implies that size_t is a
string type, and NULL is a string macro.
No.
The same reasoning implies that size_t is a standard type
and that NULL is a standard macro.
I should have said stddef type and stddef macro.
>All conforming C implementations define those in <stddef.h>.

Of course, but they're *also* defined in <string.h>.
Not every conforming implementation that defines those,
has <string.h>.
So if any function declared in <string.his a "string function" (I'm
still not sure that's what you meant),
It is what I meant.
wouldn't it follow that any
macro defined in <string.his a "string macro"?
No.
They would just be duplicates of stddef macros.
That's the way the standard describes it.
7.17 Common definitions <stddef.h>

[#1] The following types and macros are defined in the
standard header <stddef.h>. Some are also defined in other
headers, as noted in their respective subclauses.
and then for all the other headers, you have:

The types declared are size_t (described in 7.17);
The macros defined are NULL (described in 7.17);

--
pete
Sep 3 '08 #107
pete wrote:
Keith Thompson wrote:
>pete <pf*****@mindspring.comwrites:
>>Keith Thompson wrote:
So you're saying that memcpy is a string function because it's
declared in <string.h>? The same reasoning implies that size_t is a
string type, and NULL is a string macro.
No.
The same reasoning implies that size_t is a standard type
and that NULL is a standard macro.

I should have said stddef type and stddef macro.
>>All conforming C implementations define those in <stddef.h>.

Of course, but they're *also* defined in <string.h>.

Not every conforming implementation that defines those,
has <string.h>.
>So if any function declared in <string.his a "string function" (I'm
still not sure that's what you meant),

It is what I meant.
>wouldn't it follow that any
macro defined in <string.his a "string macro"?

No.
They would just be duplicates of stddef macros.
That's the way the standard describes it.
7.17 Common definitions <stddef.h>

[#1] The following types and macros are defined in the
standard header <stddef.h>. Some are also defined in other
headers, as noted in their respective subclauses.
and then for all the other headers, you have:

The types declared are size_t (described in 7.17);
The macros defined are NULL (described in 7.17);
But the standard doesn't refer to any types or macros
as string types or string macros,
or stddef types or stddef macros,
so I don't think I would either.

--
pete
Sep 3 '08 #108
On Tue, 2 Sep 2008 10:19:35 -0700 (PDT), ja*********@verizon.net
wrote:
>Barry Schwarz wrote:
>On Tue, 02 Sep 2008 11:09:37 -0400, Eric Sosman <Er*********@sun.com>
wrote:
>Barry Schwarz wrote:
On Tue, 02 Sep 2008 10:14:45 -0400, Eric Sosman <Er*********@sun.com>
wrote:
...
>> char hello[5] = "Hello";

hello is not a literal.

Agreed. Neither is char, [, 5, ], =, ;, or the white space.
Everything else in the source line is a string literal.

The string literal used to initialize it, if
it does exist in the object module (it need not), will certainly
contain the terminating '\0'.

Chapter and verse?

6.4.5-5 seems to fit. 6.4.5-6 adds confirmation.

That applies only to code like

char *hello = "Hello";
There is nothing in 6.4.5-5 that mentions what the string literal is
used for. It simply states that the '\0' is appended automatically
every time.
>
When a string literal is used as an initializer for a char array,
6.7.8p14 is the relevant clause, and according to that clause a '\0'
comes into play only if the the array has enough room for it, or if
the array size is unknown. In this case, the array has a known length
It says the '\0' is used as part of the initialization process only if
there is room. It does not say anything about the contents of the
string literal itself so you must fall back to 6.4.5-5.
>of 5, which is not sufficient room for the terminating null; therefore
a terminating null is not even required to exist.
The entire literal is not required to exist. It would be perfectly
acceptable for the initialization code to consist of five independent
move instructions, each moving one character into the array.
>
It certainly cannot "copy the '\0'," just as it cannot copy
a three-kilogram slab of luminiferous ether. As far as I can tell,
the Standard says the same thing about the existence of the former
and the latter, to wit, nothing at all.

So you think the initialization of an automatic array of char does not
involve any code to copy the initial value into the array? How does
recursion work if code is not involved?

He didn't say that code was not involved, he said that a '\0' was not
involved. One obvious possibility is that the initialization is
achieved by copying 5 chars from some location which need not contain
a null character after the fifth char.
True, but then the string literal doesn't exist either which is why my
first response above included the clause "if it exists". 6.4.5-5 does
not allow the string literal to exist without the terminating '\0'.

--
Remove del for email
Sep 3 '08 #109
On Tue, 02 Sep 2008 13:31:54 -0400, Eric Sosman <Er*********@sun.com>
wrote:
>Barry Schwarz wrote:
>On Tue, 02 Sep 2008 11:09:37 -0400, Eric Sosman <Er*********@sun.com>
wrote:
>>Barry Schwarz wrote:
On Tue, 02 Sep 2008 10:14:45 -0400, Eric Sosman <Er*********@sun.com>
wrote:

Richard Heathfield wrote:
>vi******@gmail.com said:
>>On Sep 2, 8:57 am, Richard Heathfield <r...@see.sig.invalidwrote:
>>>vipps...@gmail.com said:
>>>>On Sep 2, 8:23 am, Keith Thompson <ks...@mib.orgwrote:
><snip>
>>
>>>>>Perhaps, but in C string has a very specific meaning.
>>>>And "string literal" has a completely different one.
>>>No, it doesn't. It is a specification, that's all. A string literal /is/
>>>a string, /and/ it's a literal. Hence, string literal.
>>A string literal needs not to be a string, for example "hello\0world"
>>is not a string.
>Right. It's several strings. (I count twelve.) I should have said "a string
>literal contains at least one string".
>>
>Now show me a string literal that doesn't contain *any* strings.
char hello[5] = "Hello";
hello is not a literal.
Agreed. Neither is char, [, 5, ], =, ;, or the white space.
Everything else in the source line is a string literal.

The string literal used to initialize it, if
it does exist in the object module (it need not), will certainly
contain the terminating '\0'.
Chapter and verse?

6.4.5-5 seems to fit. 6.4.5-6 adds confirmation.
>>> The code that initializes hello with
the literal will not copy the '\0'.
It certainly cannot "copy the '\0'," just as it cannot copy
a three-kilogram slab of luminiferous ether. As far as I can tell,
the Standard says the same thing about the existence of the former
and the latter, to wit, nothing at all.

So you think the initialization of an automatic array of char does not
involve any code to copy the initial value into the array?

No, but I think the initialization of this automatic array
(if it is in fact automatic) does not involve any code to copy
a '\0' as part of the initial value.
I agree.
>
The Standard citations you and Richard Heathfield offer do
appear to make your case: A character string literal always ends
with a '\0', whether it is needed or not, whether it is observable
or not. That seems to me a defect in the Standard, but since it's
survived many editorial inspections I guess I'll just have to accept
it. (Margaret Fuller: "I accept the Universe." Thomas Carlyle:
"Gad! She'd better!")

Here's another string literal whose stringiness is of no
importance:

size_t twelve = 2 * sizeof "Hello";
Since sizeof "Hello" is a compile time constant, it would seem to a
QOI issue if the literal even existed in the program. 6.4.5-5 does
not say every string literal in the source results in a string literal
in the object. It only says that if it does it must have the
terminating '\0'.
>
I can think of no C program that could test whether the six
characters generated by this string literal do or do not actually
exist, and it seems a shame that the Standard promulgates an
untestable requirement. Maybe "as if" saves the day.
There seems to be agreement in this group that the optimizer can do
away with unreferenced variables and unreachable code. I see no
reason why the same should not apply to unused constants.

--
Remove del for email
Sep 3 '08 #110
On Tue, 02 Sep 2008 17:19:44 -0700, Keith Thompson <ks***@mib.org>
wrote:
>pete <pf*****@mindspring.comwrites:
>Keith Thompson wrote:
>>So you're saying that memcpy is a string function because it's
declared in <string.h>? The same reasoning implies that size_t is a
string type, and NULL is a string macro.

No.
The same reasoning implies that size_t is a standard type
and that NULL is a standard macro.
All conforming C implementations define those in <stddef.h>.

Of course, but they're *also* defined in <string.h>.

So if any function declared in <string.his a "string function" (I'm
still not sure that's what you meant), wouldn't it follow that any
macro defined in <string.his a "string macro"?
Could one then say that size_t is a string type and a stddef type and
a stdlib type ...?

--
Remove del for email
Sep 3 '08 #111
Barry Schwarz wrote:
On Tue, 02 Sep 2008 17:19:44 -0700, Keith Thompson <ks***@mib.org>
wrote:
>pete <pf*****@mindspring.comwrites:
>>Keith Thompson wrote:
So you're saying that memcpy is a string function because it's
declared in <string.h>? The same reasoning implies that size_t is a
string type, and NULL is a string macro.
No.
The same reasoning implies that size_t is a standard type
and that NULL is a standard macro.
All conforming C implementations define those in <stddef.h>.
Of course, but they're *also* defined in <string.h>.

So if any function declared in <string.his a "string function" (I'm
still not sure that's what you meant), wouldn't it follow that any
macro defined in <string.his a "string macro"?

Could one then say that size_t is a string type and a stddef type and
a stdlib type ...?

I suppose.
I don't think it would be confusing to refer to FILE
as an stdio type.
And I don't think there would be anything wrong
with refering to qsort and bsearch as stdlib functions.

However, "string function" is a term used in the standard
in a context that applies to memcpy;
and more plainly, K&R2 explains that the term
"string function" does apply to memcpy.

--
pete
Sep 3 '08 #112
Eric Sosman <Er*********@sun.comwrote:
jacob navia wrote:
Antoninus Twink wrote:

They love to discuss this stuff over and over.
Like void main, or casting the result of malloc.

There we go. Another exciting discussion in perspective

Jacob, please stop feeding the troll.
How cruel of you to ask him to starve himself.

Richard
Sep 3 '08 #113
Richard Bos wrote:
Eric Sosman <Er*********@sun.comwrote:
>jacob navia wrote:
>>Antoninus Twink wrote:

They love to discuss this stuff over and over.
Like void main, or casting the result of malloc.

There we go. Another exciting discussion in perspective
Jacob, please stop feeding the troll.

How cruel of you to ask him to starve himself.
You mistake me. Jacob is not a troll, not in my opinion at
any rate. He argues in an offensive manner for some positions
I strongly disagree with, but he impresses me as sincere.

The troll he fed, however, is a waste of food and oxygen.

--
Er*********@sun.com
Sep 3 '08 #114
vi******@gmail.com writes:
On Sep 1, 2:49 pm, Tim Rentsch <t...@alumnus.caltech.eduwrote:
vipps...@gmail.com writes:
The subject might be misleading.
Regardless, is this code valid:
#include <stdio.h>
void f(double *p, size_t size) { while(size--) printf("%f\n", *p++); }
int main(void) {
double array[2][1] = { { 3.14 }, { 42.6 } };
f((double *)array, sizeof array / sizeof **array);
return 0;
}
Assuming casting double [2][1] to double * is implementation defined
or undefined behavior, replace the cast with (void *).
Since arrays are not allowed to have padding bytes in between of
elements, it seems valid to me.
Despite what some other people have been saying,
this is valid. If foo is an array, doing (some_type *)foo
gives access to all of foo. Since 'array' is made up
(ultimately) of double's, using '(double*)array' will
work just fine.

Well, now I'm at loss again. I think the only way to settle this is to
provide quotes from the standard that agree (or disagree) with you.
I don't really expect to convince anyone, but for those who are
interested here is a citation plus some reasoning.

As far as I can tell, the standard never defines the term array,
but it does define 'array type', in 6.2.5 p 20:

An array type describes a contiguously allocated nonempty set of
objects with a particular member object type, called the element
type.

(In the text, 'array type' and 'element type' are italicized.)

In the circumstance given above (double array[2][1]), there
certainly is a contiguously allocated nonempty set of objects,
all of type double.

Now, what is accessible. The principle is that taking the
address of an element in an array allows access to other elements
in the array. We see this all the time with ordinary arrays;
here are some other cases:

int m[3][5];
volatile int (*vm)[5] = (volatile int (*)[5]) &m[0];

Access through vm is the same as through m, except that access
through vm treats elements as volatile.

_Complex double z[10];
double (*ri_z)[2] = (double (*)[2]) &z[0];

Access through z gives one-dimensional access to complex values;
access through ri_z gives component access, with ri_z[i][0] being
the real part of z[i], and with ri_z[i][1] being the imaginary
part of z[i].

Pretty obviously, we expect the above cases to work. And there
is nothing special about using 0 in &m[0] or &z[0]; using 1
instead would work fine, if the first index to vm or ri_z were
adjusted accordingly. Casting the address of an array element
preserves access to the entire array; since that is true in
these cases, it should also be true for another cast, namely:

double array[2][1] = { ... };
double *one_d = (double*) array; /* same as &array[0] */

Because what's being converted is the address of an element
of 'array', all of 'array' is accessible through the converted
pointer, just as it would be in the other cases above.

Whether this is all convincing depends crucially on what is
meant in 6.5.6 p 8 by the phrase "points to an element of an
array object". Unfortunately this key phrase is not explained
clearly. What is the most consistent reading of all that the
standard says? My view is expressed above, along with some
of the reasoning behind it.
Sep 4 '08 #115
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
...
The very same argument applies if we don't use xa but just use
a directly; the value '(void*)a' has the same extent as the
array a. And so must '(double*)a', or any other pointer
conversion of a, provided of course that alignment requirements
are satisfied.

That's where your argument breaks down. A double* is governed by rules
about the limits of pointer addition that char* is specifically exempted
from, and which are meaningless for void*. I've described the problem in
more detail in another branch of this discussion, so I won't repeat the
description here.
Presumably you're talking about 6.3.2.3p7, more specifically the
last two sentences:

When a pointer to an object is converted to a pointer to a
character type, the results points to the lowest addressed
byte of the object. Successive increments of the result, up
to the size of the object, yield pointers to the remaining
bytes of the object.

These sentences don't say anything about accessing other elements
of an array; they apply only to the single object pointed at.
(Read literally they don't say anything about indexing or pointer
arithmetic either, only "successive increments", but let's ignore
that.) The access afforded here is only within the object pointed
at, not any surrounding array that the object might be in. In fact,
if we judge just by the statements here, then the code

int x[10];
unsigned char c, *p;
*p = (unsigned char*) &x[2];
c = p[ 2 * sizeof x[2] ];

yields undefined behavior, because the index is bigger than the
size of the object pointed at (namely, x[2]).

So unless you have another reference to cite, the claim that char *
is exempted from limits on pointer addition is unsupported by the
standard.
Sep 4 '08 #116
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
James Kuyper <ja*********@verizon.netwrites:
James Tursa wrote:
...
OK, that's fine for objects, but that doesn't answer my question. What
is it about 2-dimensional (or multi-dimensional) arrays of double that
does not allow them to be stepped through with a double* ?
Ultimately, nothing more or less than the fact that the standard says
that the behavior is undefined. Because the behavior is undefined,
compilers are allowed to generate code that might fail if such stepping
is attempted (though this is rather unlikely). More importantly,
compilers are allowed to generate code that assumes that such stepping
will not be attempted, and therefore fails catastrophically if it
actually is attempted - the most plausible mode of failure is a failure
to check for aliasing.

Specific details:

Given

double array[2][1];
double *p = (double*)array;

If there is code which sets array[1][i] to one value, and p[j] to
another value, the compiler not required to consider the possibility
that p[j] and array[1][i] might point at the same location in memory.
It's allowed to keep either value in a register, or to keep the two
values in different registers. It's not required to make the next
reference to array[1][i] give the same value as the next reference to p[j].

This is because the behavior would be undefined if 'i' and 'j' had
values that might ordinarily cause you the expect array[1][i] and p[j]
to refer to the same location. Note: this convoluted wording is
necessary, because if 'i' and 'j' have such values, then at least one of
the two expressions has undefined behavior, rendering it meaningless to
talk about which location that expression actually refers to.
You're starting with the conclusion, and then "proving" the
conclusion. This conclusion isn't consistent with other
behavior and language in the standard.

I was not trying to prove that the behavior was undefined. I was trying
to explain how it is that the fact that the behavior is undefined can
make it dangerous to rely upon such code.

I've presented my argument that the behavior IS undefined elsewhere,
most recently in the response I just posted to Ben Bacarisse.

...
>... And
ultimately, I would also ask if it is safe/conforming to use memcpy or
the like to copy values from/to such an array wholesale. e.g., is it
Yes, it is, and the reason is that the standard explicitly allows access
to entirely of an object through lvalues of "unsigned char", and the
behavior of memcpy() is defined in terms of operations on "unsigned
char" lvalues. There is no similar exemption for "double*".
Irrelevant, because that's talking about whether a memory access
can have undefined behavior because of an invalid representation.
It's just as illegal to access outside of an array using unsigned
char as it is using double. The only question is, what memory
may be accessed. Since 'array' is what was converted, any memory in
array may be accessed.

There are multiple arrays involved, and the question is - which array is
the one which 6.5.6p8 is referring to? A careful examination of 6.5.6p8
reveals that what is says constitutes utter nonsense unless the element
type of the relevant array is the same as the type pointed at by the
pointer. Example:

int matrix[3][5];
int *pi = matrix[1];

For purposes of integer additions to "pi", if the array referred to by
6.5.6p8 were "matrix" rather than matrix[1], then because pi points at
the second element of matrix, matrix[1], pi+1 would have to point at the
third element, matrix[2]. If "matrix" were the relevant array, then the
largest amount which could be added to matrix[1] would be 2, not 5,
because the length of matrix is only 3, while the length of matrix[1] is 5.

This is a wholly indefensible interpretation of 6.5.6p8. The only array
that it could possibly be referring, when applied to "pi", is matrix[1].
A plausible analysis, but not on point, since the example code
above doesn't cast array[1], it casts array, which allows access
to the whole object.

People have claimed that there is a one-dimensional array of 15 ints
which should be used when applying 6.5.6p8, but I see no such array
declared anywhere in the above code.
This whole argument seems predicated on the assumption that arrays
exist only where there are array declarations. Clearly this
assumption is false:

int *p = malloc( 15 * sizeof *p );

No array declarations, but indexing of p is allowed (assuming of
course that p != NULL).

Or, how about this:

int flat[15];
int (*not_flat)[3] = (int (*)[3])flat;

Since the only array object declared here is flat, by your
reasoning an access like not_flat[2][4] should be legal.

Sep 4 '08 #117
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Mon, 01 Sep 2008 05:24:14 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Sun, 31 Aug 2008 19:16:40 +0000, James Tursa wrote:
[restoring snipped portion}
>int main(void) {
char array[2][1] = { { 'a' }, { 'b' } };
f((char *)array, sizeof array / sizeof **array);
return 0;

}
OK, that's fine for objects, but that doesn't answer my question.
What is it about 2-dimensional (or multi-dimensional) arrays of
double that does not allow them to be stepped through with a double*
?

The fact that double[2][3] doesn't have elements such as x[0][5]. There
must be a valid double, 5*sizeof(double) bytes into x. However, x[0][5]
doesn't mean just that. x[0][5] (or ((double*)x)[5]) means you're
looking 5*sizeof(double) bytes into x[0]. x[0] doesn't have that many
elements.
That doesn't matter since array isn't being accessed as a
two-dimensional array. Converting array (not array[0], but array) gives
a pointer that has access to all the same memory as array.

With the exception of character types, does the standard describe the
conversion of an array to anything other than its initial element?
Strictly speaking, I can't even find where the standard describes the
result of converting double(*)[3] to double* at all, but the only way to
perform that conversion indirectly is by taking the address of the first
element of the first sub-array, and I accept that a direct conversion
should mean the same thing. If you can point out where more permissions
are given, please do so.
Conversion of one pointer type to another is always allowed,
subject to the condition that the pointer in question is
suitably aligned for the new type. The alignment of the
array pointer type double(*)[3] is guaranteed to be sufficient
for conversion to double*, because the array pointer type
points at something that has exactly three doubles in it,
and specifically has one right at the beginning.

Does it help if this conversion is written

f( (double*) &array[0], sizeof array / sizeof **array )

instead?

The principle is, taking the address of an element of
an array preserves access to the entire array that the
element is in. That is, we expect for

T xyz[ BLAH ];

g( & xyz[ k ] );

that g will be able to access all of xyz, even though
what was passed is just a pointer to the k'th element.

That make more sense now?
Sep 4 '08 #118
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
...
converted is array, and it's being converted to double *. Whatever
the type of elements of an array X, doing (some_type*) X treats all
of X as though it has elements of some_type (with the usual caveats
about alignment).

The standard says nothing about that. It says remarkably little about
what (sometype*)X does in general. Most of what it does say is that,
under some circumstances (none of which are relevant to this case),
conversion back to the original type returns a pointer that compares
equal to the original. Of the few cases where it says anything more than
that about what the result of (sometype*)X is, none apply here.
Yes, the standard doesn't explicitly say exactly that, but it's
the most consistent reading based on what is said. There's
plenty of other examples of similar cases, such as:

1. If we have an array x of type T, then (T*)&x == &x[0].

2. If we have a type struct X with a (non-bitfield) member m,
and there is a variable s of type struct X, then
(struct X*) ((char*)&s.m - offsetof(struct X,m)) == &s.

3. If p is a pointer to an array element, then (unsigned char*)p
can be used, as though it pointed into an array of unsigned
char, to access the entire original array.

The standard doesn't say any of these things, yet most people
accept that they are correct, because in each case it's the
most consistent reading based on what the standard does say.

Of course, some people may prefer a strict constructionist view
like the one you express above, but they should be prepared to
reject cases 1, 2, and 3 (among others) as well; you can't have
it both ways. Personally, I think the "most consistent reading"
model is more likely to yield results that agree with how other
people (both on and off the committe) read it.

Sep 4 '08 #119
Tim Rentsch wrote:
....
Presumably you're talking about 6.3.2.3p7, more specifically the
last two sentences:

When a pointer to an object is converted to a pointer to a
character type, the results points to the lowest addressed
byte of the object. Successive increments of the result, up
to the size of the object, yield pointers to the remaining
bytes of the object.

These sentences don't say anything about accessing other elements
of an array; they apply only to the single object pointed at.
When you define

int matrix[3][5];

matrix[1][2] is an object. So is matrix[2], and so it matrix itself. The
standard specifically acknowledges that there can be multiple different
overlapping objects at any given memory location. 6.3.2.3p7 applies to
all three objects, not just the smallest one. If you apply 6.3.2.3p7 to
"matrix" as a whole, it prohibits bounds checking of unsigned char*
pointers that point anywhere inside of "matrix"; only when pointer
addition pushes such a pointer across the boundaries of "matrix" itself
is bounds-checking allowed.
Sep 4 '08 #120
Tim Rentsch wrote:
vi******@gmail.com writes:
>On Sep 1, 2:49 pm, Tim Rentsch <t...@alumnus.caltech.eduwrote:
>>vipps...@gmail.com writes:
The subject might be misleading.
Regardless, is this code valid:
#include <stdio.h>
void f(double *p, size_t size) { while(size--) printf("%f\n", *p++); }
int main(void) {
double array[2][1] = { { 3.14 }, { 42.6 } };
f((double *)array, sizeof array / sizeof **array);
return 0;
}
Assuming casting double [2][1] to double * is implementation defined
or undefined behavior, replace the cast with (void *).
Since arrays are not allowed to have padding bytes in between of
elements, it seems valid to me.
Despite what some other people have been saying,
this is valid. If foo is an array, doing (some_type *)foo
gives access to all of foo. Since 'array' is made up
(ultimately) of double's, using '(double*)array' will
work just fine.
Well, now I'm at loss again. I think the only way to settle this is to
provide quotes from the standard that agree (or disagree) with you.

I don't really expect to convince anyone, but for those who are
interested here is a citation plus some reasoning.

As far as I can tell, the standard never defines the term array,
but it does define 'array type', in 6.2.5 p 20:

An array type describes a contiguously allocated nonempty set of
objects with a particular member object type, called the element
type.

(In the text, 'array type' and 'element type' are italicized.)

In the circumstance given above (double array[2][1]), there
certainly is a contiguously allocated nonempty set of objects,
all of type double.

Now, what is accessible. The principle is that taking the
address of an element in an array allows access to other elements
in the array. We see this all the time with ordinary arrays;
here are some other cases:

int m[3][5];
volatile int (*vm)[5] = (volatile int (*)[5]) &m[0];

Access through vm is the same as through m, except that access
through vm treats elements as volatile.

_Complex double z[10];
double (*ri_z)[2] = (double (*)[2]) &z[0];

Access through z gives one-dimensional access to complex values;
access through ri_z gives component access, with ri_z[i][0] being
the real part of z[i], and with ri_z[i][1] being the imaginary
part of z[i].

Pretty obviously, we expect the above cases to work. And there
is nothing special about using 0 in &m[0] or &z[0]; using 1
instead would work fine, if the first index to vm or ri_z were
adjusted accordingly. Casting the address of an array element
preserves access to the entire array; since that is true in
these cases, it should also be true for another cast, namely:

double array[2][1] = { ... };
double *one_d = (double*) array; /* same as &array[0] */

Because what's being converted is the address of an element
of 'array', all of 'array' is accessible through the converted
pointer, just as it would be in the other cases above.

Whether this is all convincing depends crucially on what is
meant in 6.5.6 p 8 by the phrase "points to an element of an
array object". Unfortunately this key phrase is not explained
clearly. What is the most consistent reading of all that the
standard says? My view is expressed above, along with some
of the reasoning behind it.
When you access an object as an array of character type,
then the object is an array of character type.

N869
6.3.2 Other operands
6.3.2.1 Lvalues and function designators
[#1]
... When an object is said to have a particular
type, the type is specified by the lvalue used to designate
the object.

--
pete
Sep 4 '08 #121
Tim Rentsch wrote:
....
A plausible analysis, but not on point, since the example code
above doesn't cast array[1], it casts array, which allows access
to the whole object.
Citation, please - where does the standard say that such a conversion
allows access to the whole object? Where, in fact, does the standard say
anything at all about what you can do with the converted pointer value,
other than convert it back to it's original type?

My argument depends only upon the type of the pointer, not on the
sequence of conversions that produced that pointer value.
This whole argument seems predicated on the assumption that arrays
exist only where there are array declarations.
Spot on!
... Clearly this
assumption is false:

int *p = malloc( 15 * sizeof *p );

No array declarations, but indexing of p is allowed (assuming of
course that p != NULL).
I believe that this is covered by the standard's promises that "The
pointer returned if the allocation succeeds is suitably aligned so that
it may be assigned to a pointer to any type of object and then used to
access such an object or an array of such objects in the space allocated
(until the space is explicitly deallocated)."

I do not agree that this provides general permission to treat any
arbitrary sequence of 'int's as an array just because they happen to
occupy memory in the same way that they would if they were declared as
an array of int.
Or, how about this:

int flat[15];
int (*not_flat)[3] = (int (*)[3])flat;

Since the only array object declared here is flat, by your
reasoning an access like not_flat[2][4] should be legal.
The result of the conversion you performed there is not specified by the
standard; the only thing that the standard guarantees about that
converted pointer value is that, if you convert it back to the original
type, it will compare equal to the original pointer.
Sep 4 '08 #122
On Thu, 04 Sep 2008 05:18:18 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
>With the exception of character types, does the standard describe the
conversion of an array to anything other than its initial element?

Conversion of one pointer type to another is always allowed, subject to
the condition that the pointer in question is suitably aligned for the
new type.
Correct. And the result is specified as a pointer that you can convert
back to the original type to get something comparing equal to the original
pointer, but nothing more than that. In the special case of a character
type, the standard points out that the result points to the first byte of
the same object, and allows access to all bytes of the same object. If it
was necessary to explicitly specify this for character types, why does it
implicitly apply to other types as well?
Sep 4 '08 #123
pete <pf*****@mindspring.comwrites:
Tim Rentsch wrote:
vi******@gmail.com writes:
On Sep 1, 2:49 pm, Tim Rentsch <t...@alumnus.caltech.eduwrote:
vipps...@gmail.com writes:
The subject might be misleading.
Regardless, is this code valid:
#include <stdio.h>
void f(double *p, size_t size) { while(size--) printf("%f\n", *p++); }
int main(void) {
double array[2][1] = { { 3.14 }, { 42.6 } };
f((double *)array, sizeof array / sizeof **array);
return 0;
}
Assuming casting double [2][1] to double * is implementation defined
or undefined behavior, replace the cast with (void *).
Since arrays are not allowed to have padding bytes in between of
elements, it seems valid to me.
Despite what some other people have been saying,
this is valid. If foo is an array, doing (some_type *)foo
gives access to all of foo. Since 'array' is made up
(ultimately) of double's, using '(double*)array' will
work just fine.
Well, now I'm at loss again. I think the only way to settle this is to
provide quotes from the standard that agree (or disagree) with you.
I don't really expect to convince anyone, but for those who are
interested here is a citation plus some reasoning.

As far as I can tell, the standard never defines the term array,
but it does define 'array type', in 6.2.5 p 20:

An array type describes a contiguously allocated nonempty set of
objects with a particular member object type, called the element
type.

(In the text, 'array type' and 'element type' are italicized.)

In the circumstance given above (double array[2][1]), there
certainly is a contiguously allocated nonempty set of objects,
all of type double.

Now, what is accessible. The principle is that taking the
address of an element in an array allows access to other elements
in the array. We see this all the time with ordinary arrays;
here are some other cases:

int m[3][5];
volatile int (*vm)[5] = (volatile int (*)[5]) &m[0];

Access through vm is the same as through m, except that access
through vm treats elements as volatile.

_Complex double z[10];
double (*ri_z)[2] = (double (*)[2]) &z[0];

Access through z gives one-dimensional access to complex values;
access through ri_z gives component access, with ri_z[i][0] being
the real part of z[i], and with ri_z[i][1] being the imaginary
part of z[i].

Pretty obviously, we expect the above cases to work. And there
is nothing special about using 0 in &m[0] or &z[0]; using 1
instead would work fine, if the first index to vm or ri_z were
adjusted accordingly. Casting the address of an array element
preserves access to the entire array; since that is true in
these cases, it should also be true for another cast, namely:

double array[2][1] = { ... };
double *one_d = (double*) array; /* same as &array[0] */

Because what's being converted is the address of an element
of 'array', all of 'array' is accessible through the converted
pointer, just as it would be in the other cases above.

Whether this is all convincing depends crucially on what is
meant in 6.5.6 p 8 by the phrase "points to an element of an
array object". Unfortunately this key phrase is not explained
clearly. What is the most consistent reading of all that the
standard says? My view is expressed above, along with some
of the reasoning behind it.

When you access an object as an array of character type,
then the object is an array of character type.

N869
6.3.2 Other operands
6.3.2.1 Lvalues and function designators
[#1]
... When an object is said to have a particular
type, the type is specified by the lvalue used to designate
the object.
I'm not sure what point you're making here. It's not possible to
access an object with an expression of array type, because arrays
are converted to pointers (to the type of their elements) before
any access happens. If (e) is an expression of array type, then
arguably the expression &(e) designates an object using an array
type, converting the address of the designated array object into
a pointer, but this doesn't happen very often, especially for
character elements -- code almost always uses (char *) rather
than a pointer-to-array type like (char (*)[]). I don't see how
6.3.2.1p1 applies to (char *) [in terms of what the quoted phrase
means], and I don't see any reason to expect there's any semantic
difference between ((char*)p) and (*(char(*)[])p) [not counting
the distinctions for & and sizeof operators]. So 6.3.2.1p1 seems
orthogonal to what I was saying.

Sep 9 '08 #124
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Thu, 04 Sep 2008 05:18:18 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
With the exception of character types, does the standard describe the
conversion of an array to anything other than its initial element?
Conversion of one pointer type to another is always allowed, subject to
the condition that the pointer in question is suitably aligned for the
new type.

Correct. And the result is specified as a pointer that you can convert
back to the original type to get something comparing equal to the original
pointer, but nothing more than that. In the special case of a character
type, the standard points out that the result points to the first byte of
the same object, and allows access to all bytes of the same object. If it
was necessary to explicitly specify this for character types, why does it
implicitly apply to other types as well?
The two cases aren't the same. In the case of, say, an int
matrix, such as

int m[3][5];

the storage for m is guaranteed to hold contiguous int objects,
with the same representation as an array of int. The "arrayness"
is already there.

Converting to (char*) is different because the object being pointed
at may be just a scalar; even non-array objects can have a
character array imposed on top of them, by converting their
address to (char*) (and similarly (unsigned char*)).

It's because of imposing an array representation onto a non-array
object that character pointers are singled out in defining their
conversion semantics.
Sep 9 '08 #125
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
...
Presumably you're talking about 6.3.2.3p7, more specifically the
last two sentences:

When a pointer to an object is converted to a pointer to a
character type, the results points to the lowest addressed
byte of the object. Successive increments of the result, up
to the size of the object, yield pointers to the remaining
bytes of the object.

These sentences don't say anything about accessing other elements
of an array; they apply only to the single object pointed at.

When you define

int matrix[3][5];

matrix[1][2] is an object. So is matrix[2], and so it matrix itself. The
standard specifically acknowledges that there can be multiple different
overlapping objects at any given memory location. 6.3.2.3p7 applies to
all three objects, not just the smallest one. If you apply 6.3.2.3p7 to
"matrix" as a whole, it prohibits bounds checking of unsigned char*
pointers that point anywhere inside of "matrix"; only when pointer
addition pushes such a pointer across the boundaries of "matrix" itself
is bounds-checking allowed.
It seems to me you're reinforcing my point. If we have

char *p = &matrix[1][2];

then p points to the int object matrix[1][2]. It does not point
to matrix, or matrix[1], or matrix[2]. 6.3.2.3p7 applies only to
the object pointed to, not other objects that happen to surround
that object; the phrasing "successive increments" makes that
clear.

Pointers to character types have exactly the same extent
restrictions that other pointer types have. 6.3.2.3p7 is there
only to explain what happens when character indexing is performed
inside those extent restrictions. I don't see any evidence to
support the claim that character-type pointers are exempt from
the same extent restrictions that other pointer types have.
Sep 9 '08 #126
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
...
A plausible analysis, but not on point, since the example code
above doesn't cast array[1], it casts array, which allows access
to the whole object.

Citation, please - where does the standard say that such a conversion
allows access to the whole object? Where, in fact, does the standard say
anything at all about what you can do with the converted pointer value,
other than convert it back to it's original type?
Which view is more reasonable:

A. Pointer conversion yields a pointer to the same object as
the original (assuming no alignment problems); or

B. Pointer conversion follows a strict constructionist view -
the only thing you can do with a converted pointer is
convert it back to the original type and compare it against
the unconverted original (assuming non-char types, etc)?

Of course, no one really believes (B); if they did, then they
should insist that a code sequence like

int i = 0;
const int *p = &i;
return *p;

produces undefined behavior.

Are you advocating a strict constructionist interpretation along
the lines of B? If not, then what interpretation are you
advocating?

My argument depends only upon the type of the pointer, not on the
sequence of conversions that produced that pointer value.
What I think you mean is that your rule about what's allowed
depends only on the type of the pointer, although whether that's
the type before conversion or after conversion I'm not sure.
Surely your argument depends at least in part on what is said in
the standard. Can you better articulate what your position is?

This whole argument seems predicated on the assumption that arrays
exist only where there are array declarations.

Spot on!
... Clearly this
assumption is false:

int *p = malloc( 15 * sizeof *p );

No array declarations, but indexing of p is allowed (assuming of
course that p != NULL).

I believe that this is covered by the standard's promises that "The
pointer returned if the allocation succeeds is suitably aligned so that
it may be assigned to a pointer to any type of object and then used to
access such an object or an array of such objects in the space allocated
(until the space is explicitly deallocated)."
So you're changing your previous position that arrays exist only
where there are array declarations?

I do not agree that this provides general permission to treat any
arbitrary sequence of 'int's as an array just because they happen to
occupy memory in the same way that they would if they were declared as
an array of int.
You're doing a better job of saying what you think the rules
aren't than saying what you think the rules are.
Or, how about this:

int flat[15];
int (*not_flat)[3] = (int (*)[3])flat;

Since the only array object declared here is flat, by your
reasoning an access like not_flat[2][4] should be legal.

The result of the conversion you performed there is not specified by the
standard; the only thing that the standard guarantees about that
converted pointer value is that, if you convert it back to the original
type, it will compare equal to the original pointer.
What is certainly true is that the standard guarantees AT LEAST
that the value of not_flat can be converted back to int* and
compare equal to flat (with the usual disclaimer about
alignment). The question is, does it guarantee anything else,
and if so what? If you want to adopt a strict constructionist
interpretation, along with the consequences of that, well, to
each his own. I think most people believe that interpretation
is not really sensible as regards pointer conversion.

What I think most people believe, and what the standard says
(somewhat poorly) and also intends, is this:

1. If (e) is an expression of array type, then &(e)[i] is a
pointer that allows access to all elements in the array object
designated by (e); this array object is limited by both the
number of elements in the array type (if it's a complete type)
and the extent of the outermost containing object, and of course
i must be an allowed index for that range.

2. Assigning a pointer value of the same type, or computing a new
pointer value based on pointer arithmetic, produces a new pointer
value with the same byte range as the original pointer.

3. Converting a pointer value produces a new pointer value that
points to the same object as, and has the same byte range as, the
orginal pointer (with the usual alignment disclaimer).

4. A pointer derived from the result of doing a malloc(N) is the
same as converting from a pointer value *(char(*)[N])malloc(N)
(here of course malloc guarantees no alignment problems).

I believe these are correct because they are most consistent with
everything the standard says about what a pointer is, what arrays
are, what conversion is, how pointer arithmetic works, what may
be expected for multi-dimensional array objects, and the rules
for effective type and object access, among others. If have a
different opinion, that's fine, but please suggest a set of rules
covering the cases included in 1-4 that offer a more consistent
viewpoint than the one given here.
Sep 9 '08 #127
On Tue, 09 Sep 2008 13:04:08 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
>On Thu, 04 Sep 2008 05:18:18 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
With the exception of character types, does the standard describe
the conversion of an array to anything other than its initial
element?

Conversion of one pointer type to another is always allowed, subject
to the condition that the pointer in question is suitably aligned for
the new type.

Correct. And the result is specified as a pointer that you can convert
back to the original type to get something comparing equal to the
original pointer, but nothing more than that. In the special case of a
character type, the standard points out that the result points to the
first byte of the same object, and allows access to all bytes of the
same object. If it was necessary to explicitly specify this for
character types, why does it implicitly apply to other types as well?

The two cases aren't the same. In the case of, say, an int matrix, such
as

int m[3][5];

the storage for m is guaranteed to hold contiguous int objects, with the
same representation as an array of int. The "arrayness" is already
there.
int m[3][5] is guaranteed to hold contiguous bytes, with the same
representation as an array of unsigned char, right? I really don't see how
your explanation doesn't apply to that just as well.
Converting to (char*) is different because the object being pointed at
may be just a scalar;
Given int i;, &i may be treated as a pointer to the first element of an
array of length 1, and i has the same representation as int[1], does it
not?
It's because of imposing an array representation onto a non-array object
that character pointers are singled out in defining their conversion
semantics.
Even if that is the case, I still don't see where the conversion for other
types is defined at all.
Sep 9 '08 #128
On Tue, 09 Sep 2008 13:20:35 -0700, Tim Rentsch wrote:
James Kuyper <ja*********@verizon.netwrites:
>Citation, please -

Which view is more reasonable:
So you're arguing for what the standard should say, instead of what it
does say? If so, I agree that the current wording is far from perfect, but
then there are a lot more options than only the literal text, and your
view. There are plenty of options in between.
Sep 9 '08 #129
Tim Rentsch wrote:
pete <pf*****@mindspring.comwrites:
>Tim Rentsch wrote:
>>vi******@gmail.com writes:

On Sep 1, 2:49 pm, Tim Rentsch <t...@alumnus.caltech.eduwrote:
vipps...@gmail.com writes:
>The subject might be misleading.
>Regardless, is this code valid:
>#include <stdio.h>
>void f(double *p, size_t size) { while(size--) printf("%f\n", *p++); }
>int main(void) {
> double array[2][1] = { { 3.14 }, { 42.6 } };
> f((double *)array, sizeof array / sizeof **array);
> return 0;
>}
>Assuming casting double [2][1] to double * is implementation defined
>or undefined behavior, replace the cast with (void *).
>Since arrays are not allowed to have padding bytes in between of
>elements, it seems valid to me.
Despite what some other people have been saying,
this is valid. If foo is an array, doing (some_type *)foo
gives access to all of foo. Since 'array' is made up
(ultimately) of double's, using '(double*)array' will
work just fine.
Well, now I'm at loss again. I think the only way to settle this is to
provide quotes from the standard that agree (or disagree) with you.
I don't really expect to convince anyone, but for those who are
interested here is a citation plus some reasoning.

As far as I can tell, the standard never defines the term array,
but it does define 'array type', in 6.2.5 p 20:

An array type describes a contiguously allocated nonempty set of
objects with a particular member object type, called the element
type.

(In the text, 'array type' and 'element type' are italicized.)

In the circumstance given above (double array[2][1]), there
certainly is a contiguously allocated nonempty set of objects,
all of type double.

Now, what is accessible. The principle is that taking the
address of an element in an array allows access to other elements
in the array. We see this all the time with ordinary arrays;
here are some other cases:

int m[3][5];
volatile int (*vm)[5] = (volatile int (*)[5]) &m[0];

Access through vm is the same as through m, except that access
through vm treats elements as volatile.

_Complex double z[10];
double (*ri_z)[2] = (double (*)[2]) &z[0];

Access through z gives one-dimensional access to complex values;
access through ri_z gives component access, with ri_z[i][0] being
the real part of z[i], and with ri_z[i][1] being the imaginary
part of z[i].

Pretty obviously, we expect the above cases to work. And there
is nothing special about using 0 in &m[0] or &z[0]; using 1
instead would work fine, if the first index to vm or ri_z were
adjusted accordingly. Casting the address of an array element
preserves access to the entire array; since that is true in
these cases, it should also be true for another cast, namely:

double array[2][1] = { ... };
double *one_d = (double*) array; /* same as &array[0] */

Because what's being converted is the address of an element
of 'array', all of 'array' is accessible through the converted
pointer, just as it would be in the other cases above.

Whether this is all convincing depends crucially on what is
meant in 6.5.6 p 8 by the phrase "points to an element of an
array object". Unfortunately this key phrase is not explained
clearly. What is the most consistent reading of all that the
standard says? My view is expressed above, along with some
of the reasoning behind it.
When you access an object as an array of character type,
then the object is an array of character type.

N869
6.3.2 Other operands
6.3.2.1 Lvalues and function designators
[#1]
... When an object is said to have a particular
type, the type is specified by the lvalue used to designate
the object.

I'm not sure what point you're making here. It's not possible to
access an object with an expression of array type, because arrays
are converted to pointers (to the type of their elements) before
any access happens.
You access the elements of an array of character type,
through a pointer to character type.

If you access the bytes of an object,
through a pointer to character type,
then the bytes of the object
are elements of an array of character type.

--
pete
Sep 9 '08 #130
On Sep 4, 2:21 pm, Tim Rentsch <t...@alumnus.caltech.edu>,
<kf*************@alumnus.caltech.eduwrote:

<snip>

I see, thanks.
Sep 10 '08 #131
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Tue, 09 Sep 2008 13:20:35 -0700, Tim Rentsch wrote:
James Kuyper <ja*********@verizon.netwrites:
Citation, please -
Which view is more reasonable:

So you're arguing for what the standard should say, instead of what it
does say? If so, I agree that the current wording is far from perfect, but
then there are a lot more options than only the literal text, and your
view. There are plenty of options in between.
It sounds like you're agreeing with me, at least in part, but let
me see if I can clarify what I'm saying.

I don't mean to argue for what the standard should say (at least,
not now). The question is, How are we to understand what the
standard does say?

One way to understand the standard is to treat it much like we
would a math textbook. Statements in the standard are "axioms";
using the axioms we prove "theorems" about what C must allow or
disallow, and how it behaves for those things it allows. This
view might also be called, or at least is very close to, a
"literal text" view.

At the other end of the spectrum, we can understand what the
standard says by judging what we believe the intentions were (or
perhaps are) of the committee members in writing what they did
(using "writing" in a collective sense here). Under this model,
the text in the standard provides hints as to what was intended,
and what was intended determines what we understand the standard
to say.

In between these two is looking for the most consistent reading.
This model is somewhat like formulating a scientific theory.
Statements in the standard are "facts", and to understand what
the standard says a "theory" is formed about what it means; one
"theory" is better than another if it's more consistent with all
"facts" stated in the standard. More directly, one reading is
better than another if it's more consistent with all statements
in the standard.

I read your question (and followup statements) as asking about
things more under the second model - for "what the standard
should say", substitute "what did the committee intend the
standard to say", and the match is pretty close.

My question ("Which view is more reasonable:...") was meant under
the third ("most consistent reading") model. I believe this
model is more productive than the other two for deciding how the
standard is to be understood. In the earlier post where two
alternatives were listed, I didn't mean to imply that these were
the only two alternatives possible, only to compare them to see
which is more consistent with all statements that the standard
makes. By all means, if you can suggest a third option that is
more consistent with the whole standard, I would like to hear it.

Or, in the alternative, if you would like to suggest a more
productive way for how we can understand what the standard means,
and why, I'd like to hear that too.
Sep 14 '08 #132
pete <pf*****@mindspring.comwrites:
Tim Rentsch wrote:
pete <pf*****@mindspring.comwrites:
Tim Rentsch wrote:
[...]
Whether this is all convincing depends crucially on what is
meant in 6.5.6 p 8 by the phrase "points to an element of an
array object". Unfortunately this key phrase is not explained
clearly. What is the most consistent reading of all that the
standard says? My view is expressed above, along with some
of the reasoning behind it.
When you access an object as an array of character type,
then the object is an array of character type.

N869
6.3.2 Other operands
6.3.2.1 Lvalues and function designators
[#1]
... When an object is said to have a particular
type, the type is specified by the lvalue used to designate
the object.
I'm not sure what point you're making here. It's not possible to
access an object with an expression of array type, because arrays
are converted to pointers (to the type of their elements) before
any access happens.

You access the elements of an array of character type,
through a pointer to character type.

If you access the bytes of an object,
through a pointer to character type,
then the bytes of the object
are elements of an array of character type.
This last implication is backwards. IF there is an array of
character objects to be indexed, THEN they can be accessed (using
indexing) through a pointer to character. Not the other way
around - just because a pointer is used in accessing, that
doesn't imply that an array must exist (except an implicit array
of length 1, as mentioned in 6.5.6 p 7). That's true for char
(and signed/unsigned char) just the same as it is for any other
type.

I agree that the standard means to imply that bytes of an object
can be accessed as though they form an array of characters. But
the existence or non-existence of said array isn't affected by
statements in 6.3.2.1 p 1.
Sep 14 '08 #133
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Tue, 09 Sep 2008 13:04:08 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Thu, 04 Sep 2008 05:18:18 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
With the exception of character types, does the standard describe
the conversion of an array to anything other than its initial
element?

Conversion of one pointer type to another is always allowed, subject
to the condition that the pointer in question is suitably aligned for
the new type.

Correct. And the result is specified as a pointer that you can convert
back to the original type to get something comparing equal to the
original pointer, but nothing more than that. In the special case of a
character type, the standard points out that the result points to the
first byte of the same object, and allows access to all bytes of the
same object. If it was necessary to explicitly specify this for
character types, why does it implicitly apply to other types as well?
The two cases aren't the same. In the case of, say, an int matrix, such
as

int m[3][5];

the storage for m is guaranteed to hold contiguous int objects, with the
same representation as an array of int. The "arrayness" is already
there.

int m[3][5] is guaranteed to hold contiguous bytes, with the same
representation as an array of unsigned char, right? I really don't see how
your explanation doesn't apply to that just as well.
Bytes aren't the same as characters; clearly the standard
distinguishes between them. The array m is guaranteed to be made
up of bytes, but that's different from saying it's guaranteed to
have contiguous unsigned char objects. Furthermore the paragraph
in question includes char and signed char as well, which don't
necessarily match the whole byte representation.

Converting to (char*) is different because the object being pointed at
may be just a scalar;

Given int i;, &i may be treated as a pointer to the first element of an
array of length 1, and i has the same representation as int[1], does it
not?
True but incidental to my point, which is that (char*) may point
"inside" an otherwise unitary object. The conversion to (char*)
_could_ have been defined as allowing access to only one byte of
an object rather than all of them; that would be inconvenient,
but it wouldn't be inconsistent.

It's because of imposing an array representation onto a non-array object
that character pointers are singled out in defining their conversion
semantics.

Even if that is the case, I still don't see where the conversion for other
types is defined at all.
Do you believe that

int z[100] = {0};
const int *cpz = z;
printf( "*cpz == %d\n", *cpz );

has defined behavior? If so then the same statements that define
conversion from (int *) to (const int *) also define conversions
between other object pointer types (with the usual disclaimer
about alignment).
Sep 14 '08 #134
On Sep 14, 3:54 pm, Tim Rentsch <t...@alumnus.caltech.eduwrote:
....
Bytes aren't the same as characters; clearly the standard
distinguishes between them. The array m is guaranteed to be made
up of bytes, but that's different from saying it's guaranteed to
have contiguous unsigned char objects. Furthermore the paragraph
in question includes char and signed char as well, which don't
necessarily match the whole byte representation.
What? How does the standard distinguish between bytes and characters?
See 3.6, the definition of byte:
addressable unit of data storage large enough to hold any member of the basic character
set of the execution environment
An array m is guaranteed to have contiguous unsigned char objects,
precisely sizeof m unsigned chars. (or bytes)
Sep 14 '08 #135
vi******@gmail.com writes:
On Sep 14, 3:54 pm, Tim Rentsch <t...@alumnus.caltech.eduwrote:
...
Bytes aren't the same as characters; clearly the standard
distinguishes between them. The array m is guaranteed to be made
up of bytes, but that's different from saying it's guaranteed to
have contiguous unsigned char objects. Furthermore the paragraph
in question includes char and signed char as well, which don't
necessarily match the whole byte representation.

What? How does the standard distinguish between bytes and characters?
See 3.6, the definition of byte:
addressable unit of data storage large enough to hold any member of the basic character
set of the execution environment

An array m is guaranteed to have contiguous unsigned char objects,
precisely sizeof m unsigned chars. (or bytes)
Some upthread context got lost. The section that guarantees we can
treat arbitrary bytes as character objects is (quoting...)

6.3.2.3p7, more specifically the last two sentences:

When a pointer to an object is converted to a pointer to a
character type, the results points to the lowest addressed
byte of the object. Successive increments of the result, up
to the size of the object, yield pointers to the remaining
bytes of the object.

This section is the only section that allows arbitrary bytes to be
treated as character objects (unsigned or otherwise). Hence this
section is necessary to treat bytes as characters.
Sep 14 '08 #136
Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
>On Tue, 09 Sep 2008 13:04:08 -0700, Tim Rentsch wrote:
....
>>It's because of imposing an array representation onto a non-array object
that character pointers are singled out in defining their conversion
semantics.
Even if that is the case, I still don't see where the conversion for other
types is defined at all.

Do you believe that

int z[100] = {0};
const int *cpz = z;
printf( "*cpz == %d\n", *cpz );

has defined behavior? If so then the same statements that define
conversion from (int *) to (const int *) also define conversions
between other object pointer types (with the usual disclaimer
about alignment).
6.3.2.2p2 is the relevant statement: "For any qualifier q, a pointer to
a non-q-qualified type may be converted to a pointer to the q-qualified
version of the type; the values stored in the original and converted
pointers shall compare equal". In this context 'q' refers to the
qualifier "const".

Per 6.5.9p6, the fact that these pointers shall compare equal implies
that they point at the same object.

Given that we know that cpz points at z[0], I don't see any basis for
claiming that the behavior of that code is undefined. However, that
would no longer be the case if z were declared as, for example, an array
of doubles.

Your claim that "the same statements ... also define conversions between
other object pointer types" implies that 6.3.2.2p2 will also be
applicable to those other conversions. How would you interpret 6.3.2.2p2
as being applicable to, for instance, the conversion from double* to
int*? Modulo the usual disclaimer about alignment, of course.

Sep 14 '08 #137
Tim Rentsch wrote:
....
I don't mean to argue for what the standard should say (at least,
not now). The question is, How are we to understand what the
standard does say?

One way to understand the standard is to treat it much like we
would a math textbook. Statements in the standard are "axioms";
using the axioms we prove "theorems" about what C must allow or
disallow, and how it behaves for those things it allows. This
view might also be called, or at least is very close to, a
"literal text" view.

At the other end of the spectrum, we can understand what the
standard says by judging what we believe the intentions were (or
perhaps are) of the committee members in writing what they did
(using "writing" in a collective sense here). Under this model,
the text in the standard provides hints as to what was intended,
and what was intended determines what we understand the standard
to say.

In between these two is looking for the most consistent reading.
This model is somewhat like formulating a scientific theory.
Statements in the standard are "facts", and to understand what
the standard says a "theory" is formed about what it means; one
"theory" is better than another if it's more consistent with all
"facts" stated in the standard. More directly, one reading is
better than another if it's more consistent with all statements
in the standard.
The best model for the standard is none of these things. It's a contract
between implementors of C and C developers. It's a contract negotiated
by the C committee, which implementors and developers are free to adopt
or ignore. The essence of the contract is that if developers write code
which adheres to the requirements of the contract, then implementors are
required to produce implementations which give that code the behavior
required by the contract.

As such, neither mathematics, nor literary criticism, nor science
provides the appropriate analogy for how this document should be read.
The right analogy is the legal system. Some obnoxious regulars regularly
refer to some of the other regulars as "Bible thumpers", because we
routinely cite sections of the standard. Well, lawyers are also noted
for their frequent use of citations, and that is a much more appropriate
analogy.

The intent of the lawmakers is always a relevant issue in a legal case,
but there are strict (though frequently debated) limits on how far a
judge should go in using the "intent" of the law to influence his
interpretation of what the law actually says.
Sep 14 '08 #138
Tim Rentsch wrote:
James Kuyper <ja*********@verizon.netwrites:
>Tim Rentsch wrote:
...
>>A plausible analysis, but not on point, since the example code
above doesn't cast array[1], it casts array, which allows access
to the whole object.
Citation, please - where does the standard say that such a conversion
allows access to the whole object? Where, in fact, does the standard say
anything at all about what you can do with the converted pointer value,
other than convert it back to it's original type?

Which view is more reasonable:

A. Pointer conversion yields a pointer to the same object as
the original (assuming no alignment problems); or
This is clearly impossible in cases where the new pointer type points at
an object of a different size than the original. What I think was the
intent of the committee is that whenever a pointer conversion is
actually permitted, it results in a pointer to an object with the same
starting location in memory. I consider it a defect of the standard that
there is no wording anywhere which says so in the general case, only in
a couple of special cases.
B. Pointer conversion follows a strict constructionist view -
the only thing you can do with a converted pointer is
convert it back to the original type and compare it against
the unconverted original (assuming non-char types, etc)?

Of course, no one really believes (B); if they did, then they
should insist that a code sequence like
I believe that in a great many cases, (B) is the only thing the standard
actually says, and that in most of those cases this was in fact the
committee's intent. The following code fragment is not one of those cases:
>
int i = 0;
const int *p = &i;
return *p;

produces undefined behavior.
As I just explained in another message, this is covered by 6.3.2.2p2.
I should have responded earlier to this point, but I was too tired of
this subject to be interested in responding to the rest of your message
(and I'm still not interested in doing so).
Sep 14 '08 #139
On Sun, 14 Sep 2008 05:54:31 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
>On Tue, 09 Sep 2008 13:04:08 -0700, Tim Rentsch wrote:
The two cases aren't the same. In the case of, say, an int matrix,
such as

int m[3][5];

the storage for m is guaranteed to hold contiguous int objects, with
the same representation as an array of int. The "arrayness" is
already there.

int m[3][5] is guaranteed to hold contiguous bytes, with the same
representation as an array of unsigned char, right? I really don't see
how your explanation doesn't apply to that just as well.

Bytes aren't the same as characters; clearly the standard distinguishes
between them. The array m is guaranteed to be made up of bytes, but
that's different from saying it's guaranteed to have contiguous unsigned
char objects.
Do you think *((unsigned char *)&m + (0 ... 15*sizeof(int)-1)) are
invalid, or that they are not separate objects, or both? The only way they
can be valid is if the bytes themselves are separate objects (and they
match the definition of an object), because the result of unary * is only
defined if its operand points to a function or to an object, and clearly
it does not point to a function.
Furthermore the paragraph in question includes char and
signed char as well,
Yes, it does.
which don't necessarily match the whole byte
representation.
Which means the result may be less meaningful when you access an object as
an array of signed char.
>[snip]
It's because of imposing an array representation onto a non-array
object that character pointers are singled out in defining their
conversion semantics.

Even if that is the case, I still don't see where the conversion for
other types is defined at all.

Do you believe that

int z[100] = {0};
const int *cpz = z;
printf( "*cpz == %d\n", *cpz );

has defined behavior?
Yes. I agree with James Kuyper's explanation of why this is valid, but
would like to add that just as he mentions that it doesn't apply to
conversions between double* and int*, it also doesn't apply to conversions
between int(*)[] and int*.
Sep 14 '08 #140
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
...
I don't mean to argue for what the standard should say (at least,
not now). The question is, How are we to understand what the
standard does say?

One way to understand the standard is to treat it much like we
would a math textbook. Statements in the standard are "axioms";
using the axioms we prove "theorems" about what C must allow or
disallow, and how it behaves for those things it allows. This
view might also be called, or at least is very close to, a
"literal text" view.

At the other end of the spectrum, we can understand what the
standard says by judging what we believe the intentions were (or
perhaps are) of the committee members in writing what they did
(using "writing" in a collective sense here). Under this model,
the text in the standard provides hints as to what was intended,
and what was intended determines what we understand the standard
to say.

In between these two is looking for the most consistent reading.
This model is somewhat like formulating a scientific theory.
Statements in the standard are "facts", and to understand what
the standard says a "theory" is formed about what it means; one
"theory" is better than another if it's more consistent with all
"facts" stated in the standard. More directly, one reading is
better than another if it's more consistent with all statements
in the standard.

The best model for the standard is none of these things. It's a contract
between implementors of C and C developers. It's a contract negotiated
by the C committee, which implementors and developers are free to adopt
or ignore. The essence of the contract is that if developers write code
which adheres to the requirements of the contract, then implementors are
required to produce implementations which give that code the behavior
required by the contract.

As such, neither mathematics, nor literary criticism, nor science
provides the appropriate analogy for how this document should be read.
The right analogy is the legal system. Some obnoxious regulars regularly
refer to some of the other regulars as "Bible thumpers", because we
routinely cite sections of the standard. Well, lawyers are also noted
for their frequent use of citations, and that is a much more appropriate
analogy.

The intent of the lawmakers is always a relevant issue in a legal case,
but there are strict (though frequently debated) limits on how far a
judge should go in using the "intent" of the law to influence his
interpretation of what the law actually says.
I notice you didn't include a disclaimer that you are not a
lawyer. :)

If we take the legal system as our model for deciding questions
about the standard, then the only real test for comparing two
opposing interpretations would be to offers arguments before some
court, and let the court decide. I don't find this model very
useful, since there are no such courts.

Or, did you mean by using the analogy of the legal system that
people should argue incessantly and there is no particular metric
for deciding which views have more merit? I don't find this
model very useful either, for obvious reasons.
Sep 14 '08 #141
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Tue, 09 Sep 2008 13:04:08 -0700, Tim Rentsch wrote:
...
>It's because of imposing an array representation onto a non-array object
that character pointers are singled out in defining their conversion
semantics.
Even if that is the case, I still don't see where the conversion for other
types is defined at all.
Do you believe that

int z[100] = {0};
const int *cpz = z;
printf( "*cpz == %d\n", *cpz );

has defined behavior? If so then the same statements that define
conversion from (int *) to (const int *) also define conversions
between other object pointer types (with the usual disclaimer
about alignment).

6.3.2.2p2 is the relevant statement: "For any qualifier q, a pointer to
a non-q-qualified type may be converted to a pointer to the q-qualified
version of the type; the values stored in the original and converted
pointers shall compare equal". In this context 'q' refers to the
qualifier "const".
I'm assuming you meant to write 6.3.2.3p2 (for 6.3.2.2p2).
Per 6.5.9p6, the fact that these pointers shall compare equal implies
that they point at the same object.
Your logic is faulty. Two pointers can compare equal and yet still
not be pointing at the same object. The stated conclusion is just not
a valid deduction.
Given that we know that cpz points at z[0], I don't see any basis for
claiming that the behavior of that code is undefined. However, that
would no longer be the case if z were declared as, for example, an array
of doubles.
Does this mean you think

printf( "*(unsigned*)z == %u\n", *(unsigned*)z );

has defined behavior, or undefined behavior?
Your claim that "the same statements ... also define conversions between
other object pointer types" implies that 6.3.2.2p2 will also be
applicable to those other conversions.
It doesn't imply that, because 6.3.2.3p2 doesn't define the results of
access, it defines only the results of comparison.
How would you interpret 6.3.2.2p2
as being applicable to, for instance, the conversion from double* to
int*? Modulo the usual disclaimer about alignment, of course.
I don't, because 6.3.2.3p2 is only about comparison, not about access.
The analogous guarantee for double* and int* is made in 6.3.2.3p7;
the guarantee is weaker in this case (not counting alignment) because
double* and int* can't be compared directly, whereas const int* and
int* can.

Sep 14 '08 #142
Tim Rentsch <tx*@alumnus.caltech.eduwrites:
James Kuyper <ja*********@verizon.netwrites:
>Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:

On Tue, 09 Sep 2008 13:04:08 -0700, Tim Rentsch wrote:
...
>>It's because of imposing an array representation onto a non-array object
that character pointers are singled out in defining their conversion
semantics.
Even if that is the case, I still don't see where the conversion for other
types is defined at all.

Do you believe that

int z[100] = {0};
const int *cpz = z;
printf( "*cpz == %d\n", *cpz );

has defined behavior? If so then the same statements that define
conversion from (int *) to (const int *) also define conversions
between other object pointer types (with the usual disclaimer
about alignment).

6.3.2.2p2 is the relevant statement: "For any qualifier q, a pointer to
a non-q-qualified type may be converted to a pointer to the q-qualified
version of the type; the values stored in the original and converted
pointers shall compare equal". In this context 'q' refers to the
qualifier "const".

I'm assuming you meant to write 6.3.2.3p2 (for 6.3.2.2p2).
>Per 6.5.9p6, the fact that these pointers shall compare equal implies
that they point at the same object.

Your logic is faulty. Two pointers can compare equal and yet still
not be pointing at the same object. The stated conclusion is just not
a valid deduction.

I'm interested in this in the real world. Any pointers I have seen which
compare equal are equal. They also point to the same object. Please
expand on this.
Sep 14 '08 #143
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Sun, 14 Sep 2008 05:54:31 -0700, Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:
On Tue, 09 Sep 2008 13:04:08 -0700, Tim Rentsch wrote:
The two cases aren't the same. In the case of, say, an int matrix,
such as

int m[3][5];

the storage for m is guaranteed to hold contiguous int objects, with
the same representation as an array of int. The "arrayness" is
already there.

int m[3][5] is guaranteed to hold contiguous bytes, with the same
representation as an array of unsigned char, right? I really don't see
how your explanation doesn't apply to that just as well.
Bytes aren't the same as characters; clearly the standard distinguishes
between them. The array m is guaranteed to be made up of bytes, but
that's different from saying it's guaranteed to have contiguous unsigned
char objects.

Do you think *((unsigned char *)&m + (0 ... 15*sizeof(int)-1)) are
invalid, or that they are not separate objects, or both? The only way they
can be valid is if the bytes themselves are separate objects (and they
match the definition of an object), because the result of unary * is only
defined if its operand points to a function or to an object, and clearly
it does not point to a function.
I believe the accesses ((unsigned char*)&m)[i], 0 <= i < sizeof(int[3][5]),
are valid, but they are valid only because of statements made in 6.3.2.3p7.

Furthermore the paragraph in question includes char and
signed char as well,

Yes, it does.
which don't necessarily match the whole byte
representation.

Which means the result may be less meaningful when you access an object as
an array of signed char.
The are just as meaningful for signed char as for unsigned char,
because 6.3.2.3p7 applies equally to both.
[snip]
It's because of imposing an array representation onto a non-array
object that character pointers are singled out in defining their
conversion semantics.

Even if that is the case, I still don't see where the conversion for
other types is defined at all.
Do you believe that

int z[100] = {0};
const int *cpz = z;
printf( "*cpz == %d\n", *cpz );

has defined behavior?

Yes. I agree with James Kuyper's explanation of why this is valid, but
would like to add that just as he mentions that it doesn't apply to
conversions between double* and int*, it also doesn't apply to conversions
between int(*)[] and int*.
That reasoning is incorrect, as I explained in my response to his
comments.
Sep 14 '08 #144
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
...
A plausible analysis, but not on point, since the example code
above doesn't cast array[1], it casts array, which allows access
to the whole object.
Citation, please - where does the standard say that such a conversion
allows access to the whole object? Where, in fact, does the standard say
anything at all about what you can do with the converted pointer value,
other than convert it back to it's original type?
Which view is more reasonable:

A. Pointer conversion yields a pointer to the same object as
the original (assuming no alignment problems); or

This is clearly impossible in cases where the new pointer type points at
an object of a different size than the original. What I think was the
intent of the committee is that whenever a pointer conversion is
actually permitted, it results in a pointer to an object with the same
starting location in memory. I consider it a defect of the standard that
there is no wording anywhere which says so in the general case, only in
a couple of special cases.
B. Pointer conversion follows a strict constructionist view -
the only thing you can do with a converted pointer is
convert it back to the original type and compare it against
the unconverted original (assuming non-char types, etc)?

Of course, no one really believes (B); if they did, then they
should insist that a code sequence like

I believe that in a great many cases, (B) is the only thing the standard
actually says, and that in most of those cases this was in fact the
committee's intent. The following code fragment is not one of those cases:
I notice you didn't answer the question about which view is more
reasonable.

Your comment in another post about using a legal system analogy is
illuminating. In law, it's perfectly acceptable to argue several
inconsistent theories at once; arguing one theory doesn't preclude
arguing another, inconsistent, theory in the very same breath. All
that matters is whether the arguments convince a jury (or judge).

Any model that explicitly allows several inconsistent interpretations
is not, IMO, an especially useful one for reading the C standard.


int i = 0;
const int *p = &i;
return *p;

produces undefined behavior.

As I just explained in another message, this is covered by 6.3.2.2p2.
As I just explained in a response to that message, your
logic there was faulty; 6.3.2.3p2 makes a guarantee
only about comparison, not about access.
Sep 14 '08 #145

"Richard" <rg****@gmail.comwrote in message
news:ga**********@registered.motzarella.org...
Tim Rentsch <tx*@alumnus.caltech.eduwrites:
>Your logic is faulty. Two pointers can compare equal and yet still
not be pointing at the same object. The stated conclusion is just not
a valid deduction.

I'm interested in this in the real world. Any pointers I have seen which
compare equal are equal. They also point to the same object. Please
expand on this.
Perhaps comparing a int* with a char* for example? They could contain the
same address but point to slightly different objects.

--
Bartc

Sep 14 '08 #146
Richard<rg****@gmail.comwrites:
Tim Rentsch <tx*@alumnus.caltech.eduwrites:
James Kuyper <ja*********@verizon.netwrites:
Tim Rentsch wrote:
Harald van =?UTF-8?b?RMSzaw==?= <tr*****@gmail.comwrites:

On Tue, 09 Sep 2008 13:04:08 -0700, Tim Rentsch wrote:
...
It's because of imposing an array representation onto a non-array object
that character pointers are singled out in defining their conversion
semantics.
Even if that is the case, I still don't see where the conversion for other
types is defined at all.

Do you believe that

int z[100] = {0};
const int *cpz = z;
printf( "*cpz == %d\n", *cpz );

has defined behavior? If so then the same statements that define
conversion from (int *) to (const int *) also define conversions
between other object pointer types (with the usual disclaimer
about alignment).

6.3.2.2p2 is the relevant statement: "For any qualifier q, a pointer to
a non-q-qualified type may be converted to a pointer to the q-qualified
version of the type; the values stored in the original and converted
pointers shall compare equal". In this context 'q' refers to the
qualifier "const".
I'm assuming you meant to write 6.3.2.3p2 (for 6.3.2.2p2).
Per 6.5.9p6, the fact that these pointers shall compare equal implies
that they point at the same object.
Your logic is faulty. Two pointers can compare equal and yet still
not be pointing at the same object. The stated conclusion is just not
a valid deduction.


I'm interested in this in the real world. Any pointers I have seen which
compare equal are equal. They also point to the same object. Please
expand on this.
Here is one example:

int m[3][5];
int *p = &m[1][5];
int *q = &m[2][0];
assert( p == q );

Even though p == q, accesses through p may not (definedly) affect the
value of *q. I believe some optimizers rely on such things being
true.

A second example:

void *stuff = malloc( 15 * sizeof (int) );
int (*p10)[10] = stuff;
int (*p15)[15] = stuff;
int (*r)[] = p10;
int (*s)[] = p15;
assert( r == s );

Here r == s, but accesses through r may affect only the first 10
elements, whereas accesses through s may affect all 15 elements.
Here the two objects overlap at the beginning, but have different
extents. Like the first case, I believe optimizers may rely on
changes through r not affecting (*s)[10] through (*s)[14].
Sep 14 '08 #147
Tim Rentsch wrote:
James Kuyper <ja*********@verizon.netwrites:
....
>6.3.2.2p2 is the relevant statement: "For any qualifier q, a pointer to
a non-q-qualified type may be converted to a pointer to the q-qualified
version of the type; the values stored in the original and converted
pointers shall compare equal". In this context 'q' refers to the
qualifier "const".

I'm assuming you meant to write 6.3.2.3p2 (for 6.3.2.2p2).
You're correct. Sorry for the typo.
>Per 6.5.9p6, the fact that these pointers shall compare equal implies
that they point at the same object.

Your logic is faulty. Two pointers can compare equal and yet still
not be pointing at the same object. The stated conclusion is just not
a valid deduction.
Keep in mind that 6.5.9p6 isn't of the form "if A, then B", which is of
course not reversible. It is of the form "if AND ONLY IF A, then B",
from which it is perfectly valid to conclude "if B, then A".

Sure; two null pointers could compare equal, but 'z' can't be null.

Two pointers past the end of the same array could compare equal, but
again 'z' can't be such a pointer.

The only way permitted by the standard for cpz to compare equal to z
without pointing at the same object is if it points one past the end of
the end of an array object that happens to immediately precede 'z' in
memory. However, the reason for that exception is to allow commonplace
implementations where such a pointer is simultaneously a pointer
one-past-the end of one array AND a pointer to the first element of the
second. I will grant that the wording used falls a little short of
actually guaranteeing that. I am sure that the committee's intent was
that wherever the standard guarantees that two non-null pointers to
objects must compare equal, it should be taken as meaning that they
point at the same object.
>Given that we know that cpz points at z[0], I don't see any basis for
claiming that the behavior of that code is undefined. However, that
would no longer be the case if z were declared as, for example, an array
of doubles.

Does this mean you think

printf( "*(unsigned*)z == %u\n", *(unsigned*)z );

has defined behavior, or undefined behavior?
The standard doesn't say where (unsigned*)z points; it follows that
dereferencing it could have undefined behavior. I consider this a defect
in the standard, and unlikely to have been the intent of the committee.
In practice, unless alignment is an issue, I wouldn't expect anything to
actually go wrong.
Sep 15 '08 #148
Tim Rentsch wrote:
James Kuyper <ja*********@verizon.netwrites:
>Tim Rentsch wrote:
...
....
>As such, neither mathematics, nor literary criticism, nor science
provides the appropriate analogy for how this document should be read.
The right analogy is the legal system. Some obnoxious regulars regularly
refer to some of the other regulars as "Bible thumpers", because we
routinely cite sections of the standard. Well, lawyers are also noted
for their frequent use of citations, and that is a much more appropriate
analogy.

The intent of the lawmakers is always a relevant issue in a legal case,
but there are strict (though frequently debated) limits on how far a
judge should go in using the "intent" of the law to influence his
interpretation of what the law actually says.

I notice you didn't include a disclaimer that you are not a
lawyer. :)
My foreign-born in-laws constantly come to me for advice on the American
legal system, about which I know a great deal more than they do. In
particular, I know enough to include such a disclaimer with every answer
I give them.

Unlike true lawyers, there's no legal requirement that language lawyers
have credentials. Therefore I'm just as much of a language lawyer as
anyone, and far better qualified to be one than most (modesty is clearly
not one of my virtues :-). No disclaimer is needed.

I apologize for taking your joking comment seriously; but I couldn't
come up with a good joking response.
If we take the legal system as our model for deciding questions
about the standard, then the only real test for comparing two
opposing interpretations would be to offers arguments before some
court, and let the court decide. I don't find this model very
useful, since there are no such courts.
The C committee is the relevant court, and you bring cases before that
court by filing Defect Reports. Just as with real courts, the C
committee sometimes makes bad or incorrect decisions, but they are
nonetheless the highest relevant authority. In principle, ISO has some
authority over them, but I doubt that ISO's authority would ever be used
to decide a technical issue over the meaning of the standard. ISO is
mainly concerned with procedural issues about how the standard is created.
Sep 15 '08 #149
Tim Rentsch wrote:
James Kuyper <ja*********@verizon.netwrites:
>Tim Rentsch wrote:
....
>>Which view is more reasonable:

A. Pointer conversion yields a pointer to the same object as
the original (assuming no alignment problems); or
This is clearly impossible in cases where the new pointer type points at
an object of a different size than the original. What I think was the
intent of the committee is that whenever a pointer conversion is
actually permitted, it results in a pointer to an object with the same
starting location in memory. I consider it a defect of the standard that
there is no wording anywhere which says so in the general case, only in
a couple of special cases.
>> B. Pointer conversion follows a strict constructionist view -
the only thing you can do with a converted pointer is
convert it back to the original type and compare it against
the unconverted original (assuming non-char types, etc)?

Of course, no one really believes (B); if they did, then they
should insist that a code sequence like
I believe that in a great many cases, (B) is the only thing the standard
actually says, and that in most of those cases this was in fact the
committee's intent. The following code fragment is not one of those cases:

I notice you didn't answer the question about which view is more
reasonable.
What I said does answer that question, but not in the fashion you
wanted. Whether A or B is more reasonable depends upon context; the
answer is different in different contexts, and is sometimes "neither
one". My answer reflects that fact.
Your comment in another post about using a legal system analogy is
illuminating. In law, it's perfectly acceptable to argue several
inconsistent theories at once; arguing one theory doesn't preclude
arguing another, inconsistent, theory in the very same breath. All
that matters is whether the arguments convince a jury (or judge).
It matters if the other side catches the lawyer at such sophistry, and
if the lawyer has integrity, it should matter to the lawyer as well.

I believe that I have a single consistent interpretation that covers all
of the relevant cases; this interpretation include recognition of
something I consider a defect in the standard, which complicates any
description of that interpretation. It means that in some cases I have
to say that "a literal interpretation of the standard says this" but "I
believe that the committee's intent was this other thing"; that's not
inconsistency, or at least it's not my inconsistency.

I find this frustrating, and I wish they would fix it, but I've been
complaining about it on comp.std.c for about a decade. I have yet to
convince anyone on the committee that it's sufficiently clearly wrong
and important enough for them to champion the idea for me; and I lack
the spare time to champion it myself. One thing that is quite clear is
that most of the committee members I've talked with about this issue
feel that the standard clearly states to be true precisely what I've
said was their intent, and that it therefore doesn't need to be modified
to express that intent more clearly. That doesn't prove that a Defect
Report filed on this issue would be decided in that fashion, but it does
make it more likely.
Sep 15 '08 #150

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: James | last post by:
I see that variations on this question have appeared before but I'm still completely stumped. I'm developing an application with a fairly robust graphics component for 3D rendering. I've written...
6
by: Gregory L. Hansen | last post by:
I'm sure I saw this mentioned somewhere, but I can't find it. How can I dynamically allocate a multi-dimensional array in C++? My next question, if this gets figured out, is if I should use the...
4
by: Richard Hayden | last post by:
Hi, Why does gcc (3.3.2) give me a 'initialization from incompatible pointer type' warning when compiling: int main(int argc, char** argv) { int testa; int** testp = testa; }
8
by: masood.iqbal | last post by:
All this time I was under the illusion that I understand the concept of multi-dimensional arrays well ---- however the following code snippet belies my understanding. I had assumed all along...
11
by: truckaxle | last post by:
I am trying to pass a slice from a larger 2-dimensional array to a function that will work on a smaller region of the array space. The code below is a distillation of what I am trying to...
1
by: Fayez Al-Naddaf | last post by:
I got this message when I tried to browse my web service "Multi-dimensional arrays are not supported. Use a jagged array instead" Can someone told me why?
0
by: volx | last post by:
Hello all: What is the proper way to implement in MC++ a web service which accepts a multi dimensional array as a parameter to one of its methods? This does compile: double...
5
by: David T. Ashley | last post by:
Hi, Aside from the obvious recursive syntax for initialization, $x = array( ... array( ...)), what is the best way to deal with multi-dimensional arrays in PHP? It seems there is no...
3
by: ben.r.wood | last post by:
I am not entirely sure, but after scanning the web believe I need to use multi-dimensional arrays for my problem - although I have not seen any examples of what I am trying to achieve. I have a...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...
1
muto222
php
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.