数据类型

目录

一,常见数据类型

1,整数类型

2,指针

3,枚举

4,位域

二,内存大小端

1,大端Big_endian、小端Little_endian

2,字节大小端

(1)int

(2)结构体

3,比特大小端

4,总结

三,浮点数

四,基本数据类型转换

1,int和double的转换

2,相同位数的整数,有符号和无符号的转换

3,不同位数的整数之间进行转换


一,常见数据类型

1,整数类型

C/C++只规定各个整数类型的最小位数,以及不同数据类型的长度的大小关系。

在满足限制的前提下,就有了几种常见的数据模型:

TYPE               LP32  ILP32  LP64  ILP64  LLP64

CHAR                8         8          8         8         8

SHORT             16       16        16       16       16

INT                  16       32        32        64      32

LONG               32       32        64       64       32

LONG LONG     64       64        64       64       64

POINTER          32       32        64       64       64 

Windows采用的是ILP32和LLP64模型,Linux采用的是ILP32和LP64模型。

2,指针

指针的长度方案是独立的,系统多少位指针就是多少位。

C语言中只有NULL,C++里面除了NULL还有nullptr

    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif

C和C++的NULL的定义不完全一样,这个和类型转换规则有关。

C++有了nullptr之后,NULL就没有任何使用场景了。

3,枚举

(1)枚举的第一个值如果没有定义,那就是0
(2)枚举的每个值(除第一个)如果没有定义,那就是上一个值加1
(3)枚举中可以有相同的值

4,位域

位域——结构体成员可以指定所占空间大小,用来节省空间

示例:

struct s{
    int a:2;
    unsigned b:2;
};

int main()
{
    s node;
    node.a=node.b=11;
    cout<<node.a<<' '<<node.b;
    return 0;
}

输出:

-1 3

二,内存大小端

1,大端Big_endian、小端Little_endian

大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这和我们的阅读习惯一致。

小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。

大小端的问题,主要有2个颗粒度,一个是字节,一个是比特。

2,字节大小端

当一个数据有n(n>1)个字节组成时,我们才需要考虑大小端,如果所有数据都是一个字节的大小,就没有大小端了。

一个字节的数据以char为代表,而n(n>1)个字节的数据以int这种原子数据为代表,结构体这种复合数据不涉及字节大小端。

在用指针转换来完成大小端实测之前,首先我们要知道:

无论大小端,也无论是int还是结构体类型的指针,指针指向的那个字节都是所有字节中最低的那个

(1)int

代码:

#include <stdio.h>

int main()
{
    int a=257;
    char *p=(char *)&a;
    printf("%d %d %d %d",*p, *(p+1),*(p+2),*(p+3));
    return 0;
}

输出:1 1 0 0

很明显,这是小端,个人PC默认是小端。

如果是大端环境,输出的就是0 0 1 1了

判断是否是小端的方法:

bool isLittle()
{
	int a = 1;
	char* p = (char*)&a;
	return *p == 1;
}

(2)结构体

代码:

#include <stdio.h>

typedef struct Node
{
    int a;
    int b;
}Node;

int main()
{
    Node s;
    s.a=1,s.b=257;
    char *p=(char *)&s;
    for(int i=0;i<8;i++)printf("%d ",*(p+i));
    return 0;
}

输出:1 0 0 0 1 1 0 0

如果是大端环境,输出的应该是0 0 0 1 0 0 1 1

也就是说,结构体内各成员的顺序就是自然顺序,没有大小端之分,但是成员内部还是按照成员本身的规则安排。

因为结构体可以嵌套结构体,所以我上面这句话其实也是个递归式的论述。

3,比特大小端

当使用位域时,我们需要考虑字节内各比特位的排列顺序,即比特大小端。

在网上找到一个示例:

struct Node{
    int a:1;
    int b:2;
    int c:3;
    int d:4;
    int e:5;
    int f:6;
    int g:11;
};

大端:
数据:abbcccdd ddeeeeef fffffggg gggggggg
小端:
数据:ddcccbba feeeeedd gggfffff gggggggg

同样的,我用小端机器输出试了下:

#include <stdio.h>

typedef struct Node
{
    int a:1;
    int b:2;
    int c:29;
}Node;

