RM新时代网站-首页

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

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

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

嵌入式物聯(lián)網(wǎng)開(kāi)發(fā) ? 來(lái)源:嵌入式物聯(lián)網(wǎng)開(kāi)發(fā) ? 作者:嵌入式物聯(lián)網(wǎng)開(kāi)發(fā) ? 2022-07-10 23:15 ? 次閱讀

1 前言

月初的時(shí)候我整理發(fā)出的技術(shù)文章:《【gcc編譯優(yōu)化系列】一文帶你了解C代碼到底是如何被編譯的》,已經(jīng)收到了好幾個(gè)朋友的點(diǎn)贊了;同時(shí),特別驚喜的是,還收到論壇元老級(jí)別的大佬 aozima推薦和打賞,真的倍感榮幸。

image-20211204230938978image-20211204230638489image-20211204230848078

這也從側(cè)面證實(shí)了,我選擇的這個(gè)【C代碼編譯】話題的確是不少人遇到的困惑,而我寫(xiě)的關(guān)于編譯問(wèn)題的文章也正好體現(xiàn)它的價(jià)值,所以我也想著盡快把C代碼編譯的第二篇文章輸出來(lái)了,也真心希望這份實(shí)戰(zhàn)分析能幫助到更多人解決他們遇到的各式各樣的編譯問(wèn)題。

事與愿違,最近事多,一拖稿就是3個(gè)星期,這不,直到現(xiàn)在(再過(guò)幾個(gè)小時(shí)就2022了)才發(fā)出來(lái)。

2 回顧

2.1 主要內(nèi)容

上一篇文章我也交代過(guò),第一篇文章側(cè)重于結(jié)合gcc編譯器把C代碼編譯的整個(gè)流程介紹清楚,第二章文章會(huì)側(cè)重于結(jié)合真實(shí)的代碼場(chǎng)景分析遇到什么樣的編譯問(wèn)題應(yīng)該怎么樣去解決。

一句話:上篇注重理論基礎(chǔ),本篇注重實(shí)戰(zhàn)演練 !

2.2 知識(shí)點(diǎn)回顧

再次強(qiáng)調(diào)下第一篇的理論基礎(chǔ):

C代碼編譯的步驟,需要經(jīng)歷預(yù)編譯、編譯、匯編、鏈接等幾個(gè)關(guān)鍵步驟,最后才能生成二進(jìn)制文件,而這個(gè)二進(jìn)制文件就是能被CPU識(shí)別并正確執(zhí)行指令的唯一憑證。

我重新補(bǔ)畫(huà)了一張圖,它基本覆蓋了一個(gè)C工程代碼在開(kāi)發(fā)階段的生命周期,本次的實(shí)戰(zhàn)演練也是主要圍繞著這張圖展開(kāi)。

image-20211204235822470

值得注意的是,本文所提及的【編譯問(wèn)題】主要在2/3/4/5/6這幾個(gè)環(huán)節(jié)。

3 實(shí)戰(zhàn)分析

下面我會(huì)就每個(gè)階段,結(jié)合案例指出需要注意的事項(xiàng)或者分析會(huì)遇到哪類問(wèn)題,以及這類問(wèn)題應(yīng)該如何去解決。

注意:下文中截取的代碼案例均來(lái)自RTT技術(shù)論壇。

3.1 代碼編寫(xiě)階段

在代碼編寫(xiě)階段,其實(shí)也沒(méi)多少好說(shuō)的,但還是有人會(huì)遇到編譯出錯(cuò)的問(wèn)題,為什么呢?

這里提幾點(diǎn)經(jīng)驗(yàn)之談:

  • 安裝IDE軟件或開(kāi)發(fā)工具,亦或建立workspace,盡量盡量盡量不要用中文,哪怕英文差一些,搞個(gè)拼音也好一些;
  • 如果是在Windows下編寫(xiě)、編譯代碼,強(qiáng)烈建議不要搞太深的目錄,你的workspace-path盡量路徑一些,不能保證每個(gè)編譯器對(duì)這種長(zhǎng)路勁目錄都支持得很好;
  • 代碼注釋也盡量別中文,因中文編碼問(wèn)題導(dǎo)致的代碼文件亂碼之后狼狽不堪的例子真的太多了;如果一定要用中文,大家一定要約定好編碼格式,GBK還是UTF-8,這里建議用UTF-8,關(guān)于它們的區(qū)別與聯(lián)系,推薦閱讀。
  • 代碼編寫(xiě)風(fēng)格盡量遵循規(guī)范,你的開(kāi)發(fā)團(tuán)隊(duì)要求怎么樣就怎么樣,這種沒(méi)有優(yōu)劣之分,有了規(guī)范就是需要遵守。

隨手抓幾個(gè)論壇中在代碼編寫(xiě)階段遇到編譯問(wèn)題的帖子看看:

Windows命令行限制導(dǎo)致的編譯問(wèn)題:本質(zhì)還是上面提及的路徑不用太長(zhǎng),目錄不要太深;在Windows下可以把命令行的內(nèi)容先寫(xiě)入一個(gè)文本文件中轉(zhuǎn)下,繞開(kāi)這個(gè)問(wèn)題,很多SDK的編譯流程在Windows下就是這么玩的,比較麻煩。

image-20211229131221787

中文編碼問(wèn)題:本質(zhì)還是少寫(xiě)中文注釋,哪怕蹩腳一點(diǎn)的英文,這不還有翻譯軟件嗎?確保語(yǔ)法沒(méi)問(wèn)題就行了。

image-20211229131413904

中文編碼相關(guān)的問(wèn)題,還不少勒:

image-20211229131524675

代碼編寫(xiě)規(guī)范的問(wèn)題:大家約定好了,就一起共同遵守吧!

image-20211229131647065

3.2 預(yù)編譯階段

預(yù)編譯階段要做的事情,參考我的上一篇文章,這里重點(diǎn)介紹幾種非常容易在預(yù)編譯階段遇到的編譯問(wèn)題。

為了更好地展示編譯錯(cuò)誤,我在下面提示編譯錯(cuò)誤的時(shí)候,都采用英文為主輔以中文的方式來(lái)描述。

3.2.1 No such file or directory (找不到某個(gè)文件或目錄)

這里舉幾個(gè)典型的例子:

例子1:?jiǎn)栴}的根源,頭文件未包含,可能還包含頭文件嵌套包含的問(wèn)題,比如頭文件A包含了頭文件B,報(bào)的是沒(méi)找到頭文件B,那么include頭文件A就能解決;

