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

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

3天內不再提示

【GCC編譯優(yōu)化系列】multiple-definition

嵌入式物聯(lián)網開發(fā) ? 來源:嵌入式物聯(lián)網開發(fā) ? 作者:嵌入式物聯(lián)網開發(fā) ? 2022-07-11 09:26 ? 次閱讀

【GCC編譯優(yōu)化系列】這種讓人看不懂的multiple-definition真的有點讓人頭疼

1 寫在前面

有印象的朋友應該記得我之前寫過一篇 關于GCC編譯報錯及對應解決辦法,在該文的 3.5.3 章節(jié)有提到幾種很典型的 multiple-definition 鏈接錯誤,也簡要分析了其出現(xiàn)問題的原因及對應解決方法。

multiple-definition 在GCC編譯報錯里面,它的報錯本質是 重復定義,可能是函數(shù)重復定義,也可能是變量重復定義。

但今天我要介紹的這個 multiple-definition 跟常規(guī)遇到的還不太一樣,否則這個問題就不值得我寫篇文章來做記錄了,詳細請看下文。

2 問題描述

事情是這樣的,前幾天一個同事給我報了一個我們SDK的問題,我想著加快復現(xiàn)問題,于是我找了他要他的應用代碼,拿到我的編譯環(huán)境環(huán)境來編譯復現(xiàn)。

結果,好巧不巧,拿他代碼一編譯,居然給我報錯了,而且這個報錯把我整不會了!朋友,請看:

/home/xxx/compiler/riscv64_unkown_elf_gcc10.2.0/Linux64/bin/../lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/bin/ld: /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(user_app.o):/home/xxx/user_app/user_app.h:76: multiple definition of `mcu_ota_t'; /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o):/home/xxx/user_app/user_app.h:76: first defined here
/home/xxx/compiler/riscv64_unkown_elf_gcc10.2.0/Linux64/bin/../lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/bin/ld: /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(user_app.o):/home/xxx/user_app/user_app.h:70: multiple definition of `notify_state_t'; /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o):/home/xxx/user_app/user_app.h:70: first defined here
/home/xxx/compiler/riscv64_unkown_elf_gcc10.2.0/Linux64/bin/../lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/bin/ld: /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(user_app.o):/home/xxx/user_app/user_app.h:60: multiple definition of `wifi_state_t'; /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o):/home/xxx/user_app/user_app.h:60: first defined here
/home/xxx/compiler/riscv64_unkown_elf_gcc10.2.0/Linux64/bin/../lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/bin/ld: /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(user_app.o):/home/xxx/user_app/user_app.h:15: multiple definition of `frame_num_t'; /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o):/home/xxx/user_app/user_app.h:15: first defined here
collect2: error: ld returned 1 exit status

里面error提示的 multiple definition 異常亮眼,但是又讓人摸不著頭腦,這有點不按常理出牌!

要知道,他的應用代碼明明都可以release版本的呀,而我的編譯環(huán)境肯定也沒有問題,畢竟 sample app 在我這都是可以編譯通過的,所謂我大膽推測問題很有可能出在他們的應用代碼上,而編譯報錯也的確提示是應用代碼的問題。

3 場景復現(xiàn)

為了準確描述這個問題,排除其他的排除干擾因素,我把相關C代碼和頭文件捋一下。 整個應用部分的工程包括2個C代碼和2個頭文件:

主要處理應用入口的app_entry.c:

/* app_entry.c */

#include "sdk.h"       //SDK的統(tǒng)一頭文件
#include "app_entry.h" //app_entry的頭文件
#include "user_app.h"  //user_app的頭文件

/* called by lower SDK */
int app_entry_main(void)
{
    /* some code */

    /* call user_app */
    user_app_init();

    /* some code */

    return 0;
}

appentry對應的頭文件appentry.h:

#ifndef __APP_ENTRY_H__
#define __APP_ENTRY_H__

/* external functions */
extern int app_entry_main(void);

#endif /* end of __APP_ENTRY_H__ */

主要處理用戶應用邏輯的user_app.c:

/* user_app.c */

#include "sdk.h"       //SDK的統(tǒng)一頭文件
#include "app_entry.h" //app_entry的頭文件
#include "user_app.h"  //user_app的頭文件

/* other functions */

/* called by app_entry */
int user_app_init(void)
{
    /* some code */

    return 0;
}

userapp對飲的頭文件userapp.h:

