Storage of char in 64 bit machine

A

aruna.mysore

Hi all,

I have a simple definitioin in a C file something like this.

main()
{
char a;
.......
int k;
}

Since character is 8 bit, how is it stored in the machine in a 64 bit
machine. If it is word aligned, what about the rest of the bytes. What
about the retrievel of the char c, will it be expensive. Is it
expensive w.r.t read or write.

Thanx and Regards,
Aruna
 
C

Chris Dollin

Hi all,

I have a simple definitioin in a C file something like this.

main()
{
char a;
.......
int k;
}

Since character is 8 bit, how is it stored in the machine in a 64 bit
machine.

That's up to the compiler, and it depends what you mean by "a 64 bit
machine".
If it is word aligned, what about the rest of the bytes.

What about the rest of what bytes?
What about the retrievel of the char c, will it be expensive.

That depends on the compiler. I wouldn't /expect/ it to be expensive.
`c` might well be stored in a register, for example [1].
Is it expensive w.r.t read or write.

That depends.

Have you a problem for which this would be an explanation?

(Ignoring your `...`s, the compiler could arrange that `main` above
is implemented with no local variables at all ...)
 
L

Lew Pitcher

Hi all,

I have a simple definitioin in a C file something like this.

main()
{
char a;
.......
int k;
}

Since character is 8 bit,

Actually, a character isn't 8 bit. I'm simplifying a bit, but a
character is guaranteed to be /at least/ 8 bits wide, and is permitted
to be as wide as necessary. For all we (or you) know, a char might be
64bits wide on your platform.
how is it stored in the machine in a 64 bit
machine. If it is word aligned, what about the rest of the bytes. What
about the retrievel of the char c, will it be expensive. Is it
expensive w.r.t read or write.

Storage and alignment are the concerns of the compiler and the
"execution platform". It /could/ be that this compiler does as you
suspect, and uses 8 bits out of 64 to store a char entity, leaving the
remaining bits unused and unusable.

Alternatively, the compiler /may/ reorganize your allocations (at any
one level) such that all the small entities are grouped together in
storage, permitting other char values to occupy the "slack" space from
your allocation of "char a;".

This is entirely up to the implementation of the compiler; AFAICR, the
C standard doesn't require any specific behaviour in this regard. If
the compiler's code organization and optimization are a problem for
you, you'll either have to switch compilers or change your code to live
more optimally within the restrictions that the compiler places on you.
Thanx and Regards,
Aruna

You're welcome
 
W

Walter Roberson

Lew Pitcher said:
Alternatively, the compiler /may/ reorganize your allocations (at any
one level) such that all the small entities are grouped together in
storage, permitting other char values to occupy the "slack" space from
your allocation of "char a;".
This is entirely up to the implementation of the compiler; AFAICR, the
C standard doesn't require any specific behaviour in this regard.

I'm not sure what you mean by "at any one level".

Note that compilers are not permitted to reorder fields in a struct,
only to put padding between the fields. (I would tend to think
that fields of any one struct are all at the same "level", provided
they are not aggregate types.)
 
L

Lew Pitcher

Walter said:
I'm not sure what you mean by "at any one level".

I wasn't too clear there, so let me elaborate

Assume the code fragment...

{
/* "level" A */
char aa; int ab, ac;
char ad;

{
/* "level" B */
char ae;

}
}

In the nesting level I've called "A", the compiler /may/ optimize the
allocations of aa, ab, ac, and ad so that aa and ad are adjacent in
"memory". For the OP's example of 8-bit char data items and 64bit
wordsizes, this could mean that 16bits of one 64bit word is occupied by
2 independant char data items, wasting only 48 bits of hidden padding
(assuming that the compiler word-aligns each allocation). The OPs
scenario would have each char data item (aa and ad) possible occupy 8
bits of unique 64bit words, leaving 112 bits (2 x 56) unused.

However, because variable ae is declared within a different "level" of
the code, I doubt that most compilers would "optimize" its allocation
to occupy another 8 bits within that 64bit allocation that aa and ad
potentially occupy.

