1.未初始化的全局變量(.bss段)
bss段用來存放 沒有被初始化 和 已經被初始化為0 的全局變量。如下例代碼:
#include<stdio.h> int bss_array[1024*1024]; int main(int argc, char *argv[]) { return 0; }
編譯並查看:
$ gcc -g mainbss.c -o mainbss $ ls -l mainbss -rwxrwxr-x. 1 hy hy 8330 Apr 22 19:33 mainbss $ objdump -h mainbss |grep bss mainbss: file format elf32-i386 24 .bss 00400020 0804a020 0804a020 00001018 2**5
$ size mainbss text data bss dec hex filename 1055 272 4194336 4195663 40054f mainbss
全局變量bss_array的大小為4MB = 1024*1024*sizeof(int) Byte = 4194304 Byte。 通過size 查看可知數據被存在了 bss 段
而 可執行文件mainbss 有8KB左右,命令 ls -l mainbss 查看得知。可知,bss類型的全局變量只占用 運行時的內存空間,而不占用可執行文件自身的文件空間。
2017-11-24
今天再讀此書時,聯想到 OR1200 編譯過程中的 RAM 和 鏈接腳本,產生一個問題。在 OR 仿真驗證中,我的RAM只有8KB的空間,若我有一個定義上訴數組,可執行文件本身不大,但是運行時需要占用4M的空間。那程序豈不是會崩潰? 帶着這個問題。在OR的驗證程序中做了測試。
個人結論: 若在運行時這個空間不足的問題,編譯器會幫忙檢查的。這些是數據,若是在運行時的 堆和棧 不足,這點編譯器沒法檢查。
#include"uart.h" volatile unsigned long timestamp = 0;
#define DRAM_MEM_SIZE (0x200) #define DRAM_ADDR_START (0x1FFF-DRAM_MEM_SIZE+1) #define DRAM_ADDR_END (0x1FFF) #define Struct_Section __attribute__((unused, section(".stdata_mem_type"), \ aligned(4))) typedef unsigned int u32; typedef signed int s32; char txbuf[]={0x01,0x03,0x07,0x0f,0x10,0x30,0x70}; char testbss; char testdata=10; typedef struct { char ch[4]; unsigned int addrinit; unsigned int len; }STDATA_MEM_TYPE; STDATA_MEM_TYPE arry_dynamic Struct_Section; int t_array[1024*1024]={}; int main(void) { //省略 }
編譯報錯如下, .bss 段不夠用:
openrisc@openrisc-VirtualBox:~/mc-or-lngit/or_bootsim_shpy/hyor_ramboot$ make or32-elf-gcc -c reset.S -o reset.o or32-elf-gcc -c main.c -o main.o or32-elf-gcc -c uart.c -o uart.o or32-elf-ld reset.o main.o uart.o -T ram.ld -o hyor_ramboot.or32 or32-elf-ld: hyor_ramboot.or32 section `.bss' will not fit in region `ram' or32-elf-ld: region `ram' overflowed by 4192276 bytes make: *** [hyor_ramboot.or32] Error 1
2. 已被初始化為非零的全局變量(.data段)
data段用來存放已經被初始化為非零的全局變量。如下代碼,只將矩陣的第一個元素初試化為1:
#include<stdio.h> int data_array[1024*1024]={1}; int main(int argc, char *argv[]) { return 0; }
編譯查看
[hy@localhost memcfg]$ gcc -g maindata.c -o maindata [hy@localhost memcfg]$ ls -l maindata -rwxrwxr-x. 1 hy hy 4202682 Apr 22 19:48 maindata [hy@localhost memcfg]$ objdump -h maindata |grep \\.data 23 .data 00400020 0804a020 0804a020 00001020 2**5 [hy@localhost memcfg]$ size maindata text data bss dec hex filename 1055 4194604 4 4195663 40054f maindata
而 可執行文件maindata 有4MB左右。通過size 查看可知數據被存在了 data 段
可知,data類型的全局變量既占用運行時的內存空間,也占用可執行文件自身的文件空間。
3.常量數據(.rodata段)
1)rodata用來存放常量數據。 ro: read only
2)字符串會被編譯器自動放在rodata中,加 const 關鍵字的常量數據會被放在 rodata 中
3)在有的嵌入式系統中, rodata放在 ROM(或 NOR Flash)里,運行時直接讀取,不須加載到RAM內存中。
所以,在嵌入式開發中,常將已知的常量系數,表格數據等造表加以 const 關鍵字。存在ROM中,避免占用RAM空間。
4.代碼(.text段)
text段存放代碼(如:函數)和部分整數常量(應該指的是一些立即數),這個段是可執行的。
5.棧(stack)
1)stack 存放函數的局部變量和函數參數
2)被調用函數的參數和返回值 被存儲到當前程序的棧區,之后被調用函數再為自身的自動變量和臨時變量在棧區上分配空間
3)函數返回時,棧區的數據會被釋放掉,先入后出(FILO)的順序。
6.堆(heap)
heap用來動態分配內存,由程序自身決定開辟和釋放。
6.1 malloc/free
#include<stdio.h> #include<stdlib.h> int main(int argc, char *argv[]) { int *p = (int *)malloc(10*1); // p= (int *)malloc(10*1); if(p==NULL) { printf("malloc p err\n"); return -1; } free(p); printf("p = %4x\n",p); p = NULL; printf("p = %4x\n",p); return 0; }
程序運行結果:
[hy@localhost memcfg]$ gcc maindata.c [hy@localhost memcfg]$ ./a.out p = 9cf4008 p = 0
1)開辟了空間,就要適時的釋放。釋放時,指針應指向開辟時的內存空間,所以在使用指針時,要注意不要修改了其地址,或者將開辟時的起始地址保存起來。
2)對指針free后,其地址不一定就為NULL。如代碼中的 p,在 free(p)后,printf("p=%4x",p)后並非為0。所以建議在free(p)后,立即加一句p=NULL。
3)檢查p的地址 if(p!=NULL){ ... }
6.2 calloc/realloc
1)calloc()函數
void *calloc(size_t nmemb, size_t size);
參數nmemb 表示要分配元素的個數,size表示每個元素的大小,分頻的內存空間大小是 nmemb*size; 返回值是 void* 類型的指針,指向分配好的內存首地址。
用法一:分配1024*sizeof(int)字節大小的內存,並清空為0
int *p = (int *)calloc(1024,sizeof(int));
用法二:與 alloc等價的 malloc 用法
int *p = (int *)malloc(1024*sizeof(int)); memset(p,0,1024*sizeof(int));
差異:用法一calloc,會根據分配的的類型來初始化為0,如:分配int型,則初始化為(int)0; 若為指針類型,則初始化為空指針;若為浮點,則初始化為浮點型。
用法二memset,不能保證初試化為空指針值和浮點型。(與NULL常量和浮點型的定義有關)
2)realloc()函數
realloc()用來重新分配正在使用的一塊內存大小。
定義:
void *realloc(void *ptr, size_t size);
用法示例:
int *p = (int *)malloc(1024); // p = (int *)realloc(512); // 重新分配為 512字節大小內存,縮小數據丟失 p = (int *)realloc(2048); // 重新分配為2048字節大小內存
注意:經過realloc()調整后的內存空間起始地址有可能與原來的不同。
摘錄書籍
《ARM嵌入式Linux系統開發詳解》 弓雷 等編著
《高質量嵌入式Linux C編程》 梁庚 等編著