数据类型
1.整型家族
整型家族有字符型char,短整型short,整型int ,长整型long和长长整型long long
类型 | 范围 |
char | 0 ~ 255 |
signed char | -128 ~127 |
unsigned char | 0 ~ 255 |
[signed] short [int] | -32768 ~ 32767 |
unsigned short [int] | 0 ~ 65535 |
[signed] int | -2147483648 ~ 2147483647 |
unsigned int | 0 ~ 42949672 |
[signed] long | -2147483647 ~ 2147483647 |
unsigned long | 0 ~ 42949672 |
[signed] long long | -9223372036854775808 ~ 9223372036854775807 |
unsigned long long | 0 ~ 18446744073709551615 |
规定:sizeof(long long) >= sizeof(long) >= sizeof(int) >= sizeof(short)
其中,short,int,long在不显示声明为signed或unsigned时,它就是signed型
但char类型若不显示声明,那么它属于signed或unsigned,则取决于编译器
2.浮点数家族
float |
double |
long double |
3.指针类型
char *p1 |
int *p2 |
float *p3 |
void *p4 |
4.构造类型
数组类型 | int [n] |
结构体类型 | struct |
枚举类型 | enum |
联合体类型 | union |
5.空类型
void |
空类型可用于函数的返回类型,函数的参数,指针类型
空类型指针可作为形参接受任意类型的指针,但空类型指针无法直接解引用
数据存储
整型在内存中的存储
原码,反码,补码
计算机中数据有三种表示形式,原码,反码,补码,对于整型数据来说,数据在内存中存放的是其补码
对于有符号数,最高位是符号位
对于无符号数,最高位也是数据位
对于正数:
原码,反码,补码都相同
对于负数:
原码:直接将数据转换为对应的二进制形式,其中最高位为符号位,正数的符号位为0,负数的符号位为1
反码:符号位不变,其它位按位取反
补码:反码加1
类型的意义
计算机看待内存空间的视角
#include<stdio.h>
int main()
{
unsigned int n = -10;
//原码:10000000 00000000 00000000 00001010
//反码:11111111 11111111 11111111 11110101
//补码:11111111 11111111 11111111 11110110
//%u打印无符号数,将要打印的数据看为无符号数,即使不是无符号数,也看作无符号数
printf("%u\n", n); //4294967286
//%d打印有符号数,将要打印的数据看为有符号数,即使不是有符号数,也看作有符号数
printf("%d\n", n); //-10
return 0;
}
当成无符号数时最高位也是数据位,因此打印出了一个很大的数
当成有符号数时最高位就是符号位,1代表负数,因此打印 -10
大小端存储
大端存储:指数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中
小端存储:指数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中
事实上大小端字节序指的是数据在电脑上存储的字节顺序
大小端字节序的测试方法:
方法1
#include <stdio.h>
int check()
{
//1在内存中的二进制序列
//00000000 00000000 00000000 00000001
//大端存储
//低地址 00 00 00 01 高地址
//小端存储
//低地址 01 00 00 00 高地址
int i = 1;
return (*(char*)&i);
}
int main()
{
int ret = check();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
方法2
#include <stdio.h>
int check()
{
union Un
{
char c;
int i;
}u;
u.i = 1;
return u.c;
}
int main()
{
int ret = check();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
关于char的一道习题
#include<stdio.h>
#include<string.h>
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
//strlen求字符串长度,找'\0'位置,统计'\0'之前的字符个数
//'\0'的ASCII码值是0
printf("%d", strlen(a));
return 0;
}
循环中将-1赋给数组首元素,-2赋给数组下标为1的元素,一直往下赋值,当有一个数组元素被赋0时,此元素前应有255个元素被赋予非0值,因此当strlen函数找'\0'时,应输出255
浮点型存储
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以写成如下形式
(-1)^S * M * 2^E
(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数
M表示有效数字,大于等于1,小于2
2^E表示指数位
例:
用红色代表S,绿色代表M,蓝色代表E
对于32位浮点数:1位S,8位E,23位M
对于32位浮点数:1位S,11位E,52位M
特别规定:
对于M:由于1≤M<2,M可以写成1.xxxxxx的形式,xxxxxx表示小数部分,因此计算机在保存M时,总是默认这个数的第一位是1,因此可以舍去,只保留小数部分。如1.011×2^2,在保存时只保存011,而舍掉第一位可以提高精度
对于E:E是一个无符号整数,而科学计数法中E可能为负数,因此,在存入E时,必须先加上一个中间数,对于8位E,中间数为127,对于11位E,中间数为1023。如1.011×2^2,E为2,但存入内存时若保存为32位浮点数,则以129存入;若保存为64位浮点数,则以1025存入
从内存中取出
E不全为0或不全为1:指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1
E全为0:那么这个浮点数一定是一个接近0的很小的数,因为它的指数加了127(或1023)还是全0,此时有效数字M不再加上第一位的1,而是还原为 0.xxxxxx的小数
E全为1:此时,若M全为0,则表示正负无穷大