#ifndef __USER_APP_H__
#define __USER_APP_H__

/* some enum definition */
enum {
    UART_FRAME_1 = 0x01,
    UART_FRAME_2,
    UART_FRAME_3,
    UART_FRAME_4,
    UART_FRAME_5,
} frame_num_t;

enum {
    WIFI_STATE_1 = 0x01,
    WIFI_STATE_2,
    WIFI_STATE_3,
    WIFI_STATE_4,
    WIFI_STATE_5.
} wifi_state_t;

enum {
    NOTIFY_STATE_1 = 0x01,
    NOTIFY_STATE_2,
    NOTIFY_STATE_3,
    NOTIFY_STATE_4,
    NOTIFY_STATE_5,
} notify_state_t;

enum {
    MCU_OTA_NO_BIN = 0x00,
    MCU_OTA_DOWNLOAD_OK,
    MCU_OTA_DOWNLOAD_FAIL,
} mcu_ota_t;

/* external functions */
extern int user_app_init(void);

#endif /* end of __USER_APP_H__ */

另外,補充說明一下,我們使用的是交叉編譯工具是針對RISCV架構的 riscv64-unknown-gcc。

簡化之后,應用代碼大概就是如上面所示,就這樣的代碼給報錯了,有點納悶。

4 深入分析

4.1 可能性分析

頭文件被重復包含了?

我看到這個報錯的第一反應是,難道頭文件被重復包含了?

比如在某個頭文件中定義了一個變量(假設真有這么寫的),如果它的頭文件沒有按照標準的 ifndef 的那種寫法來寫,那么當這個頭文件被一個C文件直接或間接包含多次的時候,這個定義的變量就會存在多個副本,這個時候就會報 “multiple definition”。

可是,我仔細檢查過user_app.h的頭部寫法,是正確的,不存在這種問題。

某個C文件里面存在多個xxx_t的副本?

這一種也是可能的,比如a.h中定義了一個xxx_t,然后b.h中也定義了同名的xxx_t,這時候某個C文件同時包含了a.h和b.h,那么xxx_t在這個C文件中就有兩個定義。

這個時候,通過查看預處理后的文件(.i)文件就可以看得出來,是否存在這種情況。

如何打開生成預編譯后的文件,可以參考 這篇文章的 4.2.2 章節(jié)介紹。

以本案例中的 mcu_ota_t 為例,很顯然,并不存在這種情況,只有一個定義呢。

xxx@ubuntu:~/user_app$ 
xxx@ubuntu:~/user_app$ find . -name user_app.i
./out/user_app@xxxevb/modules/home/xxx/user_app/user_app.i
xxx@ubuntu:~/user_app$ cat ./out/user_app@xxxevb/modules/home/xxx/user_app/user_app.i | grep -nw mcu_ota_t
5547:}mcu_ota_t;
xxx@ubuntu:~/user_app$ 

4.2 分析map文件

既然是 multiple definition,那么我搜搜看!

給我上 grep大法,不搜不知道,一搜嚇一跳。以 mcuotat 為例:

xxx@ubuntu:~/user_app$ grep -rsnw mcu_ota_t
user_app.h:77:}mcu_ota_t;
out/user_app@xxxevb/modules/home/xxx/user_app/user_app.i:5547:}mcu_ota_t;
out/user_app@xxxevb/modules/home/xxx/user_app/user_app.s:2488:        .globl  mcu_ota_t
out/user_app@xxxevb/modules/home/xxx/user_app/user_app.s:2554:        .section        .sbss.mcu_ota_t,"aw",@nobits
out/user_app@xxxevb/modules/home/xxx/user_app/user_app.s:2555:        .type   mcu_ota_t, @object
out/user_app@xxxevb/modules/home/xxx/user_app/user_app.s:2556:        .size   mcu_ota_t, 1
out/user_app@xxxevb/modules/home/xxx/user_app/user_app.s:2557:mcu_ota_t:
out/user_app@xxxevb/modules/home/xxx/user_app/user_app.s:3361:        .4byte  mcu_ota_t
out/user_app@xxxevb/modules/home/xxx/user_app/user_app.s:9661:        .string "mcu_ota_t"
Binary file out/user_app@xxxevb/modules/home/xxx/user_app/user_app.o matches
Binary file out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.o matches
out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.i:3807:}mcu_ota_t;
out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.s:87: .globl  mcu_ota_t
out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.s:94: .section        .sbss.mcu_ota_t,"aw",@nobits
out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.s:95: .type   mcu_ota_t, @object
out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.s:96: .size   mcu_ota_t, 1
out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.s:97:mcu_ota_t:
out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.s:574:        .4byte  mcu_ota_t
out/user_app@xxxevb/modules/home/xxx/user_app/app_entry.s:1136:       .string "mcu_ota_t"
out/user_app@xxxevb/binary/user_app@xxxevb.map:1811: .sbss.mcu_ota_t
out/user_app@xxxevb/binary/user_app@xxxevb.map:1879: .sbss.mcu_ota_t
out/user_app@xxxevb/binary/user_app@xxxevb.map:47777:mcu_ota_t                                         /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o)
Binary file out/user_app@xxxevb/libraries/user_app.a matches
Binary file out/user_app@xxxevb/libraries/user_app.stripped.a matches
image-20220330081507885

