第二章 变量与基本类型
2.1.2字符型与无字符型
字符型可以用来表示负值,但无字符型可以表达的范围更大
8比特位的signde char:-127 - 127;
unsigende char:0 - 255;
当我们赋给无符号类型一个超出范围的值时,结果是初始值对无符号类型表示值总数取模后的余数。例如当把-1赋给一个unsigned char时,输出的结果最终为255。
用无符号值进行循环时可能会出现死循环/*本循环的关键是要将0也输出*/
#include<iostream>
int main()
{
for (unsigned u = 10; u >= 0; u--)
{
std::cout << u<<std::endl;
}
return 0;
}
因为当循环进行到u = 0时,u--本该等于-1;但是由于u为无符号数,所以u会自动转化为最大数42946....。所以程序会陷入无限循环之中。
改正:
1.当无需打印0时直接将u>=0改为u>0;即可
2.我们可以在循环到0时进行判断,此时可以借助while循环来进行。
#include<iostream>
int main()
{
unsigned u = 11;//此处是为了将10也打印出来,否则u先自减无法将10打印出。
while (u > 0)
{
u--;
std::cout << u << std::endl;
}
return 0;
}
特殊:
请勿将无符号型与有符号型的变量混用。
例:当两个变量都是有符号型的
#include<iostream>
int main()
{
int a = -1;
int b = 1;
std::cout << a * b;
return 0;
}
此时的输出结果正常,为-1。
当一个变量为有符号型,另一个为无符号型时:
#include<iostream>
int main()
{
int a = -1;
unsigned int b = 1;
std::cout << a * b;
return 0;
}
此时的结果却无法得到-1,而是4294967295(无符号数中十进制的最大值)
这是由于当两者混用时有符号型的值会自动的转化为无符号型,使得a的值由原来的-1变成了无符号数中的最大值4294967295。
2.1.3 字面值常量
以0开头的数为八进制数;以0x或OX开头的数为十六进制的数
20/*十进制*/ 020/*八进制数*/ 0x14/*十六进制数*/
浮点型字面值表现为一个小数或奕科学计数法表示的指数。(指数部分用e/E来表示)
3.14159 3.14159e0 0e0 .001
由单括号括起来的字符称为char型字面值;
有双括号括起来的零个或多个字符则构成了字符串;
转义字符(常用)
换行符 \n 制表符\t
/后面也可以跟上一到三个八进制的数即泛化的转义序列。
也可以/x后面跟上一个或多个十六进制的数。
换行符 /12 空格 /40 空字符 /0
2.2.2 变量声明和定义的关系
声明使得变量为程序所知,一个文件如果想使用其他文件定义的变量必须要声明。
其他文件是指与源文件不同的文件 (头文件...)
声明的变量必须是先前被定义的
声明的主要作用是为了读取其他文件中的变量值
定义是负责创建与名字相关联的实体。
如果想单单声明变量 只需在原本定义之前加入(extern):
extern int i; //声明了变量而非定义
int j;//声明并定义了一个新变量j
注:
变量能且只能被定义一次,但是可以被多次声明。
嵌套的作用域
#include<iostream>
int m = 1;
int main()
{
int m = 2;
std::cout << m << std::endl;//输出局部变量
std::cout <<:: m << std::endl;//输出全局变量
return 0;
}
运用:一般在for循环中 常常会不经意间定义一个已经存在的变量名作为全局变量,但是当for循环结束以后,for循环中定义的值无法被读出。
2.3.1 引用:
引用不占用地址,只是另一个数的别名
#include<iostream>
int main()
{
int i;
int& m = i;
m = 2;
std::cout << i;
return 0;
}
此处m即为i的引用,通过更改m的值来间接的修改i的值
对指针的引用
#include<iostream>
#include<cstdlib>
int main()
{
int i = 1;
int* p;
int*& r = p;
r = &i;
std::cout << *r << std::endl << r;
return 0;
}
注:1.由于引用不占用地址,所以不能定义一
个指向引用的指针。
2.不能定义引用的引用
3.引用的对象必须是同一类型的
2.32 指针
利用指针来访问对象时则需要使用解引用符(*)
关键概念:某些符号有着多重含义
#include<iostream>
int main()
{
int i = 42; //定义一个整数i;
int& r = i; //r为i的引用;
int* p;
p = &i; //p为指向i的指针
*p = i; //对p进行解指针得到的值赋为i
int& r2 = *p; //r2为p所指对象的引用
return 0;
}
空指针
定义空指针的三种方式
#include<iostream>
#include<cstdlib>
int main()
{
int* p = nullptr;
int* p2 = 0;
int* p3 = NULL;
return 0;
}
//在新标准下最好使用nullptr 而不是NULL
//不能将一个变量直接赋给指针(尽管变量的值恰好为0)
指针也可以当做一个判断条件
if(p) ->当p为一个非空指针时执行循环体
//p为非空 即表示p有一个所指的对象
2.4const 限定符
作用:定义一个变量,使其的值不再发生改变。但是const定义的变量必须初始化。
可以把引用绑定在const上,实现对常量的绑定,但是不能修改其值
const int c = 1024;
const int &i = c;
const:
可以将常量绑定到变量上;但是不可以将变量绑定到常量之上。
#include<iostream>
#include<cstdlib>
int main()
{
int i = 1;
const int m = 2;
//int& r = m;//将变量绑定到了一个常量上 程序会报错
const int& x = i;//将常量绑定到了一个变量上,可以通过修改变量的值来间接的修改常量的值。
//std::cout << r;
std::cout<< x;
return 0;
}
const的分类
顶层const:指的是对数值进行常量化;
底层const:指的是对地址进行常量化。
2.4.4 constexpr和常量表达式
声明为constexpr的变量一定是一个常量,所以定义为constexpr的变量赋值表达式一定是一个可以立马得到数值的常量。
当你认定一个数为常量时,可以优先考虑使用constexpr来定义变量。(由constexpr修饰的变量一定是个常数)
#include<iostream>
#include<cstdlib>
int main()
{
const int i = 1;
const int* p = nullptr;//定义了一个指向常量的指针
constexpr int* q = nullptr;//定义了一个指向整数的常量指针
constexpr int* m = &i;//指向一个整数i的常量指针
std::cout << q;
return 0;
}
指针常量和类型别名
typedef char* pstring;
const pstring sctr = 0;//sctr 是一个指向char的常量指针。
const pstring* ps;//ps 是一个指向char的常量指针
const char* sctr = 0;//与上面的表达式不相等;这里表示的是一个指向const char 的指针。
2.5.2 auto的运用:
auto可以让计算机判断变量的数值类型(auto定义的变量必须要有初始值)
auto可以同时定义多个变量,但是必须都是同一个类型的变量。
2.5.1 类型别名
decltype类型指示符(完全模拟对象的性质)
当我们想仅仅定义个变量,而不像对其进行赋给初始值时,我们可以考虑使用decltype来进行对变量类型的定义。
const int ci = 0;
int i = 1;
const int& cj = ci;
decltype (ci) m; //m的类型为const int。
decltype (cj) x = i;//x为i的引用,直接对cj的模仿。 x的类型为const int &;
如果括号内为解引用操作,则得到的类型即为int & ,此时定义的变量就必须要有初始值(此时变量为一个引用,所以必须要初始化)
如果decltype的表达式带上了括号则结果为引用。(双括号的结果一定为引用)
代码演示:
int i = 42;
int* p = &i;
decltype (*p) m = i;//m为i的引用
decltype ((i)) m = i;//与上面的表达式意义相同
自定义一个数据结构:
(参考线性表的定义)