Reputation: 67
I want to know how data types are stored in c, so i wrote a program to check how values are stored. when i saw the output i couldn't understand how different data typed store values in memory.
here is the program i tried,
#include <stdio.h>
int main() {
int x;
int valI, i;
short valS;
long valL;
signed valSi;
unsigned valUn;
// printf("enter a num");
// scanf("%d",&x);
x = -10;
valI = x;
valS = x;
valL = x;
valSi = x;
valUn = x;
printf("\n\t%i\t%li\n",valI,sizeof(valI));
for(i = 8*sizeof(valI); i >= 0 ; i--)
{
printf("%i",(valI & (1<<i))? 1 : 0);
}
printf("\n\t%i\t%li\n",valS,sizeof(valS));
for(i = 8*sizeof(valS); i >= 0 ; i--)
{
printf("%i",(valS & (1<<i))? 1 : 0);
}
printf("\n\t%li\t%li\n",valL,sizeof(valL));
for(i = 8*sizeof(valL); i >= 0 ; i--)
{
printf("%i",(valL & (1<<i))? 1 : 0);
}
printf("\n\t%i\t%li\n",valSi,sizeof(valSi));
for(i = 8*sizeof(valSi); i >= 0 ; i--)
{
printf("%i",(valSi & (1<<i))? 1 : 0);
}
printf("\n\t%i\t%li\n",valUn,sizeof(valUn));
for(i = 8*sizeof(valUn); i >= 0 ; i--)
{
printf("%i",(valUn & (1<<i))? 1 : 0);
}
printf("\n\n");
}
the output for input = 10
enter a num10 10 4 000000000000000000000000000001010 10 2 00000000000001010 10 8 00000000000000000000000000000101000000000000000000000000000001010 10 4 000000000000000000000000000001010 10 4 000000000000000000000000000001010
the output for input = -10
enter a num-10 -10 4 011111111111111111111111111110110 -10 2 11111111111110110 -10 8 01111111111111111111111111111011011111111111111111111111111110110 -10 4 011111111111111111111111111110110 -10 4 011111111111111111111111111110110
can someone explain why this is happening? & how different datatypes store values in memory? thanks in advance
Upvotes: 2
Views: 186
Reputation: 1
I want to know how data types are stored in C
Pedantically, that has no sense. How the data is stored is an implementation detail (and the C99 or C11 standard does not define how data is stored), and in principle you should not bother and try to write portable code.
Practically, how data is stored and represented, and how it is transmitted in function calls etc..., is specified in a document called Application Binary Interface. These conventions are specific to a processor and often to an operating system and followed by the compiler (and other tools).
Notice that some data might not be in memory, but e.g. only in a register.
You might read wikipages on two's complement, instruction sets, x86, calling conventions, x86 calling conventions, processor registers, address space, virtual memory, endianness, data-structure alignment, integer (computer science), floating point, IEEE floating point, ....
For x86-64 Linux you could read its ABI spec.
In practice, data representation is strongly machine & system specific. It is different on your ARM/Android tablet and on your Linux/x86-64 desktop and on your Arduino kit or an IBM System Z mainframe (so your program would give different results on these).
Notice that C99 gives you <stdint.h>
with standard types like int32_t
, uint64_t
, intptr_t
If you care about interoperability, read more about serialization and favor well-defined textual formats (like e.g. JSON).
Upvotes: 8
Reputation: 7352
Your positive numbers and unsigned numbers are represented as standart binary, with the unused bits filled with 0s.
Negative numbers in signed variables are stored in a two's-complement. Have a read, its a basic concept in computer science.
Basicly, a twos complement is a way of representing a negative value in binary.
As example, -10 is
011111111111111111111111111110110
In a two's complement of 32 bits. You can find out a two's complement for a given negative number like this:
-10:
First, take the positive number in binary. for 10 this is
00000000000000000000000000001010
Now invert it (change all 0s to 1s and vice versa)
11111111111111111111111111110101
Now add a single 1 in normal addition.
11111111111111111111111111110110
and voila, you have the binary representation of -10 in a 32bit number.
Although, the exact way the values are stored is, as the other answer mentioned, up to the compiler to determine, as there are no set rules for it.
Upvotes: 1
Reputation: 84561
The storage requirements for any type
depend on the underlying operating system and hardware. There are significant differences between x86_64
and x86
storage sizes. You can use a slightly different version to do essentially what you have started out to do. For example:
#include <stdio.h>
#if defined(__LP64__) || defined(_LP64)
# define BUILD_64 1
#endif
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
/* signed data type low storage limit */
long long limit_s_low (unsigned char bytes)
{ return -(1ULL << (bytes * CHAR_BIT - 1)); }
/* signed data type high storage limit */
long long limit_s_high (unsigned char bytes)
{ return (1ULL << (bytes * CHAR_BIT - 1)) - 1; }
/* unsigned data type high storage limit */
unsigned long long limit_u_high (unsigned char bytes)
{
if (bytes < sizeof (long long))
return (1ULL << (bytes * CHAR_BIT)) - 1;
else
return ~1ULL - 1;
}
int main (void) {
#ifdef BUILD_64
printf ("\n data type sizes for x86_64:\n\n");
printf (" sizeof (char) : %lu\n", sizeof (char));
printf (" sizeof (char*) : %lu (all pointers)\n", sizeof (char*));
printf (" sizeof (short) : %lu\n", sizeof (short));
printf (" sizeof (int) : %lu\n", sizeof (int));
printf (" sizeof (long) : %lu\n", sizeof (long));
printf (" sizeof (long long) : %lu\n\n", sizeof (long long));
#else
printf ("\n data type sizes for x86 (32-bit) :\n\n");
printf (" sizeof (char) : %u\n", sizeof (char));
printf (" sizeof (char*) : %u (all pointers)\n", sizeof (char*));
printf (" sizeof (short) : %u\n", sizeof (short));
printf (" sizeof (int) : %u\n", sizeof (int));
printf (" sizeof (long) : %u\n", sizeof (long));
printf (" sizeof (long long) : %u\n\n", sizeof (long long));
#endif
#ifdef BUILD_64
printf (" data type storage sizes for x86_64:\n\n");
#else
printf (" data type storage sizes for x86:\n\n");
#endif
printf (" char - signed : %11lld to %lld\n",
limit_s_low (sizeof (char)), limit_s_high (sizeof (char)));
printf (" char - unsigned : %11d to %llu\n",
0, limit_u_high (sizeof (char)));
printf (" short - signed : %11lld to %lld\n",
limit_s_low (sizeof (short)), limit_s_high (sizeof (short)));
printf (" short - unsigned : %11d to %llu\n",
0, limit_u_high (sizeof (short)));
#ifdef BUILD_64
printf (" int - signed : %lld to %lld\n",
limit_s_low (sizeof (int)), limit_s_high (sizeof (int)));
printf (" int - unsigned : %11d to %llu\n",
0, limit_u_high (sizeof (int)));
printf (" (l)long - signed : %.4e to %.4e\n",
(double)limit_s_low (sizeof (long)),
(double)limit_s_high (sizeof (long)));
printf (" (l)long - unsigned : %11d to %.4e %llu\n\n",
0, (double)limit_u_high (sizeof (long)),
limit_u_high (sizeof (long long)));
#else
printf (" int/long - signed : %lld to %lld\n",
limit_s_low (sizeof (int)), limit_s_high (sizeof (int)));
printf (" int/long - unsigned : %11d to %llu\n",
0, limit_u_high (sizeof (int)));
printf (" llong - signed : %.4e to %.4e\n",
(double)limit_s_low (sizeof (long long)),
(double)limit_s_high (sizeof (long long)));
printf (" llong - unsigned : %11d to %.4e %llu\n\n",
0, (double)limit_u_high (sizeof (long long)),
limit_u_high (sizeof (long long)));
#endif
return 0;
}
The #if defined(__LP64__) || defined(_LP64)
block test whether the system is running x86_64
, if not, then defaults to assessing storage sizes for x86
. It is equally compilable/runable on x86
. On x86_64
, you will generally see the following:
Output
$ ./bin/typesize
data type sizes for x86_64:
sizeof (char) : 1
sizeof (char*) : 8 (all pointers)
sizeof (short) : 2
sizeof (int) : 4
sizeof (long) : 8
sizeof (long long) : 8
data type storage sizes for x86_64:
char - signed : -128 to 127
char - unsigned : 0 to 255
short - signed : -32768 to 32767
short - unsigned : 0 to 65535
int - signed : -2147483648 to 2147483647
int - unsigned : 0 to 4294967295
(l)long - signed : -9.2234e+18 to 9.2234e+18
(l)long - unsigned : 0 to 1.8447e+19 18446744073709551613
On a 32-bit box, the output would be:
$ ./datatype/typecast/bin/typesize_32
data type sizes for x86 (32-bit) :
sizeof (char) : 1
sizeof (char*) : 4 (all pointers)
sizeof (short) : 2
sizeof (int) : 4
sizeof (long) : 4
sizeof (long long) : 8
data type storage sizes for x86:
char - signed : -128 to 127
char - unsigned : 0 to 255
short - signed : -32768 to 32767
short - unsigned : 0 to 65535
int/long - signed : -2147483648 to 2147483647
int/long - unsigned : 0 to 4294967295
llong - signed : -9.2234e+18 to 9.2234e+18
llong - unsigned : 0 to 1.8447e+19 18446744073709551613
Let me know if you have any questions or would like additional help.
Upvotes: 1