image-20211229134215028

例子2:?jiǎn)栴}還是出在include上面,這個(gè)頭文件也的確是存在的,但是編譯器報(bào)找不到這個(gè)頭文件,原因在于頭文件所在的目錄沒(méi)有在編譯器檢索頭文件的列表里面,這個(gè)檢索列表一般包括:系統(tǒng)級(jí)別的include目錄(linux下/usr/include、/usr/local/include這些),編譯器所在include目錄,最后就是用戶自定義目錄。指定這個(gè)目錄,有不同的方法,以gcc編譯為例,是需要在CFLAGS里面添加-Ixxx(I是大寫(xiě)的,xxx可以是相對(duì)路徑也可以是絕對(duì)路徑),具體可以參考下這篇文章。另外提一點(diǎn),如果你的工程中有同名的頭文件,一定要注意他們被搜索的順序,這是決定你的代碼究竟include哪個(gè)頭文件的唯一參考。

image-20211229134130895

例子3:這個(gè)問(wèn)題的本質(zhì)還是頭文件查找的問(wèn)題。

image-20211230122406739

例子4:這個(gè)本質(zhì)也是頭文件查找路勁的問(wèn)題,樓主通過(guò)把頭文件拷貝到指定路勁解決了的問(wèn)題,但比較好的解決方案,我認(rèn)為是將原頭文件的路徑添加進(jìn)頭文件檢索的路徑比較好。

image-20211230122632830

總結(jié)一下:出現(xiàn)No such file or directory的時(shí)候重點(diǎn)排查幾個(gè)方面:

  • 先確認(rèn)提示的這個(gè)文件是否真的不存在?如果文件存在,往下排查;
  • 如果文件是頭文件,那么首先應(yīng)該考慮的是頭文件所在的目錄是否在編譯器頭文件檢索的列表里面?
  • 如果文件是C文件,則需要排查下Makefile或CMake或sconc的配置對(duì)該C文件的添加情況。

3.2.2 宏定義的問(wèn)題

在C代碼中,可以說(shuō)宏定義真的是太常見(jiàn)了,可以說(shuō)是無(wú)處不在。

可以毫不客氣地說(shuō),沒(méi)有哪一個(gè)號(hào)稱精通C語(yǔ)言的大神玩宏定義玩得不6的。

在宏定義中,經(jīng)常遇到的編譯問(wèn)題就是,宏定義是如何展開(kāi)的?

宏定義展開(kāi)之后,對(duì)上下文代碼編譯的影響是什么?

搞清楚這個(gè),解決因宏定義引發(fā)的編譯問(wèn)題就一點(diǎn)都不難了。

欲知,如何查看宏定義展開(kāi)后的代碼原型,你可以嘗試跳到 4.2.2 章節(jié)提前了解。

另外,還有一種宏定義報(bào)錯(cuò)比較常見(jiàn)的就是某個(gè)宏定義定義在a.h頭文件中,但是引用該頭文件的C文件沒(méi)有包括該頭文件,導(dǎo)致編譯報(bào)錯(cuò);這種錯(cuò)誤,本質(zhì)是一個(gè)3.2.1提到的頭文件包含問(wèn)題。

下面就宏定義編譯報(bào)錯(cuò)的問(wèn)題,截取論壇中比較典型的錯(cuò)誤:

例子1:錯(cuò)誤的宏定義導(dǎo)致編譯失敗。

image-20211231125025460

例子2:這個(gè)本質(zhì)還是編譯器宏定義的問(wèn)題,引發(fā)的頭文件包含一系列的問(wèn)題。

image-20211231125313249

例子3:頭文件中缺少GCC宏定義的判斷,導(dǎo)致有些頭文件或宏定義沒(méi)有定義,進(jìn)而編譯報(bào)錯(cuò)。

image-20211231124818146

另外,在論壇的問(wèn)題里面檢索宏定義的時(shí)候,發(fā)現(xiàn)了這么一個(gè)問(wèn)題,留給大家討論討論。

image-20211231125432395

3.2.3 條件編譯的問(wèn)題

常用的條件編譯有:

  • #if 一般后面接一個(gè)宏定義表達(dá)式,當(dāng)表達(dá)式的值為非0時(shí),#if后面的代碼參與編譯,否則就不參與;
  • #else#if類似;
  • #elif 這是條件編譯的嵌套寫(xiě)法,支持多個(gè)條件編譯;
  • #idef 一般后面接一定宏定義,當(dāng)這個(gè)宏定義有被定義時(shí)(可能是頭文件中顯式地使用#define定義,也有可能是通過(guò)編譯選項(xiàng)傳遞進(jìn)去,比如gcc就支持-Dxxx來(lái)定義宏定義),后面的代碼將會(huì)參與編譯,否則就不參與;
  • #if defined(xxx) *這是#ifdef*的另一種寫(xiě)法,本質(zhì)是等價(jià)的;
  • #endif 表示條件編譯代碼的結(jié)束。

這里有幾點(diǎn)補(bǔ)充一下:

  • 一個(gè)宏定義xxx沒(méi)有被定義時(shí),它的值默認(rèn)是0,所以使用#if xxx的結(jié)果是false;
  • #ifdef#if defined(xxx)這種寫(xiě)法只管宏定義,是定義了還是沒(méi)有定義,而不管宏定義的值是多少;如果既要關(guān)注有沒(méi)有定義,又要關(guān)注定義的值是多少,請(qǐng)使用#if
  • 使用#if時(shí),后面的表達(dá)式是可以使用 &&||等邏輯運(yùn)算符的。

在這種條件編譯的預(yù)處理下,往往我們看到很多可裁剪、可移植的代碼都是使用這些手段來(lái)實(shí)現(xiàn)。

舉個(gè)rt-thread內(nèi)核組件代碼的例子:

/* 摘自rt-thread/components/finsh/finsh.h片段 */

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2010-03-22     Bernard      first version
 */
#ifndef __FINSH_H__
#define __FINSH_H__

#include 

#if defined(_MSC_VER)
    #pragma section("FSymTab$f",read)
#endif

typedef long (*syscall_func)(void);
#ifdef FINSH_USING_SYMTAB
#ifdef __TI_COMPILER_VERSION__
    #define __TI_FINSH_EXPORT_FUNCTION(f)  PRAGMA(DATA_SECTION(f,"FSymTab"))
