C++ 变量

两类内部变量

算数型和空类型

1、数值转换convert

// An highlighted block
bool  b = 42;//b = true
int i = b;//i = 1
i = 3.1415926//i = 3
double pi = i;//pi = 3.0
unsigned char c1 = -1;//c = 255(假设char类型占8bits)
//一般对于unsigned(不限于char)取值超出范围则对其最大值+1取余数得到其实际值
//而signed超出范围则认为未定义(机器可能继续工作或程序崩溃)
signed char c2 = 256;//c2的值是未定义的(假设char类型占8bits)
/*
char 和 signed char 还有 unsigned char 三者只会表现出两种形式
char 表现是具体哪一种根据编译器来决定(一般是 signed 但是还是建议使用signed char)
*/

2、带符号和无符号

2.1、有/无符号类型的表达式

// An highlighted block
using namespace std;
unsigned int u = 10;//这里直接写unsigned 不加int 也可定义,但是这是什么意思
int i = -42;
cout << i+u << endl;//int 32位情况下输出 4294967264
cout << u+i << endl;//同上
cout << i+i << endl;//输出 -84
/*
在表达式中同时出现signed和unsigned类型,将会全部都转化成unsigned类型
注意没有 unsigned/signed double/float这种类型 
*/

unsigned int u1 = 10, u2 = 42;
cout << u2 - u1 << endl;//输出32
cout << u1 - u2 << endl;//也输出32,这种结果得到的是取模之后的值

2.2、unsigned/signed直接定义,省略后面的变量类型

unsigned i = 3.14;
//编译器会默认理解为你定义的是一个int类型变量
//此时 i 的实际值是3

3、声明和定义

//定义只定义一次,声明可以在多个使用到该变量的地方)不同文件出现
int i = 0;
//多次重复定义一个变量将会引发错误
extern int i;
//声明处不允许对变量初始化

4、变量命名规范

1、标识符要可以体现实际意义。
2、变量名一般用小写字母,不要开头大写字母。
3、用户自定义class名一般用答谢字母开头。
4、如果标识符有多个单次组成,则单次之间应该有明显的区分。

5、变量作用域


main.cpp

#include<iostream>

using namespace std;

int reused = 10;

int main(void)
{
	int reused = 20;
	cout<< "reused = " << reused << endl;//1、
	
	for(int reused = 0; resused<=200; reuesd++)
		cout<< "reused = " << reused << endl;//2、
	cout<< "reused = " << ::reused << endl;//3、
}


any_file.h
#include<iostream>
using namespace std;
extern int reused;

inline void print_test(void)
{
	cout<< "reused = " << reused << endl;//4、
}

    在以上四个位置打印出的reused是不同意义的
1、reused = 20 访问的是局部变量,在整个main函数中除去其他大括号或语句内部,直接访问到的都是在main函数中最开始定义的那个局部变量。
2、reused 从0开始循环到200, 此时是在for语句中,reused在语句内部定义,使用的就是内部定义的值,仅在for语句内部有效。
3、访问::reused,显式的访问reused全局变量,reused = 10。

"::"是作用域操作符,如果我们文件前不加 using namespace std;
则后面使用cout cin这种必须写成 std::cout std::cin;

使用"::"来覆盖原本的作用域(main函数中),由于全局作用域本身没有名字,
故以上的用法就是直接用全局作用域来代替当前作用域,进而访问全局变量reused

4、调用该函数时,打印的就是在该文件中声明的全局变量,如果没有在其他文件中更改过reused的值,那么reused = 10,就是全局变量定义时的初始化值。

6、复合类型

    是指基于其他类型定义的类型。C++有多种复合类型,在这里我们只介绍引用和指针。

6.1、引用

    引用本身不是一个对象,定义时必须绑定到一个对象上,对引用的改变就相当于对原本被绑定对象的改变。

/*正确使用的示例*/
int val = 10;
double val_dou = 3.14;
int &ref_val = val;//我们将ref_val绑定到val上面
int next_val = ref_val;//等同于next_val = val;
ref_val = 100;//可以通过修改引用值来修改val值,实际上等同于 val= 100; 给val重新赋值
int &ref_dou = val_dou;//是合法的,允许不同类型变量之间的绑定,此时ref_dou = 3
/*错误示例演示*/
int &ref_val;//错误:不能直接定义一个引用,引用定义时必须初始化—,绑定到一个特定对象上
int &next_ref = ref_val;//错误:引用本事不是对象,引用不能绑定到引用上
double &ref_dou = 3.14;//错误:这是赋值不是初始化,引用未绑定到特定对象上 

6.2、指针

    建议使用指针前初始化所有指针,即定义时就初始化;如果不清楚它之后具体要求指向哪里,就将其定义为空指针 *p = nullptr 这样便于之后程序检测它到底是有没有指向一个具体的对象。

//错误示例
double dval;
double *pd = &dval;//正确
double *pd2 = pd;//正确
int *pi = pd;//错误:指针类型和所指向的对象类型不匹配
pi = &dval;//错误:同上