That's what I meant by "at any one level"
Note that compilers are not permitted to reorder fields in a struct,
only to put padding between the fields. (I would tend to think
that fields of any one struct are all at the same "level", provided
they are not aggregate types.)

Up to this point, structure (and union) allocations haven't been part
of the discussion. Your point is taken; the compiler isn't permitted to
reorder fields within a structure, even in order to take advantage of
the potential space savings that such a reorganization might offer.
 
M

Mikhail Teterin

Lew said:
Actually, a character isn't 8 bit. I'm simplifying a bit, but a
character is guaranteed to be /at least/ 8 bits wide, and is permitted
to be as wide as necessary. For all we (or you) know, a char might be
64bits wide on your platform.

So, comparing, say, 4-char arrays (like currency codes) can NOT be done in
the following way?

typedef union {
char acCUR[4];
int32_t iCUR;
} xCUR;

int
CurEqual(xCUR *c1, xCUR *c2)
{
if (c1->iCUR == c2->iCUR)
printf("Same currency %s\n", c1->acCUR);
else
printf("%s and %s are different\n",
c1->acCUR, c2->acCUR);
}

Having to call a strcmp() in such cases seems like a bad waste to me, but I
don't see, how the compiler could possibly optimize such a code without the
trick above...

-mi
 
W

Walter Roberson

Lew Pitcher wrote:
So, comparing, say, 4-char arrays (like currency codes) can NOT be done in
the following way?
typedef union {
char acCUR[4];
int32_t iCUR;
} xCUR;

Note that int32_t is not certain to exist at all. There is an
int_least32_t that is certain to be at least 32 bits, and that will
exist on all C99 platforms.

sizeof int32_t will tell you how many "bytes" int32_t requires, and
by definition each char is exactly one byte long. However, it should
not be assumed that int32_t and char acCUR[sizeof int32_t] both
offer the same number of bits of "useable" storage, as the signed
int types are permitted to have internal non-value bits. When
you are trying to do type-punning via unions, you should use
unsigned char to be sure to be able to access all bits (including
the ones that the other fields might happen to treat as non-value bits.)
 
L

Lew Pitcher

Mikhail said:
Lew said:
Actually, a character isn't 8 bit. I'm simplifying a bit, but a
character is guaranteed to be /at least/ 8 bits wide, and is permitted
to be as wide as necessary. For all we (or you) know, a char might be
64bits wide on your platform.

So, comparing, say, 4-char arrays (like currency codes) can NOT be done in
the following way?

typedef union {
char acCUR[4];
int32_t iCUR;
} xCUR;

Not portably, no.

int32_t isn't guaranteed to exist in every compliant compilation system

CHAR_BITS isn't guaranteed to be equal to 8 in every compliant
compilation system

char acCUR[4]; isn't guaranteed to contain 32 bits in every compliant
compilation system
 
F

Flash Gordon

Mikhail said:
Lew said:
Actually, a character isn't 8 bit. I'm simplifying a bit, but a
character is guaranteed to be /at least/ 8 bits wide, and is permitted
to be as wide as necessary. For all we (or you) know, a char might be
64bits wide on your platform.

So, comparing, say, 4-char arrays (like currency codes) can NOT be done in
the following way?

typedef union {
char acCUR[4];
int32_t iCUR;
} xCUR;

int
CurEqual(xCUR *c1, xCUR *c2)
{
if (c1->iCUR == c2->iCUR)
printf("Same currency %s\n", c1->acCUR);
else
printf("%s and %s are different\n",
c1->acCUR, c2->acCUR);
}

It definitely cannot. Apart from the fact that int could be only 16 bits
it could also have a trap representation, or two representations of the
same value (+0 and -0).
Having to call a strcmp() in such cases seems like a bad waste to me, but I
don't see, how the compiler could possibly optimize such a code without the
trick above...

It could optimise it very easily using strcmp (inline the function and
then optimise it in place with the rest of the code). Potentially it
could be *more* efficient since it might compare fewer bytes.

