美文网首页CPP
extern、static 与 const 关键字

extern、static 与 const 关键字

作者: 顽强的猫尾草 | 来源:发表于2018-05-03 11:10 被阅读15次

一、extern 关键字

extern 用在变量或者函数的声明前,用来说明 “此变量/函数是在别处定义的,要在此处引用”。extern 关键字可以重复声明同一个变量而不报错。

1、引用同一个文件中的变量:提前声明。使用 extern 关键字修饰的变量,定义的代码可以晚于使用的代码。对于全局函数来说,这个 extern 可以省略,它默认就是 extern 的。

#include<stdio.h>

void func();    // 默认 extern

int main() {
    func();    // 3
    extern int num;    // 告诉编译器 num 这个变量是存在的, 但不是在这之前声明的, 你到别的地方找找吧
    printf("%d",num);    // 3
    return 0;
}

int num = 3;

void func() {
    printf("%d\n",num);
}

2、引用另一个文件中的变量。extern 不能省略。

// a.c
char a = 'A';
void msg() {
    printf("Hello\n"); 
}

// main.c
extern void msg();    // 先声明再使用
int main(void) {    
    extern char a;    // 先声明再使用
    printf("%c", a);
    msg();
    return 0;
}

程序的运行结果是:

A Hello

问题:如果我想引用一个全局变量或函数,我只要直接在源文件中包含 #include <xxx.h> 不就可以了么,为什么还要用 extern 呢?
头文件实际上只是对用户的说明——函数、参数、各种各样的接口的说明。那么头文件里面放的自然就是关于函数、变量、类的“声明”了,而不是“定义”。最好不要在头文件里定义什么东西。比如全局变量:

#ifndef _XX_头文件.H
#define _XX_头文件.H
int A;
#endif

如果这个头文件被多次引用的话,A 会被重复定义。只不过有了这个 #ifndef 的条件编译,能保证你的头文件只被引用一次。但若多个文件包含这个头文件时还是会出错的,因为宏名有效范围仅限于本源文件,所以在多个文件编译时虽然不会出错的,但在链接时就会报错,说你多处定义了同一个变量。

3、在 C++ 中 extern 还有另外一种作用,用于指示 C 或者 C++ 函数的调用规范。比如在 C++ 中调用 C 库函数,就需要在 C++ 程序中用 extern “C” 声明要引用的函数。告诉链接器在链接的时候用 C 函数规范来链接。主要原因是 C++ 和 C 程序编译完成后在目标代码中命名规则不同,用此来解决名字匹配的问题。

二、static 关键字

static 的用法:修饰函数、局部变量和全局变量。每种用法的意图或作用不尽相同,主要有以下三点:

1、最重要的一点:隐藏

当我们同时编译多个文件时,所有未加 static 前缀的全局变量和函数都具有全局可见性。用 static 进行修饰的话会使其作用域仅限于本文件。如 一.2 中的例子把 a.c 中的 a 变量用 static 修饰,这个变量就对 main.c 不可见了:

main.cpp:(.rdata$.refptr.a[.refptr.a]+0x0): undefined reference to `a'

利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。

在 C++ 中 static 还可以对类中的某个函数用 static 进行修饰,表示该函数属于一个类而不属于此类的任何对象;如果对类中的某个变量进行 static 修饰,表示该变量为类以及其所有的对象所有。它们在存储空间中都只存在一个副本。可以通过类和对象去调用。对于静态成员函数,只能访问静态成员函数和静态成员变量,不能访问非静态成员函数或者变量

对于函数来讲,static 的作用仅限于隐藏。对于变量,static 还有下面两个作用。

2、保持变量内容的持久

存储在静态存储区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和 static 变量,只不过和全局变量比起来,static 可以控制变量的可见范围,说到底 static 还是用来隐藏的。

这种用法不常见,但还是举个简单的例子帮助理解:

#include <stdio.h>

int fun(void) {
    static int count = 10;    // 此赋值语句只有第一次会被执行
    return count--;
}

int count = 1;

int main(void) {    
    printf("global\t\tlocal static\n");
    for(; count <= 10; ++count)
        printf("%d\t\t%d\n", count, fun());    
    return 0;
}

程序的运行结果是:

global          local static
1               10
2               9
3               8
4               7
5               6
6               5
7               4
8               3
9               2
10              1

3、技巧性的作用:默认初始化为 0

全局变量和 static 变量均位于静态存储区,静态存储区内存中所有的字节默认值都是 0x00,某些时候这一特点可以减少程序员的工作量。

比如初始化一个稀疏矩阵,我们可能会一个一个地把所有元素都置 0,然后把不是 0 的几个元素赋值。如果定义成静态的,就省去了一开始置 0 的操作。

再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加 ’\0’ 太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是 ’\0’。

不妨举个例子验证一下:

#include <stdio.h>

int a;
int main(void) {
    int i;
    static char str[10];
    printf("integer: %d;  string: (begin)%s(end)", a, str);
    return 0;
}

程序的运行结果如下

integer: 0;  string: (begin)(end)

总结
首先 static 最主要的功能是隐藏,其次因为 static 变量存放在静态存储区,所以它具备持久性和默认值 0。

问题:应该在什么时候考虑使用 static?
1、当你不想创建对象来调用类的方法,或访问类的变量的时候(例如一些工具类的方法);
2、当你的变量想保存上一次的结果的时候。

三、const 关键字

const 关键字用来修饰右边的基本变量和指针变量。被 const 修饰的变量只读,也就是只能获取,不能修改。

const 与 指针
如果 const 关键字不涉及到指针,我们很好理解,下面是涉及到指针的情况:

int b = 500;
const int* a = &b;    // [1]
int const *a = &b;    // [2]
int* const a = &b;    // [3]
const int* const a = &b;    // [4]

首先 const 与变量类型的顺序不会对修饰效果造成影响,也就是情况 [1] 与 [2] 等价。

其次,如果 const 位于 * 的左侧,那么 const 就是用来修饰指针所指向的变量,即指针指向的是常量,在情况 [1] 与 [2] 中,不能 *a = 3;反之如果 const 位于 * 的右侧,const 就是修饰指针本身,即指针的值(一个地址)是常量,指针所指向的内容不是常量,在情况 [3] 中,a++ 操作是错误的。

情况 [4] 为指针本身和指向的内容均为常量。

const 与引用
如果在声明引用时用 const 修饰,那么该引用就被称为常引用。常引用所引用的对象不能被更新。如果用常引用做为形参,便不会产生对实参不希望的修改。如:

int a = 5;
const int &b = a;

其中,b 是一个常引用,它所引用的对象不允许更改,如果出现:

b = 6;

则是非法的(如果写 a = 6 不会出问题,因为 a 不是 const 类型变量)。

const 与类成员

  1. 常数据成员
    类的数据成员可以是常量或常引用。如果在一个类中声明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他的函数都不能对该成员函数赋值。

  2. 常成员函数
    在类中使用关键字 const 的函数称为常成员函数,const 是函数类型的组成部分,所以在函数的实现部分也要带关键字 const。在常成员函数中不得修改类中的任何数据成员的值。

相关文章

网友评论

    本文标题:extern、static 与 const 关键字

    本文链接:https://www.haomeiwen.com/subject/asfkrftx.html