前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ 变量和复合类型

C++ 变量和复合类型

作者头像
乐百川
发布2018-01-09 10:47:56
1.1K0
发布2018-01-09 10:47:56
举报
文章被收录于专栏:乐百川的学习频道

前面说了C++的基本数据类型,下面来看看在C++中如何定义变量和常量。

变量

定义和初始化

C++定义变量的方式和C语言一样,也可以在定义的同时初始化。值得一提的是列表初始化,在原来的C++版本中可以用于初始化数组等。C++ 11标准增加了更广泛的列表初始化,所以可以用列表初始化来初始化单个变量。

代码语言:javascript
复制
//定义变量
int a;
//定义并初始化
int b = 1;
//同时定义多个变量
int c = 5, d = 6;
//C++ 11 新特性:列表初始化
int e{3};

列表初始化有一个限制,如果要初始化的值超过了可容纳的范围,就会引发编译错误,而直接赋值就可以。

代码语言:javascript
复制
//不能编译
//short s{3.1415};
//可以编译
short s = 3.1415;

对于函数内部的局部变量来说,如果不初始化的话,值是未定义的。对于未初始化的局部变量进行操作可能导致无法预料的后果。对于全局变量,如果没有给定初始值,默认值是0。

定义和声明变量

变量定义告诉编译器,我要创建一个变量,以后再用它。而变量声明告诉编译器,我要引用一个变量,所以你先按照这个变量的类型和名字去找它。声明变量需要使用extern关键字,而且声明的时候不能赋值。如果使用extern关键字并赋值,那么变量声明就变成了变量定义,而且这只能用于全局变量的声明和定义。如果对一个函数内部的本地变量声明添加初始化式,就会引发编译错误。

代码语言:javascript
复制
//定义了一个全局变量
int global_count;

//声明在另一个文件中定义的全局变量
extern int global_count;

标识符

标识符也就是变量、函数、类的名字,用于标识不同的对象。和大多数编程语言一样,C++的标识符需要以字母或下划线开头,有数组、字母和下划线组成,而且对大小写敏感。

作用域

如果一个标识符定义在花括号外面,那么这个标识符的作用域就是全局作用域。全局作用域的变量可以在本文件的任何地方访问,如果在其他文件中声明这个标识符,那么还可以在其他文件中访问。

如果一个标识符在某对花括号中定义,那么这个标识符的作用域就在这对花括号中,这就是局部作用域。局部作用域的标识符在超出这个块后,就无法被访问了。如果有一个全局变量,然后在某个作用域中又定义了一个同名变量,那么这个局部变量就会屏蔽对全局变量的访问。如果希望访问全局变量,需要使用域操作符::来指定。

代码语言:javascript
复制
//声明在另一个文件中定义的全局变量
extern int global_count;

void declare_and_define()
{
    cout << global_count << endl;
    if (true)
    {
        int global_count = 5;
        cout << "同名局部变量覆盖全局变量:" << global_count << endl;
        cout << "使用全局变量:" << ::global_count << endl;
    }
}

复合类型

复合类型指的是基于其他类型定义的更复杂的类型,这些复合类型也是C++语言的重点和难点。

指针

指针是C++语言从C语言中继承的类型。每个变量在内存中都有一个地址来存储,指针就是这个地址。利用指针我们可以直接对变量进行修改。定义指针需要在指针名前添加星号*。如果要在一行定义多个指针,那么每一个指针前都需要星号。

代码语言:javascript
复制
//指针
int *p1, *p2;

有了指针,还需要将变量的地址赋给它,这需要使用取地址符&。注意指针和变量的类型必须匹配,将int型变量的地址赋给double *类型的指针是错误的。

代码语言:javascript
复制
int d1 = 5, d2 = 6;
//指针
int *p1, *p2;
p1 = &d1;
p2 = &d2;
cout << "d1=" << d1 << ",p1=" << *p1 << endl;

如果想由指针访问其指向的对象,使用解引用符*。由于指针指向的是对象本身,所以使用解引用符修改对象会修改实际对象的值。

代码语言:javascript
复制
*p1 = 100;
cout << "d1=" << d1 << ",p1=" << *p1 << endl;

指针有两种状态,一种是指向某个内存空间,另一种是无效状态。对于无效状态的指针进行解引用会引发不可预料的后果,所以这种情况应该尽量避免。对于无效状态的指针,最好将其清空。在老版本的C++语言中,我们需要引用cstdlib头文件,并且使用其中预定义的NULL来清空指针,这个预定义的值实际上是0。在C++ 11标准中引入了一个新的字面量nullptr来代替NULL,所以在以后的程序中,我们最好使用nullptr

引用

引用是C++语言新增的一种类型,它和指针既有相似之处,也有不同之处。

先来看看如何定义引用。

代码语言:javascript
复制
int d1 = 5;
//d2是d1的引用
int& d2 = d1;

如果要在一行同时定义多个引用,需要在每个引用名前添加&

代码语言:javascript
复制
int &r1 = d1, &r2 = d2;