If you think something like that is an optimisation worth considering
then you should not attempt *any* optimisation since at best you are
likely to have no effect or make it slower, but you are just as likely
to break it. Write code to be simple and understandable and let the
optimisation phase of the compiler do its job, it's probably a lot
better at it than you.
 
E

Eric Sosman

Mikhail Teterin wrote On 08/14/06 13:50,:
Lew Pitcher wrote:

Actually, a character isn't 8 bit. I'm simplifying a bit, but a
character is guaranteed to be /at least/ 8 bits wide, and is permitted
to be as wide as necessary. For all we (or you) know, a char might be
64bits wide on your platform.


So, comparing, say, 4-char arrays (like currency codes) can NOT be done in
the following way?

typedef union {
char acCUR[4];
int32_t iCUR;
} xCUR;

int
CurEqual(xCUR *c1, xCUR *c2)
{
if (c1->iCUR == c2->iCUR)
printf("Same currency %s\n", c1->acCUR);
else
printf("%s and %s are different\n",
c1->acCUR, c2->acCUR);
}

Having to call a strcmp() in such cases seems like a bad waste to me, but I
don't see, how the compiler could possibly optimize such a code without the
trick above...

The trick will work on many machines, but you're right:
it works "by chance, not by design."

But is the loss worth weeping over? Consider: You've
saved a strcmp() of two short strings, but at what cost?
If your currency codes are "naturally" strings, you've now
got to bundle them up into xCUR unions; you must actually
copy the string characters into the acCUR members. This
uglifies your code -- and avoiding a strcmp() at the cost
of two calls to strcpy() doesn't seem like a step in the
right direction!

If you're making "a lot" of these comparisons, it might
make more sense to convert the strings to numeric codes at
the point when they're read in or whatever. You're probably
going to validate the strings by looking them up in a table
of "known" currency codes or some such, right? Having done
the lookup, it's pretty easy to get the table to provide an
easily-manipulated numeric code that can be used elsewhere in
the program; you'd just deal with strings "on the periphery."
That'd be cleaner, probably faster, and certainly more portable.
 
M

Mikhail Teterin

Eric said:
If you're making "a lot" of these comparisons, it might
make more sense to convert the strings to numeric codes at
the point when they're read in or whatever.

Yes, this is an alternative... But being able to access them as strings at
the same time (such as for printing) remains desirable.
You're probably going to validate the strings by looking them up in a
table of "known" currency codes or some such, right? šHaving done
the lookup, it's pretty easy to get the table to provide an
easily-manipulated numeric code that can be used elsewhere in
the program; you'd just deal with strings "on the periphery."