int main()
{
    Node s;
    s.a=1,s.b=2,s.c=3;
    printf("%d",s);
    return 0;
}

输出:29 即(11101)

判断是否是比特序小端序的方法:

typedef struct Node
{
	int a : 1;
	int b : 31;
}Node;
bool isBitLittle()
{
	Node s;
	s.a = 1, s.b = 0;
	char* p = (char*)&s;
	return *p == 1 || *(p + 3) == 1;
}

4,总结

如果抛弃字节的概念,把所有内存都看成按低比特到高比特的排列,那么规律就比较简洁统一:

(1)大端就是高位在低比特,小端就是高位在高比特,所有原子类型都符合这个排列规则

(2)结构体成员按照成员顺序从低比特到高比特排列,无论是大端还是小端,无论是普通原子类型还是结构体还是位域。而结构体成员内部的比特排列,按照规则递归定义。

三,浮点数

打印浮点数的二进制位:

bool isLittle()
{
	int a = 1;
	char* p = (char*)&a;
	return *p == 1;
}
typedef struct Node
{
	int a : 1;
	int b : 31;
}Node;
bool isBitLittle()
{
	Node s;
	s.a = 1, s.b = 0;
	char* p = (char*)&s;
	return *p == 1 || *(p + 3) == 1;
}

void printUchar(unsigned char x)
{
	if (isBitLittle()) {
		int m = 128;
		while (m) {
			cout << ((m & x) ? 1 : 0);
			m /= 2;
		}
	}
	else {
		/
	}
	cout << " ";
}
void printFloat(float x)
{
	if (isLittle()) {
		unsigned char* p = (unsigned char*)(&x) + 3;
		printUchar(*(p--));
		printUchar(*(p--));
		printUchar(*(p--));
		printUchar(*(p--));
	}
	else {
		
	}
}

int main()
{
	float x = 3.5;
	printFloat(x);
	return 0;
}

0.5
00111111 00000000 00000000 00000000
0.75
00111111 01000000 00000000 00000000
1
00111111 10000000 00000000 00000000
1.5
00111111 11000000 00000000 00000000
2
01000000 00000000 00000000 00000000
2.5
01000000 00100000 00000000 00000000
3
01000000 01000000 00000000 00000000
3.5
01000000 01100000 00000000 00000000

0.15625
00111110 00100000 00000000 00000000

float是32位的,由符号+指数+小数构成,指数有8位,小数有23位

指数的范围是-127到128,把指数加127就是0到255,用unsigned char来编码。

小数是0到1的二进制小数。

浮点数的绝对值是(1+小数)* 2^指数

以3.5为例,3.5=1.75*2,小数0.75表示成11000000000000000000000,指数1加127等于128,用unsigned char来编码是10000000,所以3.5的二进制是 0 10000000 11000000000000000000000

四,基本数据类型转换

1,int和double的转换

int转double相当于乘以1.0

double转int相当于向下取整函数

2,相同位数的整数,有符号和无符号的转换

代码:

    int x=-1;
    cout<<unsigned int(x)<<"   ";
    unsigned int y=4294967295;
    cout<<int(y);

输出:

4294967295   -1

也就是说,相同位数的整数,相互转换的时候,不改变每一位的值,直接按照有无符号的自己的理解来理解就行。

3,不同位数的整数之间进行转换

以char和int为例

PS:程序员一定要理解char就是整数类型这个思想。

(1)有符号低位转高位

    char c='\0';
    cout<<int(c)<<"  ";
    cout<<int(--c)<<"  ";
    c='\0';
    cout<<unsigned int(c)<<"  ";
    cout<<unsigned int(--c)<<"  ";

输出:

0  -1  0  4294967295

也就是说,有符号低位转高位是填充首位,即以1开头的数就补上很多1,以0开头的数就补上很多0

(2)无符号低位转高位

    unsigned char c='\0';
    cout<<int(c)<<"  ";
    cout<<int(--c)<<"  ";
    c='\0';
    cout<<unsigned int(c)<<"  ";
    cout<<unsigned int(--c)<<"  ";

输出:

0  255  0  255

也就是说,无符号低位转高位是填充0

(3)高位转低位

    long long x=(1LL<<63);
    cout<<x<<"  "<<int(x);

输出:

-9223372036854775808  0

也就是说,高位转低位是直接截取

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值