空指针:空指针不指向任何对象,以下均为生成空指针的方法(建议使用第一种方法)
int *p1 = nullptr;//以这种方法定义一个空指针,等价于 *p1 = 0;
int *p2 = 0;//直接将p2初始化为字面常量0
int *p3 = NULL;//等价于 *p1 = 0; 需要#include <cstdlib>

/*错误范例如下*/
int zero = 0;
int *p = &zero;//虽然是想让*p = 0;使其成为空指针.但不能把int型变量直接赋值给指针
int xxx = 0,yyy = 0;
int *p1 = &xxx, *p2 = &yyy;
if(p1)//用于判断是否是空指针,指针是否指向一个特定的对象
	// ......
if(p1 == p2)//用于对同类型指针作比较,用来判断二指针是否指向同一对象
	// ......
//void* 类型指针
特殊类型指针可以存放任意对象的地址
double obj = 3.14, *pd = &obj;
void *pv = &obj;
pv = pd;//以上都是合法的,pv可以存放任意类型的地址

std::cout << *pv << std::endl;//错误
//由于void* 类型内存放的地址对应数据类型未知,故不能做取出地址内数据的操作,仅仅知道地址而已
指针是对象
引用不是对象
存在指向指针的指针;存在绑定指针的引用;不存在绑定引用的引用

int val;
int *p = &val;
int **PP = &p;//指向指针的指针

int val_next;
int *pv = &val_next;
int *&ref_pv = pv;//ref_pv是对pv的引用,但注意其定义的形式
//当然可以对引用做如下操作
ref_pv = &val;//将指针pv重新指向val而不是val_next
*ref_pv = 10;//相当于从地址处改变val的值

7、const 限定符

    学习时一直有的一个疑问是:为什么要使用const而不是直接使用宏定义#define,二者的区别是什么(之后学到define时进行自己的理解)

const int buf_size = 256;
//使用时表示后面定义的变量不可以被更改,响应的,第一次定义时必须初始化变量。当然,在声明时不必

7.0、杂项

关于在不同文件中的同名变量定义

main.cpp 
const int xxx = 100;
int yyy = 200;
any_other.cpp
const int xxx = 10;//合法,这里的xxx和main.cpp中的xxx相当于两个不同的变量。
//但如果是在.h文件中定义则不允许让main.cpp包含该.h文件,会造成冲突
int yyy = 20;//不合法,两个重名的全局变量,即使不是相互包含声明也不允许这样定义

7.1、const 与 引用&、指针*

const 和 引用 &

int i = 333;
const int &r1 = i;//允许i进行重新赋值(之后r1的值也会相应进行改变),但不允许通过r1进行对i的更改。
const int &r2 = 333;
const int &r3 = r1 * 3;
//以上均合法,除了r1是普通的用法,之后的两种去掉const均不合法,是适用于const关键字的特例

const 和 指针 *

const int xxx = 10;
const int *px = &xxx;//指向常量的指针必须加const限定符

int yyy = 100;
const int *py = &yyy;//指针可以指向非常量
*py = 10;//错误:不允许通过常量指针修改其值

7.2、*const 常量指针

int err_num = 0;
int *const cur_err = &err_num;//常量指针必须在定义时初始化
const double pi = 3.141592654;
const double *const pip = &pi;//这是一个指向常量对象的常量指针

    注意一下区别,*const xxx = &yyy; 意思是地址本身是一个定值,存放变量的地址是不变的,永远指向那个变量;但你可以通过该地址改变其中存放变量的值(如果变量不是常量)。
    而const int *xxx = &yyy;意思是你不能通过该地址改变其中存放的变量(无论其中存放的是否为常量);但是你可以改变指针本身,让它指向其他的变量,当然你仍无法改变那个变量。

顶层const(指针的值不能被改变)与底层const(指针的值可以被改变)
int i = 0;
const int *pi = &i;//底层const,可以改变指针值,但是内部存放的数据不可通过指针改变
int *const ppi = &i;//顶层const,不可以改变指针值,内部数据可通过指针访问来改变

7.3、constexpr 与 常量表达式

在程序编译过程中就得到值(或得计算结果),且值不会改变的表达式

const int exp = 100;//是常量表达式
const int p_next = exp + 10;//是常量表达式
int any = 20;//值会改变,是常量表达式
const int size = get_size();//虽然值不变,但只有当程序运行到此处时才能得到值,并非在编译时得到

C++11新标准规定允许将变量声明为constexpr类型来使编译器分辨它是不是一个常量表达式

constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr int sz = size();//此时size()应该是一个定义为constexpr类型的函数
constexpr类型的函数就是在编译过程中就可以得知其结果的函数,一般比较简单
const int *p = nullptr;//底层指针
constexpr int *q = nullptr;//顶层指针

8、处理类型

8.2、auto类型说明符(C++11引进)

    引入原因是编程中常遇到这样的问题:将表达式的值在定义时赋值给变量,但是我们无法知道表达式的具体类型。故引进auto类型,让编译器从表达式中自行推断表达式类型,再给定义。

