基本内置类型
算术类型
除去布儿型和扩展的字符型之外,其他整形可以划分为**带符号(signed)的,和无符号(unsigned)**的的两种。带符号的可以表示正数,负数或0,无符号的类型则仅能表示大于等于0的值。
int ,short, long, long long都是带符号的,通过在这些类型名前添加unsigned就可以得到无符号的类型。
注意:
1.在算术表达中不要使用char或者bool,只有在存放字符或者布尔值时才使用它们。因为类型char在一些机器上是有符号的,在另一些机器上确实没有符号的。
2.如果表达式里既有带符号类型又有无符号类型,当带符号类型取值为负时会出现异常结果,因为带符号的数会自动转换成无符号数。
类型转换
1.把一个浮点数赋给整数类型的时,做了近似处理。结果值仅将保留浮点数中小数点之前的部分。
字面值常量
以0开头的整数代表八进制数,以0x开头的开头的代表十六进制数。
变量
变量定义
1.当对象在创建时获得一个特定的值,我们就说这个对象被初始化了。在c++语言中,初始化和赋值是两个完全不同的操作。
初始化不是赋值,初始化的含义是创建变量的时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。
2.如果定义变量时没有指定初始值,则变量被默认初始化。
定义于任何函数体之外的变量初始化为0,定义在函数体内部的内置类型变量将不被初始化,string类规定如果没有指定初始值生成了一个空串。
变量声明和定义的关系
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量。
extern int i; //声明i而非定义i
int j;//声明并定义j
任何包含了显示初始化的声明都是定义我们能给由extern关键字标记的变量赋值一个初始值,但是这么做也会抵消extern的作用。extern语句如果包含初始值就不再是声明了,而变成了定义
extern double i= 3.14;
在函数内部,如果试图初始化一个由extern关键字标记的变量,将引发错误!
标识符
名字的作用域
如果函数有可能用到某全局变量,则不宜在定义一个同名的局部变量。
#include <iostream>
int resued = 42;
int main
{
int unique = 0;
std::cout<<resued<<"" <<unique<<std::endl
int resued = 0;
std::cout<<resued<<"" <<unique<<std::endl
//显示地访问全局变量resued,输出42,0
std::cout<<::resued<<"" <<unique<<std::endl
}
}
复合类型
关于引用
1.不可以直接引用常量,必须是指向常量的引用才可以,这样使用引用就可以不用测试其有效性了。
2.引用并非对象,只是为一个已经存在的对象起的别名。
3.将引用变量作为参数的时候,函数将使用原始数据,而非副本。
4.当数据所占内存较大时,建议使用引用参数。
5.引用只能绑定到对象上,而不能与字面值或某个表达式的计算结果绑定在一起。
double &d = 12.5;//错误
const &d = 12.5;//正确
int int_value = 1024;
int &ref_value = int_vale;
指针
指针实现了对其他对象的间接的访问。
指针本身就是一个对象,允许对指针进行赋值和拷贝,而且在指针的生命周期内它可以先后指向不同的对象。
指针在定义的时候无需赋值。和其他内置类型一样,在块作用域内定义的指针如果没有初始化,也将拥有一个不确定的值。
如果指针指向了一个对象,则允许使用解引用符 * 来访问该对象。
int ival = 42;
int* p = &ival;
cout << *p;
//输出42
解引用操作仅适用于那些确实指向了某个对象的有效指针。
赋值和指针的重要区别:
1.引用本身不是对象,一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。
2.而指针和它存放的地址之间就没有这种限制了。给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。
赋值语句到底是改变了指针的值还是改变了指针所指对象的值:
赋值永远改变的是等号左侧的对象
void* 指针
void指针可以和别的指针做比较,作为函数的输入或输出,或者赋值给另外一个void指针。不能直接操作void*指针所指的对象,因为我们不知道这个对象到底是什么类型的,也就无法确定能在这个对象上面做什么操作。
指针的指针
指针是内存中的对象,像其他对象一样也有自己的地址 ,因此允许把指针的地址再存放到另一个指针当中。
//例子一:
int ival = 1024;
int *p = &ival;
int **ppi = &p;
std::cout<<"the value of ival\n"<<"直接的值 :"<< ival<<"\n"
<<"地址:"<<p<<"\n"
<<"解一次出来的是地址 :"<<*ppi<<std::endl;
/**
直接的值 :1024
地址:0x7ffeefbff56c
解一次出来的是地址 :0x7ffeefbff56c
*/
//例子二:
int ival = 1024;
int *p = &ival;
int **ppi = &p;
std::cout<<"the value of ival\n"<<"直接的值 :"<< ival<<"\n"
<<"间接取出来的值:"<<*p<<"\n"
<<"双重取值 :"<<**ppi<<std::endl;
/**
直接的值 :1024
地址:0x7ffeefbff56c
解一次出来的是地址 :0x7ffeefbff56c
*/
指向指针的引用
int i =42;
int *p;//p 是一个int型的指针
int *&r = p;//r是一个对指针p的引用
r = &i;//r引用了指针 因此给r赋值&i就是令p指向i
*r = 0;//解引用r得到i,也就是p指向的对象,将i的值改为0
要理解r的类型到底是什么,最简单的办法就是从右向左阅读r的定义。离变量名最近的符号(&r的符号&)对变量的类型有最直接的影响,因此r是一个引用。声明符的其余部分用来确定r的引用类型是什么,此例中的符号*说明r引用的是一个指针。最后,声明的基本数据类型部分 指出r引用的是一个int型的指针。
const限定符
const对象一旦创建后其值就不能再改变了,所以const对象必须初始化。当然,初始值可以是任意复杂的表达式。
默认情况下,const对象被设定为仅在当前文件内有效。如果想在多个文件之间共享const对象 ,必须在变量的定义之前添加extern关键字。
初始化和对const的引用:
引用的类型必须与其所引用的对象的类型一致,但是有两个例外。第一种是:初始化常量引用时允许用任意表达式作为初始值,只要该表达式结果能转化成引用的类型即可
int i = 42;
const int &r1 = i;//正确,允许将const int&绑定到一个普通的int对象上面
const int &r2 = 42;
const int &r3 = r1*2;
int &r4 = r1*2;//错误:r4是一个普通的非常量引用
指针和const:
1.指向常量的指针不能用于改变其所指对象的值,想要存放常量对象的地址,只能使用指向常量的指针。和常量引用一样,指向常量的指针也没有规定其所指对象必须是一个常量。
const double pi = 3.14;//pi是个常量,它的值不能改变
double *ptr = π//错误:ptr是个普通的指针
const double *cptr = π//正确,cptr可以指向一个双精度常量
*cptr = 42;//错误,不能给*cptr赋值
2.常量指针必须初始化,一旦初始化完成,则它的值(存放在指针中的那个地址)就不能改变了。把*放在const关键字之前用以说明指针是一个常量,这样的书写形式隐含着一层意味,即不变的是指针本身的值,而非指向的那个值
int errNumb = 0;
int *const curErr = &errNumb;//curErr将一直指向errNumb
const double pi = 3.14159;
const double *const pip = π//pip是一个指向常量对象的常量指针
顶层const
顶层(top-level const )表示指针本身是一个常量。(任意的对象是常量)
底层(low-level const)表示指针指向的对象是一个常量。
//注意拷贝后的结果
int i= 0;
int *const p1 = &i;
const int ci = 42;
const int *p2 = &ci;
const int *const p3 = p2;
const int &r = ci;
i = ci;
p2 = p3;
// int *p = p3; 错误,p3包含底层const含义,而p没有
p2= &ci;
// int &r= ci;错误,普通的int& 不能绑定到int常量上
const int &r2 =i;
常量表达式
是指值不会改变并且在编译 过程就能得到结果的表达式。
将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明constexpr的变量一定是一个常量,而且必须用常量表达式初始化。一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象。
constexpr int mf = 20;
constexpr int limit = 20+mf;
constexpr int sz = size();
constexpr int *np = nullptr;
int j = 0;
//其中j和i都必须定义在函数体之外
constexpr const int *p = &i;//p是常量指针,指向整型常量i
constexpr int *p1 = &j;//p1是常量指针,指向整数j
处理类型
类型别名
有两种方法可以定义:
1.使用typedef:
2.使用using
typedef double wages;
using SI = Scales_items;
SI item; // 等价于Scales_items item
typedef char* pstring;
//cstr是指向char的常量指针(这里➡️)
const pstring cstr = 0;
//ps是一个指针,它的对象是指向char的常量指针
const pstring *ps;
注意⚠️:
const char* cstr = 0; 这样理解是错误的。➡️中的语句声明了一个指向char的常量指针,后面这个解释成了声明一个指向const char的指针。改写的结果是,const char成了基本的数据类型,而原始的语句基本数据类型是指针
auto类型说明符
auto关键字是让编译器替代我们去分析表达式所属的类型,当然auto定义的变量必须有初始值。
int i = 0,&r = i;
auto a =r;
const int ci = i, &cr = ci;
auto b = ci;//b是一个整数,ci的顶层const特性被忽略了
auto c = cr;//c是一个整数,cr是ci的别名,ci本身是一个顶层const
auto d = &i;//d是一个整型指针,整数的地址就是指向整数的指针
auto e = &ci;//e是一个指向整数常量的指针,对象常量对象取地址是一种底层const
//如果希望推断出auto类型是一个顶层const,需要明确指出:
const auto f = ci;
auto &g = ci;//g是整型常量引用,绑定到ci
auto &h = 42;//错误!!!!不能为非常量引用绑定字面值
const auto &j = 42; //正确,可以为常量引用绑定字面值
auto k = ci,&l = i;//正确,k是整数,l是整型引用
auto &m = ci,*p = &ci;//m是对整型常量的引用,p是指向整型常量的指针
auto &n = i,*p2 = &ci;//错误!!!!i的类型int 而&ci是const int
decltype类型指示符
从表达式的类型推断出要定义的变量的类型,但是不想用该表达式的值初始化变量,则使用decltype,它的作用是选择并返回操作数的数据类型。
//如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层的const和引用在内)
const int ci = 0,&cj = ci;
decltype(ci)x = 0;//x的类型是const int
decltype(cj)y = x;//y的类型是const int&,y绑定到变量x
decltype(cj)z;//错误z是一个引用,必须初始化
//如果decltype使用的表达式不是一个变量,,则decltype返回表达式结果对应的类型。
int i = 42,*p = &i,&r = i;
decltype(r+0)b;//b是一个int
//如果表达式的内容是解引用的操作,的decltype将得到引用类型
decltype(*p)c;//错误!!!!c是一个int&,必须初始化
注意⚠️:
decltype((variable)) (注意是双层括号)的结果永远都是引用,而decltype(variable)结果只有当decltype本身就是一个引用时才是引用
decltype((i))d;//d是int& ,必须初始化
decltype(i)e;//正确,e是一个(未初始化的)int
术语表
- 地址(address)是一个数字,根据它可以找到内存中的一个字节。
- 常量指针(const pointer)是一种指针, 它的值永不改变。
- 别名声明( alias declaration)为另外一种类型定义一个同义词:使用“名字=类型”的格式将名字作为该类型的同义词。
- 常量引用(const reference)是一种习惯叫法,含义是指向常量的引用。
- 算术类型(arithmetic type)布尔值、 字符、整数、浮点数等内置类型。
- 常量表达式(const expression)能在编译时计算并获取结果的表达式。
- 数组(array)是一种数据结构,存放着一组未命名的对象,可以通过索引来访问这些对象。
- constexpr是一种函数,用于代表一条常量表达式。
- auto是一个类型说明符,通过变量的初始值来推断变量的类型。
- 转换(conversion)一种类型的值转变成另外一种类型值的过程。C++语言支持内置类型之间的转换。
- 基本类型(base type)是类型说明符,可用const修饰,在声明语句中位于声明符之前。基本类型提供了最常见的数据类型,以此为基础构建声明符。
- 数据成员(data member) 组成对象的数据元素,类的每个对象都有类的数据成员的一份拷贝。数据成员可以在类内部声明的同时初始化。
- 绑定(bind)令某个名字与给定的实体关联在一起,使用该名字也就是使用该实体。例如,引用就是将某个名字与某个对象绑定在一起。
- 声 明(declaration) 声称存在一个变量`函数或是别处定义的类型。名字必须在定义或声明之后才能使用。
- 字节(byte)内存中可寻址的最小单元,大多数的机器字节占8位。
- decltype是一个类型说明符,从变量或表达式推断得到类型。
- const是一种类型的修饰符,用于说明永不改变的对象。const对象一旦被定义就无法被再赋新值,所以必须被初始化。
- 定义(definition) 为某一特定类型的变量申请存储空间,可以选择初始化该变量。名字必须在定义或声明之后才能使用。
- 转义序列(escape sequence)字符特别是那些不可打印字符的替代形式。转义以反斜线开头,后面紧跟一个字符,或者不多于3个八进制数字,或者字母x加上1个十六进制数。
- 全局作用域(globalscope)位于其他所有作用城之外的作用域。
- 头文件保护符(header guard) 使用预处理变量以防止头文件被某个文件重复包含。
- 标识符(identifer) 组成名字的字符序列,标识符对大小写敏感。
- 类内初始值(in-class initilizer)在声明类的数据成员时同时提供的初始值,必须置于等号右侧或花括号内。
- 在作用域内(in scope)名字在当前作用域内可见。
- 被初始化(nitalized) 变量在定义的同时被赋予初始值,变量一般都应该被初始化。
- 内层作用域(inner scope)嵌套在其他作用域之内的作用域。
- 整型(integral type)参见算术类型。
- 列表初始化( list initialization)利用花括号把一个或多个初始值放在一起的初始化形式。
- 字面值(literal) 是一个不能改变的值,如数字、字符、字符串等。单引号内的是字符字面值,双引号内的是字符串字面值。局部作用域(local scope) 是块作用域的习惯叫法。
- 成员(member)类的组成部分。
- 不可打印字符(nonprintable character)不具有可见形式的字符,如控制符、退格、换行符等。
- 空指针(null pointer)值为0的指针,空指针合法但是不指向任何对象。
- nullptr是表示空指针的字面值常量。
- 对象(object)是内存的一块区域,具有某种类型大变量是命名了的对象。
- 外层作用域(outer scope)嵌套着别的作用域的作用域。
- 指针(pointer) 是一个对象,存放着某个对象的地址,或者某个对象存储区域之后的下一地址,或者0.
- 指向常量的指针( pointer to const) 是一个指针,存放着某个常量对象的地址。指向常量的指针不能用来改变它所指对象的值。
- 预处理器( preprocessor) 在C+ +编译过程中执行的一段程序。
- 预处理变量( preprocessor variable)由预处理器管理的变量。在程序编译之前,预处理器负责将程序中的预处理变量替换成它的真实值。
- 引用(reference) 是某个对象的别名。
- 对常量的引用(reference to const)是一个引用,不能用来改变它所绑定对象的值。对常量的引用可以绑定常量对象,或者非常量对象,或者表达式的结果。
- 作用域(scope)是程序的部分,在其中某些名字有意义。C++有儿级作用域:
- 全局(global)一名字定义在所有其他作用域之外。
- 类(class) 一名字定义在类内部。
- 命名空间(namespace) 一 名字定 义在命名空间内部。
- 块(block)名字定义在块内部。名字从声明位置开始直至声明语旬所在的作用域末端为止都是可用的。
- 分离式编译( separate compilation)把程序分割为多个单独文件的能力。
- 带符号类型(signed)保存正数、负数或0的整型。
- 字符串(string) 是一种库类型,表示可变长字符序列。
- struct是一个关键字,用于定义类。
- 临时值( temporary)编译器在计算表达式结果时创建的无名对象。为某表达式创建了一个临时值,则此临时值将。 直存在直到包含有该表达式的最大的表达式计算完成为止。
- 顶层const ( top-level const) 是.个const,规定某对象的值不能改变。
- 底层const (low-level const)一个不属于顶层的const,类型如果由底层常量定义则不能被忽略。
- 类型别名(type alias)是一个名字, 是另外一个类型的同义间,通过关键宁typedef或别名声明语句来定义。
- 类型检查(type checking) 是“个过程,编译器检查程序使用某给定类型对象的方式与该类型的定义是否致。
- 类型说明符(type specifier)类型的名字。typedef为某类型定义一个别名。当关键字typedef作为声明的基本类型出现时,声明中定义的名字就是类型名。
- 未定义(undefined)即C++语言没有明确规定的情况。不论是否有意为之,未定义行为都可能引发难以追踪的运行时错误、安全问题和可移植性问题。
- 未初始化( uninitialized)变量已定义但未被赋予初始值。般来说, 试图访问未初始化变量的值将引发未定义行为。
- 无符号类型(unsigned) 保存大于等于0的整型。
- 变量(variable) 命名的对象或引用。C++语言要求变量要先声明后使用。
- void*可以指向任意非常量的指针类型,不能执行解引用操作。
- void类型是一种有特殊用处的类型,既无操作也无值。不能定义一个void类型的变量。
- 字(word)在指定机器上进行整数运算的自然单位。.般水说,字的空间足够存放地址.32位机器上的字通常古据4个字节。
- &运算符(& operator)取地址运算符。 * 运算符(* operator)解引用运算符。解引用一个指针将返回该指针所指的对象,为解引用的结果赋值也就是为指针所指的对象赋值。
- #define是一条 预处理指令,用于定义一个预处理变量。
- &运算符(& operator)取地址运算符。运算符( operator)解引用运算符。解引用一个指针将返回该指针所指的对象,为解引用的结果赋值也就是为指针所指的对象赋值。
- #endif是条预处理指令, 用于结束个#ifdef或#ifndef区域。
- #ifdef是条 预处理指令,用于判断给定的变量是否已经定义。
- #ifndef是一条预处理指令,用于判断给定的变量是否尚未定义。
补充
c++ 语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。
1.值传递的例子:
由于func1函数内的x是外部变量n的一份拷贝,改变x的值不会影响n,所以n的值仍然是0.
void func(int x)
{
x = x+10;
}
...
int n = 0;
func(n);
cout<< "n = "<<n<<emdl;//n = 0
2.指针传递
由于func2函数体内的x是指向外部变量n的指针,改变该指针的内容将导致n的值改变,所以n的值成为10.
void Func2(int *x)
{
(* x) = (* x) + 10;
}
...
int n = 0;
Func2(&n);
cout << "n = " << n << endl; // n = 10
3.引用传递
由于 Func3 函数体内的 x 是外部变量 n 的引用,x 和 n 是同一个东西,改变 x 等于改变 n,所以 n 的值成为 10。
void Func3(int &x)
{
x = x + 10;
}
...
int n = 0;
Func3(n);
cout << "n = " << n << endl; // n = 10