CPP 基礎

CPP 基礎

C 語言基礎

C 語言關鍵字

char, short, int, long, float, double, struct, union, enum, void, const, signed, unsigned, sizeof, typedef, auto, static, register, extern, if, else, switch, case, default, for, while, do, break, continue, return

基本數據類型

  • char, short, int, long, long long, float, double, void, struct, union, enum等。
  • 在基本數據類型前加unsigned表示無符號類型,最高位不作爲符號位,而是數值位。
  • 整數默認是int,小數默認是double。所以定義long類型時最好在數字末尾加l, L,定義float類型最好在數字末尾加f, F
基本數據類型 字節數 範圍
char 1 -128~127
short 2 -32768~32767
int 2 或 4 -231~231-1
long 4 -231~231-1
long long 8 -264~264-1
float 4
double 8

變量的存儲類型

變量除了有數據類型,還有另外一個重要屬性,存儲類型。數據類型決定了變量的大小,而存儲類型決定了變量的生命週期和可見性。

C 語言中,變量的存儲類型有 4 種,auto, static, register, extern,一般使用的只有前兩個。

  • auto,默認存儲類型。auto 只能用在函數內,修飾局部變量。
  • static,全局變量默認以 static 修飾。當修飾局部變量時,必須且只能被初始化一次
  • register,用於定義存儲在寄存器中而不是 RAM 中的局部變量,用於運算要求高的變量。
  • extern,用於提供一個全局變量的引用,全局變量對所有的程序文件都是可見的。

算數運算符

  • +, -, *, /, %, ++, --, <, <=, >, >=, ==, !=, &&, ||, !, &, |, ~, ^, >>, <<, +=, sizeof, ., [], *, (), ?:, 強制類型轉換
  • a^b=c, 則a^c=b, b^c=a,這一規律廣泛運用於密碼學。
  • a>>n,相當於a/(2^n)
    • 有符號數,右移時最高位補符號位。
    • 無符號數,右移時最高位補 0。
  • a<<n,相當於a*2^n,最低位直接補 0。

數組與字符串

  • int a[10]={1, 2, 3},前 4 個數分別爲 1, 2, 3,後面的數均初始化爲 0。
  • 字符串的末尾必須是\0,佔一個字節。

指針

變量都存放在從某個內存地址開始連續的若干個字節中。

  • 開始的內存地址:指針。
  • 連續的若干字節:指針對應類型的字節長度。
  • int *p = (int *)1000,指針指向內存地址爲 1000 的位置,管轄後面連續的 4 個字節。

指針變量的大小

指針變量是一種數據類型,它的大小均爲sizeof(T *) = 4,故無論指針變量指向的是何種數據類型,它的大小均爲 4 字節。

這是因爲當今主流的 CPU 爲 32 位,尋址範圍是 2^32=4G,所以指針變量只需要 32 位即可。當然對於 64 位 CPU,編譯器產生的指針長度也就是sizeof(T *) = 8

主機字節順序

  • 大端:低地址存放高位數據,這種最爲直觀。
  • 小端:低地址存放低位數據,在程序,網絡中,一般採用的是小端。

例如,將0x1234bacd以不同的方式存儲:

地址 大端 小端
0x0000 12 cd
0x0001 34 ba
0x0002 ab 34
0x0003 cd 12
unsigned short a = 0x1234;
char *p = (char *)&a;
for(int i=0;i<2;i++){
	printf("%2x\n", *(p+i));
}

如果short a = 0xf234,在輸出首字節的0xf2時,會出現fffffff2而不是f2,猜測可能是由於printf()函數在輸出時將char自動轉換成了 4 字節的int造成的,因爲%x對應的就是整型,整型默認是int

指針運算

  • 同類型的指針可以比較。
  • 同類型的指針可以相減。p2-p1 = (p2-p1)/sizeof(T)
  • 指針可以和整型常量相加,相減。p ± n = p ± n*sizeof(T)
  • 指針可以自增,自減。

指針與動態分配內存

  • 分配內存:int *p = new int, int *p = new int[10]
  • 釋放內存:delete p, delete[] p