map文件清晰地顯示,在BSS段中有個object叫 mcuotat,要知道在BSS段中出現(xiàn),這玩意就是global的東西了。

這什么意思?

意思就是編譯器已經把mcuotat當做一個 全局變量 了。

那么我們來梳理一下,當userapp.h里面定義了一個 mcuotat 的全局變量,這個userapp.h同時被appentry.c和userapp.c包含,自然在這兩個C文件中,都有這個mcuotat全局變量的副本存在;那么根據 【經驗總結】一文帶你了解C代碼到底是如何被編譯的 提及的,在鏈接階段,編譯器就會去查找并鏈接它們,這個時候多個同名全局變量,肯定是不允許的,自然而然,就報了 “multiple definition” 錯誤。

4.3 扒一扒基礎語法

為此,我特意去查了一下C語言教科書,找了一些關于C語言的枚舉定義的介紹,再學習了一下。

果然userapp.h中的那幾個 xxxt 枚舉并不是一種規(guī)范寫法,倒不是說不可以這么寫,只是這樣寫之后容易造成干擾,嚴重的情況下還會導致語法錯誤。

相關學習資料,可以看 參考鏈接 附錄里面的文章。

image-20220330080320875

4.4 GCC的版本差異

那么問題來了,為何同事的編譯環(huán)境沒報錯,而我的編譯環(huán)境報錯了呢?

原來,前段時間我們的SDK因一個廠商私有庫比較新,特意升級了GCC的版本,由 riscv64unkownelfgcc8.3.0 升級到了 riscv64unkownelfgcc10.2.0,而應用那邊還沒來得及升級這個版本。所以才造成了這樣的沖突。

至于為何兩個版本有差異呢?后面我也會提到,其實8.3.0版本對這種寫法是有報 警告 的,而10.2.0版本是報 錯誤 的;在我們的編譯環(huán)境中,除編譯器版本不一樣外,其他由構建層傳入的所有編譯選項都是一模一樣的。

那么,下面分析下究竟的差異在哪里。

4.4.1 對比map文件和匯編代碼

如下圖所示:

image-20220330135821105

匯編文件顯示,兩者編譯出來的段分布是不一樣的,一個在.common段,一個在.global段;

而map文件中,在.global段的被分配到了 .sbss段中,作為全局的object而存在;所以就報了 mutiple definiton 的錯誤。

這個簡單分析,基本就可以確定是在編譯階段引入的問題,而不是在鏈接階段引入的問題,所以后面的排查中,應重點關注編譯選項,而不是鏈接選項。

4.4.2 如何查看GCC默認使用的編譯選項

如何你將 “**” 這幾個關鍵字去搜索,你很大概率拿到的是這個鏈接,它的方法是這樣的:

echo "" | gcc -v -x c++ -E -

然后查看輸出的內容中的:COLLECTGCCOPTIONS

對應我這邊,替換掉對應的gcc版本,8.2.0和10.3.0版本的輸出分別是:

GCC8.3.0    COLLECT_GCC_OPTIONS='-v' '-E' '-march=rv64imafdc' '-mabi=lp64d'
GCC10.2.0   COLLECT_GCC_OPTIONS='-v' '-E' '-march=rv64imafdc' '-mabi=lp64d' '-march=rv64imafdc'

眼看,壓根看不出差異,對不對。那我倒懷疑是方法有問題。

我想起之前寫過 一篇文章關于GCC默認鏈接選項 的,里面倒是提到了取默認參數(shù)的蛛絲馬跡,立馬實踐下。

