一些关键字的使用解析

一,前言

平时在使用时,我们或多或少都会有疑惑,一个类型前加一个const到底有什么作用呢?类似的还有static,volatile,extern,extern “C”等,今天我便会把自己总结的知识,记录在这里。
二,主要内容

2.1 const与不加const
关键字const并不能把变量变成常量,在一个符号前加上const限定符只是表明这个符号不能被赋值,也就是说它的值对于这个符号来说是只读的,但它并不能防止通过程序的内部(甚至是外部)的方法来修改这个值。
const最有用之处就是用它来限定函数的形参,这样该函数就不会修改实参指针所指的数据。
以上内容来自《c专家编程》

首先来看个例子:

#pragma once
#include<iostream>
/*
const 与 非const的使用
*/

void test()
{
    const int a = 3;
    a = 4;
}

这个例子十分简单,我们声名并定义了一个int型的const变量a,然后试图去修改a的值,但发现此时编译器已经提示“表达式必须是可修改的左值”,此时有同学可能会疑惑,什么是左值?相应的什么是右值呢?
所谓的左值是指赋值运算符“=”左边的数,它代表一个地址;
而右值便是指“=”右边的数,它往往代表一个值。
当我们使用赋值运算符时,往往是把右值赋给左值所代表的地址空间里,而这个地址,在程序运行时会被自动分配。
那么a为什么不能被修改呢?正如上面那段话所说,被const修饰,则a当前的属性为只读的(read-only),意味着你不能对它进行赋值,那么a就是一个常量了?请看下面的代码:

void test()
{
    const int a = 3;
    int* b = (int*)&a;
    *b = 4;
    cout << "a=" << a << endl;
    cout << "*b=" << *b << endl;

}

运行之后,我们发现,虽然通过强制类型转换,我们得到到a的地址,并通过指向a的指针b修改了a的值,但输出的结果,依旧是a=3,*b=4,这是为什么呢?
此时便正式引出了“volatile”关键字,该关键字的中文意思是:多变的,不稳定的。正如它的字面意思,它修饰一个变量,是为了告诉编译器这个变量是经常变化的,让编译器每次都去相应的地址取值。原来啊,编译器为了优化程序的运行效率,它把const变量a的值存入了寄存器中,这样每次取a的值时就不用通过地址去取相应的变量,直接使用了寄存器里的值,这也就是为什么我们修改了a的值,但打印出来的依旧是寄存器里面没有发生变化的。然后再看改进的版本:

void test()
{
    volatile const int a = 3;
    int* b = (int*)&a;
    *b = 4;
    cout << "a=" << a << endl;
    cout << "*b=" << *b << endl;

}

结果:
这里写图片描述

注意:在c++里面,如果一个类的成员用mutable关键字修饰时,则它在const的成员函数里可以发生改变,不会发生错误。
2.2 指针与const
在日常使用中,我们通常会遇到下面几种形式:
int a=3;
1, int const *b=&a;
2,const int *b=&a;
3, int* const b=&a;
4,const int* const b=&a;
其中1,2是相同的形式,它们都定义了指针b并指向a的地址,const修饰的是*b,即b所指向的那个int变量的值是常量,此时b又称为指针常量,“指针”为形容词,而“常量”是名词
第三个,称为常量指针,const离指针b更近,它表示指针b所指向的地址是不能被改变的。
第四个则表示指针b所指向的地址不能被修改,而且那个地址所存放的那个整型的值也不能被修改,b又被称为常指针。
我们知道const修饰的变量需要在定义的时候被初始化,但由于1,2中const所修饰的是指针变量,所以const修饰的是指向的那个整型的值,所以对于指针b来说,并不需要使用地址对指针进行初始化,而3,4由于修饰的是指针,所以指针需要显式的用地址来进行初始化。

2.3 char*与const char*
首先请看代码:

void test()
{
    char *a = nullptr;
    const char *b = nullptr;
    b = a; //1
    a = b; //2

}

