gcc背后的故事—c程序常量变量的分配地址

1.引言

在C语言中,常量变量是程序运行过程中不可修改的值。这些值在程序的执行期间只能读取,而不能修改。对于常量变量的存储和地址分配,不同的编译器可能会有不同的实现方式。本篇文章将通过分析GCC编译器的内部实现,深入探讨C程序中常量变量的存储位置和地址分配机制。

2.常量和变量的存储位置

全局常量和全局变量

全局常量(如const int GLOBAL_CONST = 100;)通常被存储在程序的只读数据段。这意味着它们的值在程序运行期间不能被修改。
全局变量(如int global_variable = 200;)通常被存储在程序的数据段中。它们的值可以在程序的任何地方进行读取和修改。

局部常量和局部变量

局部常量(如const int local_const = 50;)通常被存储在栈内存中。它们的值在定义时初始化,并且在整个局部作用域内是不可修改的。
局部变量(如int local_variable = 100;)也通常被存储在栈内存中。它们的生命周期与其所在的局部作用域相关,当离开该作用域时,这些变量的内存将被释放。

静态常量和静态变量

静态常量(如static const int STATIC_CONST = 50;)通常被存储在程序的只读数据段。与全局常量一样,它们的值在程序运行期间不能被修改。
静态变量(如static int static_variable = 100;)通常被存储在程序的数据段中,不同之处在于它们具有局部作用域。静态变量在声明时被初始化,并且在整个程序执行期间都存在。

3.全局常量和变量

全局常量和全局变量是在程序的任何地方都可访问的常量和变量,它们具有全局作用域。

全局常量

  • 全局常量是在程序开始执行前就分配了内存,并且它们的值在程序执行期间不能被修改。 通常使用const关键字进行声明,并在声明时进行初始化。
  • 全局常量的作用域从其定义的位置开始,一直持续到程序的结束。 全局常量可以在程序的任何地方进行读取,但不能修改其值。
  • 在多个源文件中使用全局常量时,可以通过在一个文件中定义并在其他文件中声明来实现。

全局变量

  • 全局变量是在程序开始执行前就分配了内存,它们的值可以在程序的任何地方读取和修改。
  • 通常在函数外部进行声明,并在需要时进行初始化。
  • 全局变量在整个程序的执行过程中都存在,因此可以在不同的函数之间共享数据。
  • 全局变量的作用域同样从其定义的位置开始,一直持续到程序的结束。
  • 在多个源文件中使用全局变量时,需要在一个文件中进行定义,并在其他文件中使用extern关键字进行声明。

4.局部常量和变量

局部常量和局部变量是在特定的代码块(函数、循环等)内部定义的常量和变量,它们的作用域限定在其所在的代码块内。

局部常量

  • 局部常量是在代码块开始执行时分配内存,并且其值在定义时初始化后不能被修改。
  • 通常使用const关键字进行声明,并在声明时进行初始化。
  • 局部常量只在其所在的代码块(函数、循环等)内部可见,代码块结束后,该常量的内存将被释放。
  • 局部常量可以用于存储不会改变的临时值或需要在代码块内部共享的常量。

局部变量

  • 局部变量是在代码块开始执行时分配内存的,它们的值和生命周期限定在所在的代码块内。
  • 通常在函数内部或者循环中进行声明,并在需要时进行初始化。
  • 局部变量只在其所在的代码块内部可见,代码块结束后,该变量的内存将被释放。
  • 局部变量用于存储临时数据、函数内的计算结果以及其他需要在特定范围内使用的数据。

5.常量与变量的地址分配

全局常量与变量的地址分配

全局常量与变量在程序执行前就已经分配了内存。这些变量在程序中的地址是固定的,可以在编译时确定。编译器通常会将全局常量变量存储在程序的只读内存段中,以防止程序在执行期间修改它们的值。
代码示例及分析

#include <stdio.h>

const int global_const = 10; // 全局常量变量
int main() {
    int local_var = 20; // 局部变量
    printf("Global const address: %p\n", &global_const); // 打印全局常量变量的地址
    printf("Local var address: %p\n", &local_var); // 打印局部变量的地址
    return 0;
}

在上面的示例中,全局常量变量’global_const’ 的地址在编译时就已经确定,并且它被存储在程序的只读内存段中。因此,无论何时运行该程序,打印出的’global_const’的地址都是相同的。而局部变量’local_var’是在函数’main’执行时才被分配内存,其地址在每次函数调用时都会改变。