這時候你先準備一個簡單得不能再簡單的helloworld.c:

#include 

int main(void)
{
    printf("hello world\r\n");
    return 0;
}

然后在對應的目錄執(zhí)行(注意替換gcc的路徑):

arm-none-gcc -v -Q hello.c

這個方法是我自己實踐摸索總結出來的參數(shù)組合,全網估計還沒人這么用!

這個方法可以順利取得GCC默認使能的參數(shù),留意輸出的 options enabled 即可!

4.4.3 對比GCC的默認使能的編譯選項

為了深究這個報錯問題,我使用關鍵字 "mutiple definition 10.2.0",找到這么一個 有效鏈接,里面描述的情景,基本跟我的差不多。

image-20220330134754159

摘抄里面的一段話,理解下:

The issue can be fixed with adding-fcommon to compiler options.

A common mistake in C is omitting extern when declaring a global variable in a header file. If the header is included by several files it results in multiple definitions of the same variable. In previous GCC versions this error is ignored. GCC 10 defaults to -fno-common, which means a linker error will now be reported. To fix this, use extern in header files when declaring global variables, and ensure each global is defined in exactly one C file. If tentative definitions of particular variables need to be placed in a common block, attribute((common)) can be used to force that behavior even in code compiled without -fcommon. As a workaround, legacy C code where all tentative definitions should be placed into a common block can be compiled with -fcommon.

順著這個編譯選項,我找到了GCC 10.x版本的 編譯選項在線說明文檔,摘抄下里面關于 -fcommon 選項 和 -fno-common 選項的說明,大家理解下:

  1. -fcommon

In C code, this option controls the placement of global variables defined without an initializer, known as tentative definitions in the C standard. Tentative definitions are distinct from declarations of a variable with theextern keyword, which do not allocate storage.

The default is -fno-common, which specifies that the compiler places uninitialized global variables in the BSS section of the object file. This inhibits the merging of tentative definitions by the linker so you get a multiple-definition error if the same variable is accidentally defined in more than one compilation unit.

The -fcommon places uninitialized global variables in a common block. This allows the linker to resolve all tentative definitions of the same variable in different compilation units to the same object, or to a non-tentative definition. This behavior is inconsistent with C++, and on many targets implies a speed and code size penalty on global variable references. It is mainly useful to enable legacy code to link without errors.

回過頭來,根據前面取得的默認編譯參數(shù),我們對比下兩個GCC版本的默認選項,我們果然發(fā)現(xiàn)了 -fcommon 有差別.

image-20220330171409237

左邊是8.3.0版本,它默認使能了 -fcommon 這個參數(shù)就決定了 mcuotat 編譯到 .common 段;從而鏈接的時候,并不會報警告,而僅僅是報了一個 warning: multiple common of 警告。

而右邊的10.2.0版本沒有 -fcommon,根據在線說明可知,10.x版本默認是關閉了該選項,即使用的是 -fno-common,所以 mcuotat 編譯到了 .global 段;這就直接導致在鏈接的時候,報了 mutiple-definiton 錯誤,因為位于 .global 段是不能有多份一樣的定義。

按照這個分析,在10.2.0版本中,手動加上 -fcommon 選項,編譯也不會報 mutiple-definiton 錯誤。

是否真是如此,留個小疑問,有心讀者可以自行驗證驗證。

4.4.4 得出結論

綜上幾個步驟下來,基本可以得出一個結論,外圍調用GCC發(fā)起編譯、鏈接等能看得見的步驟里,兩個版本的參數(shù)都是一模一樣的,很顯然不是因為上層傳入的編譯選項導致的;經過精準地資料輔助分析,得出是 GCC 10.2.0 版本默認使用的 -fno-common 選項惹的禍,但它的本意初衷是好的,只不過不被程序猿所熟知而已。

一個看似簡單的 mutiple-definiton 問題,繞了一圈,終于發(fā)現(xiàn)、理解并有效解決地解決這個問題。

5 修復驗證

5.1 問題修復

明白了上面的基礎語法和GCC的編譯特性之后,修復的方法就很簡單了,只需要把 user_app.h 中所有的枚舉定義加上一個 typedef,正如 C語言--enum,typedef enum 枚舉類型詳解 所介紹的方法三那樣。

修改后的代碼如下:

#ifndef __USER_APP_H__
#define __USER_APP_H__

