Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #91241 > unrolled thread
| Started by | DFS <nospam@dfs.com> |
|---|---|
| First post | 2016-07-28 11:38 -0400 |
| Last post | 2016-07-30 09:17 -0700 |
| Articles | 16 — 11 participants |
Back to article view | Back to comp.lang.c
Question DFS <nospam@dfs.com> - 2016-07-28 11:38 -0400
Re: Question Melzzzzz <mel@zzzzz.com> - 2016-07-28 17:50 +0200
Re: Question BartC <bc@freeuk.com> - 2016-07-28 17:49 +0100
Re: Question Melzzzzz <mel@zzzzz.com> - 2016-07-28 18:52 +0200
Re: Question Keith Thompson <kst-u@mib.org> - 2016-07-28 11:58 -0700
Re: Question BartC <bc@freeuk.com> - 2016-07-28 20:57 +0100
Re: Question "D. Aaron Sawyer" <aaron@110.net> - 2016-07-28 13:55 -0300
Re: Question Barry Schwarz <schwarzb@dqel.com> - 2016-07-28 11:38 -0700
Re: Question DFS <nospam@dfs.com> - 2016-07-28 15:20 -0400
Re: Question Tim Rentsch <txr@alumni.caltech.edu> - 2016-07-30 09:24 -0700
Re: Question Ian Collins <ian-news@hotmail.com> - 2016-07-29 07:23 +1200
Re: Question James Kuyper <jameskuyper@verizon.net> - 2016-07-28 16:05 -0400
Re: Question DFS <nospam@dfs.com> - 2016-07-29 10:14 -0400
Re: Question "James R. Kuyper" <jameskuyper@verizon.net> - 2016-07-30 14:45 -0400
Re: Question supercat@casperkitty.com - 2016-08-01 05:19 -0700
Re: Question Tim Rentsch <txr@alumni.caltech.edu> - 2016-07-30 09:17 -0700
| From | DFS <nospam@dfs.com> |
|---|---|
| Date | 2016-07-28 11:38 -0400 |
| Subject | Question |
| Message-ID | <nnd8ue$nnp$2@dont-email.me> |
Take a C structure:
struct imageType {
imageOpsType *ops;
coordType width;
coordType height;
int32_t depth; /* 1..32 */
int32_t linebytes; /* bytes per line */
int32_t allocated; /* bytes allocated */
int64_t fill; /* color to fill with */
int8_t sign;
imageType *primary; /* for secondary, assoc primary */
coordType primaryX; /* for secondary, offset in primary */
coordType primaryY; /* for secondary, offset in primary */
int8_t *data;
uint32_t flags; /* image access mode(s) */
int32_t verbose;
int32_t page; /* number of the current page */
double resX; /* pixels per unit in X */
double resY; /* pixels per unit in Y */
int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
char modes[MAX_MODES]; /* modes used with open */
char name[MAX_NAME];
int64_t clientData[CLIENT_SIZE]; };
What does the following statement accomplish, given that
"image" and "src" are both pointers to imageType?
(*(imageType**)&(image)->clientData[0])=(src)
Thanks for help.
[toc] | [next] | [standalone]
| From | Melzzzzz <mel@zzzzz.com> |
|---|---|
| Date | 2016-07-28 17:50 +0200 |
| Message-ID | <20160728175049.15165e8b@maxa-pc> |
| In reply to | #91241 |
On Thu, 28 Jul 2016 11:38:54 -0400
DFS <nospam@dfs.com> wrote:
> Take a C structure:
>
> struct imageType {
> imageOpsType *ops;
> coordType width;
> coordType height;
> int32_t depth; /* 1..32
> */ int32_t linebytes; /* bytes per line */
> int32_t allocated; /* bytes allocated */
> int64_t fill; /* color to fill
> with */ int8_t sign;
> imageType *primary; /* for secondary, assoc
> primary */ coordType primaryX; /* for secondary, offset
> in primary */ coordType primaryY; /* for secondary, offset in
> primary */ int8_t *data;
> uint32_t flags; /* image access
> mode(s) */ int32_t verbose;
> int32_t page; /* number of the current
> page */ double resX; /* pixels per unit in
> X */ double resY; /* pixels per unit in
> Y */ int32_t resUnit; /* 0=nores 1=unk 2=inch
> 3=cm */ char modes[MAX_MODES]; /* modes used with
> open */ char name[MAX_NAME];
> int64_t clientData[CLIENT_SIZE]; };
>
>
> What does the following statement accomplish, given that
> "image" and "src" are both pointers to imageType?
>
> (*(imageType**)&(image)->clientData[0])=(src)
>
>
>
> Thanks for help.
>
On Mill pointer can't be stored in integer...
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-07-28 17:49 +0100 |
| Message-ID | <nndd2r$7lg$1@dont-email.me> |
| In reply to | #91241 |
On 28/07/2016 16:38, DFS wrote:
> Take a C structure:
>
> struct imageType {
> imageOpsType *ops;
> coordType width;
> coordType height;
> int32_t depth; /* 1..32 */
> int32_t linebytes; /* bytes per line */
> int32_t allocated; /* bytes allocated */
> int64_t fill; /* color to fill with */
> int8_t sign;
> imageType *primary; /* for secondary, assoc primary */
> coordType primaryX; /* for secondary, offset in primary */
> coordType primaryY; /* for secondary, offset in primary */
> int8_t *data;
> uint32_t flags; /* image access mode(s) */
> int32_t verbose;
> int32_t page; /* number of the current page */
> double resX; /* pixels per unit in X */
> double resY; /* pixels per unit in Y */
> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
> char modes[MAX_MODES]; /* modes used with open */
> char name[MAX_NAME];
> int64_t clientData[CLIENT_SIZE]; };
>
>
> What does the following statement accomplish, given that
> "image" and "src" are both pointers to imageType?
>
> (*(imageType**)&(image)->clientData[0])=(src)
It depends on whether the & applies to just (image) or to the -> part
too (I don't know off-hand).
It might be casting clientData[0], which is int64, to be a pointer to
imageType so that it can store src. Although the cast looks more
elaborate than it needs to be.
Alternatively, (src) could have been cast to int64.
(Note than imageType is a struct tag name rather than a type name, so I
guess this is not the exact code.)
--
Bartc
[toc] | [prev] | [next] | [standalone]
| From | Melzzzzz <mel@zzzzz.com> |
|---|---|
| Date | 2016-07-28 18:52 +0200 |
| Message-ID | <20160728185225.65e5a003@maxa-pc> |
| In reply to | #91250 |
On Thu, 28 Jul 2016 17:49:24 +0100
BartC <bc@freeuk.com> wrote:
> On 28/07/2016 16:38, DFS wrote:
> > Take a C structure:
> >
> > struct imageType {
> > int64_t clientData[CLIENT_SIZE]; };
> >
> >
> > What does the following statement accomplish, given that
> > "image" and "src" are both pointers to imageType?
> >
> > (*(imageType**)&(image)->clientData[0])=(src)
>
> (Note than imageType is a struct tag name rather than a type name, so
> I guess this is not the exact code.)
>
It's probably C++...
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2016-07-28 11:58 -0700 |
| Message-ID | <ln7fc58u92.fsf@kst-u.example.com> |
| In reply to | #91250 |
BartC <bc@freeuk.com> writes:
> On 28/07/2016 16:38, DFS wrote:
>> Take a C structure:
>>
>> struct imageType {
>> imageOpsType *ops;
>> coordType width;
>> coordType height;
>> int32_t depth; /* 1..32 */
>> int32_t linebytes; /* bytes per line */
>> int32_t allocated; /* bytes allocated */
>> int64_t fill; /* color to fill with */
>> int8_t sign;
>> imageType *primary; /* for secondary, assoc primary */
>> coordType primaryX; /* for secondary, offset in primary */
>> coordType primaryY; /* for secondary, offset in primary */
>> int8_t *data;
>> uint32_t flags; /* image access mode(s) */
>> int32_t verbose;
>> int32_t page; /* number of the current page */
>> double resX; /* pixels per unit in X */
>> double resY; /* pixels per unit in Y */
>> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
>> char modes[MAX_MODES]; /* modes used with open */
>> char name[MAX_NAME];
>> int64_t clientData[CLIENT_SIZE]; };
>>
>>
>> What does the following statement accomplish, given that
>> "image" and "src" are both pointers to imageType?
>>
>> (*(imageType**)&(image)->clientData[0])=(src)
>
> It depends on whether the & applies to just (image) or to the -> part
> too (I don't know off-hand).
You could easily find out before posting.
[...]
> (Note than imageType is a struct tag name rather than a type name, so I
> guess this is not the exact code.)
Perhaps -- or perhaps the code was compiled as C++, and the OP
mistakenly assumed it was C.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-07-28 20:57 +0100 |
| Message-ID | <nndo31$goe$1@dont-email.me> |
| In reply to | #91266 |
On 28/07/2016 19:58, Keith Thompson wrote: > BartC <bc@freeuk.com> writes: >>> (*(imageType**)&(image)->clientData[0])=(src) >> >> It depends on whether the & applies to just (image) or to the -> part >> too (I don't know off-hand). > > You could easily find out before posting. It doesn't matter. I go on to assume that & is applied to everything up to the = as everything then fits into place. But & followed by a sprawling term like that, and with extraneous parentheses (which looks like some of my machine-generated code), I would write more clearly like this, even if the precedences render that unnecessary: &(image->clientData[0]) (A unary prefix op like that in front of a binary infix operation and having lower precedence is unusual, if "->" is thought of as a binary operator. So another reason to treat it as more special.) >> (Note than imageType is a struct tag name rather than a type name, so I >> guess this is not the exact code.) > > Perhaps -- or perhaps the code was compiled as C++, and the OP > mistakenly assumed it was C. Or maybe there *is* a typedef 'imageType' which is not shown. (This is a strike against having a separate namespace for struct tags as then there would be less doubt.) -- Bartc
[toc] | [prev] | [next] | [standalone]
| From | "D. Aaron Sawyer" <aaron@110.net> |
|---|---|
| Date | 2016-07-28 13:55 -0300 |
| Message-ID | <nndddp$8th$1@dont-email.me> |
| In reply to | #91241 |
On 2016.07.28 12:38, DFS wrote:
> Take a C structure:
>
> struct imageType {
> imageOpsType *ops;
> coordType width;
> coordType height;
> int32_t depth; /* 1..32 */
> int32_t linebytes; /* bytes per line */
> int32_t allocated; /* bytes allocated */
> int64_t fill; /* color to fill with */
> int8_t sign;
> imageType *primary; /* for secondary, assoc primary */
> coordType primaryX; /* for secondary, offset in primary */
> coordType primaryY; /* for secondary, offset in primary */
> int8_t *data;
> uint32_t flags; /* image access mode(s) */
> int32_t verbose;
> int32_t page; /* number of the current page */
> double resX; /* pixels per unit in X */
> double resY; /* pixels per unit in Y */
> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
> char modes[MAX_MODES]; /* modes used with open */
> char name[MAX_NAME];
> int64_t clientData[CLIENT_SIZE]; };
>
>
> What does the following statement accomplish, given that
> "image" and "src" are both pointers to imageType?
>
> (*(imageType**)&(image)->clientData[0])=(src)
>
>
>
> Thanks for help.
>
What is sizeof (imageType *) ?
What is sizeof int64_t ?
If they are different:
At best, you will get a compiler warning or error.
At worst, you get a silent corruption of clientData.
If they are the same:
Good luck with that.
=Aaron
[toc] | [prev] | [next] | [standalone]
| From | Barry Schwarz <schwarzb@dqel.com> |
|---|---|
| Date | 2016-07-28 11:38 -0700 |
| Message-ID | <rrhkpb5jras93nkoepeobroclnisdsmu7t@4ax.com> |
| In reply to | #91241 |
On Thu, 28 Jul 2016 11:38:54 -0400, DFS <nospam@dfs.com> wrote:
>Take a C structure:
>
>struct imageType {
> imageOpsType *ops;
> coordType width;
> coordType height;
> int32_t depth; /* 1..32 */
> int32_t linebytes; /* bytes per line */
> int32_t allocated; /* bytes allocated */
> int64_t fill; /* color to fill with */
> int8_t sign;
> imageType *primary; /* for secondary, assoc primary */
> coordType primaryX; /* for secondary, offset in primary */
> coordType primaryY; /* for secondary, offset in primary */
> int8_t *data;
> uint32_t flags; /* image access mode(s) */
> int32_t verbose;
> int32_t page; /* number of the current page */
> double resX; /* pixels per unit in X */
> double resY; /* pixels per unit in Y */
> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
> char modes[MAX_MODES]; /* modes used with open */
> char name[MAX_NAME];
> int64_t clientData[CLIENT_SIZE]; };
>
>
>What does the following statement accomplish, given that
>"image" and "src" are both pointers to imageType?
>
>(*(imageType**)&(image)->clientData[0])=(src)
-> has higher precedence than &, so this is equivalent to
(*(imageType**)& ( (image)->clientData[0] ) )=(src)
The parentheses around image contribute nothing. So this is
equivalent to
(*(imageType**)& ( image->clientData[0] ) )=(src)
clientData is an array. clientData[0] is the first element of the
array. The & operation evaluates to the address of this element. This
address has type pointer to int64_t.
The cast operation converts this address to type pointer to pointer to
struct.
The dereference operation evaluates this address and treats the result
as a pointer to struct located at that address.
The assignment operation stores the value of src (the address of some
struct) in the pointer evaluated above. The net result is
clientData[0] contains the same value that src contains. This assumes
that pointer to struct and int64_t have the same size. If the pointer
is large, the behavior is undefined. If the pointer is smaller, the
result in clientData[0] could be weird. Even if they are the same
size, the effect of treating this address as an int64_t is
implementation defined.
This entire process could have been performed much more easily by
simply casting the value of src with
image->clientData[0] = (int64_t)src;
Why they chose to store an address in an int64_t is a different
question. Maybe there is a reason clientData cannot have type pointer
to struct. If it did, then the code is even simpler
image->clientData[0] = src.
--
Remove del for email
[toc] | [prev] | [next] | [standalone]
| From | DFS <nospam@dfs.com> |
|---|---|
| Date | 2016-07-28 15:20 -0400 |
| Message-ID | <nndltc$8g5$2@dont-email.me> |
| In reply to | #91261 |
On 7/28/2016 2:38 PM, Barry Schwarz wrote:
> On Thu, 28 Jul 2016 11:38:54 -0400, DFS <nospam@dfs.com> wrote:
>
>> Take a C structure:
>>
>> struct imageType {
>> imageOpsType *ops;
>> coordType width;
>> coordType height;
>> int32_t depth; /* 1..32 */
>> int32_t linebytes; /* bytes per line */
>> int32_t allocated; /* bytes allocated */
>> int64_t fill; /* color to fill with */
>> int8_t sign;
>> imageType *primary; /* for secondary, assoc primary */
>> coordType primaryX; /* for secondary, offset in primary */
>> coordType primaryY; /* for secondary, offset in primary */
>> int8_t *data;
>> uint32_t flags; /* image access mode(s) */
>> int32_t verbose;
>> int32_t page; /* number of the current page */
>> double resX; /* pixels per unit in X */
>> double resY; /* pixels per unit in Y */
>> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
>> char modes[MAX_MODES]; /* modes used with open */
>> char name[MAX_NAME];
>> int64_t clientData[CLIENT_SIZE]; };
>>
>>
>> What does the following statement accomplish, given that
>> "image" and "src" are both pointers to imageType?
>>
>> (*(imageType**)&(image)->clientData[0])=(src)
>
> -> has higher precedence than &, so this is equivalent to
> (*(imageType**)& ( (image)->clientData[0] ) )=(src)
>
> The parentheses around image contribute nothing. So this is
> equivalent to
> (*(imageType**)& ( image->clientData[0] ) )=(src)
>
> clientData is an array. clientData[0] is the first element of the
> array. The & operation evaluates to the address of this element. This
> address has type pointer to int64_t.
>
> The cast operation converts this address to type pointer to pointer to
> struct.
>
> The dereference operation evaluates this address and treats the result
> as a pointer to struct located at that address.
>
> The assignment operation stores the value of src (the address of some
> struct) in the pointer evaluated above. The net result is
> clientData[0] contains the same value that src contains. This assumes
> that pointer to struct and int64_t have the same size. If the pointer
> is large, the behavior is undefined. If the pointer is smaller, the
> result in clientData[0] could be weird. Even if they are the same
> size, the effect of treating this address as an int64_t is
> implementation defined.
>
> This entire process could have been performed much more easily by
> simply casting the value of src with
> image->clientData[0] = (int64_t)src;
>
> Why they chose to store an address in an int64_t is a different
> question. Maybe there is a reason clientData cannot have type pointer
> to struct. If it did, then the code is even simpler
> image->clientData[0] = src.
Awesome reply. Thanks Barry.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-07-30 09:24 -0700 |
| Message-ID | <kfnlh0jnlfr.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #91261 |
Barry Schwarz <schwarzb@dqel.com> writes:
> On Thu, 28 Jul 2016 11:38:54 -0400, DFS <nospam@dfs.com> wrote:
>
>> Take a C structure:
>>
>> struct imageType {
>> [...]
>> int64_t clientData[CLIENT_SIZE]; };
>>
>> What does the following statement accomplish, given that
>> "image" and "src" are both pointers to imageType?
>>
>> (*(imageType**)&(image)->clientData[0])=(src)
>
> [...]
>
> This entire process could have been performed much more easily by
> simply casting the value of src with
> image->clientData[0] = (int64_t)src;
I think it is worth pointing out that this way of doing the
assignment may not do the same thing as the original (under
the natural assumption that the orginal is the same as copying
bytes with a suitable call to memcpy()). If the original
assignment is replaced with this modified version, that may
very well cause some other part of an otherwise-working
program to fail.
(Sorry, I've been bitten too many times by things like this
to let it go by without comment.)
[toc] | [prev] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2016-07-29 07:23 +1200 |
| Message-ID | <dvv4ddFakf4U8@mid.individual.net> |
| In reply to | #91241 |
On 07/29/16 03:38 AM, DFS wrote:
> Take a C structure:
>
> struct imageType {
> imageOpsType *ops;
> coordType width;
> coordType height;
> int32_t depth; /* 1..32 */
> int32_t linebytes; /* bytes per line */
> int32_t allocated; /* bytes allocated */
> int64_t fill; /* color to fill with */
> int8_t sign;
> imageType *primary; /* for secondary, assoc primary */
> coordType primaryX; /* for secondary, offset in primary */
> coordType primaryY; /* for secondary, offset in primary */
> int8_t *data;
> uint32_t flags; /* image access mode(s) */
> int32_t verbose;
> int32_t page; /* number of the current page */
> double resX; /* pixels per unit in X */
> double resY; /* pixels per unit in Y */
> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
> char modes[MAX_MODES]; /* modes used with open */
> char name[MAX_NAME];
> int64_t clientData[CLIENT_SIZE]; };
>
>
> What does the following statement accomplish, given that
> "image" and "src" are both pointers to imageType?
>
> (*(imageType**)&(image)->clientData[0])=(src)
A failed code review...
--
Ian
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-07-28 16:05 -0400 |
| Message-ID | <nndoie$ip1$1@dont-email.me> |
| In reply to | #91241 |
On 07/28/2016 11:38 AM, DFS wrote:
> Take a C structure:
>
> struct imageType {
> imageOpsType *ops;
> coordType width;
> coordType height;
> int32_t depth; /* 1..32 */
> int32_t linebytes; /* bytes per line */
> int32_t allocated; /* bytes allocated */
> int64_t fill; /* color to fill with */
> int8_t sign;
> imageType *primary; /* for secondary, assoc primary */
> coordType primaryX; /* for secondary, offset in primary */
> coordType primaryY; /* for secondary, offset in primary */
> int8_t *data;
> uint32_t flags; /* image access mode(s) */
> int32_t verbose;
> int32_t page; /* number of the current page */
> double resX; /* pixels per unit in X */
> double resY; /* pixels per unit in Y */
> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
> char modes[MAX_MODES]; /* modes used with open */
> char name[MAX_NAME];
> int64_t clientData[CLIENT_SIZE]; };
>
>
> What does the following statement accomplish, given that
> "image" and "src" are both pointers to imageType?
Assuming:
typedef struct imageType imageType;
imageType *image;
imageType *src;
Then the following code:
> (*(imageType**)&(image)->clientData[0])=(src)
takes image, and finds the clientData element in the struct that it
points at, and then locates the first element of that array. Then it
takes the address of that element, and converts the resulting pointer to
be imageType**. then it dereferences that pointer, and stores the value
of src into the location that it points at.
Here's the key problem: the conversion from int64_t* to imageType** is
covered only by 6.3.2.3p7: "If the resulting pointer is not correctly
aligned68) for the referenced type, the behavior is undefined.
Otherwise, when converted back again, the result shall compare equal to
the original pointer."
The first key point: the standard gives no guarantee that int64_t is
correctly aligned to hold an imageType*. It's fairly likely that it will
be correctly aligned, but portable code shouldn't make the assumption
that this code had defined behavior
In C2011, you could force clientData to have the correct alignment, by
adding _Alignas(struct imageType*) to the declaration of clientData. In
previous versions of C, the only way to guarantee that it was correctly
aligned was to put it into a union with an imageType*.
The second key point is hard to notice: the standard never specifies
where the resulting pointer points. If the alignment is not a problem,
most people just assume that the resulting pointer points at memory
starting in the same location as clientData. People make such
assumptions because that's the way it's almost always implemented - but
the standard makes no such guarantee.
The third key point is that the only operation that the standard
guarantees to be permitted on the resulting pointer value is conversion
back to its original type. Using it in any other way (for instance, by
dereferencing it) has behavior which is undefined by reason of a lack of
a definition.
The fourth key point is that if you are correct in your assumption that
it still points at the same location in memory as the original pointer,
then this code tries to access memory which has an effective type of
int64_t, using an lvalue with the type imageType*. That is not one of
the cases permitted by the anti-aliasing rule, 6.5p7 (unless int64_t is
a character type, which is technically possible, but extremely
unlikely), so the behavior is undefined.
You should understand the purpose of this rule: if another piece of code
refers to image->clientData[i], the compiler is NOT required to notice
that, for i=0, this expression refers to the same piece of memory as the
other expression, since they have two unrelated types. Reading and
writing through lvalues of one type need not be kept synchronized with
reads and writes through the other. It could keep one stored in a
register, while writing the other to memory. The net result is
unpredictable.
The Fifth point is that there's no guarantee that clientData will be big
enough to store the imageType* object. With int64_t, that's extremely
unlikely to happen, particularly if CLIENT_SIZE is big enough. However,
this kind of type punning is extremely popular, and it's not unusual in
other cases for someone to forget to make the target object big enough.
Any one of those issues would be sufficient to make me choose an
alternative way of handling this - and there is one. Just make
clientData a union of all the different types that client data might
come in (which could be problematic, if that list is large).
Alternatively, use memcpy() to load the representation of one type into
memory with a different type. Either of those approaches would avoid
every single issue I raised above.
However, most of the issues I've raised above are unlikely to come up in
practice, which is why type-punning like this is relatively popular. The
'where does it point" issue is the one least likely to be a problem. The
anti-aliasing rule, however, has a significant chance of causing
problems for real-world code using real-world compilers.
[toc] | [prev] | [next] | [standalone]
| From | DFS <nospam@dfs.com> |
|---|---|
| Date | 2016-07-29 10:14 -0400 |
| Message-ID | <nnfobq$ees$3@dont-email.me> |
| In reply to | #91275 |
On 7/28/2016 4:05 PM, James Kuyper wrote:
> On 07/28/2016 11:38 AM, DFS wrote:
>> Take a C structure:
>>
>> struct imageType {
>> imageOpsType *ops;
>> coordType width;
>> coordType height;
>> int32_t depth; /* 1..32 */
>> int32_t linebytes; /* bytes per line */
>> int32_t allocated; /* bytes allocated */
>> int64_t fill; /* color to fill with */
>> int8_t sign;
>> imageType *primary; /* for secondary, assoc primary */
>> coordType primaryX; /* for secondary, offset in primary */
>> coordType primaryY; /* for secondary, offset in primary */
>> int8_t *data;
>> uint32_t flags; /* image access mode(s) */
>> int32_t verbose;
>> int32_t page; /* number of the current page */
>> double resX; /* pixels per unit in X */
>> double resY; /* pixels per unit in Y */
>> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
>> char modes[MAX_MODES]; /* modes used with open */
>> char name[MAX_NAME];
>> int64_t clientData[CLIENT_SIZE]; };
>>
>>
>> What does the following statement accomplish, given that
>> "image" and "src" are both pointers to imageType?
>
> Assuming:
> typedef struct imageType imageType;
> imageType *image;
> imageType *src;
>
> Then the following code:
>
>> (*(imageType**)&(image)->clientData[0])=(src)
>
> takes image, and finds the clientData element in the struct that it
> points at, and then locates the first element of that array. Then it
> takes the address of that element, and converts the resulting pointer to
> be imageType**. then it dereferences that pointer, and stores the value
> of src into the location that it points at.
>
> Here's the key problem: the conversion from int64_t* to imageType** is
> covered only by 6.3.2.3p7: "If the resulting pointer is not correctly
> aligned68) for the referenced type, the behavior is undefined.
> Otherwise, when converted back again, the result shall compare equal to
> the original pointer."
>
> The first key point: the standard gives no guarantee that int64_t is
> correctly aligned to hold an imageType*. It's fairly likely that it will
> be correctly aligned, but portable code shouldn't make the assumption
> that this code had defined behavior
> In C2011, you could force clientData to have the correct alignment, by
> adding _Alignas(struct imageType*) to the declaration of clientData. In
> previous versions of C, the only way to guarantee that it was correctly
> aligned was to put it into a union with an imageType*.
>
> The second key point is hard to notice: the standard never specifies
> where the resulting pointer points. If the alignment is not a problem,
> most people just assume that the resulting pointer points at memory
> starting in the same location as clientData. People make such
> assumptions because that's the way it's almost always implemented - but
> the standard makes no such guarantee.
>
> The third key point is that the only operation that the standard
> guarantees to be permitted on the resulting pointer value is conversion
> back to its original type. Using it in any other way (for instance, by
> dereferencing it) has behavior which is undefined by reason of a lack of
> a definition.
>
> The fourth key point is that if you are correct in your assumption that
> it still points at the same location in memory as the original pointer,
> then this code tries to access memory which has an effective type of
> int64_t, using an lvalue with the type imageType*. That is not one of
> the cases permitted by the anti-aliasing rule, 6.5p7 (unless int64_t is
> a character type, which is technically possible, but extremely
> unlikely), so the behavior is undefined.
> You should understand the purpose of this rule: if another piece of code
> refers to image->clientData[i], the compiler is NOT required to notice
> that, for i=0, this expression refers to the same piece of memory as the
> other expression, since they have two unrelated types. Reading and
> writing through lvalues of one type need not be kept synchronized with
> reads and writes through the other. It could keep one stored in a
> register, while writing the other to memory. The net result is
> unpredictable.
>
> The Fifth point is that there's no guarantee that clientData will be big
> enough to store the imageType* object. With int64_t, that's extremely
> unlikely to happen, particularly if CLIENT_SIZE is big enough. However,
> this kind of type punning is extremely popular, and it's not unusual in
> other cases for someone to forget to make the target object big enough.
>
> Any one of those issues would be sufficient to make me choose an
> alternative way of handling this - and there is one. Just make
> clientData a union of all the different types that client data might
> come in (which could be problematic, if that list is large).
> Alternatively, use memcpy() to load the representation of one type into
> memory with a different type. Either of those approaches would avoid
> every single issue I raised above.
>
> However, most of the issues I've raised above are unlikely to come up in
> practice, which is why type-punning like this is relatively popular. The
> 'where does it point" issue is the one least likely to be a problem. The
> anti-aliasing rule, however, has a significant chance of causing
> problems for real-world code using real-world compilers.
Thanks for the excellent, detailed reply.
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-07-30 14:45 -0400 |
| Message-ID | <c2aa5c4d-cbf3-242a-f760-a654cf033d19@verizon.net> |
| In reply to | #91275 |
On 07/28/2016 04:05 PM, James Kuyper wrote: ... > The fourth key point is that if you are correct in your assumption that > it still points at the same location in memory as the original pointer, > then this code tries to access memory which has an effective type of > int64_t, using an lvalue with the type imageType*. That is not one of > the cases permitted by the anti-aliasing rule, 6.5p7 (unless int64_t is > a character type, which is technically possible, but extremely > unlikely), so the behavior is undefined. Tim Rentsch notified me by e-mail of a point I got wrong in that explanation: while a character type can be used to alias a pointer type, the reverse is not true. I even discussed this issue recently (with supercat@casperkitty.com?), yet I still forgot about it. The reason in this case isn't tiredness, but the fact that I find this asymmetry in the anti-aliasing rules counter-intuitive. If an array of character type is sufficiently long, correctly aligned (which is easier to arrange in C2011 (using _Alignas()) than it was in older versions of C, and does not contain a trap representation for the aliasing type, it seems to me that you should be permitted to alias it - but the C standard only allows aliasing in the other direction. supercat - I'll be ignoring your inevitable replies to this message.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-08-01 05:19 -0700 |
| Message-ID | <e7444e89-94de-4db0-9992-9ec8976d6262@googlegroups.com> |
| In reply to | #91371 |
On Saturday, July 30, 2016 at 1:46:01 PM UTC-5, James R. Kuyper wrote: > Tim Rentsch notified me by e-mail of a point I got wrong in that > explanation: while a character type can be used to alias a pointer type, > the reverse is not true. I even discussed this issue recently (with > supercat@casperkitty.com?), yet I still forgot about it. The reason in > this case isn't tiredness, but the fact that I find this asymmetry in > the anti-aliasing rules counter-intuitive. If an array of character type > is sufficiently long, correctly aligned (which is easier to arrange in > C2011 (using _Alignas()) than it was in older versions of C, and does > not contain a trap representation for the aliasing type, it seems to me > that you should be permitted to alias it - but the C standard only > allows aliasing in the other direction. I've seen no reason to believe that the asymmetry was intended with regard to character arrays, which can of course only be accessed with (wait for it)... ** pointers to character types ** Any workable set of aliasing rules would be a fair bit longer than the rules in C89, and the authors of the Standard almost certainly expected that any quality compiler would recognize aliasing in the cases where it is obvious. While it would have been odd to omit the case where an object of its exact type, I think their real interest was ensuring that all compilers would recognize certain cases *beyond* those where it would be obvious--those where pointers differ only by qualifiers or signedness, or where things were accessed via character pointer.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-07-30 09:17 -0700 |
| Message-ID | <kfnpopvnlrs.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #91241 |
DFS <nospam@dfs.com> writes:
> Take a C structure:
>
> struct imageType {
> imageOpsType *ops;
> coordType width;
> coordType height;
> int32_t depth; /* 1..32 */
> int32_t linebytes; /* bytes per line */
> int32_t allocated; /* bytes allocated */
> int64_t fill; /* color to fill with */
> int8_t sign;
> imageType *primary; /* for secondary, assoc primary */
> coordType primaryX; /* for secondary, offset in primary */
> coordType primaryY; /* for secondary, offset in primary */
> int8_t *data;
> uint32_t flags; /* image access mode(s) */
> int32_t verbose;
> int32_t page; /* number of the current page */
> double resX; /* pixels per unit in X */
> double resY; /* pixels per unit in Y */
> int32_t resUnit; /* 0=nores 1=unk 2=inch 3=cm */
> char modes[MAX_MODES]; /* modes used with open */
> char name[MAX_NAME];
> int64_t clientData[CLIENT_SIZE]; };
>
>
> What does the following statement accomplish, given that
> "image" and "src" are both pointers to imageType?
>
> (*(imageType**)&(image)->clientData[0])=(src)
You've gotten good explanations from some other people.
I would like to add a slightly different slant.
When you run into a problem like this, I suggest taking
a first step of pruning the example down to its barest
essentials. In the case here that might look like this:
struct imageType {
/* ... other members ... */
int64_t clientData[CLIENT_SIZE];
};
typedef imageType *Pointer_to_image;
Pointer_to_image image, src;
* (Pointer_to_image *) & image->clientData[0] = src;
Doing this puts you a leg up on understanding the problem, and
on being ready for an answer. And it does that even if your
"simplification" is wrong.
If/when you post to the newsgroup, post the full source first,
and the stripped down version second. Doing this will let
responders determine whether you did the first step correctly,
and advise you if not. Also, seeing that you put in an effort
to understand the problem makes it more likely that you will
get good answers.
Simplifying the problem in this way also lets you see immediately
a key element of what's wrong here. In particular, the code
takes an address of something of type int64_t, and casts that
address to some sort of pointer type, and then stores through
that casted pointer value. Doing that is almost always wrong.
Also it should raise a red flag to indicate that something else
is wrong somewhere else in the program, but for now let's assume
that the rest of the program is okay, and limit our concerns to
just this one line.
The simplified assignment line here may be (and I hope is) simple
enough so you can see what it means to accomplish: copying the
value of 'src' to 'image->clientData[0]', as raw data rather than
converting between data types. In most cases a good way to do
that is with a call to memcpy():
memcpy( & image->clientData[0], &src, sizeof src );
Writing the copy as a call to memcpy() should make it easier to
understand what is going on. Also it avoids the undefined
behavior that the storing-through-a-casted-pointer-type method
almost always incurs.
Does that all make sense?
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web