C/C++编程心得
http://antkillerfarm.github.io/
參考資料
http://www.cplusplus.com/
這個(gè)網(wǎng)站可以查到C++的標(biāo)準(zhǔn)庫(kù)的用法。
頭文件被多個(gè)源文件引用
在頭文件中定義字符串時(shí),如果該頭文件會(huì)被多個(gè)源文件引用的話,字符串必須被定義為const,否則會(huì)有重定義錯(cuò)。當(dāng)然最好在頭文件中只放聲明,不要放定義。
使用諸如
#ifndef _COMMON_STRING_ #define _COMMON_STRING_ ... ... ... #endif解決不了這個(gè)問題。因?yàn)檫@段代碼解決的是同一個(gè)源文件重復(fù)包含某個(gè)頭文件的問題。而這里的問題是不同的源文件包含同一頭文件時(shí),產(chǎn)生的重復(fù)定義的問題。const關(guān)鍵字保證了同一標(biāo)識(shí)符只會(huì)定義一次。
指針數(shù)組與數(shù)組指針
int (*p)[10] 先是 *p起作用 ,聲明一個(gè)指針(p肯定是指針了),然后是[10](數(shù)組說(shuō)明),則p是指向10個(gè)整形元素組成的數(shù)組的指針?!獢?shù)組指針。
若是int *p[10] 則首先是[ ]起用,它是一個(gè)數(shù)組了。然后是*,所以這個(gè)數(shù)組元素是指針型的。——指針數(shù)組。
引申一下
int a[10];printf("%x\n",&a);printf("%x\n",a);可以看到&a和a的值是相等的,但含義不同。a相當(dāng)于int *p,而&a相當(dāng)于int (*p)[10]。
類似的
int (*p[])(int)函數(shù)指針數(shù)組。
int (*p())[]返回?cái)?shù)組指針的函數(shù)。
int *p()[]字面上可以解釋為返回指針數(shù)組的函數(shù),不過函數(shù)是不能返回?cái)?shù)組的。
int *(*a())()這是一個(gè)函數(shù),它沒有參數(shù),它的返回值是一個(gè)函數(shù)指針,這個(gè)指針指向的函數(shù),也沒有參數(shù),且返回值是int型的指針。
嵌入式程序員應(yīng)知道的幾個(gè)基本問題
http://linux.chinaitlab.com/c/713810.html
深入理解C++中的mutable關(guān)鍵字
http://dev.yesky.com/393/3007393.shtml
UNREFERENCED_PARAMETER的作用
http://blog.csdn.net/apunix/archive/2008/01/14/2043945.aspx
sizeof進(jìn)行結(jié)構(gòu)體大小的判斷
http://blog.csdn.net/van150/archive/2005/12/05/544454.aspx
VC的默認(rèn)規(guī)則基本如上所示,gcc的默認(rèn)規(guī)則就是4字節(jié)對(duì)齊。
可以比較一下
struct s {char a;double b;int c; }與
struct s {char a;int b;double c; }在各個(gè)平臺(tái)上的sizeof值。
附帶說(shuō)一下如果看到以下的代碼片段,也就不要覺得驚奇了。
typedef enum _XXX {XXX_0 = 0,XXX_1 = 1,XXX_FORCE_DWORD = 0x7FFFFFFF/* 編譯器對(duì)齊 */ }XXX;字節(jié)對(duì)齊的設(shè)定
1)編譯器選項(xiàng)指定
VC:/Zp
gcc:-fpack-struct
2)代碼指定
VC和gcc都可以用#pragma pack(4)
gcc還可以用__attribute__((packed))
Java中創(chuàng)建對(duì)象的時(shí)候代碼執(zhí)行順序(這個(gè)問題C++應(yīng)該也存在)
http://jacob777.blog.sohu.com/106426297.html
宏展開函數(shù)的小技巧
有的時(shí)候,會(huì)使用宏展開的方式定義“偽”函數(shù)。
這些“偽”函數(shù)有以下特征:
不是真正的函數(shù),而是接受參數(shù)的宏。
形式上與普通函數(shù)相同。
為了實(shí)現(xiàn)這個(gè)功能,教科書上給出的做法是這樣的:
#define st(x) do { x } while (0) #define HAL_DMA_SET_SOURCE( pDesc, src ) \st( \pDesc->srcAddrH = (uint8)((uint16)(src) >> 8); \pDesc->srcAddrL = (uint8)( (uint16)(src) & 0xFF ); \)但是do { x } while (0)在有些編譯器上會(huì)報(bào)warning。最近看Ti的代碼的時(shí)候,看到了一種更好的辦法:
#define st(x) do { x } while (__LINE__ == -1)C++ 11的新特性
我是在2003年以后學(xué)習(xí)C++的,后來(lái)直到2009年以前,C++都是我的主要工作語(yǔ)言。但由于本人先學(xué)的C語(yǔ)言,所以編程的思想一直是函數(shù)式的。完成一項(xiàng)任務(wù),無(wú)論用C、C++、Java、C#,還是Matlab、Python,風(fēng)格都是差不多的。這也是后來(lái)我對(duì)GTK情有獨(dú)鐘的一個(gè)重要的原因。
總的來(lái)說(shuō),我對(duì)C++用的多,但理解的卻不深。只對(duì)單繼承、成員函數(shù)的封裝、訪問之類的概念有一定的認(rèn)識(shí)和使用。多重繼承、模板會(huì)看不會(huì)寫。更不必提C++ 03和C++ 11的新特性了。最近因?yàn)檠芯緾ocos2d-x,而接觸到這些新特性,頗有些感覺到自己已經(jīng)是老古董了.
不過好在Java語(yǔ)言用的還可以,大部分的C++新特性,學(xué)起來(lái)倒也難不倒我。
1.auto關(guān)鍵字
C++11中引入的auto主要有兩種用途:自動(dòng)類型推斷和返回值占位。auto在C++98中,標(biāo)識(shí)臨時(shí)變量的語(yǔ)義,由于使用極少且多余,在C++11中已被刪除。前后兩個(gè)標(biāo)準(zhǔn)的auto,完全是兩個(gè)概念,這點(diǎn)尤其需要注意。
http://blog.csdn.net/huang_xw/article/details/8760403
2.引入nullprt
http://blog.csdn.net/huang_xw/article/details/8764346
3.仿函數(shù)、lambda表達(dá)式和閉包
http://www.cnblogs.com/npbool/p/3434757.html
4.C++、Java和C#的lambda表達(dá)式的格式
C++
auto func = [=](int x, int y)->int {return x * y;};
Java
Runnable r1 = (int x, int y) -> { return x * y; }
C#
Func<int, int, int> towParams = (x, y) => x * y
添加廢棄提示
有些類庫(kù)出于兼容性的考慮,仍然保留了對(duì)舊函數(shù)的支持。但是繼續(xù)使用這些函數(shù),顯然不是作者的初衷。因此,有必要在編譯時(shí),給出廢棄的提示。
近日瀏覽cocos2d-x v3的代碼,發(fā)現(xiàn)可以這樣做:
宏定義:
#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) #define CC_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) #elif _MSC_VER >= 1400 //vs 2005 or higher #define CC_DEPRECATED_ATTRIBUTE __declspec(deprecated) #else #define CC_DEPRECATED_ATTRIBUTE #endif宏使用:
CC_DEPRECATED_ATTRIBUTE static TextureCache * getInstance();
關(guān)于引用的一個(gè)常見錯(cuò)誤
class Base {public:void something(Base& b){} };int main() {Base b;b.something(Base());return 0; }上面的代碼在編譯時(shí),會(huì)出現(xiàn)如下錯(cuò)誤信息:
abc.cpp:12:20: error: no matching function for call to ‘Base::something(Base)’ abc.cpp:12:20: note: candidate is: abc.cpp:6:7: note: void Base::something(Base&) abc.cpp:6:7: note: no known conversion for argument 1 from ‘Base’ to ‘Base&’這是由于Base()生成的是臨時(shí)變量,將之賦值給一個(gè)non-const的引用是不行的。
解決方法是
void something(const Base& b){}
可以參看下文:
http://stackoverflow.com/questions/20247525/about-c-conversion-no-known-conversion-for-argument-1-from-some-class-to
常用libc實(shí)現(xiàn)
libc是C語(yǔ)言標(biāo)準(zhǔn)庫(kù)的簡(jiǎn)稱,它有多種實(shí)現(xiàn)。除了最常用的gcc自帶的glibc之外,還有musl、uClibc、dietlibc等。
http://www.etalabs.net/compare_libcs.html
這個(gè)網(wǎng)址是以上4種libc實(shí)現(xiàn)的比較結(jié)果。從結(jié)果來(lái)看,musl比較有投資價(jià)值。實(shí)際上,最近(2015.5)的OpenWrt項(xiàng)目就已經(jīng)將libc由uClibc改為musl。我也是因?yàn)檫@個(gè)原因,才知道m(xù)usl的。
當(dāng)然這個(gè)表并不完整,其他的libc可以參見:
https://en.wikipedia.org/wiki/C_standard_library
數(shù)組定義時(shí)的賦值方法
struct mtd_partition {const char *name; /* identifier string */uint64_t size; /* partition size */uint64_t offset; /* offset within the master MTD space */ };static struct mtd_partition parts[] = {{name: "boot", offset: 0, size:0x500000,},{name: "setting", offset: 0x500000, size:0x300000,},{name: "linux", offset: 0x800000, size:0x500000,}, {name: "config", offset: 0xd00000, size:0x100000,}, {name: "rootfs", offset: 0xe00000, size:0x3200000,},{name: "app", offset: 0x4e00000, size:0x800000,},};static struct resource pxa27x_resource_ohci[] = {[0] = {.start = 0x4C000000,.end = 0x4C00ff6f,.flags = IORESOURCE_MEM,},[1] = {.start = IRQ_USBH1,.end = IRQ_USBH1,.flags = IORESOURCE_IRQ,}, };這些方法雖然不知道是C的那個(gè)標(biāo)準(zhǔn)引入的,但是見到有代碼這樣用。
switch…case
switch (reg) {case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR:return 1;case TAS5086_INPUT_MUX:case TAS5086_PWM_OUTPUT_MUX:return 4; }可以用上面的辦法,少寫一些case語(yǔ)句。
動(dòng)態(tài)鏈接
這兩天實(shí)踐了一下怎樣在linux下創(chuàng)建動(dòng)態(tài)鏈接。感覺網(wǎng)上的資料雖然翔實(shí),但仍然有疏漏之處。
1)g++和gcc的區(qū)別
本來(lái)只想給鏈接的,以顯示這不是我的原創(chuàng)。但是現(xiàn)在的鏈接失效的也太快了。。。囧
只好拿華麗的分隔符來(lái)表示引用的內(nèi)容。
誤區(qū)一:gcc只能編譯c代碼,g++只能編譯c++代碼
兩者都可以,但是請(qǐng)注意:
1.后綴為.c的,gcc把它當(dāng)作是C程序,而g++當(dāng)作是c++程序;后綴為.cpp的,兩者都會(huì)認(rèn)為是c++程序,注意,雖然c++是c的超集,但是兩者對(duì)語(yǔ)法的要求是有區(qū)別的。C++的語(yǔ)法規(guī)則更加嚴(yán)謹(jǐn)一些。
2.編譯階段,g++會(huì)調(diào)用gcc,對(duì)于c++代碼,兩者是等價(jià)的,但是因?yàn)間cc命令不能自動(dòng)和C++程序使用的庫(kù)聯(lián)接,所以通常用g++來(lái)完成鏈接,為了統(tǒng)一起見,干脆編譯/鏈接統(tǒng)統(tǒng)用g++了,這就給人一種錯(cuò)覺,好像cpp程序只能用g++似的。
誤區(qū)二:gcc不會(huì)定義__cplusplus宏,而g++會(huì)
實(shí)際上,這個(gè)宏只是標(biāo)志著編譯器將會(huì)把代碼按C還是C++語(yǔ)法來(lái)解釋,如上所述,如果后綴為.c,并且采用gcc編譯器,則該宏就是未定義的,否則,就是已定義。
誤區(qū)三:編譯只能用gcc,鏈接只能用g++
嚴(yán)格來(lái)說(shuō),這句話不算錯(cuò)誤,但是它混淆了概念,應(yīng)該這樣說(shuō):編譯可以用gcc/g++,而鏈接可以用g++或者gcc -lstdc++。因?yàn)間cc命令不能自動(dòng)和C++程序使用的庫(kù)聯(lián)接,所以通常使用g++來(lái)完成聯(lián)接。但在編譯階段,g++會(huì)自動(dòng)調(diào)用gcc,二者等價(jià)。
因此,某些時(shí)候編不過去,可以試試換換cc的值。
2)gcc4.1.1下似乎對(duì)類型檢查嚴(yán)了一些,dlsym返回的void*類型不能轉(zhuǎn)換為相應(yīng)的函數(shù)指針類型,需要強(qiáng)制轉(zhuǎn)換。某些網(wǎng)上的例子在這里編不過去。
3)顯式調(diào)用時(shí),要注意動(dòng)態(tài)庫(kù)函數(shù)的聲明,可能要加extern "C"才能正常執(zhí)行。(顯式調(diào)用是運(yùn)行時(shí)加載,所以編譯能過,執(zhí)行卻不對(duì)了。)可以用nm命令看看鏈接庫(kù)的符號(hào)表,以確定問題所在。
4)鏈接的時(shí)候需要注意文件的順序。
比如下面的例子:
https://github.com/antkillerfarm/antkillerfarm_crazy/tree/master/helloworld/linux_so
gcc -o main_link main_link.c -L. -lhello
這條命令中的main_link.c如果放到-lhello之后就會(huì)出問題。也考慮使用--start-group和--end-group之類的鏈接選項(xiàng)解決鏈接順序問題。
cout格式化輸出
cout<<hex<<i<<endl; //輸出十六進(jìn)制數(shù) cout<<oct<<i<<endl; //輸出八進(jìn)制數(shù) cout<<dec<<i<<endl; //輸出十進(jìn)制數(shù)strtok
strtok函數(shù)多用于分割字符串,但它會(huì)改變被分割字符串的值。因此,如果該字符串以后還有用的話,需要首先復(fù)制該字符串,然后對(duì)復(fù)制的字符串執(zhí)行strtok函數(shù)。
K&R風(fēng)格
在C語(yǔ)言的函數(shù)定義上,我們通常用的函數(shù)定義方式為ANSI C的函數(shù)定義方式。但是在C語(yǔ)言之父創(chuàng)立C語(yǔ)言之時(shí),函數(shù)的定義形式并非現(xiàn)在我們所見到的形式。
這種風(fēng)格被稱為K&R風(fēng)格,多見于一些歷史悠久的項(xiàng)目或者老的書籍中。出于兼容性考慮,現(xiàn)代的C編譯器仍然支持K&R風(fēng)格。
詳見:
http://blog.chinaunix.net/uid-7426920-id-2627743.html
總結(jié)
- 上一篇: 大杂烩, 硬盘安装Linux
- 下一篇: C/C++编程心得(二)