/* some enum definition */
typedef enum {
    UART_FRAME_1 = 0x01,
    UART_FRAME_2,
    UART_FRAME_3,
    UART_FRAME_4,
    UART_FRAME_5,
} frame_num_t; //注意:此處的frame_num_t為枚舉型enum frame_num_t的別名

typedef enum {
    WIFI_STATE_1 = 0x01,
    WIFI_STATE_2,
    WIFI_STATE_3,
    WIFI_STATE_4,
    WIFI_STATE_5.
} wifi_state_t; //注意:此處的wifi_state_t為枚舉型enum wifi_state_t的別名

typedef enum {
    NOTIFY_STATE_1 = 0x01,
    NOTIFY_STATE_2,
    NOTIFY_STATE_3,
    NOTIFY_STATE_4,
    NOTIFY_STATE_5,
} notify_state_t; //注意:此處的notify_state_t為枚舉型enum notify_state_t的別名

typedef enum {
    MCU_OTA_NO_BIN = 0x00,
    MCU_OTA_DOWNLOAD_OK,
    MCU_OTA_DOWNLOAD_FAIL,
} mcu_ota_t; //注意:此處的mcu_ota_t為枚舉型enum mcu_ota_t的別名

/* external functions */
extern int user_app_init(void);

#endif /* end of __USER_APP_H__ */

主要的核心修改,就是把enum的寫法糾正了,我跟對應的應用開發(fā)的童鞋聊過,他說可能就是寫代碼的時候 偷懶 了點,壓根沒寫到這樣的寫法有啥不妥,最最最重要的是 riscv64unkownelf_gcc8.3.0 的默認編譯參數(shù),放任了這種有問題的寫法(僅僅是編譯警告,而不是編譯錯誤),從而沒有在第一時間暴露出來,造成代碼的語法隱患。

riscv64unkownelf_gcc8.3.0 版本的編譯輸出,注意其實這里是有 警告 的!

Making user_app@xxxevb.elf
/home/xxx/compiler/riscv64_unkown_elf_gcc8.3.0/Linux64/bin/../lib/gcc/riscv64-unknown-elf/8.3.0/../../../../riscv64-unknown-elf/bin/ld: /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(user_app.o) and /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o): warning: multiple common of `mcu_ota_t'
/home/xxx/compiler/riscv64_unkown_elf_gcc8.3.0/Linux64/bin/../lib/gcc/riscv64-unknown-elf/8.3.0/../../../../riscv64-unknown-elf/bin/ld: /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(user_app.o) and /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o): warning: multiple common of `notify_state_t'
/home/xxx/compiler/riscv64_unkown_elf_gcc8.3.0/Linux64/bin/../lib/gcc/riscv64-unknown-elf/8.3.0/../../../../riscv64-unknown-elf/bin/ld: /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(user_app.o) and /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o): warning: multiple common of `wifi_state_t'
/home/xxx/compiler/riscv64_unkown_elf_gcc8.3.0/Linux64/bin/../lib/gcc/riscv64-unknown-elf/8.3.0/../../../../riscv64-unknown-elf/bin/ld: /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(user_app.o) and /home/xxx/user_app/out/user_app@xxxevb/libraries/user_app.a(app_entry.o): warning: multiple common of `frame_num_t'

Making user_app@xxxevb.bin
Making user_app@xxxevb.hex

5.2 問題驗證

代碼修復之后,使用 riscv64unkownelfgcc8.3.0 版本的GCC和 riscv64unkownelfgcc10.2.0版本的GCC,均一次編譯通過,這才是正統(tǒng)的C語言寫法,容不得半點偷懶?。?/p> image-20220330072625523

同時,我們再分析下問題修復之后,map文件里面對這幾個定義的變化,以 mcuotat 為例:

image-20220330074002526

如同我們所預料的,加上typedef之后,這個mcuotat已經是一個枚舉類型的別名,并不是一個變量,自然在map文件肯定找不到它,但是原來的那種寫法能找到的原因是,它那種是定義了一個全局變量叫 mcuotat。這才是兩者的本質區(qū)別。

