📂CPP
🔖CPP
2023-07-20 20:39阅读: 231评论: 0推荐: 0

C++Const变量的存储位置

const变量/对象的存储位置

const局部变量

const局部基础变量和自定义变量都存储在栈上

copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
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 查看栈上的变量:

copy
  • 1
  • 2
  • 3
  • 4
  • 5
(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全局基础变量,打印其地址

copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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命令查看符号表验证:

copy
  • 1
  • 2
$ nm const_storage_cpp | c++filt | grep global_const 00000000000007f8 r global_const_inited

其变量名前的符号为r,表示该变量存储在只读常量区。

接下来看看自定义变量:

copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
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命令验证以下:

copy
  • 1
  • 2
  • 3
$ 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段和栈段是可读、可写的。只要我们通过了编译器的检查,我们可以使用某种方式在运行期对这种类型的只读变量进行修改。

具体可以看看下面的程序:

copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
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语言仍然会将它定义在只读数据段中

copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
struct diy_class{ int a; int b; }; const int global_const_inited = 1; // 只读常量区 const struct diy_class global_const_diy= {1,2}; // 依然是只读常量区
copy
  • 1
  • 2
nm const_stotage | grep global_const_diy 00000000000007f8 R global_const_diy

所以,在C语言中,全局的自定义变量也是不能修改的:

copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
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); }

输出如下,看到前两个变量成功绕过编译器检查修改成功:

copy
  • 1
  • 2
  • 3
a = 10 local_const_diy.a = 10 Segmentation fault (core dumped)
copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
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; }
copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
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; }

本文作者:别杀那头猪

本文链接:https://www.cnblogs.com/HeyLUMouMou/p/17569629.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   别杀那头猪  阅读(231)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起