在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

【gcc編譯優化系列】如何(不)回收未發生調用的函數

嵌入式物聯網開發 ? 來源:嵌入式物聯網開發 ? 作者:嵌入式物聯網開發 ? 2022-07-11 09:12 ? 次閱讀

1 問題場景

大家都知道,我們在開發單片機類的嵌入式固件時,一般使用的FLASH存儲空間都是比較有限的,小的可能幾十KB,大一點的可能也就幾百KB,可以說是寸金寸土的FLASH空間,可容不得我們半點垃圾代碼。 如果我們在寫代碼的過程中,隨便寫一些沒用的代碼,比如一些測試代碼,最后版本釋放的時候,這些測試代碼又沒有刪掉,還是參與了編譯,那么勢必最后這個函數的代碼實現就會保留在我們的固件包里面,這樣我們的固件包的bin文件大小勢必會增加,這顯然不是我們想要的。 另外,還有一種場景下,有些函數我們使用static修飾的局部函數,只在初始化的時候通過初始化列表的形式調用一下,比如RT-Thread的初始化實現,INIT_DEVICE_EXPORT(device_init_func),那么我們是不希望這個函數被優化掉的,否則最后會出邏輯問題。 在使用GCC作為編譯器的環境下,有什么辦法可以實現呢?

2 需求分析

這里的需求兩點:

  1. 沒有被調用的函數需要移除,不出現在最后的固件文件里面;
  1. 某些特殊的函數實現,沒有被顯式調用,但是需要保留它,不能被優化掉。

3 需求實現

3.1 示例代碼

實現的一個示例代碼如下所示,功能很簡單就定義了2個沒被調用的函數,一個我希望優化移除,一個我希望被優化保留。

#include 

#define CODE_SECTION(x)               __attribute__((section(x)))
#define CODE_KEEP_USED                CODE_SECTION(".text.keep.used.code")

void unused_func1(int a)
{
    printf("a: %d\n", a);
}

CODE_KEEP_USED void unused_func2(int a)
{
    printf("a: %d\n", a);
}

int main(int argc, const char *argv[])
{
    printf("Hello world !\n");
    return 0;
}

3.2 鏈接腳本

鏈接腳本是GCC在鏈接所有目標文件變成可執行文件的時候,需要讀取的一個配置文件,該文件決定了最后的可執行文件是如何分布的。 我這里使用的是Ubuntu X64平臺 GCC默認的鏈接腳本,由此改造而來。 至于如何獲取GCC默認的鏈接腳本,請參考這里的教程。 修改后的鏈接腳本如下:

/* Script for -z combreloc -z separate-code */
/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
          "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
SECTIONS
{
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id  : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rela.dyn       :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
      *(.rela.ifunc)
    }
  .rela.plt       :
    {
      *(.rela.plt)
      PROVIDE_HIDDEN (__rela_iplt_start = .);
      *(.rela.iplt)
      PROVIDE_HIDDEN (__rela_iplt_end = .);
    }
  . = ALIGN(CONSTANT (MAXPAGESIZE));
  .init           :
  {
    KEEP (*(SORT_NONE(.init)))
  }
  .plt            : { *(.plt) *(.iplt) }