指針類型

  • 空指針,也稱野指針,未指向任何地址的指針。p = NULL
  • void 指針,指向 void 類型的指針,可以強制轉換爲任意類型指針。
  • 指向指針的指針,int **p
  • 數組和字符串與指針,指向數組和字符串首地址的指針。對於二維數組,還區分行指針和列指針,行指針也就是指向指針的指針。
  • 指向函數的指針,該類指針指向某一類型的函數。
  • 常量指針,指針的指向可以改,但是指向的值不可以改。const int *p = &a
  • 指針常量,指針的指向不可以改,但是指向的值可以改。int * const p = &a
int add(int a, int b)
{
    return a+b;
}

int main()
{
    int (*p)(int, int); // 定義了一個指向某一類型的函數指針p
    p = add;
    cout << p(1, 2) << endl;
}

結構體

定義結構體

stuct student
{
	string name;
	int age;
};

使用結構體

// 初始化
student s1;
student s2 = {"Tom", 18};

cout << s2.name << endl; // 變量

student s3 = {
	name : "Alice",
	age : 16,
};
student *p = &s3;

cout << p->name << endl; // 指針

聯合體

定義聯合體

union data
{
	int a;
	float b;
	char c[20];
}
  • 聯合體的大小以最大的類型爲主,也就是說上面定義的data聯合體的大小爲 20 字節。

使用聯合體

聯合體在同一時間只使用一個變量,因爲他們共用一塊最大的內存。

union data data1;

data1.i = 10;
data1.f = 220.5; // 此時會覆蓋掉數據10
strcpy( data1.str, "C Programming"); // 此時會覆蓋掉數據220.5

printf( "data.str : %s\n", data1.str);

枚舉類型

// 第一個枚舉成員的默認值爲整型的 0,後續枚舉成員的值在前一個成員上加 1
enum DAY
{
    SUN, MON, TUE, WED, THU, FRI, SAT
};

// 可以爲單獨某個值指定數值,後續枚舉成員的值仍在這個基礎上加 1
// 如下,spring = 0, summer = 3, autumn = 4, winter = 5
enum season {spring, summer=3, autumn, winter};

預處理

引用頭文件

  • #include <stdio.h>
  • #include "mymath.h"

宏定義與條件編譯

#define, #undef, #ifdef, #ifndef, #if, #else, #elif, #endif

帶參數宏

#define add(a, b) a+b

頭文件的多次引用

如果一個頭文件被引用兩次,編譯器會處理兩次頭文件的內容,這將產生錯誤。爲了防止這種情況,標準的做法是把文件的整個內容放在條件編譯語句中,一般使用#idndef xx來解決這一問題,放在頭文件中。

#ifndef _MYMATH_H
#define _MYMATH_H
// xxx
#endif

讀寫操作

標準輸入輸出

  • printf(),標準輸出到stdout, stderr
  • scanf(),標準輸入到stdin
  • int fgetc(FILE *),返回輸入的一個字符,EOF時返回-1。
  • int fputc(int c, FILE *),輸出一個字符。
  • char *fgets(char *s, int n, FILE *),輸入一段文本以回車結束。
  • int fputs(const char *s, FILE *),輸出一段文本。
  • sscanf(buf, "%d%d", &a, &b),從數組中讀。
  • sprintf(buf, "%d%d", a, b),寫入數組。
  • fscanf(FILE *, 其他),從文件讀。
  • fprintf(FILE *, 其他),寫入文件。

重定向

  • freopen("in.txt", "r", stdin);
  • freopen("out.txt", "w", stdout);

文件讀寫

打開文件

FILE *fopen(const char * filename, const char * mode);,其中,filename是文件路徑的字符串,mode是打開模式,如下:

模式 含義
r 只讀
w 覆蓋寫,若文件不存在則創建
a 追加寫,若文件不存在則創建
xb 操作的是二進制
x+ 允許讀寫
讀寫文件
  • int fputc( int c, FILE *fp );,返回輸出的字符,否則 EOF。
  • int fputs( const char *s, FILE *fp );,成功返回一個非負值,否則 EOF。
  • int fgetc( FILE * fp );,返回讀取的字符,否則 EOF。
  • char *fgets( char *buf, int n, FILE *fp );
  • fscanf(FILE *, 其他),從文件讀。
  • fprintf(FILE *, 其他),寫入文件。
  • size_t fread(void *buf, size_t size_of_elements, size_t number_of_elements, FILE *a_file);,返回已讀取字節數。
  • size_t fwrite(const void *buf, size_t size_of_elements, size_t number_of_elements, FILE *a_file);,返回已寫入字節數。

  • int fseek(FILE *stream, long offset, int whence);,移動文件指針到指定位置。
    • offset是相對於 whence 的偏移位置,正數往後,負數往前。
    • whence可以是SEEK_SET, SEEK_CUR, SEEK_END,分別是文件頭,當前位置,文件尾。
  • int ftell(FILE *stream),返回當前文件指針位置距離文件頭的字節數。

