c语言数据类型、存取方式、类型转换
一、c语言的数据类型
1. 数据类型的意义:
(1) 为变量分配内存大小
(2) 规定这个变量的存取规则
2. 数据类型占用内存大小
每种数据类型,在不同的机器平台上占用内存是不同的。
32位CPU:
//整型 sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 4 sizeof(long long) = 8 //指针 sizeof(char*) = 4 //浮点型 sizeof(float) = 4 sizeof(double) = 8
64位CPU:
//整型 sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 8 sizeof(long long) = 8 sizeof(char*) = 8 sizeof(float) = 4 sizeof(double) = 8
32位与64位占用内存长度有区别的类型有:
long 4 8 char * 4 8 long double 12 16
3. 整形常量的表示形式:
十进制: 10,36
八进制(以数字0开头): 012
十六进制: 0x36
4. 有符号与无符号
对于char short int long等整形类型的数,都分有符号有无符号数。
而对于float和double这种浮点型数来说,只有有符号数,没有无符号数。
对于有符号数和无符号数来说,存储方式不同的。
对于float和double这种浮点类型的数,它在内存中的存储方式和整形数不一样。所以float和
int相比,虽然都是4字节,但是在内存中存储的方式完全不同。所以同一个4字节的内存,如果存储时是按照int存放的,取的时候一定要按照int型方式去取。如果存的时候和取的时候理解的方式不同,那数据就完全错了。
5. void关键字
C语言中的void类型,代表任意类型,而不是空的意思。任意类型的意思不是说想变成谁就变成谁,而是说它的类型是未知的,是还没指定的。
void * 是void类型的指针。void类型的指针的含义是:这是一个指针变量,该指针指向一个void类型的数。void类型的数就是说这个数有可能是int,也有可能是float,也有可能是个结构体,哪种类型都有可能,只是我当前不知道。
void型指针的作用就是,程序不知道那个变量的类型,但是程序员自己心里知道。程序员如何知道?当时给这个变量赋值的时候是什么类型,现在取的时候就还是什么类型。这些类型对不对,能否兼容,完全由程序员自己负责。编译器看到void就没办法帮你做类型检查了。
在函数的参数列表和返回值中,void代表的含义是:
一个函数形参列表为void,表示这个函数调用时不需要给它传参,如何传参,编译器会报错。
返回值类型是void,表示这个函数不会返回一个有意义的返回值。所以调用者也不要想着去使用该返回值。
不可以使用定义void类型的变量,因为编译器不知道分配多大内存,可以定义void*变量。
void *p = NULL;
int a = 0x203;
p = &a;
printf(“a = %d\n”, (int)p); //a = 515
printf(“a = %d\n”, (char)p); //a = 3
printf(“a = %f\n”, (float)p); //a = 0.000000
6. 常量后缀
问题描述:C/C++程序许多时候会看到一个常数后面会跟一个后缀说明,比如UL。常数后缀许多时候不用也不会有问题,只要考虑常数赋给变量时不溢出,因为变量进行计算时会将常数转换成自己的类型。但是有时候就是因为常数没有指定类型,而且数值很小(比如2)不会有溢出问题,也会产生很隐蔽的bug。比如有次将编译器选项的优化项选择为最高(那样源代码编译后生成的二进制文件会最小),在使用printf函数在LCD屏输出常数2时就出现了问题,输出的这个数字会不断改变,然后将该常数类型用后缀限定后,就一切正常了。所以在使用的编译器不是那么聪明,或者将编译器选项按照自己要求进行了修改后,程序中许多地方都要严格的进行书写。因为常数后缀在许多时候的关键作用,于是搜集资料进行了整理与学习。
数的进制:二进制数,八进制数(O),十进制数,十六进制数(0x)。程序中十进制数和十六进制数最为常见,八进制数很少见,二进制数只是一种表示(程序中并不能直接书写)。
整数常数后缀:u或U(unsigned)、l或L(long)、u/U与l/L的组合(如:ul、lu、Lu等),u和l没有顺序区别。例:100u; -123u; 0x123l。
浮点常数后缀:科学计数形式和小数点形式。浮点常数默认是double的。f或F(单精度浮点数)、l或L(长双精度浮点数)。(注:因浮点型常数总是有符号的,故没有u或U后缀)。例:1.23e5f; 1.23l; -123.45f。
unsigned long long laddr = 0xffffffffULL << 10;
二、各种数据类型的打印方式
http://c.biancheng.net/view/1793.html
http://c.biancheng.net/view/1758.html
http://c.biancheng.net/view/1760.html
三. 数据类型存取方式
分为整形存储 和 浮点型存储
存取方式上主要有两种,一种是整形一种是浮点型,这两种存取方式完全不同,没有任何关联,所以是绝对不能随意改变一个变量的存取方式。在整形和浮点型之内,譬如说4种整形char、short、int、long只是范围大小不同而已,存储方式是一模一样的。float和double存储原理是相同的,方式上有差异,导致了能表示的浮点型的范围和精度不同。
1. 整型存储方式
https://www.geek-share.com/detail/2502279623.html
2. 浮点型存储方式
https://www.geek-share.com/detail/2602947381.html
https://www.geek-share.com/detail/2607600717.html
int main() { float x = 1.0; cout<<(int &)x<<endl; cout<<*(int *)&x<<endl; return 0; } 我们发现输出结果均为1065353216 分析: 由于1.0为float型数据,占4字节,可以知道1.0在内存中存储为0 01111111 00000000000000000000000, 对于语句 *(int *)&x,意思就是说先将float型的x的指针强制转换为int型的指针,然后取出值。由于是按照 float型数据存储的,而却解释成int型,即对应的int整数为,而(int &)x就相当于*(int *)&x
四. 数据类型转换
0. int与char类型转换
1. int->char
从长字节数据类型转换为短字节数据类型,会产生截断:
如从4字节的int类型转换成1个字节的char类型,则取int数据的最低的一个字节,将这个字节的数据赋给char型数据。
2. char/unsigned char -> int
从char转换为int:则在前面的三个字节补符号位,即补上0xffffff(char的首位为1),或0x000000(char的首位为0),char的后一个字节保持不变复制给int。
从unsigned char转换为int,则前面补上0x000000.
int main() { int s=128; //取s的最后一个字节给char 0x80 unsigned char unChar=s; //unchar = 0x80 char Char=s; //char = 0x80 printf("%x\t%x\n",Char,unChar); //按16进制输出 //char转int 0xff ff ff 80 unchar转int 0x80 printf("%d\t%d\n",Char,unChar); //按10进制输出 } ffffff80 80 -128 128 //int类型 0xff ff ff 80就是-128 #include <stdio.h> int main() { int s1,s2; unsigned char unChar=128; //int转unsigned char unchar = 0x80 char Char=128; //int转char char = 0x80 s1=(int)unChar; //unsigned char转int 补3个字节的0 0x80 s2=(int)Char; //char转int,根据char符号位,补3个0xff或0x00 这里补0xff ff ff 80 printf("%x\t%x\n",s1,s2);//按16进制输出 printf("%d\t%d\n",s1,s2);//按10进制输出 } 80 ffffff80 128 -128
1. 整数与小数之间相互赋值
http://c.biancheng.net/view/1763.html
3. 数据类型转换
http://c.biancheng.net/view/1775.html
https://blog.csdn.net/SwordArcher/article/details/82351996
关于unsigned char、unsigned short使用printf(“%d”)进行输出的结果
关于short强制转换成int
short 和 int 类型的转换
相同数据类型运算不需要转换类型,但可能存在溢出。
a, b 都是unsigned int类型,做减法的时候不需类型转换。但a -b发生了溢出。
int 20000 main() { unsigned int a = 1, b = 2; unsigned int c = a - b; int c = a - b; printf("c = %d\n", c); printf("c = %u\n", c); return 0; } c = -1 c = 4294967295
五、float类型比较
float类型只能精确到小数点以后前六位。所以无法比较更长的两个数是否相等,因为比较的话只会比较小数点以后前7位 是否相等。再长的话后面也不会比较了。
所以比较两个数是不是相等,可以比较a -b < 0.0000001 && a - b > -0.0000001 比较前7位
float a = 1.12345612; float b = 1.12345623; float c = 1.12345601; float d = 1.12345613; printf("a = %f, b = %f, a - b = %f, a - c = %f\n", a, b, a - b, a - c); printf("a == b ? %d %d\n", a == b, a - b > -0.0000001 && a -b < 0.0000001); printf("a == c ? %d %d \n", a == c, a - c > -0.0000001 && a - c < 0.0000001); printf("a == d ? %d %d\n", a == d, (a - d > -0.0000001) && (a - d < 0.0000001)); float c1 = 0.00001, c2 = 0.0000001, c3 = 0.0000000123; printf("c1 = %f, c2 = %f, c3 = %f\n", c1, c2, c3); /* root@lipenghui-virtual-machine:/mnt/hgfs/vmshare# ./test a = 1.123456, b = 1.123456, a - b = -0.000000, a - c = 0.000000 a == b ? 0 0 a == c ? 0 0 a == d ? 1 1 c1 = 0.000010, c2 = 0.000000, c3 = 0.000000 */
float和0比较,可以这样
if (a > -0.0000001 && a < 0.0000001)
六、float类型0在内存中的存储
+0,-0 为特殊编码 ,+0 每一位都是0,-0 除了符号位是1,其余都是0
float a =0.0;
printf(“0x%08x”,*(int *)&a);
int main() { float a = 1.0; cout << (int)a << endl; cout << (int&)a << endl; a = 0.0; cout << (int)a << endl; cout << (int&)a << endl; a = +0.0; cout << (int)a << endl; cout << (int&)a << endl; a = -0.0; cout << (int)a << endl; cout << (int&)a << endl; return 0; } 1 1065353216 0 0 0 0 0 -2147483648
七、位域
http://m.biancheng.net/view/2037.html
位域的压缩:
linux下位域是压缩存放的,前面的类型只是限制后面的长度不能超过这个类型的长度,对于数据的存放占用大小是没有影响的,数据存放内存占用大小只与后面的位域宽度有关。结构体中出现了位域,各个数据之间都会压缩着存放。
struct test { short a:5; char :4; int b:3; //前面3个一起占用12位,2个字节 char d; char f; //总共4个字节 }; struct bs{ unsigned m: 12; //占两12位,2个字节,而不会是int类型的4个字节 unsigned short ch; unsigned char ch2; unsigned p: 4; //占两4位,1个字节,而不会是int类型的4个字节 unsigned char ch3; }; int main() { cout << sizeof(test) << endl; //4 cout << sizeof(bs) << endl; //8 return 0; }
但位域的类型会对最后的结构体对齐长度有影响
struct bs{ char b1:5; unsigned short ch:2; unsigned int ch2:2; //4 }; struct bs2{ char b1:5; unsigned short ch:2; unsigned short ch2:2; //2 }; struct bs{ char b1:3; unsigned char ch2:2; //1 }; struct bs2{ char b1:3; unsigned short ch2:2; //2 }; struct bs3{ char b1:3; unsigned ch2:2; //4 }; struct bs4{ char b1:3; unsigned char ch2:2; char b2; //2 }; struct bs5{ char b1:3; unsigned short ch2:2; //2 char b2; }; struct bs6{ char b1:3; unsigned ch2:2; //4 char b2; };
- c/c++语言数据类型转换的方式及常见问题【绝笔整理】
- c语言,数据类型转换
- C#调用C++的DLL搜集整理的所有数据类型转换方式
- C语言 变量名、数据类型及类型转换
- C语言中强制数据类型转换的总结
- c语言中数据类型的自动转换原则
- C# 课堂总结2-数据类型及转换方式
- C#调用C++的DLL搜集整理的所有数据类型转换方式
- php 数据类型转换强制转换的三种方式
- C语言数据类型转换
- c语言中各数据类型在内存中的存储方式
- C语言中隐式数据类型转换的总结
- C语言数据类型转换详解
- 通过loadrunner 11常规通用的C语言API类型的Vuser 方式,测试验证MySQL数据库插入、查询、修改、删除数据性能脚本实例
- 解剖C语言---数据类型转换与数据运算集锦
- C语言运算中的数据类型自动转换原则
- IEEE754浮点格式简述 和 C语言基本数据类型转换实质
- C#调用C++的DLL 所有数据类型转换方式
- C语言中的数据类型转换-横向箭头的真正意思
- 详解C语言中的char数据类型及其与int类型的转换