1 进制英文表示
binary 二进制的
octal 八进制的
hexadecimal 十六进制的
decimal 十进制的
2 C语言使用
默认是10进制的;C,C++规定,一个数如果要指明是二进制的,开头加0b;一个数如果要指明它采用八进制,必须在它前面加上一个0,如:123是十进制,但0123则表示采用八进制;16进制数必须以 0x开头。
3 内部存储
数据是由二进制补码存储的,第一位为符号位(1负,0正):
1)当原码为正数的时候,正数的原码反码补码都相同,即00011的反码也为00011,补码也为00011。
2)当原码为负数的时候,反码即按位取反,比如10011为原码,10011可表示-3,那么符号位不变,其余位按位取反即反码11100.那么10011的补码便是11101.
4 基本变量
4.1 基本类型变量
char(%c、%d、%u)
short(%hd)
int(%d)
long(%ld)
long long(%lld)
//对应无符号类型
unsigned char(%c、%d、%u)
unsigned short(%hu、%ho、%hx)
unsigned int(%u,、%o、%x)
unsigned long(%lu、%lo、%lx)
unsigned long long(%llu、%llo、%llx)
//无符号类型打印出的值就是内存中存数的实际二进制值
float(%f)
double(%lf)
long double(%Lf)
4.2 符号扩展
需要扩展的变量量为有符号数,扩展存储位数的方法,即在新的高位字节使用当前最高有效位即符号位的值进行填充。
例1:
char a=0xff; //有符号值为-1,二进制为11111111,其中最高位为符号位
short b=a; //b的有符号值为-1,在内存中存储的值为1111111111111111
例2:
char a=1; //有符号值为1,二进制为00000001,其中最高位为符号位
short b=a; //b的有符号值为1,在内存中存储的值为0000000000000001
4.3 零扩展
对于要扩展量无符号数,扩展存储位数的方法。在新的高位直接填0。
unsigned char a=0xff;//二进制为11111111,所有值都是有效值
unsigned short b=a;//b经过零扩展后,内存中存储的值为0000000011111111
4.4 其他
其实这里注重的是要扩展的量是有符号量还是无符号量。若要扩展量为有符号量,不管扩展成有符号还是无符号,都遵循符号扩展;若要扩展量为无符号量,不管扩展成有符号还是无符号,都遵循零扩展。
例1:
char a =0xff;//a为-1,其为有符号量,二进制为11111111
unsigned shortb=a;//此处a要进行符号扩展,b的二进制为11111111 11111111
例2:
unsigned char a=0xff;//a为无符号量,二进制为11111111
short b=a;//此处a要进行零扩展,b的二进制为00000000 11111111
5 类型转换
C语言的类型转换,可以分为两种:自动类型转换(隐式类型转换,有编译器帮你去完成)和强制类型转换(显示类型转换,你知道自己想要什么,所以才转换)。
5.1 类型转换和扩展:
一、有符号数的转换规则
从 | 到 | 方法 |
---|---|---|
char | short | 符号位扩展 |
char | long | 符号位扩展 |
char | unsigned char | 最高位失去符号位意义,变为数据位 |
char | unsigned short | 符号位扩展到short;然后从short转到 unsigned short |
char | unsigned long | 符号位扩展到long; 然后从long 转到unsigned long |
char | float | 符号位扩展到long; 然后从long 转到float |
char | double | 符号位扩展到long; 然后从long 转到double |
char | long double | 符号位扩展到long; 然后从long 转到long double |
short | char | 保留低位字节 |
short | long | 符号位扩展 |
short | unsigned char | 保留低位字节 |
short | unsigned short | 最高位失去符号位意义,变为数据位 |
short | unsigned long | 符号位扩展到long; 然后从long转到unsigned double |
short | float | 符号位扩展到long; 然后从long 转到float |
short | double | 符号位扩展到long; 然后从long 转到double |
short | long double | 符号位扩展到long; 然后从long 转到double |
long | char | 保留低位字节 |
long | short | 保留低位字节 |
long | unsigned char | 保留低位字节 |
long | unsigned short | 保留低位字节 |
long | unsigned long | 最高位失去符号位意义,变为数据位 |
long | Float | 使用单精度浮点数表示。可能丢失精度。 |
long | double | 使用双精度浮点数表示。可能丢失精度。 |
long | long double | 使用双精度浮点数表示。可能丢失精度。 |
二、无符号数的转换规则
从 | 到 | 方法 |
---|---|---|
unsigned char | char | 最高位作为符号位 |
unsigned char | short | 0扩展 |
unsigned char | long | 0扩展 |
unsigned char | unsigned short | 0扩展 |
unsigned char | unsigned long | 0扩展 |
unsigned char | float | 转换到long;再从 long 转换到float |
unsigned char | double | 转换到long;再从 long 转换到double |
unsigned char | long double | 转换到long;再从 long 转换到double |
unsigned short | char | 保留低位字节 |
unsigned short | short | 最高位作为符号位 |
unsigned short | long | 0扩展 |
unsigned short | unsigned char | 保留低位字节 |
unsigned short | unsigned long | 0扩展 |
unsigned short | float | 转换到long;再从 long 转换到float |
unsigned short | double | 转换到long;再从 long 转换到double |
unsigned short | long double | 转换到long;再从 long 转换到double |
unsigned long | char | 保留低位字节 |
unsigned long | short | 保留低位字节 |
unsigned long | long | 最高位作为符号位 |
unsigned long | unsigned char | 保留低位字节 |
unsigned long | unsigned short | 保留低位字节 |
unsigned long | float | 转换到long;再从 long 转换到float |
unsigned long | double | 直接转换成double |
unsigned long | long double | 转换到long;再从 long 转换到double |
总结:
一、短数据类型扩展为长数据类型
- 要扩展的短数据类型为有符号数
进行符号扩展,即短数据类型的符号位填充到长数据类型的高字节位(即比短数据类型多出的那一部分),保证扩展后的数值大小不变。
char x=0b10001001;
short y=x;
//则y的值应为11111111 10001001b;
char x=0b00001001;
short y=x;
//则y的值应为00000000 00001001b;
- 要扩展的短数据类型为无符号数
进行零扩展,即用零来填充长数据类型的高字节位。
unsigned char x=0b10001001;
short y=x;
//则y的值应为00000000 10001001b;
unsigned char x=0b00001001;
short y=x;
//则y的值应为00000000 00001001b;
二、长数据类型缩减为短数据类型
如果长数据类型的高字节全为1或全为0,则会直接截取低字节赋给短数据类型;如果长数据类型的高字节不全为1或不全为0,则转会就会发生错误。
三、同一长度的数据类型中有符号数与无符号数的相互转化
直接将内存中的数据赋给要转化的类型,数值大小则会发生变化。另短类型扩展为长类型时,但短类型与长类型分属有符号数与无符号数时,则先按规则一进行类型的扩展,再按本规则直接将内存中的数值原封不动的赋给对方。
5.2 隐式转换原则
变换操作数采取就高不就低的原则,即级别低的操作数先被转换成和级别高的操作数具有同一类型,然后再进行运算,结果的数据类型和级别高的操作数相同。且有符号的转换成无符号的。
高 double ←← float
↑ ↑
↑ long
↑ ↑
↑ unsigned
↑ ↑
低 int ←← char,short
5.3 结构、联合变量及其成员变量
同类型的结构可以相互赋值:
typedef struct test{
char*name;
unsigned short age;
} Struct_test;
Struct_test a,b={“zhangsan”,20};
a = b;//a和b中的数据是一样的,但在不同的存储空间
通过共用体来确定机器是大端序还是小端序的例子:
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
#include <stdio.h>
union test {
char a[4];
int b;
};
int main(int argc, char **argv) {
union test tst;
tst.a[0] = 0x04;
tst.a[1] = 0x03;
tst.a[2] = 0x02;
tst.a[3] = 0x01;
printf("0x%x\n", tst.b);
return 0;
}
若打印出0x4030201则为大端序,若为0x1020304则为小端序
5.4 void **的转换
C语言中没有通用指针类型,void *之所以可以用作通用指针,是因为当它和其他类型相互赋值的时候,如果需要,它可以自动转换成其他类型。但是,void **就不会自动转换了,原因是,当你使用void **指针的时候,例如用*操作符访问void **所指向的void *值得时候,编译器无法知道void *值是否从其他类型的指针转换而来,从而,编译器只能认为它仅仅是个void *指针,所以程序就无法正确访问到想要的结果。
换言之,你使用的任何void **值必须是某个位置的void *值得地址,(void **)&dp这样的类型转化虽然能编译通过,但执行结果可能不是我们想要的。如果void **指针指向的不是void *类型,并且这个类型的大小和内存表示和void *也不同,则程序就无法正确访问到此类型。
6 C++隐式类型转换
6.1 原因
C++面向对象的多态特性,就是通过父类的类型实现对子类的封装。通过隐式转换,你可以直接将一个子类的对象使用父类的类型进行返回。
某些方面来说,隐式转换给C++程序开发者带来了不小的便捷。
C++是一门强类型语言,类型的检查是非常严格的。如果没有类型的隐式转换,这将给程序开发者带来很多的不便。
当然,凡事都有两面性,在你享受方便快捷的一面时,你不得不面对太过智能以至完全超出了你的控制。风险就在不知不觉间出现。
6.2 隐式调用构造函数
当构造函数只有一个参数,或者含有默认参数的时候,且参数类型为内建类型时,可以不用显示调用构造函数。
文章链接
【csapp】【微软面试题】有符号数到无符号数隐式转换:https://blog.csdn.net/u012162613/article/details/40887457