auto any = val_1 + val_2;//两个后面变量是什么类型,表达式就是什么类型,前面any就定义为什么类型
在一条语句中声明多个变量
auto i = 0, *p = &i;//分别对应整型和整型指针
auto sz = 0, pi = 3.1416;//不行,这种推断发生冲突,前者推断为int型,后者为double型 
int i = 0, &r = i;
auto a = r;//其中a是实体对象,一个正数,不是引用
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)

8.3、decltype类型指示符(C++11引进)

    以上auto类型定义时就必须赋初值,如果有何auto使用时同样的要求(需要定义的类型未知)但又不想用该表达式的值作为其初值或根本就不想赋初值,就会使用到decltype这种类型。

decltype(func()) xxx = any;

其中func返回一个值,或是一个表达式,编译器通过它的值来推断出xxx的定义类型。

decltype和引用&的结合使用与和auto的一些区别
const int ci = 3, &cj = ci;
decltype(ci) x = 0;//x 会被认为是 const int 类型
decltype(cj) y = &x;//y 会被认为是 const int& 类型
decltype(cj) z;//错误: z 被认为是一个引用但没有初始化绑定对象
auto a = cj;//同样的使用auto,其中 a 就会被认为是const int 类型而不是引用!注意区别
int i = 42, *pi = &i, &r = i;
decltype(r + 0) xxx = 0;//表达式得到的结果是一个int型数,故此处xxx是int型
decltype(*pi) yyy;//错误:*是“解引用符”,这里得到的yyy应该是一个引用类型,因没有绑定引发错误
decltype 的表达形式——>是否加括号
int i = 20;
decltype(i) d;//错误:d是int&引用类型,没有初始化绑定对象
decltype((i)) e;//正确:e是int类型

注意双层括号得到的永远使引用类型,单层括号只有当括号内部本身就是一个引用时才是引用类型

9、类型别名 与 常量、指针

typedef double weight;//weight是double的同义词,可以用作double的代替来定义同类型变量
typedef weight Gandalf, *p;//base是weight的同义词,而p是double*的同义词

另外C++11还规定了一种新的方法使用“别名声明”
using SI = sale_item;//其中二者是同义词
typedef char *pstring;//pstring 是 char*的同义词
const pstring cstr = 0;//const是对指定类型的修饰,这个语句中其后跟的指定类型就是pstring,所以就是对char*类型的修饰
//即const pstring 变量 cstr是一个指向char的常量指针
const pstring *ps;//原话是说:ps是一个指针,它的对象时指向char的常量指针???怎么理解
//我的理解是:ps是指向常量指针的指针,这种理解对不对呢???
承接上文中定义的变量,对比理解以下两句
const pstring cstr = 0;
const char* cstr = 0;

    后面这句是对前者的错误理解,不可以直接这样替换来进行理解。注意之前提到过的:const是对给定类型的修饰,只是对紧跟在const之后类型的修饰。
    于是前者是对pstring的修饰,而pstring可以替换为char*,但后者不行,对于后者,const只修饰char而非char*,可以理解为pstring使char*绑定在一起,可以捆绑着被const同时修饰。
    于是得到的结果就是:前者声明了一个指向char的常量指针,而后者则声明了一个指向const char的指针




啊啊啊()待补充




问题留存

1、我用以下两条语句的区别(反正程序运行过程中也不会改变)

#define ANY		10
const int ANY = 10;

2、我用以下两条语句的区别(???)

const int any1 = 10;
constexpr int any = 10;

3、它说 i 和 j 都必须定义在函数体之外,为什么?

constexpr int *np = nullptr;
int j = 0;
constexpr int i = 42;
constexpr const int *p = &i;//指针p和i都应该是不变的常量
constexpr int *p1 = &j;//指针不能改变,指针内数据可以通过访问指针指向数据来改变

4、关于上面8.2的第一个程序段,当val_1和val_2的类型不统一时如何推断表达式的值?

5、关于上面8.2第二个程序段,当定义为浮点数时,auto被推断为double还是float?

6、8.3 decltype 可不可以不赋初值?

7、9中的第二个代码段,书中原文说“因此,const pstring就是指向char的常量指针,而非指向常量字符的指针”。这句话还没有理解透彻,它想说的是“顶层指针”和“底层指针”之间的区别吗?

8、9中的第二个代码段最后一句//我的理解是:ps是指向常量指针的指针,这种理解对不对呢???

9、发现自己无法理解“指向XXX(类型可以是char/int啥的)德指针”,为什么指针不是指向变量的呢?而是指向某一个类型?!它本来的意思是不是想说明这个指针本身指向变量的类型?再去前面看看理解一下

10、8.2的第三个代码段,对于 顶层const 和 底层const 这类说法,不应该是针对指针来说的吗?为什么这里总是强调auto会忽略掉 顶层const 的特性,但却没有明确表明哪里有指针类型?!很不能理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值