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;
}


posted @ 2023-07-20 20:39  别杀那头猪  阅读(193)  评论(0编辑  收藏  举报