.plt.got        : { *(.plt.got) }
.plt.sec        : { *(.plt.sec) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(SORT(.text.sorted.*))
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  . = ALIGN(CONSTANT (MAXPAGESIZE));
  /* Adjust the address for the rodata segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)));
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .eh_frame_hdr   : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
  /* These sections are generated by the Sun/Oracle C++ compiler.  */
  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges*) }
  /* Thread Local Storage sections  */
  .tdata      :
   {
     PROVIDE_HIDDEN (__tdata_start = .);
     *(.tdata .tdata.* .gnu.linkonce.td.*)
   }
  .tbss          : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array    :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array    :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
    PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array    :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }

  /* Here use to keep some sections, which are not wanted to be removed. */
  .text_keep_used_code :
  {
    KEEP (*(.text.keep.used.code))
  }

  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  .got            : { *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
  .got.plt        : { *(.got.plt) *(.igot.plt) }
  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  . = .;
  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we do not
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  .lbss   :
  {
    *(.dynlbss)
    *(.lbss .lbss.* .gnu.linkonce.lb.*)
    *(LARGE_COMMON)
  }
  . = ALIGN(64 / 8);
  . = SEGMENT_START("ldata-segment", .);
  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
  }
  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.ldata .ldata.* .gnu.linkonce.l.*)
    . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  . = ALIGN(64 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  /* DWARF Extension.  */
  .debug_macro    0 : { *(.debug_macro) }
  .debug_addr     0 : { *(.debug_addr) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

關鍵地方在于.textkeepused_code段的定義,其他都是默認鏈接腳本就已有的內容。

3.3 編譯腳本

Ubuntu x64下的編譯腳本,支持編譯啟用回收優化和不啟用回收優化的情況,參考如下:

#! /bin/bash -e

CFLAGS="-save-temps=obj -Wall"
LDFLAGS="-Wl,-Map=test.map"

CFLAGS_GC="-fdata-sections -ffunction-sections"
LDFLAGS_GC="-Wl,--gc-sections"
LDFLAGS_MAP_GC="-Wl,-Map=test_gc.map"

PRINT_GC="-Wl,--print-gc-sections"

GCC_LDS=default.lds

if [ "$1" = "clean" ]; then
    rm -rf test* *.i *.s *.o *.map
    echo "Clean build done !"
    exit 0
elif [ "$1" = "gc" ]; then
    echo "gcc compile with gc ..."
    single_c_file=`ls *.c | cut -d . -f 1`
    cmd1="gcc -o $single_c_file.o -c *.c $CFLAGS $CFLAGS_GC"
    cmd2="gcc -o test_gc $single_c_file.o $LDFLAGS_GC $LDFLAGS_MAP_GC -T $GCC_LDS $PRINT_GC"
    echo "$cmd1 && $cmd2" && $cmd1 && $cmd2
else
    echo "gcc compile without gc ... (default)"
    cmd="gcc *.c $CFLAGS $LDFLAGS -o test"
    echo $cmd && $cmd
fi

exit 0

3.4 驗證測試

3.4.1 驗證不啟用編譯回收優化的情況

使用./build.sh編譯輸出各種文件,使用grep-rsn unused_func驗證:

**grep -rsnw unused_func1**
main.c:9:void unused_func1(int a)
**Binary file test matches**
test.i:735:void unused_func1(int a)
Binary file test.o matches
test.s:7:       .globl  unused_func1
test.s:8:       .type   unused_func1, @function
test.s:9:unused_func1:
test.s:31:      .size   unused_func1, .-unused_func1
test.map:193:                0x0000000000001169                unused_func1

**grep -rsnw unused_func2**
main.c:14:CODE_KEEP_USED void unused_func2(int a)
**Binary file test matches**
test.i:740:__attribute__((section(".text.keep.used.code"))) void unused_func2(int a)
Binary file test.o matches
test.s:33:      .globl  unused_func2
test.s:34:      .type   unused_func2, @function
test.s:35:unused_func2:
test.s:57:      .size   unused_func2, .-unused_func2
test.map:197:                0x00000000000011b7                unused_func2

我們可以發現,在最后生成的test可執行文件中,都找到了unusedfunc1和testfunc2,也就是說在不啟用回收優化的情況下,跟我們之前的預期是一樣的,這樣增加固件包的尺寸。

3.4.2 驗證啟用編譯回收優化的情況

使用./build.sh gc編譯輸出各種文件,使用grep-rsn unused_func驗證:

**grep -rsnw unused_func1**
Binary file main.o matches
main.c:9:void unused_func1(int a)
test_gc.map:35: .text.unused_func1
main.i:735:void unused_func1(int a)
main.s:6:       .section        .text.unused_func1,"ax",@progbits
main.s:7:       .globl  unused_func1
main.s:8:       .type   unused_func1, @function
main.s:9:unused_func1:
main.s:31:      .size   unused_func1, .-unused_func1

**grep -rsnw unused_func2**
Binary file main.o matches
main.c:14:CODE_KEEP_USED void unused_func2(int a)
test_gc.map:215:                0x0000000000401169                unused_func2
main.i:740:__attribute__((section(".text.keep.used.code"))) void unused_func2(int a)
main.s:33:      .globl  unused_func2
main.s:34:      .type   unused_func2, @function
main.s:35:unused_func2:
main.s:57:      .size   unused_func2, .-unused_func2
**Binary file test_gc matches**

從中,我們發現最后的可執行文件testgc里面只有unusedfunc2,而unused_func1就沒回收了,這個就實現了我們前面定義的需求。

4 原理分析

4.1 實現原理

這里實現的原理主要有4個部分: 第1部分主要修改的是代碼編寫階段,在不希望被回收優化的函數前面添加特殊的段名稱,比如__attribute__((section(".text.keep.used.code"))), 第2部分主要修改的是編譯階段,通過在CFLAGS中添加-fdata-sections-ffunction-sections來實現, 第3部分主要修改的是鏈接階段,通過在LDFLAGS中添加-Wl,-gc-sections來實現, 第4部分主要修改的是鏈接腳本,通過在段名稱中,新增下面的段申明,主要是為了限制指定的段,不被回收。

.text_keep_used_code :
  {
    KEEP (*(.text.keep.used.code))
  }

從原理上說,編譯時使用-fdata-sections-ffunction-sections是為了讓data數據和每一個函數都生成特定的段,以函數xxx為例,那么它將會放在.text.xxx段里面,然后在鏈接階段的時候使用-Wl,-gc-sections回收那些不使用的段。 注意這里回收的最小單位是,所以編譯階段那兩個選項是必不可少的。

4.2 原理驗證分析

4.2.1 確認編譯階段的函數所在的段

這個確認我們可以map文件和.s匯編文件就可以確認,

/* map文件中 unused_fun1的描述 */
35  .text.unused_func1
36                 0x0000000000000000       0x28 main.o

/* 文件中 unused_fun2的描述 */
213  .text.keep.used.code
214                 0x0000000000401169       0x28 main.o
215                 0x0000000000401169                unused_func2

/* 匯編文件中 unused_fun1的描述 */
  6         .section        .text.unused_func1,"ax",@progbits
  7         .globl  unused_func1
  8         .type   unused_func1, @function
  9 unused_func1:
 10 .LFB0:
 11         .cfi_startproc
 12         endbr64
 13         pushq   %rbp
 14         .cfi_def_cfa_offset 16
 15         .cfi_offset 6, -16

/* 匯編文件中 unused_fun2的描述 */
 32         .section        .text.keep.used.code,"ax",@progbits
 33         .globl  unused_func2
 34         .type   unused_func2, @function
 35 unused_func2:
 36 .LFB1:
 37         .cfi_startproc
 38         endbr64
 39         pushq   %rbp
 40         .cfi_def_cfa_offset 16
 41         .cfi_offset 6, -16
 42         movq    %rsp, %rbp
 43         .cfi_def_cfa_register 6

從上面的分析,可以知道函數的段分布是完全符合預期的。

4.2.2 確認鏈接階段的函數所在的段的回收情況

為了驗證這一點,我們可以在LDFLAGS里面添加這一個選項-Wl,--print-gc-sections,這樣我們就可以觀察到鏈接階段最后移除了那些沒有引用的段,從而確認unusedfunc1和unusedfunc2是否被回收。 輸出的關鍵log如下:

 ./build.sh gc
gcc compile with gc ...
gcc -o main.o -c *.c -save-temps=obj -Wall -fdata-sections -ffunction-sections && gcc -o test_gc main.o -Wl,--gc-sections -Wl,-Map=test_gc.map -T default.lds -Wl,--print-gc-sections
/usr/bin/ld: removing unused section '.rodata.cst4' in file '/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o'
/usr/bin/ld: removing unused section '.data' in file '/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o'
/usr/bin/ld: removing unused section '.text.unused_func1' in file 'main.o'

log很明顯就告訴我們,unused section '.text.unused_func1'被回收移除了,而.text.keep.used.code是沒有被回收的,這切好證明了我們的猜想。

5 經驗總結

  • 使用-gc-sections可以回收不使用的代碼段,從而減少代碼尺寸,降低固件占用FLASH的存儲空間;
  • 在特定場景下,修改鏈接腳本可以實現某個函數不被鏈接優化,達到特定的目的。

6 更多分享

本項目的所有測試代碼和編譯腳本,均可以在我的github倉庫01workstation中找到。

歡迎關注我的github倉庫01workstation,日常分享一些開發筆記和項目實戰,歡迎指正問題。

同時也非常歡迎關注我的CSDN主頁和專欄:

【CSDN主頁:架構師李肯】

【RT-Thread主頁:架構師李肯】

【C/C++語言編程專欄】

【GCC專欄】

信息安全專欄】

【RT-Thread開發筆記】

freeRTOS開發筆記】

有問題的話,可以跟我討論,知無不答,謝謝大家。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 單片機
    +關注

    關注

    6040

    文章

    44592

    瀏覽量

    636891
  • GCC
    GCC
    +關注

    關注

    0

    文章

    107

    瀏覽量

    24857
  • 函數
    +關注

    關注

    3

    文章

    4341

    瀏覽量

    62806
收藏 人收藏

    評論

    相關推薦

    GCC編譯優化系列】前后編譯的兩版本固件bin大小不一樣?

    GCC編譯優化系列】前后編譯的兩個版本固件bin大小不一樣,怎么辦?
    的頭像 發表于 09-09 09:01 ?4809次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優化</b><b class='flag-5'>系列</b>】前后<b class='flag-5'>編譯</b>的兩版本固件bin大小不一樣?

    Linux 下GCC編譯

    一、Linux 下多文件編譯 在上一篇 Linux 下的 C 編程我們知道了 Linux 下的編譯器為 GCC ,以及如何使用 GCC 進行編譯
    的頭像 發表于 09-11 15:18 ?2674次閱讀
    Linux 下<b class='flag-5'>GCC</b>的<b class='flag-5'>編譯</b>

    使用gcc編譯優化優化問題

    同樣的程序,使用gcc編譯優化優化的結果不一代碼如下:1. #include 2.3. int main()4. {5.int i
    發表于 09-27 10:33

    gcc編譯中斷函數的方法

    在x86中,一般函數通過"call"指令調用,"ret"指令返回,但是中斷函數不同,它在中斷或者異常發生時自動切入(或者使用"int"指令
    發表于 12-09 06:20

    【原創精選】RT-Thread征文精選技術文章合集

    編譯優化系列】使用GCC如何把C文件編譯成可執行文件【GCC
    發表于 07-26 14:56

    AVR系列單片機GCC免費編譯工具

    AVR系列單片機GCC免費編譯工具
    發表于 04-13 15:23 ?54次下載

    高效的C編程之函數調用

    14.9 函數調用 函數設計的基本原則是使其函數體盡量的小。這樣編譯器可以對函數做更多的
    發表于 10-17 16:49 ?6次下載
    高效的C編程之<b class='flag-5'>函數</b><b class='flag-5'>調用</b>

    常見gcc編譯警告整理以及解決方法

     GCC有很多的編譯選項,警告選項;指定頭文件、庫路徑;優化選項。本文針整理一下GCC的警告選項以及gcc
    發表于 11-14 11:19 ?2.1w次閱讀

    GCC編譯優化指南

    (cpp) → 編譯(gcc或g++) → 匯編(as) → 連接(ld) ;括號中表示每個階段所使用的程序,它們分別屬于 GCC 和 Binutils 軟件包。顯然的,優化應當從
    發表于 04-02 14:36 ?555次閱讀

    編譯優化函數的影響

    編譯器如gcc,可以指定不同的優化參數,在某些條件下,有些函數可能會被優化掉。
    的頭像 發表于 06-22 14:58 ?2854次閱讀
    <b class='flag-5'>編譯</b>器<b class='flag-5'>優化</b>對<b class='flag-5'>函數</b>的影響

    如何讓gcc編譯中斷函數

    在x86中,一般函數通過"call"指令調用,"ret"指令返回,但是中斷函數不同,它在中斷或者異常發生時自動切入(或者使用"int"指令
    發表于 11-26 11:06 ?7次下載
    如何讓<b class='flag-5'>gcc</b><b class='flag-5'>編譯</b>中斷<b class='flag-5'>函數</b>

    GCC編譯優化系列】實戰分析C代碼遇到的編譯問題及解決思路

    GCC編譯優化系列】實戰分析C工程代碼可能遇到的編譯問題及其解決思路
    的頭像 發表于 07-10 23:15 ?1459次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優化</b><b class='flag-5'>系列</b>】實戰分析C代碼遇到的<b class='flag-5'>編譯</b>問題及解決思路

    GCC編譯優化系列】multiple-definition

    GCC編譯優化系列】這種讓人看不懂的multiple-definition真的有點讓人頭疼
    的頭像 發表于 07-11 09:26 ?7299次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優化</b><b class='flag-5'>系列</b>】multiple-definition

    GCC編譯優化系列】-specs=kernel.specs

    GCC編譯優化系列GCC編譯鏈接時候--specs=kernel.specs鏈接屬性究竟是個
    的頭像 發表于 07-11 09:25 ?3534次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優化</b><b class='flag-5'>系列</b>】-specs=kernel.specs

    Linux使用gcc編譯程序的語法

    01. 調試相關的宏 在Linux使用gcc編譯程序的時候,對于調試的語句還具有一些特殊的語法。 gcc編譯的過程中,會生成一些宏,可以使用這些宏分別打印當前源文件的信息,主要內容是當
    的頭像 發表于 06-22 10:51 ?725次閱讀
    主站蜘蛛池模板: 在线天堂中文新版有限公司| 色爱区综合激月婷婷激情五月| 亚洲码在线| 视频在线免费| aa毛片| 久久精品男人影院| 色妞网| 亚洲人成电影| 99在线国产| 欧美日韩亚洲一区| 欧美人与zoxxxx| 高清一本之道加勒比在线| 人人澡 人人澡 人人看| 97影院理论片在线观看| 夜夜操夜夜摸| 在线播放免费| 成人三级电影在线观看| 国产精品资源站| 黄色一级日本| 开心激情小说| 久久综合欧美| 给我一个可以看片的www日本| 国产一级真人毛爱做毛片| 免费黄色大片网站| 日日噜噜爽爽狠狠视频| 五月天婷婷在线视频| 亚洲国产精品综合久久网络| 伊人啪| 五月婷在线观看| 午夜免费看片| 欧美爱爱网| 看全色黄大色大片免费久久怂| 国产亚洲美女| 天天插日日射| 精品视频在线观看视频免费视频| 男女性高爱潮免费的国产| 看片久久| 国产精品久久久久久久免费 | 亚洲国产精品综合久久2007| 日本三级在线观看免费| 欧美三级免费看|