C語言和C++都有一個專爲調試而準備的工具函數,就是 assert()函數c++
這個函數是在C語言的 assert.h 庫文件裏定義的,程序員
因此包含到C++程序裏咱們用如下語句:#include <cassert>express
做用是,若是它的條件返回錯我,則終止程序的執行。編程
區分char *和char []:char *定義的是一個指向字符串的指針(注意:C語言中沒有對應字符串的內置類型或者類類型),而char []就是C語言中的用來定義字符數組(注意:字符數組是不一樣於字符串,若是字符數組以'\0'結尾,那麼能夠視爲字符串)。數組
char a[]在運行時賦值,值會從靜態區賦值到函數的棧中,對它進行修改不會產生任何問題。char *a在編譯時就肯定了,a指向靜態區中的值,沒有賦值到函數棧中, 所以對指針的內容進行修改會產生錯誤數據結構
瞭解一下 一個由 C / C++ 編譯的程序佔用的內存分爲如下幾個部分:
一、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操做方式相似於數據結構中的棧。
二、堆區(heap) — 通常由程序員分配釋放, 若程序員不釋放,程序結束時可能由 OS 回收。注意它與數據結構中的堆是兩回事,分配方式卻是相似於鏈表。
三、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另外一塊區域。 - 程序結束後由系統釋放 。
四、文字常量區 —常量字符串就是放在這裏的。 程序結束後由系統釋放
五、程序代碼區—存放函數體的二進制代碼。函數
關於頭文件和源文件的分別工具
首先,咱們能夠將全部東西都放在一個.cpp文件內.this
而後編譯器就將這個.cpp編譯成.obj,obj是什麼東西?url
就是編譯單元了.一個程序,能夠由一個編譯單元組成,
也能夠有多個編譯單元組成. 若是你不想讓你的源代碼變得很難閱讀的話,
就請使用多個編譯單元吧.(一個函數不能放到兩個編譯單元裏面,但兩個以上
就能夠分別放在一個單元,也就是cpp裏面)
那麼就是一個.cpp對應一個.obj,而後將全部的obj連接起來(經過一個叫連接器的程序),
組成一個.exe,也就是程序了.
若是一個.cpp要用到另外一個.cpp定義的函數怎麼辦? 只需在這個.cpp種寫上他的函數聲明
就能夠了.其他工做由連接器幫你完成,你能夠隨便調用該函數.
連接器將全部的obj鏈接起來,可是若是碰巧有相同的函數或外部變量怎麼辦?他如何識別?
通常來講是不能容許在同一個程序中,出現兩個同樣的函數名或外部變量名.
可是隻得慶幸的是,c++能夠經過一種叫作連接屬性的關鍵字來限定,你這個函數是屬於整個程序
公用的,仍是隻是在一個編譯單元obj裏面使用的.
這些關鍵字就是extern 和 static; extern是外部連接的意思,也就是除了這個單元,外部的單元也是可以訪問這個函數的.static 是內部連接,自屬於本身單元.
說了這麼久,尚未說.h的做用呢?
其實沒有.h也能很好的工做,可是當你發現一個外部連接的函數或外部變量,須要許多份
聲明,由於c++這種語言,在使用函數和變量的時候,必須將他聲明,爲什麼要聲明?聲明以後才
知道他的規格,才能更好的發現不和規格的部分.你別妄想一個編譯單元,會自動從另外一個
編譯單元那裏獲得什麼信息,知道你是如何定義這個函數的.
因此說,只要使用到該函數的單元,就必須寫一份聲明在那個.cpp裏面,這樣是否是很麻煩,
並且,若是要修改,就必須一個一個修改.這真讓人受不了.
.h就是爲了解決這個問題而誕生,他包含了這些公共的東西.而後全部須要使用該函數的.cpp,只須要
用#include包含進去即可.之後須要修改,也只是修改一分內容.
請注意不要濫用.h,.h裏面不要寫代碼,.h不是.cpp的倉庫,什麼都塞到裏面.
若是在裏面寫代碼,當其餘.cpp包含他的時候,就會出現重複定義的狀況,
好比將函數func(){printf};放到頭文件a.h,裏面還有一些a.cpp須要的聲明等;
而後你發現b.cpp須要用到a.cpp裏面的一個函數,就很高興的將a.h包含進來.
注意,#include並非什麼申請指令,他就是將指定的文件的內容,原封不動的拷貝
進來.
這時候實際上a.cpp和b.cpp都有一個func()函數的定義.
若是這個函數是內部連接static的話,還好,浪費了一倍空間;
若是是extern,外部連接(這個是默認狀況),那麼根據在同一個程序內不可出現
同名函數的要求,鏈接器會絕不留情給你一個鏈接錯誤!
爲了避免同一個文件被include屢次
1 #ifndef方式
2 #pragma once方式
在可以支持這兩種方式的編譯器上,兩者並無太大的區別,可是二者仍然仍是有一些細微的區別。
方式一:
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
... ... // 一些聲明語句
#endif
方式二:
#pragma once
... ... // 一些聲明語句
#ifndef的方式依賴於宏名字不能衝突,這不光能夠保證同一個文件不會被包含屢次,也能保證內容徹底相同的兩個文件不會被不當心同時包含。固然,缺點就是若是不一樣頭文件的宏名不當心「撞車」,可能就會致使頭文件明明存在,編譯器卻硬說找不到聲明的情況
#pragma once則由編譯器提供保證:同一個文件不會被包含屢次。注意這裏所說的「同一個文件」是指物理上的一個文件,而不是指內容相同的兩個文件。帶來的好處是,你沒必要再費勁想個宏名了,固然也就不會出現宏名碰撞引起的奇怪問題。對應的缺點就是若是某個頭文件有多份拷貝,本方法不能保證他們不被重複包含。固然,相比宏名碰撞引起的「找不到聲明」的問題,重複包含更容易被發現並修正。
1 #include <stdio.h> 2 #include <stdlib.h> 3 int main() 4 { 5 printf("The program test print style!\n"); 6 /* 以十進制形式輸出帶符號整數(正數不輸出符號) */ 7 printf("%d\n", 223); 8 printf("%d\n", -232); 9 printf("\n"); 10 /* 以八進制形式輸出無符號整數(不輸出前綴O) */ 11 printf("%o\n", 223); 12 printf("%o\n", -232); 13 printf("\n"); 14 /* 以十六進制形式輸出無符號整數(不輸出前綴OX) */ 15 printf("%x\n", 223); 16 printf("%x\n", -232); 17 printf("\n"); 18 /* 以十進制形式輸出無符號整數 */ 19 printf("%u\n", 223); 20 printf("%u\n", -232); 21 printf("\n"); 22 /* 以小數形式輸出單、雙精度實數 */ 23 printf("%f\n", 223.11); 24 printf("%f\n", 232.11111111); 25 printf("%f\n", -223.11); 26 printf("%f\n", -232.11111111); 27 printf("\n"); 28 /* 以指數形式輸出單、雙精度實數 */ 29 printf("%e\n", 223.11); 30 printf("%e\n", 232.11111111); 31 printf("%e\n", -223.11); 32 printf("%e\n", -232.11111111); 33 printf("\n"); 34 /* 以%f%e中較短的輸出寬度輸出單、雙精度實數 */ 35 printf("%g\n", 223.11); 36 printf("%g\n", 232.111111111111); 37 printf("%g\n", -223.11); 38 printf("%g\n", -232.111111111111); 39 printf("\n"); 40 /* 輸出單個字符 */ 41 printf("%c\n", 'a'); 42 printf("%c\n", 97); //輸出爲 'a' 43 printf("\n"); 44 /* 輸出單個字符 */ 45 printf("%s\n", "this is a test!"); 46 printf("%s\n", "2342o34uo23u"); 47 printf("\n"); 48 getch(); 49 }
assert的做用是現計算表達式 expression ,若是其值爲假(即爲0),那麼它先向stderr打印一條出錯信息,而後經過調用 abort 來終止程序運行
C編譯器要求變量的聲明須要放在做用域的開頭,這裏是函數的開始處。而C++編譯器不要求,在哪用就在哪聲明,也能夠在開頭聲明好。因此,寫C++代碼是就比較放得開,何時須要何時聲明就能夠了。
sizeof(數組名)計算數組大小並非每次都管用哦。sizeof(數組名)/sizeof(數組一個元素)也並非每次均可以正確計算數組元素個數哦。
強行記憶學很差編程
1 #include <stdio.h> 2 #include <stdlib.h> 3 int main() 4 { 5 char url[] = "www.baidu.com"; 6 char bb[] = { 0,1,2,3,4 }; 7 int age[]= { 0,1,2,3,4 }; 8 int isizeurl = sizeof(url); 9 int isizebb = sizeof(bb); 10 int isizeage = sizeof(age); 11 printf("isizeurl:%d\n", isizeurl); 12 printf("isizebb:%d\n", isizebb); 13 printf("isizeage:%d", isizeage); 14 getchar(); 15 }
你能夠看到,sizeof(數組名)是能夠計算獲得數組的字節大小的。這並非所有的事實!
Linux系統下C程序開發詳解:
1 .ii爲擴展名的文件,是已經預處理過的C++源代碼文件,也是中間代碼文件。
2 .o爲擴展名的文件,是編譯後的目標文件,源文件生成的中間目標文件。
3 .s爲擴展名的文件,是彙編語言源代碼文件。
4 .S爲擴展名的文件,是通過預編譯的彙編語言源代碼文件。
5 .o爲擴展名的文件,是編譯之後的程序目標文件(Object file),目標文件通過鏈接成可執行文件
注意 NULL 就是 NULL,它被宏定義爲 0:
#define NULL 0
不少系統下除了有 NULL 外,還有 NUL
NUL 是 ASCII 碼錶的第一個字符,表示的是空字符,其 ASCII 碼值爲 0。其值雖然都爲 0,但表示的意思 徹底不同。
函數宏中的#和##運算符
在函數宏中#能夠實現由函數宏實參生成字符串常量,##實現了由函數宏實參生成標識符的一部分。(前者用於拼接字符串後者用於拼接標示符)看一下下邊的示例:
假如但願在字符串中包含宏參數,ANSI C容許這樣做,在類函數宏的替換部分,#符號用做一個預處理運算符,它能夠把語言符號轉化程字符串。例如,若是x是一個宏參量,那麼#x能夠把參數名轉化成相應的字符串。該過程稱爲字符串化
1 #include<stdlib.h> 2 #include<stdio.h> 3 #define psqs(x) printf("the square of"#x "is %d.\n",(x)*(x)) 4 int main(void) { 5 int y = 4; 6 psqs(y); 7 psqs(2 + 4); 8 system("pause"); 9 }
第一次調用宏時使用「y」代替#x;第二次調用時用「2+4″代#x。
##運算符能夠使用類函數宏的替換部分。另外,##還能夠用於類對象宏的替換部分。這個運算符把兩個語言符號組合成單個語言符號。例如:
#define XNAME(n) x##n
這樣宏調用:
XNAME(4)
展開後:x4