C++Const变量的存储位置
const变量/对象的存储位置
const局部变量
const局部基础变量和自定义变量都存储在栈上
struct diy_class{
int a;
int b;
diy_class(int a, int b ) : a(a), b(b){
}
};
int main()
{
int b = 1; // 这个肯定在栈上
const int a = 10; // 比较a b两个变量的地址,看看a在哪里
printf("address a = %p, address b = %p\n", &a, &b);
const diy_class dd(1,2);
printf("address of diy_class = %p \n", &dd);
// address a = 0x7ffd6926e44c, address b = 0x7ffd6926e448
// address of diy_class = 0x7ffd6926e450
}
对比3个变量的地址, 可知b在栈上。或者你也可以用GDB用 info locals 查看栈上的变量:
(gdb) # 打断点在printf("address a = %p, address b = %p\n", &a, &b);处
(gdb) info locals
b = 1
a = 10
dd = {a = -8016, b = 32767} # 这个栈变量还没有被初始化
const全局变量
再定义一个const全局基础变量,打印其地址
const int global_const_inited = 1; // 存储于只读常量区
int main()
{
int b = 1; // 这个肯定在栈上
const int a = 10; // 比较a b两个变量的地址,看看a在哪里
printf("address a = %p, address b = %p\n", &a, &b);
const diy_class dd(1,2);
printf("address of diy_class = %p \n", &dd);
// address a = 0x7ffd6926e44c, address b = 0x7ffd6926e448
// address of diy_class = 0x7ffd6926e450
printf("address of global_const_inited = %p\n", &global_const_inited);
// address of global_const_inited = 0x560d0df107f8
}
可以看到全局常量的地址明显不在栈上,那在哪? -- 常量数据区,可以用nm命令查看符号表验证:
$ nm const_storage_cpp | c++filt | grep global_const
00000000000007f8 r global_const_inited
其变量名前的符号为r,表示该变量存储在只读常量区。
接下来看看自定义变量:
const int global_const_inited = 1; // 只读常量区
const diy_class global_const_diy(1,2);
int main()
{
int b = 1; // 这个肯定在栈上
const int a = 10; // 比较a b两个变量的地址,看看a在哪里
printf("address a = %p, address b = %p\n", &a, &b);
const diy_class dd(1,2);
printf("address of diy_class = %p \n", &dd);
printf("address of global_const_inited = %p\n", &global_const_inited);
printf("address of global_const_diy = %p\n", &global_const_diy);
// address of global_const_inited = 0x558b9d1dc888
// address of global_const_diy = 0x558b9d3dd018
}
两个地址很相近,那么表示自定义对象的地址也在只读常量区吗? 我们使用nm命令验证以下:
$ nm const_storage_cpp | c++filt | grep global_const
0000000000201018 b global_const_diy
0000000000000888 r global_const_inited
发现并不是,对于只读自定义对象,存储在了BSS段。这与static自定义对象相同,它们都“存储”在了ELF文件的BSS段,并在main函数前完成初始化,详见我之前写的内容。
不能修改const变量?
能修改const变量吗? --- 我们可以绕过编译器的限制,但是不能绕过操作系统的限制。要分情况看:
经过上文的探索,g++对const变量大致分为两种处理方式
- 将变量存储在只读数据段
- 将变量存储在栈和BSS段
操作系统在加载只读数据段时,会将该段设置为只读,无论进程以怎样的方式对它进行修改,都会触发缺页中断的读错误,操作系统在确定进程没有权限进行写时,会立刻向进程强制发送SIGV信号,然后进程就结束了。因此这种类型只读变量的不可变性是由操作系统和ELF格式决定的,无论如何都不能改变这种类型的只读变量。
然而BSS段和栈段是可读、可写的。只要我们通过了编译器的检查,我们可以使用某种方式在运行期对这种类型的只读变量进行修改。
具体可以看看下面的程序:
struct diy_class{
int a;
int b;
diy_class(int a, int b ) : a(a), b(b){
}
};
const int global_const_inited = 1; // 只读常量区
const diy_class global_const_diy(1,2);
int main()
{
// 1. 编译器错误 !
// global_const_diy.a = 10;
// 2. 绕过编译器,成功修改。C++种使用const_cast去除变量的只读属性
diy_class* cc = const_cast<diy_class*>(&global_const_diy) ;
cc->a = 10;
printf("global_const_diy.a = %d\n", global_const_diy.a);
// 3. 逃过编译器的检查,但没能逃过操作系统的检查. Segmentation fault!
int* ee = const_cast<int*>(&global_const_inited);
*ee = 2;
printf("global_const_inited = %d", global_const_inited);
}
注意在C++中,使用const_cast去除变量的只读属性
C语言中呢?
大体上说,C语言在基础变量上的行为与C++是一样的。
但对于自定义全局对象,C语言仍然会将它定义在只读数据段中。
struct diy_class{
int a;
int b;
};
const int global_const_inited = 1; // 只读常量区
const struct diy_class global_const_diy= {1,2}; // 依然是只读常量区
nm const_stotage | grep global_const_diy
00000000000007f8 R global_const_diy
所以,在C语言中,全局的自定义变量也是不能修改的:
const int global_const_inited = 1; // 只读常量区
char* str = "sdasd";
struct diy_class{
int a;
int b;
};
const struct diy_class global_const_diy= {1,2};
int main()
{
// 只有局部const变量能够被修改
const int a = 1;
int* aa = (int*)&a;
*aa = 10;
printf("a = %d\n", a);
// 局部struct也能修改
const struct diy_class local_const_diy = {1,2};
// local_const_diy.a = 2;
struct diy_class* local_const_diy_aa = (struct diy_class* )&local_const_diy;
local_const_diy_aa->a = 10;
printf("local_const_diy.a = %d\n", local_const_diy.a);
// 全局struct就不能修改了, 同样segmentation fault
struct diy_class* global_const_diy_aa = (struct diy_class* )&global_const_diy;
global_const_diy_aa->a = 10;
printf("global_const_diy.a = %d\n", global_const_diy.a);
}
输出如下,看到前两个变量成功绕过编译器检查修改成功:
a = 10
local_const_diy.a = 10
Segmentation fault (core dumped)
int main() {
int module_num = 0;
cin >> module_num;
vector<int> indegree(module_num,0);
unordered_map<int, vector<int>> umap(module_num); // 某模块号 : 依赖这个某块号的块号队列
for (int i = 0; i < module_num; i++) {
printf("moudle num == %d\n",i);
int depend_num = 0;
cin >> depend_num;
if (depend_num == 0) continue;
indegree[i] = depend_num;
printf("depend_num = %d\n", depend_num);
int num = 0;
for (int j = 0; j < depend_num; j++) {
cin >> num;
printf("depend on %d\n", num);
umap[num - 1].push_back(i);
}
}
int batch = -1;
int count = 0;
queue<int> que;
for (int i = 0 ; i < indegree.size(); i++) {
if (indegree[i] == 0) {
que.push(i);
count++;
}
}
if (count == 0) cout << -1;
batch++;
while (!que.empty()) {
int curLeleveSize = que.size();
for (int i = 0; i < curLeleveSize; i++) {
int modNum = que.front();
que.pop();
for (int i : umap[modNum]) {
if (--indegree[i] == 0) {
que.push(i);
count++;
}
}
}
batch++;
}
if (count < indegree.size()) cout << -1;
else cout << batch << endl;
return 0;
}
using namespace std;
class Node{
public:
Node(int id_) : id(id_){}
int id;
Node* next;
Node* prev;
};
class ResourcePool{
public:
void addToTail(Node* node) {
Node* temp = tail->prev;
node->next = tail;
tail->prev = node;
node->prev = temp;
temp->next = node;
}
void removeFromList(Node* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
node->next = nullptr;
node->prev = nullptr;
}
int getFirst() {
return head->next->id;
}
void allocateFirst() {
if (head->next == tail) return;
if (head->next != tail) {
// cout << "allocate first() \n";
removeFromList(head->next);
umap.erase(head->next->id);
// printList(head);
// 需要delete吗?
// delete head->next;
}
}
void allocateID(int num) {
if (head->next == tail) return;
if (umap.count(num) == 0) return;
else {
Node* nodeToAllocate = umap[num];
removeFromList(nodeToAllocate);
umap.erase(num);
}
}
void releaseID(int num) {
if (umap.count(num) != 0) return;
if (num >= begin_id && num <= end_id) {
Node* newNode = new Node(num);
umap[num] = newNode;
}
}
ResourcePool(int begin, int end): head(new Node(-1)), tail(new Node(-1)), begin_id(begin), end_id(end) {
head->next = tail;
tail->prev = head;
// cout << "de1\n";
for (int i = begin; i <= end; i++) {
Node* newNode = new Node(i);
addToTail(newNode);
umap[i] = newNode;
}
// printList(head);
}
void printList(Node* head) {
while (head) {
printf("%d ", head->id);
head = head->next;
}
}
public:
unordered_map<int, Node*> umap; // ID : Node指针
Node* head; //
Node* tail; // 尾部是新加入的
int begin_id, end_id;
};
int main()
{
// please define the C++ input here. For example: int a,b; cin>>a>>b;;
// please finish the function body here.
// please define the C++ output here. For example:cout<<____<<endl;
int begin = 0, end = 0;
cin >> begin;
cin >> end;
int opnum = 0;
cin >> opnum;
vector<vector<int>> ops(opnum, vector<int>(2,0));
for (int i = 0; i < opnum; i++) {
cin >> ops[i][0];
cin >> ops[i][1];
}
// printVec(ops);
ResourcePool resPool(begin, end);
// cout << "resPool inited\n";
for (int i = 0; i < opnum; i++) {
vector<int> op = ops[i];
// cout << "op num = " << i << endl;
if (op[0] == 1) {
for (int j = 0; j < op[1]; j++) {
// cout << "allocate first\n";
resPool.allocateFirst();
}
}else if (op[0] == 2) {
resPool.allocateID(op[1]);
// cout << "allocate once:\n";
// resPool.printList(resPool.head);
}else if (op[0] == 3){
// cout << "release:\n";
resPool.releaseID(op[1]);
// resPool.printList(resPool.head);
}
}
int firstId = resPool.getFirst();
cout << firstId << endl;
return 0;
}