#endif
#ifdef FINSH_USING_DESCRIPTION
#ifdef _MSC_VER
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc)      \
                const char __fsym_##cmd##_name[] = #cmd;            \
                const char __fsym_##cmd##_desc[] = #desc;           \
                __declspec(allocate("FSymTab$f"))                   \
                const struct finsh_syscall __fsym_##cmd =           \
                {                           \
                    __fsym_##cmd##_name,    \
                    __fsym_##cmd##_desc,    \
                    (syscall_func)&name     \
                };
#pragma comment(linker, "/merge:FSymTab=mytext")

#elif defined(__TI_COMPILER_VERSION__)
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc)      \
                __TI_FINSH_EXPORT_FUNCTION(__fsym_##cmd);           \
                const char __fsym_##cmd##_name[] = #cmd;            \
                const char __fsym_##cmd##_desc[] = #desc;           \
                const struct finsh_syscall __fsym_##cmd =           \
                {                           \
                    __fsym_##cmd##_name,    \
                    __fsym_##cmd##_desc,    \
                    (syscall_func)&name     \
                };

#else
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc)                      \
                const char __fsym_##cmd##_name[] RT_SECTION(".rodata.name") = #cmd;    \
                const char __fsym_##cmd##_desc[] RT_SECTION(".rodata.name") = #desc;   \
                RT_USED const struct finsh_syscall __fsym_##cmd RT_SECTION("FSymTab")= \
                {                           \
                    __fsym_##cmd##_name,    \
                    __fsym_##cmd##_desc,    \
                    (syscall_func)&name     \
                };

#endif
#else
#ifdef _MSC_VER
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc)      \
                const char __fsym_##cmd##_name[] = #cmd;            \
                __declspec(allocate("FSymTab$f"))                   \
                const struct finsh_syscall __fsym_##cmd =           \
                {                           \
                    __fsym_##cmd##_name,    \
                    (syscall_func)&name     \
                };
#pragma comment(linker, "/merge:FSymTab=mytext")

#elif defined(__TI_COMPILER_VERSION__)
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc)      \
                __TI_FINSH_EXPORT_FUNCTION(__fsym_##cmd);           \
                const char __fsym_##cmd##_name[] = #cmd;            \
                const struct finsh_syscall __fsym_##cmd =           \
                {                           \
                    __fsym_##cmd##_name,    \
                    (syscall_func)&name     \
                };

#else
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc)                      \
                const char __fsym_##cmd##_name[] = #cmd;                            \
                RT_USED const struct finsh_syscall __fsym_##cmd RT_SECTION("FSymTab")= \
                {                                                                   \
                    __fsym_##cmd##_name,                                            \
                    (syscall_func)&name                                             \
                };

#endif
#endif /* end of FINSH_USING_DESCRIPTION */
#endif /* end of FINSH_USING_SYMTAB */

如果是第一次接觸這種條件編譯的代碼,或者即便是老手,第一次閱讀這種N層條件編譯嵌套的代碼時(shí),分分鐘被勸退。

所以在這一小節(jié)講到的條件編譯問(wèn)題,不單單是代碼編譯的時(shí)候會(huì)遇到,你首先閱讀代碼就會(huì)遇到。

如果不幸你的代碼顯示在被條件編譯括起來(lái)的地方編譯報(bào)錯(cuò)了,那么你第一時(shí)間應(yīng)該要搞清楚你的代碼究竟哪個(gè)代碼塊是應(yīng)該被編譯的,因?yàn)楹芏鄷r(shí)候往往是因?yàn)槟愕暮甓x開(kāi)關(guān)沒(méi)有正確配置,而導(dǎo)致把不該編譯的代碼編譯進(jìn)去,自然就編譯報(bào)錯(cuò)了。

所以面對(duì)這種條件編譯的代碼,第一時(shí)間就是弄明白,并確認(rèn)被編譯器預(yù)處理展開(kāi)后的代碼,是不是你要的代碼?

怎么確認(rèn)呢?

你可以嘗試跳到 4.2.2 章節(jié)提前了解。

因篇幅問(wèn)題,這里僅截取一個(gè)論壇中關(guān)于條件編譯的問(wèn)題,以供大家參考。

image-20211231125942283

3.3 編譯階段

編譯階段,對(duì)于新手而言,可能也是一個(gè)編譯出錯(cuò)的重災(zāi)區(qū)。

為什么呢?

因?yàn)檫@里就涉及到C語(yǔ)言的基礎(chǔ)語(yǔ)法了,對(duì)基礎(chǔ)語(yǔ)法的不夠了解和理解不夠細(xì)致,就可能會(huì)出一些編譯錯(cuò)誤。

下面舉幾個(gè)比較典型的例子看看:

例子1:這個(gè)編譯報(bào)錯(cuò)的原因是結(jié)構(gòu)體原型升級(jí)了,名稱也改了,然后新的版本中某些成員變量沒(méi)有了,進(jìn)而引發(fā)語(yǔ)法報(bào)錯(cuò)。

image-20211231130814738

例子2:這個(gè)問(wèn)題的核心是沒(méi)有包含對(duì)應(yīng)的頭文件沒(méi)導(dǎo)致結(jié)構(gòu)體類型無(wú)法識(shí)別,進(jìn)而報(bào)了語(yǔ)法錯(cuò)誤。

image-20211231131039594

例子3:這個(gè)問(wèn)題本質(zhì)是因宏開(kāi)關(guān)不恰當(dāng)導(dǎo)致結(jié)構(gòu)體的成員變量被消失而引發(fā)的語(yǔ)法錯(cuò)誤問(wèn)題。

image-20211231131301541

3.4 匯編階段

匯編階段,相對(duì)來(lái)說(shuō),編譯出錯(cuò)的情況會(huì)少很多,為啥呢?

這是因?yàn)?,我們大部分時(shí)候都是寫(xiě)高級(jí)的C語(yǔ)言,很少部分是寫(xiě)匯編代碼,如果由C代碼到匯編代碼這個(gè)階段出錯(cuò),很有可能就是傳遞給編譯的參數(shù)沒(méi)搞對(duì)。

很典型的就是ARM處理器下,它的ARM指令集和Thumb指令集是不一樣的;如果你寫(xiě)的是C語(yǔ)言和匯編混合編程(很多底層代碼都有這種寫(xiě)法需求),那么你一定要搞清楚,你寫(xiě)的是ARM指令還是Thumb指令,對(duì)應(yīng)傳遞給編譯器的編譯選項(xiàng)是不一樣的。

下面舉幾個(gè)比較典型的例子看看:

例子1:這個(gè)應(yīng)該是匯編指令的問(wèn)題,問(wèn)題帖子中有給答復(fù)。

image-20211231131439254

例子2:不同編譯器直接的編譯轉(zhuǎn)換問(wèn)題,涉及到匯編指令,可以到評(píng)論席看看大家的答復(fù)。

image-20211231131557769

例子3:具體編譯器對(duì)匯編代碼的支持問(wèn)題,看評(píng)論席。

image-20211231131822560

3.5 鏈接階段

鏈接階段是整個(gè)完成編譯的關(guān)鍵一步,你寫(xiě)的所有C代碼,能不能完整串起來(lái),變成一個(gè)可執(zhí)行程序,就得看這一步,所以往往這一步也是編譯報(bào)錯(cuò)的重災(zāi)區(qū)。

這里羅列幾種常見(jiàn)的編譯鏈接錯(cuò)誤:

3.5.1 undefined reference to ‘xxx’

這個(gè)錯(cuò)誤真的真的太常見(jiàn)了,很多人一見(jiàn)這個(gè)就說(shuō)是“編譯報(bào)錯(cuò)”,其實(shí)準(zhǔn)確的術(shù)語(yǔ)應(yīng)該是“鏈接報(bào)錯(cuò)”。

它的錯(cuò)誤的核心就是:xxx函數(shù)(符號(hào))在你的所有C代碼(已經(jīng)被編譯成.o文件)和你加入?yún)⑴c鏈接的所有庫(kù)文件(包括靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù),當(dāng)然也包括標(biāo)準(zhǔn)C庫(kù)),都找不到xxx的實(shí)現(xiàn)。

明白了這一點(diǎn)之后,你就應(yīng)該知道如何解決這種鏈接報(bào)錯(cuò)的編譯問(wèn)題了。

舉幾個(gè)論壇中的問(wèn)題看看,真的就是重災(zāi)區(qū)。

image-20211231132530236

解決這類問(wèn)題,我的思路一般就以下幾步:

  • 確認(rèn)這個(gè)xxx符號(hào)是函數(shù)還是宏定義,還是變量;
  • 如果是宏定義,應(yīng)該就是你的宏定義沒(méi)被包含進(jìn)來(lái),導(dǎo)致宏定義沒(méi)有被展開(kāi);
  • 如果是變量或者函數(shù),確認(rèn)下這其中的代碼是否參與了編譯,具體可以看.i文件或者.o文件;
  • 還有一種情況,如果是庫(kù)函數(shù)(標(biāo)準(zhǔn)庫(kù)或者第三方庫(kù)),確認(rèn)下這個(gè)函數(shù)在哪個(gè)庫(kù),這個(gè)庫(kù)文件是否在你的鏈接列表里面,比如gcc編譯就是使用-lxxx指定鏈接的庫(kù)。

3.5.2 cannot find -lxxx

這種就表示xxx是一個(gè)庫(kù)文件,在鏈接的時(shí)候找不到它,一般解決這類問(wèn)題,可以從以下幾個(gè)方面考慮:

  • 首先確認(rèn)xxx庫(kù)文件有沒(méi)有搞錯(cuò)?在gcc編譯下,一個(gè)libxxx.a的靜態(tài)庫(kù)或lixxxx.so的動(dòng)態(tài)庫(kù),這里的庫(kù)名稱是xxx而不是libxxx.a或libxxx.so,新手往往容易忽略;
  • 確認(rèn)下庫(kù)檢索的路勁對(duì)不對(duì),比如一些第三方庫(kù),在gcc下要使用-Lxxx把你庫(kù)的路徑傳進(jìn)去才能找到你的庫(kù);
  • 檢查下有沒(méi)有庫(kù)函數(shù)遞歸依賴的問(wèn)題,以及庫(kù)函數(shù)鏈接順序的問(wèn)題,往往也容易出錯(cuò)。

3.5.3 multiple definition of ’xxx‘

這個(gè)錯(cuò)誤就是如其錯(cuò)誤提示的那樣,重復(fù)定義了,排查問(wèn)題的關(guān)鍵是找到那里重復(fù)定義了。

舉幾個(gè)典型例子看看:

例子1:函數(shù)重復(fù)定義錯(cuò)誤,評(píng)論席有答案。

image-20211231150346602

例子2:兩個(gè).c文件都有clear函數(shù),鏈接得時(shí)候都搞一起了,能不重復(fù)定義嗎?static函數(shù)了解下。

image-20211231151322387

例子3:main函數(shù)重復(fù)定義,這個(gè)還是比較少見(jiàn),見(jiàn)評(píng)論席。

image-20211231151459340

3.6 轉(zhuǎn)換階段

這個(gè)轉(zhuǎn)換階段一般出問(wèn)題的情況也很少,在使用GCC編譯的時(shí)候,這一步使用的objcopy命令,它的一般用法是:

生成bin文件

objcopy -O binary xxx.elf xxx.bin

生存hex文件

objcopy -O ihex xxx.elf xxx.hex

往往在實(shí)際工程項(xiàng)目中,還有添加-R選項(xiàng):

objcopy -O binary -R .eh_frame -R .init -R .fini -R .comment xxx.elf xxx.bin
objcopy -O ihex -R .eh_frame -R .init -R .fini -R .comment  xxx.elf xxx.hex

其中-R選項(xiàng)表示:去掉這些段。

$ objcopy -h
Usage: objcopy [option(s)] in-file [out-file]
 Copies a binary file, possibly transforming it in the process
 The options are:
  -R --remove-section        Remove section  from the output
     --remove-relocations    Remove relocations from section 

3.7 其他階段

下載運(yùn)行階段 -> 功能測(cè)試階段 -> 解BUG階段 -> 版本發(fā)布階段

本文對(duì)這些階段不做過(guò)多闡述,畢竟每個(gè)芯片平臺(tái)有不同的下載方式及調(diào)試運(yùn)行的方法,每個(gè)功能的測(cè)試方法也差異很大,每個(gè)人調(diào)試解決BUG的方法各有不同,各自為政,為我所用即可。

到了版本發(fā)布這一階段,基本就功能穩(wěn)定了,且已達(dá)到規(guī)劃的功能需求,開(kāi)始走軟件發(fā)布流程了,這也是每一個(gè)項(xiàng)目最期待能走到的階段。這里特別需要注意的是(血淚的教訓(xùn)):軟件發(fā)布一定要有規(guī)范的流程,且發(fā)布的代碼一定要能被追蹤到指定的提交記錄,否則出了問(wèn)題,你可能會(huì)欲哭無(wú)淚,還可能被各種DISS。