局部常量与变量的地址分配

局部常量与变量在程序执行到其声明的函数时才会被分配内存。这些变量通常存储在栈内存中,其地址在该函数的作用域内是固定的。然而,它们可以被复制到寄存器中,以提高程序的执行效率。在函数返回时,局部常量变量会被释放内存,以便释放栈内存空间供其他变量使用。
代码示例及分析

#include <stdio.h>

void func() {
    const int local_const = 20; // 局部常量变量
    printf("Local const address: %p\n", &local_const); // 打印局部常量变量的地址
}
int main() {
    func(); // 调用函数 func
    return 0;
}

在上面的示例中,局部常量变量’local_const’在函数’func’执行时才被分配内存。每次调用函数’func’时,都会为其分配一个新的内存空间来存储’local_const’的值。因此,每次打印’local_const’的地址都会有所不同。当函数’func’返回时,'local_const’会被释放内存。

6. 堆和栈的分配

堆和栈是存储器中常用的两种分配方式。

在Ubuntu系统中,C程序使用动态内存分配时可以使用malloc()函数从堆中分配内存。例如:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* ptr = (int*)malloc(sizeof(int) * 5);
    // 在堆中分配了一个可以存放5个int类型变量的连续内存空间
    if (ptr == NULL) {
        printf("Failed to allocate memory!");
        return -1;
    }
    free(ptr);  // 释放堆内存
    return 0;
}

将上面的代码放入nano文本编辑器中,进行编译,结果如下:
在这里插入图片描述

在STM32中,由于其特殊的硬件架构,通常没有操作系统的支持,因此堆的分配会比较复杂。一般情况下,STM32上的C程序使用栈来分配局部变量,在编译和链接时,需要对栈进行适当的配置。

为了验证地址分配情况,我们可以通过串口打印输出相关信息。以下是在STM32上使用串口输出的示例代码:

#include "stm32f10x.h"
#include "stdio.h"

void func() {
    int local_var = 300; // 局部变量

    USART_InitTypeDef USART_InitStructure;

    // 初始化串口
    // ...

    while (1) {
        char buffer[64];
        sprintf(buffer, "Local var address in STM32: %p\n", &local_var);
        for (int i = 0; buffer[i] != '\0'; i++) {
            USART_SendData(USART2, buffer[i]);
            while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
        }
    }
}

int main() {
    int global_var = 200; // 全局变量

    USART_InitTypeDef USART_InitStructure;

    // 初始化串口
    // ...

    while (1) {
        char buffer[64];
        sprintf(buffer, "Global var address in STM32: %p\n", &global_var);
        for (int i = 0; buffer[i] != '\0'; i++) {
            USART_SendData(USART2, buffer[i]);
            while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
        }
        func();
    }
}

编译成功,将代码烧录进入芯片,按下reset键,单片机发送数据,串口调试助手结果如下:
在这里插入图片描述

7.存储器地址映射

在ARM Cortex-M及STM32F10x系列单片机中,存储器的地址映射如下:

0x0000 0000 - 0x1FFF FFFF:代码区(Flash存储器)
0x2000 0000 - 0x2000 FFFF:SRAM区域
0x4000 0000 - 0x5FFF FFFF:外设区域
0xE000 0000 - 0xE003 FFFF:片上外设区域
其他:保留地址空间

8.总结

本文介绍了C语言中常量变量的存储位置和地址分配机制。对于全局常量和变量,它们在程序开始执行前就已经分配内存,并且其地址是固定的,可以在编译时确定。而局部常量和变量则在程序执行到其所在的函数或代码块时才被分配内存,并通常存储在栈内存中,其地址在函数或代码块的作用域内是固定的。在地址分配方面,全局常量和变量通常存储在程序的只读数据段或数据段中,而局部常量和变量通常存储在栈内存中,其内存空间在函数或代码块结束时被释放。
通过编写C程序并在Ubuntu和STM32中进行验证,我们可以得出以下结论:

  • 在Ubuntu系统中,C程序可以使用堆内存分配函数(如malloc())从堆中分配内存。
  • 在STM32中,由于其特殊的硬件架构和资源受限的特点,常使用栈来分配局部变量,并在编译和链接时对栈进行配置。
  • 通过串口输出变量地址,我们可以在Ubuntu和STM32中观察到堆、栈、全局和局部变量的分配情况,并对ARM
  • Cortex-M及STM32F10x的存储器地址映射有一定理解。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值