Question on Data Type Declarations

P

phaedrus

Hi guys,

Clue me up, please.

When we declare variables like

float x;
int y;
char z;
long w;

and so on, are we doing anything more than just reserving a certain
number of bytes for these data types to occupy?
I mean, the compiler doesn't flag up an error if we try to store (for
example) a letter 'a' in an int, does it? Presumably we can store
*any* type of data in *any* variable type provided said declared type
has enough room to accommodate it?
Aren't the names 'float', 'int', 'char' and so forth simply there to
remind the programmer what data type the variable should be? The
compiler is blind to this human distinction, isn't it?
 
S

Seebs

Clue me up, please.

What you ask may be possible.
When we declare variables like
float x;
int y;
char z;
long w;
and so on, are we doing anything more than just reserving a certain
number of bytes for these data types to occupy?

Yes.

We are reserving space and specifying how that space is to be interpreted.
I mean, the compiler doesn't flag up an error if we try to store (for
example) a letter 'a' in an int, does it?

No. But that's because:
1. 'a' is a constant of type int to begin with.
2. Even if you get the character (say, referring to "a"[0]), it's still got
an integral-type value.
Presumably we can store
*any* type of data in *any* variable type provided said declared type
has enough room to accommodate it?
Nope.

Aren't the names 'float', 'int', 'char' and so forth simply there to
remind the programmer what data type the variable should be? The
compiler is blind to this human distinction, isn't it?

Not in the least.

What might throw you off is that the compiler will, in many cases,
automatically perform the given conversion.

Consider:
#include <stdio.h>

int main(void) {
float f = 3.14;
int i;
i = f;
f = i;
printf("%f\n", f);
return 0;
}

This, on my system (where sizeof(float) happens to == sizeof(int)), prints
"3.000000". No warnings, no errors.

When f is assigned to i, the compiler automatically converts the float to
an int. When i is assigned back to f, the compiler automatically converts
back. But converting float to int truncates -- it drops fractional parts.

This can happen the other way, too:

#include <stdio.h>

int main(void) {
int i = 67108865;
float f;
f = i;
i = f;
printf("%d\n", i);
return 0;
}

On my system, this yields 67108864.

Perfectly reasonable, etcetera.

So, no. It's not just amount of storage; it's also representation that is
defined.

-s
 
S

Seebs

I consider myself duly clued! THanks!

You didn't insult anyone, and you didn't make up an even less likely claim
and defend it to the last. I think they'll yank your Usenet license.

Seriously, though, interesting question; it wouldn't have occurred to me,
but it was interesting to think about.

-s
 
K

Keith Thompson

Seebs said:
Clue me up, please.

What you ask may be possible.
When we declare variables like
float x;
int y;
char z;
long w;
and so on, are we doing anything more than just reserving a certain
number of bytes for these data types to occupy?

Yes.

We are reserving space and specifying how that space is to be interpreted.
I mean, the compiler doesn't flag up an error if we try to store (for
example) a letter 'a' in an int, does it?

No. But that's because:
1. 'a' is a constant of type int to begin with.
2. Even if you get the character (say, referring to "a"[0]), it's still got
an integral-type value.
Presumably we can store
*any* type of data in *any* variable type provided said declared type
has enough room to accommodate it?
Nope.

Aren't the names 'float', 'int', 'char' and so forth simply there to
remind the programmer what data type the variable should be? The
compiler is blind to this human distinction, isn't it?

Not in the least.

What might throw you off is that the compiler will, in many cases,
automatically perform the given conversion.
[snip]

On the other hand, in many cases there is no implicit conversion,
and the compiler will reject any attempt to perform an assignment.
(Well, actually it's only required to issue a diagnostic, but that
should tell you to reject it yourself if the compiler isn't kind
enough to do it for you.)

All the examples you used (float, int, char, long) are numeric types,
and any numeric type can be implicitly converted to any other numeric
type.

Try compiling this:

#include <stddef.h>
int main(void)
{
double d = 0.0;
int *p = NULL;
struct { int x; int y; } s = { 0, 0 };

d = 42; /* ok, implicit conversion of int to double */
p = 42; /* invalid, can't convert int to pointer (unless it's
a null pointer constant. */
s = 42; /* complete nonsense; you can't convert *anything* to a
struct type unless it's already of the same type */
return 0;
}
 
P

phaedrus

All the examples you used (float, int, char, long) are numeric types