4 分享幾個(gè)經(jīng)驗(yàn)

4.1 分享幾個(gè)非常奇葩的編譯問(wèn)題

4.1.1 宏定義的這種寫(xiě)法

例子1:一個(gè)freeRTOS的宏定義的問(wèn)題,之前有寫(xiě)過(guò)一篇文章(【freeRTOS開(kāi)發(fā)筆記】記一次坑爹的freerTOS-v9.0.0升級(jí)到freeRTOS-v10.4.4),感興趣的可以一看。

image-20211231151651606

4.1.2 static和inline搞什么

例子2:static與inline修飾函數(shù)定義的問(wèn)題,可以參考這個(gè)(帖子](https://club.rt-thread.org/ask/question/431613.html),之前也針對(duì)這個(gè)問(wèn)題寫(xiě)過(guò)一篇文章(【gcc編譯優(yōu)化系列】static與inline的區(qū)別與聯(lián)系),感興趣可以一看。

image-20211231152503336

4.1.3 環(huán)境變量的鍋

例子3:環(huán)境變量引發(fā)的不可思議的問(wèn)題?看看這個(gè)問(wèn)題!環(huán)境變量的使用,請(qǐng)謹(jǐn)慎!

image-20211231153203455

4.1.4 身邊的例子

例子4:最后報(bào)一個(gè)我實(shí)際工作中,同事遇到的朋友,當(dāng)時(shí)排查了一會(huì)才發(fā)現(xiàn)端倪。

報(bào)的錯(cuò)誤是:multiple definition of ’xxx‘!

我當(dāng)時(shí)的排查思路也是按照上面羅列的幾點(diǎn)一個(gè)個(gè)排查,發(fā)現(xiàn)都不是那些。

奇怪呢!還能玩出花來(lái)?最后還是 4.2.2章節(jié)的方法,把鏈接的完整log輸出來(lái)一看,傻眼了!

居然有重復(fù)的.o文件添加到鏈接中,這不重復(fù)定義才怪呢!

下面簡(jiǎn)單復(fù)盤(pán)下當(dāng)時(shí)的場(chǎng)景,有則改之,無(wú)則加勉!

我們整個(gè)編譯構(gòu)建是基于Makefile來(lái)的,整體劃分了N個(gè)模塊,每個(gè)子模塊有自己獨(dú)立的Makefile, 該模塊下所有需要參與編譯的代碼會(huì)在Makefile中列出來(lái), 正常的情況下,類似這樣的寫(xiě)法:

SRC_C-y := ./src/a.c
SRC_C-y += ./src/b.c
SRC_C-y += ./src/c.c
SRC_C-y += ./src/d.c

這樣最后參與鏈接的.o文件就是:a.o b.o c.o d.o,編譯沒(méi)有問(wèn)題; 后面這哥們不知道怎么手誤碰還是怎么著,把冒號(hào)給改成了加號(hào)

SRC_C-y += ./src/a.c
SRC_C-y += ./src/b.c
SRC_C-y += ./src/c.c
SRC_C-y += ./src/d.c

結(jié)果最后參與鏈接的.o文件就是:a.o b.o c.o d.o ... a.o b.o c.o d.o ... 也就是這個(gè)模塊的.o文件都拷貝了一份,那當(dāng)然會(huì)報(bào)重復(fù)定義?。?/p>

后面進(jìn)一步跟進(jìn),發(fā)現(xiàn)構(gòu)建流程有點(diǎn)缺陷,每個(gè)子模塊的Makefile會(huì)被加載兩次, 這就導(dǎo)致了如果按第二種寫(xiě)法,SRC_C-y就會(huì)變成:./src/a.c ./src/b.c ./src/c.c ./src/d.c ./src/a.c ./src/b.c ./src/c.c ./src/d.c 但是按第一種寫(xiě)法就不會(huì),這個(gè)就需要了解下Makefile中 :=+= 的語(yǔ)法區(qū)別了,之前寫(xiě)過(guò)一篇文章,感興趣的可以了解下。

總之,當(dāng)時(shí)覺(jué)得這個(gè)問(wèn)題真的有點(diǎn)狗血,大家引以為戒。

4.2 分享幾個(gè)常用于排查編譯問(wèn)題的方法

4.2.1 打開(kāi)編譯過(guò)程的完整log輸出

為什么要這么做?

因?yàn)檫@樣你能看到你的編譯構(gòu)建環(huán)境傳遞給編譯器的具體細(xì)節(jié),從而了解更多編譯器的行為。

4.2.1.1 Makefile構(gòu)建環(huán)境下

一般做編譯構(gòu)建的時(shí)候,為了保持編譯log輸出的整潔性,都會(huì)把編譯的完整輸出默認(rèn)關(guān)閉,你能看到的就是類似這樣的:

    CC    components/mqtt/src/impl/MQTTConnectClient.o
    CC    components/mqtt/src/impl/MQTTDeserializePublish.o
    CC    components/mqtt/src/impl/MQTTPacket.o
    CC    components/mqtt/src/impl/MQTTSerializePublish.o
    CC    components/mqtt/src/impl/MQTTSubscribeClient.o
    CC    components/mqtt/src/impl/MQTTUnsubscribeClient.o
    CC    components/mqtt/src/impl/iotx_mqtt_client.o
    CC    components/mqtt/src/mqtt_api.o

甚至連CC是哪種編譯器你都看不到,可能是gcc,也可能是ARMCC,也可能是其他。

那么如何打開(kāi)編譯log的完整輸出呢?你可以嘗試下如下命令,在輸入make的時(shí)候,添加一下控制變量:

make V=1
或
make VERBOSE=1

這樣,你看到的編譯輸出就是非常完整的,具體到你用了哪個(gè)編譯器,傳入了哪些參數(shù),一看便知,只不過(guò)log是真的多而長(zhǎng),考驗(yàn)?zāi)愕膶?duì)log信息的檢索能力的時(shí)候來(lái)了。