通過上面兩個函數可以獲取一個文件的大小:

FILE *fp = fopen("in.txt", "r");
fseek(fp, SEEK_END);
int size = ftell(fp);
fseek(fp, SEEK_SET);

關閉文件

int fclose( FILE *fp );

時間函數

struct tm {
   int tm_sec;         /* 秒,範圍從 0 到 59 */
   int tm_min;         /* 分,範圍從 0 到 59 */
   int tm_hour;        /* 小時,範圍從 0 到 23 */
   int tm_mday;        /* 一月中的第幾天,範圍從 1 到 31 */
   int tm_mon;         /* 月,範圍從 0 到 11 */
   int tm_year;        /* 自 1900 年起的年數 */
   int tm_wday;        /* 一週中的第幾天,範圍從 0 到 6 */
   int tm_yday;        /* 一年中的第幾天,範圍從 0 到 365 */
   int tm_isdst;       /* 夏令時 */
};
  • time_t time(time_t *t),返回自元時間以來經過的秒數,該秒數會同時返回給參數和返回值。
  • struct tm *localtime(const time_t *t),返回當地時區時間。
  • struct tm *gmtime(const time_t *t),返回用標準格林尼治時區(GMT)表示。
  • time_t mktime(stuct tm *time),將結構體時間轉化爲秒數。
  • double difftime(time_t t2, time_t t1),返回 t2-t1 的差值。
  • size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr),根據 format 中定義的格式化規則,格式化結構 timeptr 表示的時間,並把它存儲在 str 中。
  • clock_t clock(void),返回處理器時鐘所以用時間,用於得到程序使用時間。

其他

命令行參數

// ./a.ot hahaha
int main(int argc, char *argv[])
{
    printf("可執行程序 %s,參數個數爲[%d],運行輸出:[%s]\n", argv[0], argc, argv[1]);
    return 0;
}

控制檯動態刷新結果

printf("\r%d", count++);
// printf("\b\b%d", count++);

CPP 的基礎

定義常量

  • #define Pi 3.141592654
  • const double PI = 3.1415926;

CPP 關鍵字

bool, namespace, using, class, private, public, protected, this, super, template, new, delete, inline, virtual, true, false

引用類型

引用變量是一個別名,引用是一種安全指針的機制。T & a = b;

int a = 5;
int &b = a; // b爲a的引用
int swap(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}

int a = 1, b= 2;
swap(a, b);

形參的默認值

在 CPP 中,函數的形參可以指定默認值,並且擁有默認值的參數必須放在最後面。

若定義的函數有前向引用申明,那麼默認參數值必須在聲明中定義,在實現的時候不可以再次指定默認值。

函數的重載

函數名相同,函數的參數個數或者參數類型不同,則稱之爲函數重載。

函數的返回值類型不可以作爲判斷函數重載的依據。

內聯函數

定義函數時在返回類型前面添加關鍵字inline,在編譯時會用在調用函數的位置展開函數體,以減少參數傳遞,控制轉移等的開銷。

內聯函數體內不能有循環和 switch 等語句,函數體必須簡單。

異常處理

try {
   // 保護代碼
}catch( ExceptionName e1 ) {
   // catch 塊
   cout << e1.what() << endl;
}catch( ExceptionName e2 ) {
   // catch 塊
}catch( ExceptionName eN ) {
   // catch 塊
}catch( ... ){
	// 處理任何異常
}
try {
	if (str == "int") throw 100;
	else throw "ahhaha";
}catch(const int a) {
	cout << a << endl;
}catch(const string b) {
	cout << b << endl;
}catch( ... ) {
	cout << "other exception" << endl;
}

多線程

#include <iostream>
#include <thread>

using namespace std;

void fun(int num)
{
    while(true){
        cout << num << endl;
    }
}

int main()
{
    thread t1(fun, 1);
    thread t2(fun, 2);

    // t1.join(),主線程會阻塞在這裏
	// t1.detach(),線程會後臺運行,無需阻塞等待
	// 主線程不會等待子線程結束
    t1.join();
    t2.join();
}
發佈了24 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章