The strings come from database, actually, and have to be memcpy-ied once
anyway. Treating them as 4-byte integers would be a nice convenience not
only for comparisions (of which the code does a lot indeed), but also for
future assignments (`trade1.cur.iCur = trade2.cur.iCur' vs.
`strcpy(trade1.cur, trade2.cur)').

Keeping an internal table of numbers vs. strings is rather inconvenient when
debugging (can't just look at the string), and also wasteful -- the four
bytes is already an overkill (2^32 possible currencies, when the maximum is
really under a thousand)...

As so often happens, the really cool things C can do are "not supported" and
frowned upon by the authoritatively-sounding gurus...

Thanks,

-mi
 
S

Stephen Sprunk

Mikhail Teterin said:
Lew said:
Actually, a character isn't 8 bit. I'm simplifying a bit, but a
character is guaranteed to be /at least/ 8 bits wide, and is permitted
to be as wide as necessary. For all we (or you) know, a char might be
64bits wide on your platform.

So, comparing, say, 4-char arrays (like currency codes) can NOT be
done in the following way?

typedef union {
char acCUR[4];
int32_t iCUR;
} xCUR; ....
Having to call a strcmp() in such cases seems like a bad waste to me,
but I don't see, how the compiler could possibly optimize such a code
without the trick above...

That'll work on most modern systems, but it's not portable and it won't work
everywhere. Use strcmp(); that's guaranteed to work.

The implementation is likely to have a very, very clever strcmp() that will
perform at least as well as your code (possibly doing the same thing
internally, if it's known to be safe) and likely even better if the compiler
is reasonably modern due special knowledge and treatment of common
functions/idioms.

Remember, premature optimization is the root of all evil. Avoid the
temptation until profiling shows the clear, less "clever" option isn't fast
enough -- and then consider using a better algorithm, if possible, before
using unportable code.

S
 
F

Flash Gordon

Flash said:
Mikhail said:
Lew said:
Actually, a character isn't 8 bit. I'm simplifying a bit, but a
character is guaranteed to be /at least/ 8 bits wide, and is permitted
to be as wide as necessary. For all we (or you) know, a char might be
64bits wide on your platform.

So, comparing, say, 4-char arrays (like currency codes) can NOT be
done in
the following way?

typedef union {
char acCUR[4];
int32_t iCUR;
} xCUR;

It definitely cannot. Apart from the fact that int could be only 16 bits

<snip>

Oops. I missed that it was int32_t! Obviously that will be 32 bits if it
exists, but it might not exist.

My comments about optimisation stand though. Don't do it. Experts only
consider micro-optimisation if they *know* they have a problem,
otherwise they write the code to be understandable.
 
E

Eric Sosman

Mikhail Teterin wrote On 08/14/06 15:34,:
Yes, this is an alternative... But being able to access them as strings at
the same time (such as for printing) remains desirable.

printf ("Currency = %s\n" CurrencyName[index]);

or maybe

printf ("Currency = %s\n", Currency[index].name);
The strings come from database, actually, and have to be memcpy-ied once
anyway. Treating them as 4-byte integers would be a nice convenience not
only for comparisions (of which the code does a lot indeed), but also for
future assignments (`trade1.cur.iCur = trade2.cur.iCur' vs.
`strcpy(trade1.cur, trade2.cur)').

Keeping an internal table of numbers vs. strings is rather inconvenient when
debugging (can't just look at the string), and also wasteful -- the four
bytes is already an overkill (2^32 possible currencies, when the maximum is
really under a thousand)...

I think that simply adds support to my suggestion: Instead of
maintaining a lot of four-byte strings, you could be storing a lot of
two-byte (probably) `short' table indices.
As so often happens, the really cool things C can do are "not supported" and
frowned upon by the authoritatively-sounding gurus...

Yeah, well. One might equally opine that only the spoiled rotten
button-clicking GUI-besotted drooling whining spaced-out brainless --
oh, sorry, didn't mean to offend -- um, only "a few extraordinary
people" consider such trickery "really cool."
 
K

Keith Thompson

Lew Pitcher said:
I wasn't too clear there, so let me elaborate

Assume the code fragment...

{
/* "level" A */
char aa; int ab, ac;
char ad;

{
/* "level" B */
char ae;

}
}

In the nesting level I've called "A", the compiler /may/ optimize the
allocations of aa, ab, ac, and ad so that aa and ad are adjacent in
"memory". For the OP's example of 8-bit char data items and 64bit
wordsizes, this could mean that 16bits of one 64bit word is occupied by
2 independant char data items, wasting only 48 bits of hidden padding
(assuming that the compiler word-aligns each allocation). The OPs
scenario would have each char data item (aa and ad) possible occupy 8
bits of unique 64bit words, leaving 112 bits (2 x 56) unused.

However, because variable ae is declared within a different "level" of
the code, I doubt that most compilers would "optimize" its allocation
to occupy another 8 bits within that 64bit allocation that aa and ad
potentially occupy.

That's what I meant by "at any one level"

There are several possible strategies for allocating objects in nested
and/or parallel blocks. For example:

void foo(void)
{
int outer;
{
int inner1;
}
{
int inner2a;
int inner2b;
}
}

A simple-minded compiler might allocate space for all 4 objects on
entry to the function.

Or the compiler might recognize that the two inner scopes cannot be
active at the same time, and overlap their allocations, but still
allocate everything on function entry (requiring space for 3 ints).

Or the compiler might allocate space for each block's local objects
only on entry to the block, and deallocate it on leaving the block.
In this case, the compiler (actually the generated code) would
allocate space for one int on entry to foo().

A compiler following the first or second strategy can freely rearrange
the allocated objects to minimize gaps, even across block boundaries.
 
K

Keith Thompson

Mikhail Teterin said:
Lew said:
Actually, a character isn't 8 bit. I'm simplifying a bit, but a
character is guaranteed to be /at least/ 8 bits wide, and is permitted
to be as wide as necessary. For all we (or you) know, a char might be
64bits wide on your platform.

So, comparing, say, 4-char arrays (like currency codes) can NOT be done in
the following way?

typedef union {
char acCUR[4];
int32_t iCUR;
} xCUR;

int
CurEqual(xCUR *c1, xCUR *c2)
{
if (c1->iCUR == c2->iCUR)
printf("Same currency %s\n", c1->acCUR);
else
printf("%s and %s are different\n",
c1->acCUR, c2->acCUR);
}

Having to call a strcmp() in such cases seems like a bad waste to me, but I
don't see, how the compiler could possibly optimize such a code without the
trick above...

Even if all your assumptions are valid, strcmp() doesn't do the same
thing as your int32_t comparison. Comparing two int32_t values
compares all the bits; strcmp() only compares up to the '\0' that
terminates each string.

For example, you could have c1->acCUR equal to
{ 'X', 'Y', '\0', 'A' }
and c2->acCUR equal to
{ 'X', 'Y', '\0', 'B' }
Not all the corresponding array elements are equal, but strcmp() will
ignore the 'A' and 'B' characters.
 
M

Mikhail Teterin

Eric said:
Yeah, well. šOne might equally opine that only the spoiled rotten
button-clicking GUI-besotted drooling whining spaced-out brainless --
oh, sorry, didn't mean to offend -- um, only "a few extraordinary
people" consider such trickery "really cool."

Give me an example, of trickery, that YOU consider really cool, that is also
portable...

-mi
 
M

Mikhail Teterin

Keith said:
For example, you could have c1->acCUR equal to
{ 'X', 'Y', '\0', 'A' }
and c2->acCUR equal to
{ 'X', 'Y', '\0', 'B' }
Not all the corresponding array elements are equal, but strcmp() will
ignore the 'A' and 'B' characters.

I know. Currencies, however, are all 3-character strings (plus the
terminating '\0'). Thus they are perfectly suited to be treated as int32_t,
when convenient.

That it is not 100% portable is already rammed into me by the friendly folks
on this board. I'd like to know an example of the actual hardware/compiler
combo, where it would not work, though...

Thanks,

-mi
 
E

Eric Sosman

Mikhail said:
Eric Sosman wrote:




Give me an example, of trickery, that YOU consider really cool, that is also
portable...

Straying from topicality, hence the change in Subject ...

Once upon a time, in my rambunctious youth, I would have had
no trouble answering your question. It was cool to use `x&(x-1)'
to zero the lowest-order one-bit, it was cool to use "horizontal
addition" to count the one-bits or compute parity, it was cool to
use `POP PC' instead of `RETURN' (yes! it was faster!), it was
cool to use a computed GOTO instead of an IF (as you may deduce,
my R.Y. was some time ago ...)

But a funny thing happened: Progress.

The first computer I used had forty thousand decimal digits
of memory, of which one hundred locations were reserved for the
table that allowed it to add and subtract integers. All other
arithmetic -- integer multiplication and division, all kinds of
floating-point -- were done via subroutines. I no longer recall
the instruction timings for that machine, but I do remember that
when it was replaced by a newer system that could execute some
kinds of instructions in LESS THAN TWO MICROSECONDS it seemed
like an onrush of unthinkable speed.

On that machine, in my R.Y., "cool tricks" were a necessity,
a staple of daily existence. They were the difference between
a program that ran and a program that failed (sorry: your code
is twenty digits bigger than memory). Moving a few instructions
out of an inner loop was a big deal -- and the compilers of the
day were not very good at such things. After all, they had only
the resources of the same slow small machine at their disposal,
and were already stressed simply to get the translations done.
(It was not unusual for a compiler to be a program of five or
so sequential "passes" communicating intermediate results via
temporary files, sometimes on reels of magnetic tape or even on
decks of punched cards.) In my R.Y. the compilers needed all the
help we could give them.

Ah, but there's been Progress.

Not quite twenty years ago I bought my first x386 machine,
and it wasn't long before I had a sort of magical realization:
My very modest 640x480 display was being driven by a bare-bones
low-end video card with SIXTEEN TIMES THE MEMORY CAPACITY OF MY
ENTIRE COLLEGE CAMPUS! In a PERIPHERAL, for Crissakes!

It sort of brought home to me the degree to which progress
had changed my world -- and the sobering fact that the cool tricks
I once employed to squeeze three more instructions into the size
of one disk sector were simply no longer relevant. The economic
facts that once ruled my profession no longer held; a different
dynamic was loose in the world.

The world's finest maker of buggy whips goes to the poorhouse
when the automobile comes along. The careful choice of leathers
for different parts of the whip, the judicious use of oak for
stiffness and ash for springiness that gives the handle its
inimitable feel, the consummate craftsmanship in the double-looped
stitching to prevent water and snow from penetrating and rotting
the interior ... Irrelevant. Useless. Outmoded. Inane.

As one can admire the skills of the buggy whip makers and the
inordinate amount of labor they would expend on a single whip, so
one can admire the "cool tricks" of the programmer-craftsmen and
their perseverance at desk-checking to avoid messing up even one
of their couple of compiles per day. (I remember once poring over
a core dump trying to see whether a failed program had at least
computed the right intermediate results before dying, doing pencil-
and-paper long division IN HEXADECIMAL to check the results.) As
I say, one can admire the ingenuity, cleverness, sneakiness, and
sweat -- but the proper venue for such admiration is the same as
for admiring buggy whips: In a museum of antiquities, not in code
written for the present day.

Can you make a fire by rubbing two sticks together? It's
doable -- I've seen it done -- but do you feel any less "able"
if you cannot do it yourself?

Do you know how to flake flint to make a knife edge? If not,
do you find your daily life impeded by the loss of that cool trick?

What kind of sapling will make a good bow? What animal's
entrails will you wind to make a good bowstring, and how do you
get them out of the carcase intact, and how do you prevent them
from rotting -- how, for that matter, do you catch and kill the
animal?

All these once-essential skills are now become irrelevant.
Interesting to antiquarians, perhaps, and in that limited sense
still cool -- but of no serious consequence any more. You could
be the world's greatest fire-maker, the best flint-flaker ever
to "Ouch!" his thumb, and the best bowyer who ever bent birch,
and you would be ... what? Not chatting on Usenet, I imagine.

I have (figuratively) lived in caves and eaten tasty nematodes
scrabbled from the dirt (there's a trick to finding the good ones,
I'll show ya how it's done). Nowadays I live in a house and get my
food from a supermarket -- and y'know? I really don't miss the
nematodes all that much.

And *that's* cool.
 
J

Jack Klein

I know. Currencies, however, are all 3-character strings (plus the
terminating '\0'). Thus they are perfectly suited to be treated as int32_t,
when convenient.

That it is not 100% portable is already rammed into me by the friendly folks
on this board. I'd like to know an example of the actual hardware/compiler
combo, where it would not work, though...

Thanks,

-mi

Almost every single DSP in existence, for starters.

I routinely work on a TI DSP these days where CHAR_BIT is 16 and
sizeof(int) is 1.

So your union of one int (and unsigned int would be better for type
punning) and an array of four chars would be four bytes long, and
comparing the int member of two unions would only compare the first
character of the array in each.

Even if you have a <stdint.h>, which TI's compiler did not provide but
I did (for the types actually supported), and have int32_t typedef'ed
to signed long (and unit32_t typedef'ed to unsigned long), the size of
the (un)signed long at the beginning of union is only two bytes.

There are some 32 bit DSPs where all of char, short, int, and long
have 32 bits.
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,218
Latest member
JolieDenha

Latest Threads

Top