Er, hang on. I'm obviously still not completely clued-up here. You say
the type 'char' is numeric?? Float, int and long are obviously
numeric, but char is obviously a single character like 'a' or 'b' or
'c' for example. A char, though capable of storing up to 0xFF of
values, is still just an alphabetical character, n'est pas?
 
N

Nick

phaedrus said:
Er, hang on. I'm obviously still not completely clued-up here. You say
the type 'char' is numeric?? Float, int and long are obviously
numeric, but char is obviously a single character like 'a' or 'b' or
'c' for example. A char, though capable of storing up to 0xFF of
values, is still just an alphabetical character, n'est pas?

Yes. It is confusing, but in C all a char is is a particular sort of
small integer, and the single quotes around a character are just a way
of saying "the integer that represents that character in the character
set being used during compilation".

On an ASCII machine:
char a = 65;
and
int a = 'A';

both result in an integer variable (as distinct from an "int") holding
the same value.
 
B

Ben Bacarisse

phaedrus said:
Er, hang on. I'm obviously still not completely clued-up here. You say
the type 'char' is numeric?? Float, int and long are obviously
numeric, but char is obviously a single character like 'a' or 'b' or
'c' for example. A char, though capable of storing up to 0xFF of
values, is still just an alphabetical character, n'est pas?

Non, mon amie. The name is confusing you. signed char and unsigned
char are simply two of C's integers types. Because characters in old
encodings like ASCII are all small numbers, char objects can be used
to hold them; but there are characters that C programmers need to
manipulate that don't fit in a char object, and char objects can be
used to manipulate things that aren't characters.

If I were manipulating pixels, I might use:

typedef unsigned char pixel[3];
pixel p = {128, 128, 128};

and do arithmetic on the components as with any other integer type:
p[2] = (p[0] + p[1]) / 2; and so on. Nowadays, I'd probably consider
using one of C99's size-specific types like uint_least8_t or
uint_fast8_t but that is a whole other issue.

On the other size, C provides wchar_t -- yet another integer type --
to hold characters that don't fit into a char.

There is no way to write "the letter A" in C simply because there is
no universal agreement about what number should be used to represent
an A. In C, 'A' is just an integer constant, and while it will be the
correct number to represent an A on the machine the C compiler is for,
the resulting code may not work if it is processing data coming from a
machine that has some other idea about the letter A.
 
P

phaedrus

OK, all understood.

I'm not exactly a C noobie, btw. I'm just very rusty and trying to get
up to speed again. I was actually posting here some 15 years ago and
always remember this group had one of the best signal/noise ratios of
any I've ever seen and a extraordinarily erudite contributorship, and
not just in computer matters. I recognize some familiar names from the
past. Nice to see this group has survived all those years without too
much spam and personal attacks blighting it. Well done, chaps.
 
B

Ben Bacarisse

Seebs said:
No. It's a small (CHAR_BIT bits) integer.

As it happens, that's sufficient to represent characters on most systems,
but it's actually just another integer type. It's in no way special or
different because of the use to represent characters.

But it is special! What other integer type has the property that
(T)-1 < 0 varies from implementation to implementation?

Whether char is special in the regard "because of the use to represent
characters" is certainly debatable, but I think a case can be that
char is different for exactly this reason. The fact that the
signedness of char is up to the compiler is a serious obstacle to
treating it as "just another integer type".
 
B

Bruce Cook

phaedrus said:
Hi guys,

Clue me up, please.

When we declare variables like

float x;
int y;
char z;
long w;

and so on, are we doing anything more than just reserving a certain
number of bytes for these data types to occupy?

yes this is correct.
I mean, the compiler doesn't flag up an error if we try to store (for
example) a letter 'a' in an int, does it? Presumably we can store
*any* type of data in *any* variable type provided said declared type
has enough room to accommodate it?
Aren't the names 'float', 'int', 'char' and so forth simply there to
remind the programmer what data type the variable should be?

yes, although the type provides hints to the compiler about how to treat
operations on that variable.

As mentioned by other posters, the compiler will automatically translate
between numeric types (such as float and int) but it will also provide
warnings for things that we know are going to cause issues (assignment
between int and pointer types and mis-matched pointer types).

In the days before C89 most compilers would happily let you commit a
multitude of sins without warning because the compiler indeed treated
variables are places to store stuff - the big one being that pointers were
considered ints so code like this compiled and ran without errors:

