c语言中的数据类型主要分为字符型,整形和浮点型,字符在系统中存储的是ASCII码,所以也是整形。
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
各种数据类型的大小如下:
printf("%d\n", sizeof(char));//1字节,8bit
printf("%d\n", sizeof(short));//2字节,16bit
printf("%d\n", sizeof(int));//4字节,32bit
printf("%d\n", sizeof(long));//4字节,32bit或8字节,64bit,32位平台下4字节,64位平台下8字节,c语言规定sizeof(long)>=sizeof(int)
printf("%d\n", sizeof(long long));//8字节,64bit
printf("%d\n", sizeof(float));//4字节,32bit
printf("%d\n", sizeof(double));//8字节,64bit
printf("%d\n", sizeof(bool));//1字节,8bit
整形在内存中的存储方式
原码反码补码
有符号整形的最高位为符号位,0表示正数,1表示负数,整形的二进制有三种表示形式:原码、反码和补码,整型在内存中存储的是补码,因为CPU中只有加法器,这样可以把减法运算变成加负数的运算。最初的二进制形式就是原码,反码是将原码的符号位不变,其余位按位取反,补码是将反码加一,所以原码得到补码需要先取反再加一,补码变回原码也可以先取反再加一,结果和先减一再取反一样,正整数的原码、反码和补码相同,下面来举个例子:
char a = 7;//这里的7是ascll码
//原码:00000111
//反码:00000111
//补码:00000111
//正数原反补相同
char b = -7;
//原码:10000111
//反码:11111000
//补码:11111001
大小端问题
大端字节序存储:把一个数据的高位字节序的内容存放在内存中的低地址处,低位字节序的内存存放在高地址处。
小端字节序存储:把一个数据的低位字节序的内容存放在内存中的低地址处,高位字节序的内存存放在高地址处。
我们常用的x86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
判断当前编译器大小端的方法
#include "stdio.h"
#include "string.h"
int main()
{
int i = 1;
char *b = (char *)&i;
if (*b == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
int类型的i为1,也就是0x00000001;如果是大端字节序,它在内存中的低地址会存放最高位,也就是00,如果是小端字节序,他在内存中的低地址会存放最低位,也就是01,对i取地址,取到的是四个字节的地址,然后强制转换成char*,得到的就是四个字节中最低字节的地址,然后解引用得到的就是最低的字节,如果是1就是小端,是0就是大端。
#include "stdio.h"
#include "string.h"
union u
{
int a;
char b;
};
int main()
{
union u s;
s.a = 1;
if (s.b == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
联合体中成员共用地址,u.b用的是u.a的低地址,所以如果u.b为1就是小端,为0就是大端。
内存中的数据范围
下面以signed char 和unsigned char类型来举例说明内存有无符号两种数据类型的存储范围:
如果是无符号型,不需要考虑符号位,内存中的补码就是原码,char八位数据,全为1时就是255,范围是0到255。
对于有符号型比较复杂,第一位表示符号位,第一位为0时还是正常的0~127,01111111+1的话等于10000000,是补码,取反加一得到原码100000000,会多出一位,c语言规定这个数为-128,也就是127+1=-128, 10000001取反加一得到11111111,就是-127,可以理解,11111111取反加一得到原码10000001,就是-1,0-1 = -1。可以得出signed char范围是-128~127。
练习
int main()
{
signed char a = -1;
unsigned char c = -1;
printf("a = %d, c = %d",a,c);
}
首先讲一个知识点,char类型在用%d打印时会发生整型提升,整型提升如果是有符号数就在前面加符号位,如果是无符号数就在前面加0。
int main()
{
char a = -128;
printf("%u\n",a);
printf("%d\n",a);
}
%u是将a当作无符号数打印,%d是当作有符号数打印。
int main()
{
char a = 128;
printf("%u\n",a);
printf("%d\n",a);
}
int main()
{
int i = -20;
unsigned int j = 10;
printf("%d",i + j);
}
int main()
{
unsigned int i ;
for(i = 9; i <= 0; i--)
{
printf("%u\n",i);
}
}
上述代码会死循环,因为i是无符号整型,只有正数,当i减到0时再减一补码会变成全1,因为时无符号类型,所以是一个很大的正数,不会出现负数也就不会跳出for循环。
int main()
{
char a[1000];
int i;
for(i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d",strlen(a));
}
int main()
{
if(strlen("abc") - strlen("abcdef") >= 0)
printf(">\n");
else
printf("<\n");
return 0;
}
strlen的返回值类型是size_t,也就是unsigned int,因为字符串长度不会是负数,但是在这里两个size_t类型的数相减,得到的结果类型也是size_t,虽然实际上会得到-3,但是作为无符号数来看就是一个很大的正数,所以结果不可能小于0,不能用上述方法来判断两个字符串长度大小。