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