void foo(a)
int a; /* before c89 we did it this way :) */
{
printf("%s", a);
}

foo("a trick that bit a lot of people");

This sort of thing caused massive problems with portability & floating
pointers that were quite hard to debug.

In the late '80s compilers such as GCC started to enforce stronger typing
which improved everyone's life no end and as the standards have evolved
stronger typing has been added.

If you want to commit sins these days you have to abuse the casting system:

double x;
*(int *)&x= 4;

Will place an integer into your double storage - and you will rot in hell
for all eternity.

(I have seen variations on this used to pass multiple ints around in double
or long variables - made for a bucket of fun when porting)

While you *may* know what your compiler is doing in the way it stores data,
you cannot assume that it will hold for other compilers, or even a later
version of the same compiler. A lot of us had to learn this the hard way in
the early days, particularly with the int/pointer assignment assumption.

Bruce
 
B

Bruce Cook

phaedrus said:
Er, hang on. I'm obviously still not completely clued-up here. You say
the type 'char' is numeric?? Float, int and long are obviously
numeric, but char is obviously a single character like 'a' or 'b' or
'c' for example. A char, though capable of storing up to 0xFF of
values, is still just an alphabetical character, n'est pas?

(I'm sure the guys wielding the standards will give exact wording of the
standard for this)

in C a char is a character sized int (these days usually a Byte).

char c; /* on x86 declares an Byte sized int. */
char c[80]; /* on x86 declares an array of 80 byte sized ints. */

c= 0x41; /* assigns the value of 41 hex to c - happens to also
be the ASCII code for the character A */
print ("%c %x", c, c); /* output: A 41 */

c= 'A'; /* assigns the integer value of the character code for
A into c (on x86 this is the ascii value 0x41 */

It is for this reason that C has no asc() or char() functions - they're
simply not required.

Bruce
 
K

Keith Thompson

Bruce Cook said:
yes this is correct.


yes, although the type provides hints to the compiler about how to treat
operations on that variable.

No, declaring "float x;" does much more than just telling the compiler
that x should occupy sizeof(float) bytes. It affects every operation
that you can perform on x. It doesn't just provide hints; it provides
information that the compiler is obligated to pay close attention to.

To put it another way, the number of bytes occupied by x is just one
of the things that "float x;" tells the compiler, and it's not
particularly the most important one.
As mentioned by other posters, the compiler will automatically translate

"Convert" is more accurate than "translate".
between numeric types (such as float and int) but it will also provide
warnings for things that we know are going to cause issues (assignment
between int and pointer types and mis-matched pointer types).

Providing a warning is the least that a compiler can do. In many
cases, compilers will print an error message and reject the program.
In the days before C89 most compilers would happily let you commit a
multitude of sins without warning because the compiler indeed treated
variables are places to store stuff - the big one being that pointers were
considered ints so code like this compiled and ran without errors:

void foo(a)
int a; /* before c89 we did it this way :) */

Or the "int a;" could be omitted.
{
printf("%s", a);
}

foo("a trick that bit a lot of people");

Pointers weren't *quite* considered to be integers, but pre-C89
compilers were generally more lenient about implicit conversions.

The above code is actually still legal (sort of) in C90 and even C99.
Its behavior is undefined, but it's likely to print "a trick that bit
a lot of people" under many implementations.

Oh, and pre-ANSI C didn't have "void".

Even in the earliest versions of C, the declaration
float x;
would tell the compiler more than just the size of x. For example,
this:
x = 42;
would generate quite different code given "float x;" than given
"int x;", even if int and float are the same size.

(I think BCPL, one of C's ancestor languages, was essentially
typeless.)

[...]
 
S

Seebs

Oh, and pre-ANSI C didn't have "void".

I think some did. There were shreds of some interesting tests in perl
back then, and some barely-ANSI or pre-ANSI compilers definitely had,
say, void, but not support for prototypes. Or partial support for void.
I seem to recall that there were some that handled "void foo()", and a
few that knew about "void *".

But it wasn't exactly a standard or stable feature.

I mostly got interested in this at a time when the ANSI standard existed
and the compilers I mostly used were at least largely ANSI-like, but I had
to use a few pre-ANSI compilers sometimes, and some of them still had
some support for void.

Hmm.

I think the three questions were:
* void return type
* void arg list (essentially implies prototypes)
* void *

Been too long; I'd have to track down 20-year-old copies of the perl source.
Which I probably have somewhere.

-s
 

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

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top