"arm-none-eabi-gcc" -c -MD  -DMCU_FAMILY="mcu_xxx"  -DSYSINFO_PRODUCT_MODEL="XXX_xxx" -DSYSINFO_DEVICE_NAME="xxx"   -DuECC_PLATFORM=uECC_arch_other -mcpu=arm968e-s -march=armv5te -mthumb -mthumb-interwork -mlittle-endian -w -save-temps=obj -DCFG_OS_FREERTOS=1 -DWIFI_BLE_COEXIST -DBK_DEBUG_UART=BK_UART_1 -DBK_CLI_UART=BK_UART_1 -DBK_CLI_ENABLE=0 -DUSR_CLI_MAX_COMMANDS=96 -DBLE_5_0                 -DEN_LONG_MTU -DEN_COMBO_NET -DEN_AUTH     -ggdb -Os  -Wall -Wfatal-errors -fsigned-char -ffunction-sections -fdata-sections -fno-common -std=gnu11  -DPLATFORM="xxx"  -include /xxx/application/bbb/xxxos_config.h  -Wall -Werror -Wno-unused-variable -Wno-unused-parameter -Wno-implicit-function-declaration -Wno-type-limits -Wno-sign-compare -Wno-pointer-sign -Wno-uninitialized -Wno-return-type -Wno-unused-function -Wno-unused-but-set-variable -Wno-unused-value -Wno-strict-aliasing -Wall -Werror -Wno-unused-variable -Wno-unused-parameter -Wno-implicit-function-declaration -Wno-type-limits -Wno-sign-compare -Wno-pointer-sign -Wno-uninitialized -Wno-return-type -Wno-unused-function -Wno-unused-but-set-variable -Wno-unused-value -Wno-strict-aliasing -Wall -Werror -Wno-unused-variable -Wno-unused-parameter -Wno-implicit-function-declaration -Wno-type-limits -Wno-sign-compare -Wno-pointer-sign -Wno-uninitialized -Wno-return-type -Wno-unused-function -Wno-unused-but-set-variable -Wno-unused-value -Wno-strict-aliasing -I/xxx/platform/mcu/xxx/bk_sdk/config -I/xxx/platform/mcu/xxx/bk_sdk/release -I/xxx/platform/mcu/xxx/bk_sdk/xxx/func/ble_wifi_exchange -I/xxx//components/wireless/bluetooth/ble/host/profile -I/xxx//include/wireless/bluetooth/blemesh -I/xxx//include/network/coap -I/xxx//include/network/hal -I/xxx//include/network/http -I/xxx//include/network/lwm2m -I/xxx//include/network/umesh -I/xxx//include/network/athost -I/xxx//include/network/sal -I/xxx//include/network/netmgr -I/xxx//include/network/rtp -I/xxx//include/utility/yloop -DBUILD_BIN -DCLI_CONFIG_SUPPORT_BOARD_CMD=1 -DCONFIG_xxxos_CLI_BOARD -DCONFIG_xxxos_UOTA_BREAKPOINT -DCONFIG_xxxos_CLI_STACK_SIZE=4096 -DDISABLE_SECURE_STORAGE=1 -DCFG_I2C1_ENABLE=1 -Dxxxos_LOOP -DINFRA_COMPAT -DINFRA_MD5 -DINFRA_NET -DINFRA_SHA256 -DINFRA_TIMER -DINFRA_STRING -Dxxxos_COMP_CLI -Dxxxos_COMP_KV -DMBEDTLS_CONFIG_FILE="mbedtls_config.h" -DCONFIG_HTTP_SECURE=1 -DCOAP_SERV_MULTITHREAD -Dxxxos_COMP_VFS -D__FILENAME__='"mem.c"'  -o /xxx/out/bbb/bbb@xxx/modules/platform/mcu/xxx/bk_sdk/xxx/func/lwip_intf/lwip-2.0.2/src/core/mem.o /xxx/platform/mcu/xxx/bk_sdk/xxx/func/lwip_intf/lwip-2.0.2/src/core/mem.c 

這個(gè)輸出log是gcc的,所以得對(duì)gcc的編譯參數(shù)有所了解才行,比如-Dxxx表示宏定義,-Ixxx表示頭文件搜索目錄等等。

注意:make加V=1或VERBOSE=1,是一般寫(xiě)得比較好的Makefile都會(huì)這么做,但不代表每個(gè)寫(xiě)Makefile的人都會(huì)這么做。

4.2.1.2 CMake構(gòu)建環(huán)境下

方法一

與Makefile類似,CMake也有完整輸出的開(kāi)關(guān),在輸入make之后,增加一個(gè)控制變量:

make VERBOSE=1

不加VERBOSE=1的效果是:

[ 16%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_import.c.o
[ 16%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_init.c.o
[ 17%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_init_copy.c.o
[ 17%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_init_multi.c.o
[ 17%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_init_set.c.o
[ 17%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_init_set_int.c.o
[ 18%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_init_size.c.o
[ 18%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_invmod.c.o
[ 18%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_invmod_slow.c.o
[ 18%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_is_square.c.o
[ 19%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_jacobi.c.o
[ 19%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_karatsuba_mul.c.o
[ 19%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_karatsuba_sqr.c.o
[ 20%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_kronecker.c.o
[ 20%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_lcm.c.o
[ 20%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_lshd.c.o
[ 20%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_mod.c.o
[ 21%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_mod_2d.c.o
[ 21%] Building C object src/CMakeFiles/yyy.dir/xxx/bn_mp_mod_d.c.o

加上VERBOSE=1的效果:

[  3%] Building C object src/CMakeFiles/yyy.dir/yyy/bncore.c.o
cd /yyy/build/x86_release/src && /usr/bin/gcc -DENCRYPTO_MODE=AES -DHAL_SHIELD=0 -I/sysroot/usr/include -I/yyy/inc -I/yyy/inc/hal -I/yyy/src/hal/9x07/linux -I/yyy/inc/hal/log -I/yyy/inc/hal/crypto -I/yyy/inc/hal/crypto/tommath -I/yyy/inc/hal/crypto/skb -I/yyy/inc/hal/asn1 -I/yyy/inc/uicc_framework -I/yyy/inc/uicc_framework/apdu -I/yyy/inc/uicc_framework/channel -I/yyy/inc/uicc_framework/comm -I/yyy/src/uicc_framework/comm -I/yyy/inc/uicc_framework/dispatcher -I/yyy/inc/uicc_framework/profile -I/yyy/inc/uicc_framework/filesystem -I/yyy/inc/uicc_framework/nvm -I/yyy/inc/uicc_framework/utils -I/yyy/inc/uicc_framework/ppi -I/yyy/inc/uicc_framework/tf -I/yyy/inc/applet -I/yyy/inc/applet/common -I/yyy/inc/applet/common/util -I/yyy/inc/applet/common/uicc -I/yyy/inc/applet/ecasd -I/yyy/inc/applet/isdp -I/yyy/inc/applet/usim -I/yyy/inc/applet/isdr -I/yyy/inc/applet/nusim  -Wall -pthread -fPIC -Wno-missing-braces -s -m32 -Werror -Wno-unused-function -Wno-unused-variable -Wno-unused-value -O2 -Os -Wmissing-prototypes -Wstrict-prototypes -DDEBUG=0 -I/yyy/build/x86_release   -o CMakeFiles/yyy.dir/yyy/bncore.c.o   -c /yyy/src/yyy/bncore.c

其實(shí)CMake最后的本質(zhì)就是Makefile。

方法二

CMakeLists.txt配置文件的相應(yīng)位置中,新增以下設(shè)置項(xiàng),也可以達(dá)到上面的效果。

  1. set ( CMAKE_VERBOSE_MAKEFILE on )

注意:有了這個(gè)配置之后呢,就只需要輸入make了,但是它的不好之處就是,你不想完整輸出的時(shí)候,你還得去改CMakeLists.txt配置文件,使用上沒(méi)有那么方便。所以我個(gè)人推薦使用方法一。

4.2.1.3 scons構(gòu)建環(huán)境下

這個(gè)構(gòu)建方式也是RTT支持的,我們可以看下它的編譯完整輸出是怎么樣的:

scons --verbose

不加控制項(xiàng),輸出如下:

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: building associated VariantDir targets: build
CC build/kernel/components/dfs/src/dfs.o
CC build/kernel/components/dfs/src/dfs_file.o
CC build/kernel/components/dfs/src/dfs_fs.o
CC build/kernel/components/dfs/src/dfs_posix.o
CC build/kernel/components/drivers/i2c/i2c-bit-ops.o
CC build/kernel/components/drivers/i2c/i2c_core.o
CC build/kernel/components/drivers/i2c/i2c_dev.o
CC build/kernel/components/drivers/misc/pin.o
CC build/kernel/components/drivers/mtd/mtd_nand.o
CC build/kernel/components/drivers/mtd/mtd_nor.o
CC build/kernel/components/drivers/rtc/rtc.o
CC build/kernel/components/drivers/rtc/soft_rtc.o
CC build/kernel/components/drivers/sdio/block_dev.o
CC build/kernel/components/drivers/sdio/mmc.o
CC build/kernel/components/drivers/sdio/mmcsd_core.o
CC build/kernel/components/drivers/sdio/sd.o
CC build/kernel/components/drivers/sdio/sdio.o
CC build/kernel/components/drivers/serial/serial.o
CC build/kernel/components/drivers/spi/sfud/src/sfud.o
CC build/kernel/components/drivers/spi/sfud/src/sfud_sfdp.o
CC build/kernel/components/drivers/spi/spi_core.o

添加verbose控制項(xiàng)之后的輸出:

arm-none-eabi-gcc -o build/kernel/components/finsh/msh_file.o -c -march=armv7-a -marm -msoft-float -Wall -g -gdwarf-2 -O0 -DHAVE_CCONFIG_H -D__RTTHREAD__ -DRT_USING_NEWLIB -I. -Idrivers -Iapplications -I/yyyrt-thread/include -I/yyyrt-thread/libcpu/arm/common -I/yyyrt-thread/libcpu/arm/cortex-a -I/yyyrt-thread/components/cplusplus -I/yyyrt-thread/components/drivers/include -I/yyyrt-thread/components/drivers/spi -I/yyyrt-thread/components/drivers/spi/sfud/inc -I/yyyrt-thread/components/net/sal_socket/include -I/yyyrt-thread/components/net/sal_socket/include/socket -I/yyyrt-thread/components/net/sal_socket/impl -I/yyyrt-thread/components/net/sal_socket/include/dfs_net -I/yyyrt-thread/components/net/sal_socket/include/socket/sys_socket -I/yyyrt-thread/components/net/netdev/include -I/yyyrt-thread/components/net/lwip-2.1.2/src -I/yyyrt-thread/components/net/lwip-2.1.2/src/include -I/yyyrt-thread/components/net/lwip-2.1.2/src/arch/include -I/yyyrt-thread/components/net/lwip-2.1.2/src/include/netif -I/yyyrt-thread/components/libc/compilers/common -I/yyyrt-thread/components/libc/compilers/gcc/newlib -I/yyyrt-thread/components/libc/posix/src -I/yyyrt-thread/components/libc/posix/pthreads -I/yyyrt-thread/components/libc/posix/signal -I/yyyrt-thread/components/libc/posix/termios -I/yyyrt-thread/components/libc/posix/aio -I/yyyrt-thread/components/libc/posix/getline -I/yyyrt-thread/components/lwp -I/yyyrt-thread/components/dfs/include -I/yyyrt-thread/components/dfs/filesystems/devfs -I/yyyrt-thread/components/dfs/filesystems/elmfat -I/yyyrt-thread/components/dfs/filesystems/ramfs -I/yyyrt-thread/components/dfs/filesystems/romfs -I/yyyrt-thread/components/finsh -I/yyyrt-thread/examples/utest/testcases/kernel /yyyrt-thread/components/finsh/msh_file.c

至于具體的編譯輸出的log啥含義,還得看具體的編譯器。gcc是我們常用的,這個(gè)還是要熟悉。

4.2.2 打開(kāi)編譯過(guò)程的中間文件的輸出

4.2.2.1 gcc編譯環(huán)境下

這個(gè)選項(xiàng)我在上一篇文章也提到過(guò),這里用一個(gè)小結(jié)再簡(jiǎn)單介紹下,對(duì)于排查編譯問(wèn)題以及排查匯編代碼級(jí)的性能問(wèn)題,用過(guò)都說(shuō)好。

這個(gè)參數(shù)就是-save-temps=obj,我們來(lái)實(shí)踐下:

gcc/gcc_helloworld$ ./build.sh clean   
Clean build done !
gcc/gcc_helloworld$ 
gcc/gcc_helloworld$ ls
build.sh  main.c  README.md  sub.c  sub.h
gcc/gcc_helloworld$ 
gcc/gcc_helloworld$ ./build.sh allinone
gcc -c main.c -o main.o -save-temps=obj
gcc -c sub.c -o sub.o -save-temps=obj
gcc main.o sub.o -o test
gcc/gcc_helloworld$ 
gcc/gcc_helloworld$ ls
build.sh  main.c  main.i  main.o  main.s  README.md  sub.c  sub.h  sub.i  sub.o  sub.s  test

就這樣,.i文件、.s文件、以及.o文件都同時(shí)輸出來(lái)了。

如果工程中,只有一個(gè)main.c的源文件的話,還可以這樣就一步搞定。

gcc main.c -o test -save-temps=obj

這些.i文件、.s文件、以及.o文件,我們稱之為中間臨時(shí)文件

總結(jié)

  • 查看預(yù)處理之后的代碼文件,請(qǐng)看.i文件 (再?gòu)?fù)雜的條件編譯你也不怕,在這個(gè)文件里面,全部暴露原型
  • 查看C代碼生成的對(duì)應(yīng)匯編代碼,請(qǐng)看.s文件
  • 查看C代碼對(duì)應(yīng)的符號(hào)表信息,請(qǐng)看.o文件

4.2.2.2 KEIL構(gòu)建環(huán)境下

其實(shí)KEIL里面也有對(duì)應(yīng)的設(shè)置項(xiàng),我手上沒(méi)有現(xiàn)成的IDE環(huán)境,我網(wǎng)上找了一篇文章,介紹得還不錯(cuò),大家可以參考下。

Listing

4.3 友情提醒

生命有限,有效編碼。

請(qǐng)尊重你自己寫(xiě)的每一行代碼。

請(qǐng)保證你的代碼編譯永遠(yuǎn)都是:0 warning 0 error 0 bug

5 新年祝福

愿大家新的一年,如虎添翼,展翅高飛,2022,逐夢(mèng)起航!

6 更多分享

歡迎關(guān)注我的github倉(cāng)庫(kù)01workstation,日常分享一些開(kāi)發(fā)筆記和項(xiàng)目實(shí)戰(zhàn),歡迎指正問(wèn)題。

同時(shí)也非常歡迎關(guān)注我的專欄,有問(wèn)題的話,可以跟我討論,知無(wú)不答,謝謝大家。

審核編輯 黃昊宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7604

    瀏覽量

    136686
  • GCC
    GCC
    +關(guān)注

    關(guān)注

    0

    文章

    107

    瀏覽量

    24835
  • 編譯
    +關(guān)注

    關(guān)注

    0

    文章

    657

    瀏覽量

    32852
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    【Linux編程】如何使用gcc編譯代碼時(shí)輸出map文件?

    【Linux編程】如何使用gcc編譯代碼時(shí)輸出map文件?
    的頭像 發(fā)表于 08-15 14:08 ?8204次閱讀
    【Linux編程】如何使用<b class='flag-5'>gcc</b><b class='flag-5'>編譯</b>源<b class='flag-5'>代碼</b>時(shí)輸出map文件?

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

    GCC編譯優(yōu)化系列】前后編譯的兩個(gè)版本固件bin大小不一樣,怎么辦?
    的頭像 發(fā)表于 09-09 09:01 ?4691次閱讀
    【<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編譯

    .c 文件,直接使用 gcc [目標(biāo)文件] -o [想要生產(chǎn)的文件名字] 那在遇到多個(gè) .c 文件的時(shí)候如何處理呢? 實(shí)際上還是使用上面這條指令,其中的 [目標(biāo)文件] 是可以為多個(gè)的
    的頭像 發(fā)表于 09-11 15:18 ?2620次閱讀
    Linux 下<b class='flag-5'>GCC</b>的<b class='flag-5'>編譯</b>

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

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

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

    編譯優(yōu)化系列】使用GCC如何把C文件編譯成可執(zhí)行文件【GCC
    發(fā)表于 07-26 14:56

    AVR系列單片機(jī)GCC免費(fèi)編譯工具

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

    淺談gcc編譯

    3.3 gcc編譯器 GNU CC(簡(jiǎn)稱為gcc)是GNU項(xiàng)目中符合ANSI C標(biāo)準(zhǔn)的編譯系統(tǒng),能夠編譯
    發(fā)表于 10-18 13:48 ?0次下載

    Linux下C/C++編譯gcc使用指南

    1.gcc包含的c/c++編譯gcc,cc與c++,g++
    發(fā)表于 11-02 10:59 ?0次下載

    Linux上安裝GCC3.4.0編譯器過(guò)程

    2004年4月20日最新版本的GCC編譯器3.4.0發(fā)布了。目前,GCC可以用來(lái)編譯C/C++、
    發(fā)表于 11-02 11:18 ?0次下載

    常見(jiàn)gcc編譯警告整理以及解決方法

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

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

    源碼包在每個(gè)子文件夾中都有 Makefile 文件,真是一件很累人的事!  CC 與 CXX  這是 CC++ 編譯器命令。默認(rèn)值一般是 "gcc" 與 "g++"。這個(gè)變量本來(lái)
    發(fā)表于 04-02 14:36 ?537次閱讀

    GCC編譯C語(yǔ)言程序的過(guò)程是怎么樣的

    使用GCCC語(yǔ)言源代碼文件生成可執(zhí)行文件的過(guò)程,需要經(jīng)歷四個(gè)的步驟:預(yù)處理(Preprocessing)編譯(Compilation)匯編(Assembly)鏈接(Linking)
    的頭像 發(fā)表于 02-18 11:47 ?4090次閱讀

    GCC編譯優(yōu)化系列】使用GCC如何把C文件編譯成可執(zhí)行文件

    自參加RTT論壇的【問(wèn)答有獎(jiǎng)】活動(dòng)以來(lái),回答了300+問(wèn)題,期間我特意去檢索過(guò)【編譯】相關(guān)的問(wèn)題,從下圖可以看得出,編譯問(wèn)題真的是很常見(jiàn)的問(wèn)題類型,不管你是新手還是老手,多多少少都遇到過(guò)奇奇怪怪的
    的頭像 發(fā)表于 07-11 09:10 ?6231次閱讀
    【<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'>GCC</b>如何把<b class='flag-5'>C</b>文件<b class='flag-5'>編譯</b>成可執(zhí)行文件

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

    GCC編譯優(yōu)化系列】這種讓人看不懂的multiple-definition真的有點(diǎn)讓人頭疼
    的頭像 發(fā)表于 07-11 09:26 ?7120次閱讀
    【<b class='flag-5'>GCC</b><b class='flag-5'>編譯</b><b class='flag-5'>優(yōu)化</b><b class='flag-5'>系列</b>】multiple-definition

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

    GCC編譯優(yōu)化系列GCC編譯鏈接時(shí)候--specs=kernel.specs鏈接屬性究竟是個(gè)
    的頭像 發(fā)表于 07-11 09:25 ?3444次閱讀
    【<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
    RM新时代网站-首页