在做题的时候发现了需要用到类型转换,于是在这里进行了简单的记录。
历史原因,慢慢整理着发现类型转换也能写老大一篇文章了。又花了时间来梳理一下就成了本文了。
cpp
之前使用的环境是DEV-C++ 5.4
,而对应的GCC版本太低了。支持c++11需要GCC版本至少达到4.8.1才可以!
1
基本数据类型是分为四种的,分别是整型,浮点型,字符型和布尔型。
四种基本类型
image-20201113122319295
从表2-1中可以看到,C++的基本数据类型有bool(布尔型)、char(字符型)、 int(整型),float(浮点型,表示实数) , double(双精度浮点型,简称双精度型)。除了bool型外,主要有两大类:整数和浮点数。
因为char型从本质上说也是整数类型,它是长度为1个字节的整数,通常用来存放字符的ASCII码。
其中关键字signed和 unsigned,以及关键字short 和long被称为修饰符。
细节ISOC++标准并没有明确规定每种数据类型的字节数和取值范围,它只是规定它们之间的字节数大小顺序满足:
(signed/unsigned)char ≤ (unsigned)short ≤ (unsigned)int ≤ (unsigned) long
不同的编译器对此会有不同的实现。
面向32位处理器(IA-32)的C++编译器,通常将int
和long
两种数据类型皆用4个字节表示。
但一些面向64位处理器(IA-64或x86-64)的C++编译器中, int
也是用4个字节表示, 但是long
用8个字节表示。
- struct A {
- char c1;
- short s1;
- };
这里不是主要说这个应用,而是分配字节的大小问题。
基础数据类型的字节长度很明显:
类型 | 长度(字节) |
---|---|
char | 1 |
short | 2 |
int | 4 |
long | 4 |
float | 4 |
double | 8 |
long long | 8 |
然而,结构体的计算方式不是类似数组类型那样简单的累计,而是需要考虑到系统在存储结构体变量时的地址对齐问题
偏移量概念:偏移量指的是结构体变量中成员的地址和结构体变量地址的差。
在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:
(1)结构体变量中某成员的偏移量必须是该成员字长大小的整数倍(0被认为是任何数的整数倍)
(2)结构体大小必须是所有成员大小的公倍数。(按顺序以已经存在的最大字长为单位)
结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。
故而有以下几个例子:
- struct A {
- char c1;//偏移量0 字长1
- char c2;//0+1=1 1
- double d1;//(1+1=2)+6=8 8
- //8+8=16是1,8的公倍数
- };
- struct B {
- char c1;//0 1
- double d1;//1+7=8 8
- char c2;//8+8=16 1
- //16+1=17不是1,8的公倍数,要扩到24才是
- };
- struct C {
- char c1;//0 1
- float f1;//1+3 4
- char c2;//8 1
- //8+1=9不是1,4的公倍数,扩到12才是
- };
其实枚举类型的应用是很简单的,不过我个人应用的话更倾向于使用map类。
类似结构体的语法。
enum 枚举类型名 {变量值列表};
附上例子。
- #include <iostream>
-
- using namespace std;
- enum GameResult {
- WIN,LOSE,TIE,CANCEL
- }; //下标依次为0,1,2,3
- int main() {
- GameResult result;//声明变量时,可以不写关键字enum
- enum GameResult omit = CANCEL;//也可以在类型名前写enum
-
- for (int count = WIN; count <= CANCEL; count++) {//枚举类型隐含类型转换为整型
- result = GameResult(count);//整型显式类型转换为枚举类型
- if (result == omit)
- cout << "The game was cancelled" << endl;
- else {
- cout << "The game was played ";
- if (result == WIN)
- cout << "and we won ! ";
- if (result == LOSE)
- cout << "and we lost . ";
- cout << endl;
- }
- }
- return 0;
- }
注意:
WIN
等是常量,不能对它们赋值。作用范围(严格来说是 main() 函数内部)内不能再定义与它们名字相同的变量。
枚举和宏其实非常类似:宏在预处理阶段将名字替换成对应的值(cpp->.i
),枚举在编译阶段将名字替换成对应的值(.i->.s
)。所以不能对枚举类型使用指针!
可以再复习一下CPP编译运行的过程就更容易理解了。
建议直接访问在线网页:https://zh.cppreference.com/w/cpp/language/ascii
有以下四个需要注意:
空格 32
‘0’ 48
‘A’ 65
‘a’ 97
image-20201113125805916
优先级 | 运算符 | 说明 | 结合性 |
---|---|---|---|
1 | :: | 范围解析(命名空间) | 自左向右 |
2 | ++ -- | 后缀自增/后缀自减 | |
() | 括号 | ||
[] | 数组下标 | ||
. | 成员选择(对象) | ||
−> | 成员选择(指针) | ||
3 | ++ -- | 前缀自增/前缀自减 | 自右向左 |
+ − | 加/减 | ||
! ~ | 逻辑非/按位取反 | ||
(type) | 强制类型转换 | ||
* | 取指针指向的值 | ||
& | 某某的地址 | ||
sizeof | 某某的大小 | ||
new, new[] | 动态内存分配/动态数组内存分配 | ||
delete, delete[] | 动态内存释放/动态数组内存释放 | ||
4 | .* ->* | 成员对象选择/成员指针选择 | 自左向右 |
5 | * / % | 乘法/除法/取余 | |
6 | + − | 加号/减号 | |
7 | << >> | 位左移/位右移 | |
8 | < <= | 小于/小于等于 | |
> >= | 大于/大于等于 | ||
9 | == != | 等于/不等于 | |
10 | & | 按位与 | |
11 | ^ | 按位异或 | |
12 | | | 按位或 | |
13 | && | 与运算 | |
14 | || | 或运算 | |
15 | ?: | 三目运算符 | 自右向左 |
16 | = | 赋值 | |
+= −= | 相加后赋值/相减后赋值 | ||
*= /= %= | 相乘后赋值/相除后赋值/取余后赋值 | ||
<<= >>= | 位左移赋值/位右移赋值 | ||
&= ^= |= | 位与运算后赋值/位异或运算后赋值/位或运算后赋值 | ||
17 | throw | 抛出异常 | |
18 | , | 逗号 | 自左向右 |
image-20201113140859803
操作数在计算之前隐式转换为类型 bool,结果的类型为 bool。
使用方式为从右向左
如果已转换的操作数是 false,则结果是 true;
如果已转换的操作数是 true,则结果是 false。
使用方式为从左至右。
如果所有的操作数都为 true,则逻辑“与”运算符 (&&) 返回布尔值 true,否则返回 false。
第一个操作数将完全计算,并且完成所有副作用,之后才会继续计算下一个逻辑“与”表达式。
如果第一个操作数的计算结果为 true(非零),才计算第二个操作数。
使用方式为从左至右。
如果任一操作数为 true,则逻辑“或”运算符 (||) 返回布尔值 true;否则返回 false。
第一个操作数将完全计算,并且完成所有副作用,之后才会继续计算下一个逻辑“或”表达式。
仅当第一个操作数的计算结果为 false (0) 时,才计算第二个操作数。
当两个数都是整数时,运算结果也是整数。
当有一个数是小数时,运算结果是小数。
对于除法,如果两个整数相除但又不能整除,则只保留整数部分,这跟将小数赋值给整数类型是一个道理。
“%”
是取余运算,只能用于整型操作数,表达式a%b
的结果是a被b除的余数。“%”的优先级与“/”相同。
在C++中,逗号也是一个运算符,它的使用形式为:表达式1,表达式2 求解顺序为先求解i,再求解⒉,最终结果为表达式2的值。
- int x=0;
- int y=(x=x+3,4,545);
- cout<<y<<" "<<x;
- // 545 3
结果是545 3
。
C++基本类型(int,char等)的指针之间不能含有隐式转换,必须要用显示转换!
因为题目要求保留一位有效数字,所以就先把这个数乘十,按照正负加减零点五,两次强制类型转换后,再除以十即可。今后碰见四舍五入问题以此类推!
- double dou=39.65;
- dou = (double)((int)(dou*10 + (dou<0?-0.5:0.5)))/10;
int转char之前,先将运算式中的每个字符都转换成ASCII码值,再进行计算,根据结果转换为字符(数值为该字符对应的ASCII码值)。
以下代码为例,其中c4的结果符合我们的预期要求。
- void int_to_char(){
- int i = 5;
-
- char c1 = i; // 5 '\x05'无法打印
- char c2 = i - 0; // 5 '\x05'无法打印
- char c3 = i - '0'; // -43 '\xd5'打印乱码
- char c4 = i + '0'; // 53 '5'可打印
- cout<<c4;
- }
char转int之前,先将运算式中的每个字符都转换成ASCII码值,再进行计算。
以下代码为例,其中i3的结果符合我们的预期要求。
- void char_to_int(){
- char c = '0';
-
- int i1 = c; // 48
- int i2 = c - 0; // 48
- int i3 = c - '0'; // 0
- int i4 = c + '0'; // 96
- cout<<i3;
- }
- void int_to_string() {
- int a = 100;
- string b = "";
- b = to_string(a);//string库,但需要c++11的支持
- cout<<b;
- }
活着是另外一个方式
- void string_to_int() {
- int a = 1;
- string b = "100abbc";
- a = atoi(b.c_str());//标准库,但是会忽略字符串中的字母,只保留数字
- cout<<a<<endl;
- a = stoi(b);//string库,但需要c++11的支持
- cout<<a;
- }
如果不支持c++11的话,可以参考博客。
- #include<sstream>
- #include<string>
- int a = 10;
- stringstream ss;
-
- ss.str("");//重复调用一个对象的话要清空,clear函数只是重置状态。
- ss << a;
- string str = ss.str();
int和bool类型之间存在隐式转换.
注意,直接输出bool类型的时候,控制台上结果为1或0
需要加上一个标志符来输出true
或者false
。
- void bool_to_int() {
- bool a = true, b = false;
- cout << "a=" << a << " , b=" << b << endl;
- cout << "a=" << boolalpha << a << " , b=" << b << endl;
- }
image-20220828230919429
主要有三种方法可以将string
转换为const char*
类型,分别是:data()、c_str()、copy()。
data()方法
- void stringt_to_char_data() {
- string str = "hello";
- const char *p1 = str.data();//不可修改
- cout << p1 << endl;
-
- char *p2 = (char *) str.data();//可修改
- p2[1]++;
- cout << p2;
- }
c_str()方法
- void stringt_to_char_c_str() {
- string str="world";
- const char *p1 = str.c_str();//不可修改
- cout << p1 << endl;
-
- char * p2=(char*)str.c_str();//可修改
- p2[1]++;
- cout << p2;
- }
copy()方法
可能会报安全性错误,自行解决即可。注意手动加结束符!!!
- void stringt_to_char_copy() {
- string str = "hmmm";
- char p[10];
- str.copy(p, 3, 0);//这里3代表复制几个字符,0代表复制的位置,
- p[3] = '\0';//注意手动加结束符!!!
- cout << p;
- }
可以直接赋值。
- string s;
- char *p = "helloworld";
- s = p;
for循环遍历输入。
- string pp = "helloworld";
- char p[20];
- int i;
- for( i=0;i<pp.length();i++)
- p[i] = pp[i];
- p[i] = '\0'; //手动添加结束符
可以直接赋值。
- string s;
- char p[20] = "helloworld";
- s = p;
可以直接赋值。
- char pp[20] = "helloworld";
- char* p = pp;
- 12
主要有两种方法可以将char*转换为char[]类型,分别是:strcpy()、循环遍历。
strcpy()方法
可能会报安全性错误,自行解决即可。
- char arr[20];
- char* tmp = "helloworld";
- strcpy(arr, tmp);
循环遍历
- char arr[20];
- char* tmp = "helloworld";
- int i = 0;
- while (*tmp != '\0')
- arr[i++] = *tmp++;
- arr[i] = '\0'; //手动添加结束符
- int main() {
- int PrintVal = 9;
- /*按整型输出,默认右对齐*/
- printf("%d\n", PrintVal);
- /*按整型输出,补齐4位的宽度,补齐位为空格,默认右对齐*/
- printf("%4d\n", PrintVal);
- /*按整形输出,补齐4位的宽度,补齐位为0,默认右对齐*/
- printf("%04d\n", PrintVal);
-
- /*按16进制输出,默认右对齐*/
- printf("%x\n", PrintVal);
- /*按16进制输出,补齐4位的宽度,补齐位为空格,默认右对齐*/
- printf("%4x\n", PrintVal);
- /*按照16进制输出,补齐4位的宽度,补齐位为0,默认右对齐*/
- printf("%04x\n", PrintVal);
-
- /*按8进制输出,默认右对齐*/
- printf("%o\n", PrintVal);
- /*按8进制输出,补齐4位的宽度,补齐位为空格,默认右对齐*/
- printf("%4o\n", PrintVal);
- /*按照8进制输出,补齐4位的宽度,补齐位为0,默认右对齐*/
- printf("%04o\n", PrintVal);
- return 0;
- }
结果如下:
- #include <iostream>
-
- #define endl '\n'
- using namespace std;
-
- int main() {
- int a, b, c, x, y;
- a = 1;
- b = 2;
- c = 0;
- cout << a++ - 1 << endl;//后缀自增运算符优先级大于加减运算符
- cout << (a && b || !c) << endl;//a和b相与结果为真
- cout << b / ++a << endl;//2除以3商0
- x = ++a || ++b && ++c;//或运算被a=4短路
- cout << a << b << c << x << endl;
- a = b = c = -1;
- y = ++a && ++b && ++c;//与运算被a=0短路
- cout << a << b << c << y << endl;
- return 0;
- }
image-20201113103823373
- int main() {
- int a = 4, b=3,c=2,d=1;
- int x, y = 0;
- x=a<b ? a+1 : c<d ? c+1 : d+1;//先判断a<b,再判断c<d,最后返回d+1
- cout <<x <<endl;
- cout <<(a+ b, b + c,c+ d) <<endl;//返回最后一个c+d
- y += a+=b+= c+ d;//从右向左
- cout<<y<<endl;
- return 0;
- }
image-20201113114428361
gitee:https://gitee.com/JunKuangKuang/KeenCPPTest-all/tree/main/basic/transition
github:https://github.com/JunKuangKuang/KeenCPPTest-all/tree/main/basic/transition
2022.8.28更新:clang-1316.0.21.2.5,诶嘿嘿嘿……
忘了啥时候更新的了:重新使用了TDM-GCC 9.2 真香!
更早以前:后来我重新下载了新版本的,GCC更新为4.9的了。现在不让下载4.9的版本了,又重新下载了6版本使用。
感谢现在努力的我