引用实际上是一个别名,一旦定义好,对引用的所有操作都相当于直接对原变量进行操作。这一点和指针很类似。

代码语言:javascript
复制
//修改引用也会修改原变量
d2 = 100;
cout << "d1=" << d1 << ",d2=" << d2 << endl;

但是需要注意一点,指针也是一个变量,所以一个指针可以多次赋值,指向不同变量的地址。而引用只能和一个变量绑定,所以引用在定义的时候必须初始化,而且一旦初始化,无法再绑定到其他变量。

复合类型总结

前面介绍了引用和指针两种复合类型,这些复合类型还可以互相组合,生成更加复杂的类型声明。对于指针和引用声明,它们是和变量组合在一起的。所以下面的定义中,p是一个指针,而d是一个变量。如果希望声明多个指针, 需要在每一个变量名前添加*号。

代码语言:javascript
复制
int *p, d;
//即使星号和类型放在一起,p仍然是指针,d仍然是变量
//int* p, d;

符合类型还可以互相组合。比如说,我们可以定义指针的指针。

代码语言:javascript
复制
int **pp = &p;

引用是一个别名,所以无法定义引用的指针。但是反过来可以定义指针的引用。

代码语言:javascript
复制
int*& r = p;

常量

常量定义

常量和变量一样,唯一的不同点是常量一经定义,它的值就不能够在改变。常量定义和变量差不多,只不过需要使用const限定符修饰。由于常量一经赋值就无法再改变,所以常量在定义的时候必须初始化。

代码语言:javascript
复制
//定义常量
const int const_count = 5;

编译器在处理常量的时候,会直接将常量替换为其对应的值,所以编译器需要知道常量的值。默认情况下,常量定义只在本文件中有效。如果在多个文件中定义了同名的常量,那么这些常量是各不相同的常量。如果需要在文件之间共享常量,就需要在常量定义和声明上都添加extern关键字。

变量的const引用

我们可以把引用标记为const的,这种情况下这个引用变为只读的,我们可以修改原变量,可以通过引用读取原变量,但是无法通过引用修改原变量。

代码语言:javascript
复制
//引用常量
int i = 5;
const int& r = i;
i = 10;
//r = 10;

指针常量

指针存储的就是对象的地址,如果我们把指针本身定义为const的,那么我们将无法将这个指针指向其他对象的地址,但是我们可以通过这个指针修改指向对象的值。需要注意,这种定义必须将const关键字置于紧挨的变量名的位置。

代码语言:javascript
复制
int j = 100;
//const指针
int *const cp = &i;
//可以修改指针指向的对象的值
*cp = 10;
//无法修改指针指向的对象
//cp = &j;

指向常量的指针

这种情况和上面那种情况正好相反,这次是将指针指向的对象声明为const的,这样一来,我们无法修改指针指向的对象的值,但是我们可以修改指针指向其他对象的地址。这种定义需要将const放到符合声明的最前面。虽然这种情况叫做指向常量的指针,但是这是对指针类型的声明,实际上这个指针完全可以指向一个变量,只不过我们无法通过指针修改这个变量的值。

代码语言:javascript
复制
const int *p = &i;
//可以修改指针指向其他对象
p = &j;
//无法通过指针修改值
//*p = 200;

顶层const和底层const

前面我们看到了,指针是一个非常复杂的话题。指针本身是一个对象,而它又指向另一个对象。这些情况和常量常量声明组合在一起,将会变得非常复杂。所以我们需要对其做出分类。我们把本身是const的对象叫做顶层const,而指向的对象是const的就叫做底层const。这样一来就比较清楚了,指向常量的指针就是底层const,而指针常量就是顶层const。

下面这种情况,变量ccp即使顶层const又是底层const。

代码语言:javascript
复制
//既是顶层const又是底层const
const int*const ccp = &i;

constexpr和常量表达式

有时候编译器要求程序中的某些值不能改变,而且必须在编译期就能计算出来,这样的值叫做常量表达式。显然,字面量和用常量表达式初始化的const对象都是常量表达式。

当然,一个变量并不是常量表达式,哪怕我们在程序中没有修改过变量的值也不行。一个用变量初始化的const对象也不是常量表达式。

C++ 11标准新规定了一个关键字constexpr,它可以让编译器检查声明的常量。如果这个常量不是合法的常量表达式,那么就无法编译。

代码语言:javascript
复制
//常量表达式
constexpr int MAX_COUNT = 100;
constexpr int MIN_COUNT = -MAX_COUNT;
//i不是常量,所以下面的代码不能编译
//constexpr int VARIABLE_COUNT = i;
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017年08月16日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 变量
    • 定义和初始化
      • 定义和声明变量
        • 标识符
          • 作用域
          • 复合类型
            • 指针
              • 引用
                • 复合类型总结
                • 常量
                  • 常量定义
                    • 变量的const引用
                      • 指针常量
                        • 指向常量的指针
                          • 顶层const和底层const
                            • constexpr和常量表达式
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档