我们定义了a和b两个指针,其中b是被const修饰的,首先看注释1,我们让b=a,此时程序并没有什么异常,但当我们把 b给a赋值时便出现了错误,提示我们 等号两边的类型不相同,这是为什么呢?眼尖的同学肯定发现了,说这两个类型不同,肯定不能直接赋值,进行一下指针强转就好了,虽然我们都知道这样的做法,但很少有人说出为什么第一个就可以了?其实这里面涉及到“相容”的问题。
标准规定,要使两种类型赋值合法,必须满足下面的条件:

    两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。

所以对于注释1,左操作数是一个指向有const限定符的char的指针。
右操作数是一个指向没有限定符的char指针
char类型与char类型是相容的,左操作数所指向的类型具有右操作数指向的限定符(无),再加上自身的限定符(const),所以这种赋值是成立的,但反过来则不可以。
2.4 static与非static
static关键字意为:静态的,不变的。
2.41 以c为说明对象
在c里面static主要有两种作用:
1,它可以改变局部变量的生命周期,我们知道一个局部变量只在它所在的作用域内有效,出了这个作用域,则变量就不再具有相应的意义,但是对于static修饰的局部变量,它的生命周期则变为整个程序的生命周期,但它的作用域不发生改变。
局部变量存储于堆栈之中,而静态变量存储与全局区(静态区,且已被初始化)或bss段(未被初始化的全局变量与static变量)。
2,修改变量或函数的可见性
在c/c++中,默认全局变量或static函数在链接时是全局可见的(这意味着你可以在别的地方经过声名使用相应的变量或方法),可如果加上了static关键字,则该变量或函数只在本文件内有效,相当于是私有的,外界不可见。接下来看个例子。

//test1.h
void test3()
{
    extern int x;
    x++;
}
//main.cpp
#include"test1.h"

int x = 3;
int main()
{
    test3();
    cout << "x=" << x << endl;
    cin.get();
    return 0;
}

打印结果:x=4
我们在test1.h中声名并定义了一个函数为test3,此时我想在该函数里使用main.cpp里面声明的变量x(此时x为全局变量),那么只需要在函数中用extern关键字声明x就好了,那么在链接时编译器会在其他文件里查找关于x这个变量的定义,如果没有找到便会抛出变量未定义的问题。
此时我们若把x用static修饰又会怎样呢?
我们来看运行结果:
这里写图片描述
此时我们发现x在test1.h中无法被解析了,这正是因为,在x声明的地方使用了static修饰,所以在main.cpp里面定义的全局变量便在别的文件中不可见了,这对于static修饰的函数来说亦是如此。
extern 关键字的作用
在上面的例子中我们正式引入了extern关键字,它的意思为:外来的。在c/c++中用来声明一个对象。
一个对象可以被声明为无数次,但它的定义却只能有一份。当遇到上述情况,我们想使用别处定义的变量或函数,只需要使用extern关键字对变量进行声明,以后便可以直接来使用这个变量了。
extern “C”关键字的作用
该关键字是c++中所使用的,我们知道c++是c的一个超集,当我们在c++里面想使用c的编译,链接规则时便可以使用该关键字告诉编译器。为什么要这样使用呢?
因为c++为了支持重载,它在联编的时候(将函数体与函数名绑定到一起),是用函数名+函数的形参的方式来区分每个函数的(返回类型不支持这样的特性。),而在c里面并没有重载这种特性,它在联编的时候只以函数名来区分每个函数,所以如果使用c++的规则去链接c中定义的函数,则有可能发生找不到相应函数的问题,所以用c的编译,链接方式就不会造成该问题了。
2.42 以c++为说明对象
在c++里面最有特色的便为类吧,static关键字可以用来修饰类的成员以及成员函数,被static修饰的成员及成员函数是作为类的一部分而存在的,不同的类的对象共享这些静态成员。因此静态成员函数也不存在默认的this指针。
1,静态的对象只能用来调用静态的成员函数。
2,静态成员需要在类外进行初始化
3,普通的对象可以调用静态成员函数

2.5 explicit关键字
该关键字用于c++之中,意思为:明确的,清楚的,它用于修饰只带有一个参数的构造函数,主要为了防止形参隐式类型转换所带来的错误,通常该错误发生之后是很难发现的,因为编译器并不报错。该关键字的出现可以使对象的创建者每次都显示的调用构造函数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值