6 經驗總結

  • 嚴謹地寫好每一行代碼:了解每一行代碼背后的基礎語法,溫故而知新。
  • 對比確認是個好方法:選擇適當?shù)谋容^方法,找出差異,往往差異的地方就是解決問題的突破口。
  • 回歸問題的本質:暫且認為 編譯器的報錯是不會騙人的,在這個基礎之上,逐步從問題報錯的表面往里面深究,為何會是 “multiple definition”,何時才會出現(xiàn)這種錯誤?
  • typedef 是個好東西,用好它:熟悉它的基礎語法,每一種寫法的搭配代表什么含義,理解并應用它,很重要。
  • 認真對待每一個編譯器提示的 編譯警告:保不準這些警告哪天就把你帶入坑里,使用GCC的-Werror是個好選擇,把警告當錯誤處理,有助于你寫出更為嚴謹?shù)拇a。
  • GCC的默認編譯參數(shù):這個了解非常有必要,不然下次遇到好端端的代碼編譯不過,就沒轍了。

7 參考鏈接

  • 【經驗科普】實戰(zhàn)分析C工程代碼可能遇到的編譯問題及其解決思路
  • 【經驗總結】一文帶你了解C代碼到底是如何被編譯的
  • 【C語言之結構體】如何定義結構體并定義結構體變量
  • 【C語言之枚舉】如何定義枚舉并定義枚舉變量
  • 【C語言之typedef】typedef的基本用法

8 更多分享

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

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

【CSDN主頁:架構師李肯】

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

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

【GCC專欄】

信息安全專欄】

【RT-Thread開發(fā)筆記】

freeRTOS開發(fā)筆記】

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

審核編輯:湯梓紅

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

    關注

    180

    文章

    7575

    瀏覽量

    134092
  • GCC
    GCC
    +關注

    關注

    0

    文章

    105

    瀏覽量

    24781
  • 編譯
    +關注

    關注

    0

    文章

    646

    瀏覽量

    32671
  • definition
    +關注

    關注

    0

    文章

    5

    瀏覽量

    6951
收藏 人收藏

    評論

    相關推薦

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

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

    Linux 下GCC編譯

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

    ESP32C3編譯出現(xiàn)multiple definition of `g_log_level\'的原因?

    如題,一個原是ESP32的物聯(lián)網例程,在改為ESP32C3后,編譯出現(xiàn)multiple definition of `g_log_level\'不良。 [2/3] Linking CXX
    發(fā)表于 06-19 08:28

    使用gcc編譯優(yōu)化與不優(yōu)化問題

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

    g++ 編譯靜態(tài)庫文件時出現(xiàn)multiple definition of `__dso_handle'錯誤,請大神指點如何修改。

    g++ 編譯靜態(tài)庫文件時出現(xiàn)multiple definition of `__dso_handle'錯誤,請大神指點如何修改。 編譯指令:g++-std=c++11 -o water
    發(fā)表于 07-16 10:39

    【原創(chuàng)精選】RT-Thread征文精選技術文章合集

    默認的鏈接腳本【GCC編譯優(yōu)化系列】static與inline的區(qū)別與聯(lián)系【GCC編譯
    發(fā)表于 07-26 14:56

    求助,ESP32C3編譯出現(xiàn)multiple definition of `g_log_level'怎么解決?

    如題,一個原是ESP32的物聯(lián)網例程,在改為ESP32C3后,編譯出現(xiàn)multiple definition of `g_log_level'不良。[2/3] Linking CXX
    發(fā)表于 02-16 06:15

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

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

    淺談gcc編譯

    3.3 gcc編譯器 GNU CC(簡稱為gcc)是GNU項目中符合ANSI C標準的編譯系統(tǒng),能夠編譯用C、C++和Object C等語言
    發(fā)表于 10-18 13:48 ?0次下載

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

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

    GCC編譯優(yōu)化指南

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

    gcc編譯優(yōu)化系列】如何獲取gcc默認的鏈接腳本

    我們都知道在一般的嵌入式開發(fā)中,使用gcc編譯固件的一般流程是,先把所有的.c文件和.s文件編譯成.o文件,然后把所有的.o文件鏈接成一個elf文件,最后由elf文件導出bin文件。 那么在鏈接成
    的頭像 發(fā)表于 07-11 09:15 ?3055次閱讀

    GCC編譯優(yōu)化系列】實戰(zhàn)分析C代碼遇到的編譯問題及解決思路

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

    GCC編譯優(yōu)化系列】-specs=kernel.specs

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

    Keil MDK使用GCC編譯器的方法

    有網友問:Keil 編譯速度有點慢,它支持GCC編譯嗎?
    的頭像 發(fā)表于 03-24 09:57 ?2723次閱讀
    Keil MDK使用<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b>器的方法