概述
无符号整数和有符号整数
当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如,8比特大小的unsigned char可以表示0至255区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对256取模后所得的余数。因此,把 -1 赋给8比特大小的unsigned char所得的结果是255。
负数对正数取模:
参考链接:https://blog.csdn.net/oqqHuTu12345678/article/details/73607116
由带余除法,任何一个整数n,都可以表示成n=kq+r,其中0<=r<q
这里的r就是n除以q的余数,通常记为n≡r(mod q)
n≡r(mod q) <==>n=kq+r,r=n-k*q,其中0<=r<q
例如-9=(-2)*5+1,则-9除以5的余数为1。
// pr2.4编写程序检查你的估计是否正确,如果不正确,请仔细研读本节直到弄明白问题所在。
int main()
{
unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl;
std::cout << u - u2 << std::endl;
int i = 10, i2 = 42;
std::cout << i2 - i << std::endl;
std::cout << i - i2 << std::endl;
std::cout << i - u << std::endl;
std::cout << u - i << std::endl;
}
指定字面值的类型:
初始值
当对象在创建时获得了一个特定的值,我们说这个对象被初始化(initialized)了。用于初始化变量的值可以是任意复杂的表达式。当一次定义了两个或多个变量时,对象的名字随着定义也就马上可以使用了。因此在同一条定义语句中,可以用先定义的变量值去初始化后定义的其他变量。
在C++语言中,初始化是用一个异常复杂的问题,我们也将反复讨论这个问题。很多程序员对于用等号=来初始化变量的方式倍感困惑,这种方式容易让人认为初始化是赋值的一种。事实上在C++语言中,初始化和赋值是两个完全不同的操作。然而在很多编程语言中二者的区别几乎可以忽略不及,即使在C++语言中有时这种区别也无关紧要,所以人们特别容易把二者混为一谈。需要强调的是,这个概念至关重要,我们也将在后面不止一次提及这一点。
默认初始化
如果定义变量时没有指定初始值,则变量被默认初始化(default initialized),此时变量被赋予了“默认值”。默认值到底是什么由变量类型决定,同时定义变量的位置也会对此有影响。
如果是内置类型的变量未被显示初始化,它的值由定义的位置决定。定义于任何函数体之外的变量被初始化为0。然而如6.1.1节(第185页)所示,一种例外情况是,定义在函数体内部的内置类型变量将不被初始化(uninitialized)。一个未被初始化的内置类型变量的值是未定义的(参见2.1.2节,第33页),如果试图拷贝或以其他形式访问此类值将引发错误。
每个类各自决定其初始化对象的方式。而且,是否允许不经初始化就定义对象也由类自己决定。如果类允许这种行为,他将决定对象的初始值到底是什么。
绝大多数类都支持无需显式初始化而定义对象,这样的类提供了一个合适的默认值。例如,string类规定如果没有指定初值则生成一个空串:
std::string empty; // empty非显式地初始化为一个空串
Sales_item item; // 被默认初始化的Sales_item对象
一些类要求每个对象都显式初始化,此时如果创建了一个该类的对象而未对其做明确的初始化操作,将引发错误。
2.2.2 变量声明和定义的关系
为了支持分离式编译,C++语言将声明和定义区分开来。声明(declaration)使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。而定义(definition)负责创建与名字关联的实体。
变量声明规定了变量的类型和名字,在这一点上定义与之相同。但是除此之外,定义还申请存储空间,也可能会为变量赋一个初始值。
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量:
extern int i; // 声明i而非定义i
int j; // 声明并定义j
任何包含了显式初始化的声明即成为定义。我们能给由extern关键字标记的变量赋一个初始值,但是这么做也就抵消了extern的作用。extern语句如果包含初始值就不再是声明了,而变成定义了:
extern double pi = 3.1416; // 定义
在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。
注:变量能且只能被定义一次,但是可以被多次声明。
声明和定义的区别看起来也许微不足道,但实际上却非常重要。如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。
2.2.3 标识符
用户自定义的标识符中不能连续出现两个下划线,也不能以下划线紧连大写字母开头。此外,定义在函数体外的标识符不能以下划线开头。
2.2.4 名字的作用域
建议:当你第一次使用变量时再定义它
一般来说,在对象第一次被使用的地方附近定义它是一种好的选择,因为这样做有助于更容易地找到变量的定义。更重要的是,当变量的定义与它第一次被使用的地方很近时,我们也会赋给它一个比较合理的初始值。
2.3.1 引用
引用必须被初始化。
引用即别名:引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。
2.3.2 指针
对指针解引用会得到所指对象,因此如果对解引用的结果赋值,实际上就是给指针所指的对象赋值。
得到空指针最直接的方法就是用字面值nullptr来初始化指针,这也是C++11新标准刚刚引入的一种方法。nullptr是一种特殊类型的字面值,它可以被转换成(参见2.1.2节,32页)任意其他的指针类型。另一种办法就如对p2的定义一样,也可以通过将指针初始化为字面值0来生成空指针。
有时候要想搞清楚一条赋值语句到底是改变了指针的值还是改变了指针所指对象的值不太容易,最好的办法就是记住赋值永远改变的是等号左侧的对象。当写出如下语句时,
pi = &ival; // pi的值被改变,现在pi指向了ival
意思是为pi赋一个新的值,也就是改变了哪个存放在pi内的地址值。相反,如果写出如下语句,
*pi = 0; // ival的值被改变,指针pi并没有改变
则*pi(也就是指针pi指向的那个对象)发生改变。
void指针
void型指针可以存放任意对象的地址,但我们不清楚该地址中到底是什么类型的对象。
利用void指针能做的事比较有限:拿他和别的指针比较、作为函数的输入或输出,或者赋给另外一个void指针。不能直接操作void*指针所指的对象、因为我们并不知道该对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。
2.3.3 理解复合类型的声明
变量定义包括一个基本数据类型(base type)和一组声明符。在同一条定义语句中,虽然基本数据类型只有一个,但是声明符的形式却可以不同。也就是说,一条定义语句可能定义出不同类型的变量:
int i = 1024, *p = &i, &r = i;
指向指针的引用
引用 本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用:
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指针。
最后
以上就是不安电脑为你收集整理的C++ Primer - Ch2笔记 2.1~2.3的全部内容,希望文章能够帮你解决C++ Primer - Ch2笔记 2.1~2.3所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复