问题描述:
- 两个源文件 file1.cpp和 file2.cpp。
- 现要在file1.cpp 中定义一个动态数组 int* p = new int[N]; 其中 N 表示数组的大小。
- 而 file1.cpp动态数组中 N 的值需要从 file2.cpp 中获取,其中 N 在 file2.cpp 定义为全局变量,即 int N = 5;。
- delete []p;操作需要在 file2.cpp 文件的void Deletep()函数下进行操作。
当然上面这个问题是从实际项目中抽象出来的,该题本身没有什么实际意义。如果读者已经知道答案了,那么这篇文章的内容相比对您的帮助也不大,如果不知道答案的,相信这篇文章会让您受益匪浅。
首先说一下这个问题的一种实现方法,然后在介绍变量存储的持续性、作用域和链接性及变量的 static 和 new 的用法。
//file1.cpp
#include <iostream>
extern int N;
int* p = new int[N];
...
//file2.cpp
#include <iostream>
extern int* p;
int N = 5;
void Deletep()
{
delete[] p;
}
...
int main()
{
...
}
读者可以先看下面这个截图,然后带着图片上提示的错误信息来阅读这篇文章,将更有助于您的理解。
一、5 种变量存储方式
存储描述 | 持续性 | 作用域 | 链接性 | 如何声明 |
---|---|---|---|---|
自动变量 | 自动 | 代码块 | 无 | 在代码块中 |
寄存器变量 | 自动 | 代码块 | 无 | 在代码块中,使用关键字 register |
静态变量,无链接性 | 静态 | 代码块 | 无 | 在代码块中,使用关键字 static |
静态变量,外部链接性 | 静态 | 文件 | 外部 | 不在任何函数内 |
静态变量,内部链接性 | 静态 | 文件 | 内部 | 不在任何函数内,使用关键字 static |
注:1.持续性
①自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的(存储在栈中)。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。
②静态存储持续性:在函数定义外定义的变量和使用关键字 static 定义的变量的存储持续性都为静态。它们在程序整个运行过程中都存在,不管是定义在函数内还是函数外。
③动态存储持续性:用 new 运算符分配的内存将一直存在,直到使用 delete 运算符将其释放或程序结束为止。这种内存的存储持续性为动态,有时被称为自由存储(free store)或堆(heap)。
2.作用域和链接性
作用域描述了变量名称在文件的多大范围内是有效的。例如,函数中定义的变量可在该函数中使用,但不能在其他函数中使用;而在文件中的函数定义之前定义的变量则可在所有函数中使用。
链接性描述了变量名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件中的函数共享。自动变量没有链接性,因此他们不能共享。要想创建链接性为外部的静态持续变量,必须在代码块的外面声明它;要创建链接性为内部的静态持续变量,必须在代码块的外面声明它,并使用 static 限定符;要创建没有链接性的静态持续变量,必须在代码块内声明它,并使用 static 限定符。具体可以参考下面例子。
...
int one = 1000; //static duration, external linkage
static int two = 50; //static duration, internal linkage
int main()
{
...
}
void func1(int n)
{
static int three = 0; //static duration, no linkage
int one = 0; //Local variable overrides global variable, global variable fails
}
二、外部变量
外部变量,在这里指的就是静态持续性、外部链接性、作用域为整个文件。不管是 C 还是 C++变量都必须先定义在使用,所以对于同一个外部变量,需要在使用它的每一个文件中都要进行定义。但是 C++ 规定变量只能定义一次。这时只需要引用声明使用关键字extern,且不进行初始化,否则就会导致重复定义变量的错误。
如果要在多个文件中使用外部变量,只需要在一个文件中包含该变量的定义,但要在使用该变量的其他所有文件中,都是用关键字 extern来声明它:
//file1.cpp
int one = 20; //definition because of initialization
int two = 22; //also a definition
int three; //also a definition
...
//file2.cpp
//use one and two from file1.cpp
extern int one; //not definitions because they use
extern int two; //extern and have no initialization
...
//file3.cpp
//use one, two, and three fleas from file1.cpp
extern int one;
extern int two;
extern int three;
...
三、单文件内部变量
单文件内部变量,具有静态持续性、内部链接性、作用域为单文件的特性。将 static 限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。在多文件程序中,内部链接性和外部链接性之间的差别有很大区别。链接性为内部的变量只能在其所属的文件中使用;但常规外部变量都具有外部链接性,即可以在其他文件中使用,如上面的示例。
如果要在其他文件中使用相同的名称来表示其他变量,可以在其他文件中定义一个静态外部变量(函数外面),其名称与另一个文件中声明的常规外部变量相同,则在该文件中,静态变量将隐藏常规外部变量。
//file1.cpp
int one = 0; //external declaration
...
//file2.cpp
static int one = 10; //known to file2 only
void show()
{
std::cout << one; //uses one defined in file2
...
}
可使用外部变量在多文件程序的不同部分之间共享数据;可使用链接性为内部的静态变量在同一个文件中的多个函数之间共享数据。另外,如果将作用域为整个文件的变量定义为静态的,就不必担心其名称与其他文件中的作用域为整个文件的变量发生冲突了。
四、函数内静态变量
函数内静态变量,是静态存储持续性、无连接性的局部变量。将 static 限定符用于代码中定义的变量。在代码块中使用 static 时,将导致局部变量的存储持续性为静态的。这意味着虽然该变量只在该代码块中可用,但它在该代码块不处于活动状态时仍然存在。因此在两次调用函数之间,静态局部变量的值将保持不变。另外,如果初始化了静态局部变量,则程序只在启动时进行一次初始化。以后在调用函数时,将不会像自动变量那样再次被初始化。
#include <iostream>
const int ArSize = 10;
void strcout(const char* str);
int main()
{
char input[ArSize];
char next;
std::cout << "Enter a line:\n";
cin.get(input, ArSize);
while(cin)
{
cin.get(next);
while(next != '\n')
cin.get(next);
strcout(input);
std::cout << "Enter next line(empty line to quit):\n";
cin.get(input, ArSize);
}
std::cout << "Bye\n";
return 0;
}
void strcout(const char* str)
{
static int total = 0;
int count = 0;
std::cout << "\"" << str << "\"contains";
while(*str++)
count++;
total += count;
std::cout << count << "characters\n";
std::cout << total << "characters total\n";
}
/*output
Enter a line:
nice pants
"nice pant" contains 9 characters
9 characters total
Enter next line(Empty line to quit):
Thanks
"Thanks" contains 6 characters
15 characters total
Enter next line(Empty line to quit):
parting is such sweet sorrow
"parting i" contains 9 characters
24 characters total
Enter next line(Empty line to quit):
Bye
*/
五、动态存储
动态内存由运算符 new 和 delete 控制,而不是由作用域和链接性规则控制。因此,可以在一个函数中分配动态内存,而在另一个函数中将其释放。与自动内存不同,其分配和释放顺序要取决于 new 和 delete 在何时以何种方式被释放。虽然存储方案概念不适用于动态内存,但适用于用来跟踪动态内存的自动和静态指针变量。例如,假设在一个函数中包含下面的语句:
float* p_free = new float[20];
由 new 分配的 80 个字节(假设 float 为 4 个字节)的内存将一直保留在内存中,直到使用delete 运算符将其释放。但当包含该声明的语句块执行完毕时,p_free 指针将消失。如果希望另一个函数能够使用这 80 个字节中的内容,则必须将其地址传递或返回给该函数。另一方面,如果将 p_free 的链接性声明为外部的,则文件中位于该声明后面的所有函数都可以使用它。另外,通过在另一个文件中使用下述声明,便可在其中使用该指针:
extern float* p_free;
删除该指针用如下函数
delete[] p_free;