C語言入門(廿二)之預處理指令、巨集、條件編譯、檔案包含、typedef、const
預處理指令
什麼是預處理指令:
在我們的檔案翻譯成0和1之前做的操作我們稱之為預處理指令。一般情況預處理指令都是以#號開頭的。
巨集定義的格式
不帶引數的巨集定義:
#define 巨集名 值
巨集定義的作用:
會在程式翻譯成0和1之前, 將所有巨集名替換為 巨集的值
巨集定義在什麼時候替換
原始碼 --> 預處理 -->彙編 -->二進位制 -->可執行程式
注意:巨集定義後面不要寫分號
巨集定義規範:
一般情況巨集名都大寫, 多個單詞之間用_隔開, 並且每個單詞全部大寫。
有的公司又要求巨集名以k開頭, 多個單詞之間用駝峰命名。
巨集定義的作用域:
從定義的那一行開始, 一直到檔案末尾。
雖然預設情況下巨集定義的作用域是從定義的那一行開始, 一直到檔案末尾. 但是我們也可以通過對應的關鍵字提前結束巨集定義的作用域。(#undef COUNT)
帶引數的巨集定義:
#define SUM(v1, v2) v1+v2
#define 代表要定義一個巨集
SUM 巨集的名稱
(v1, v2) 引數, 注意點, 不需要寫資料型別
v1+v2 用於替換的內容
巨集定義並不會做任何運算, 無論是有引數還是沒有引數都僅僅是在翻譯成0和1之前做一個簡單的"替換"
帶引數的巨集定義注意點
1.一般情況下建議寫帶引數的巨集的時候, 給每個引數加上一個()
2.一般情況下建議寫帶引數的巨集的時候, 給結果也加上一個()
如:
#define PF(v1) ((v1)*(v1))
什麼時候用帶引數的巨集定義什麼時候用函式?
如果函式內部的功能比較簡單, 僅僅是做一些簡單的運算那麼可以使用巨集定義, 使用巨集定義效率更好, 運算速度更快。
如果函式內部的功能比較複雜, 不僅僅是一些簡單的運算, 那麼建議使用函式。
條件編譯
#define DEBUG 1 // 0是除錯階段 1是釋出階段 #if DEBUG == 0 // 除錯階段 #define NJLog(format, ...) printf(format,## __VA_ARGS__) #else // 釋出階段 #define NJLog(format, ...) #endif
條件編譯和選擇結構if的共同點
都可以對給定的條件進行判斷, 新增滿足或者不滿足都可以執行特定的程式碼
條件編譯和選擇結構if的區別
1.生命週期不同
if 執行時
#if 編譯之前
2.#if需要一個明確的結束符號 #endif
為什麼需要一個明確的結束符號?
如果省略掉#endif, 那麼系統就不知道條件編譯的範圍, 那麼會將滿足條件之後的第二個條件之後的所有內容都清除
3.if會將所有的程式碼都編譯到二進位制中
#if只會將滿足條件的部分一直到下一個條件的部分 編譯到二進位制中
條件編譯的優點
1.縮小應用程式的大小
應用場景:
用於除錯和釋出階段進行測試
除錯階段: 程式寫程式碼的階段
釋出階段: 上傳到AppStore的階段
預處理指令什麼時候執行? 編譯之前 。
變數什麼時候定義? 執行了才會定義。
注意點: 條件編譯不能用來判斷變數, 因為不在同一個生命週期。
一般情況下, 條件編譯是和巨集定義結合在一起使用的。
#include <stdio.h>
#define SCORE 100
//#define COUNT 50
int main(int argc, const char * argv[]) {
#ifdef SCORE // 判斷是否定義了後面的巨集
printf("score\n");
#elif COUNT
printf("count\n");
#else
printf("OTHER\n");
#endif
#ifndef SCORE // 是不是沒有定義名稱叫做SCORE的巨集
printf("no score\n");
#else
printf("score\n");
#endif
/*
#if defined(SCORE) // 判斷是否定義了SCORE這個巨集
printf("score\n");
#else
printf("no score\n");
#endif
#if !defined(SCORE) // 判斷是否沒有定義SCORE這個巨集
printf("no score\n");
#else
printf("score\n");
#endif
*/
return 0;
}
檔案包含
#include <>
<>會先去編譯器環境下查詢, 找不到再去系統的環境下查詢
#include ""
""會先在當前檔案查詢, 找不到再去編譯器環境下查詢, 找不到再去系統的環境下查詢
作用:
將""或者<>中的內容完全拷貝過來
注意:
如何正確的編寫.h檔案
如何防止迴圈拷貝 A拷貝B, B拷貝A
間接拷貝問題 A拷貝B, B拷貝C, C拷貝D
typedef
什麼是typedef, 它有什麼作用
typedef可以給一個已知的資料型別起別名 (外號)
利用typedef給資料型別起別名的格式:
typedef 原有的資料型別 別名(外號);
注意:
1. typedef不僅能給系統原有的資料型別起別名, 也可以給一個自定義的資料型別起別名。
2. 利用typedef給資料型別起別名, 並不會生成一個新的資料型別, 僅僅是給原有的型別起了一個別名而已。
typedef可以給指向函式的指標起別名
// functionPotinter == int(*functionPotinter)(int , int)
typedef int(*functionPotinter)(int , int);
int main(int argc, const char * argv[]) {
// 9如何定義變數 : 資料型別 變數名稱;
// int (*sumP)(int , int ) = sum;
functionPotinter sumP = sum;
printf("sum = %i\n", sumP(10 , 20));
// int (*minusP)(int, int) = minus;
functionPotinter minusP = minus;
printf("minus = %i\n", minusP(20, 10));
return 0;
}
可以給指標起別名
// String == char *
typedef char * String;
void test4()
{
// char *name = "lnj";
// 注意: 如果給指標起別名之後, 那麼以後利用別名定義變數就不用再加*了
String name = "lnj";
printf("name = %s\n", name);
}
可以給列舉型別起別名
// 1.先定義列舉型別, 再給列舉型別起別名
/*
enum Gender
{
kGenderMale,
kGenderFemale
};
typedef enum Gender SEX;
*/
// 2.定義列舉型別的同時給列舉型別起別名
/*
typedef enum Gender
{
kGenderMale,
kGenderFemale
} SEX;
*/
// 3.定義列舉型別的同時給列舉型別起別名, 並且省略列舉原有型別名稱
typedef enum
{
kGenderMale,
kGenderFemale
} SEX;
給結構體型別起別名
// 1.先定義結構體型別, 再給型別起別名
/*
struct Person
{
int age;
double height;
char *name;
};
// SPerson == struct Person
typedef struct Person SPerson;
*/
// 2.定義結構體型別的同時, 給結構體型別起別名
/*
typedef struct Person
{
int age;
double height;
char *name;
} SPerson;
*/
// 3.定義結構體型別的同時, 給結構體型別起別名, 並且省略掉原有型別的名稱
typedef struct
{
int age;
double height;
char *name;
} SPerson;
給基本資料型別起別名
// Integer == int
typedef int Integer;
typedef Integer myInt;
// int float doulbe char
void test1()
{
int num = 10;
printf("num = %i\n", num);
Integer age = 30;
printf("age = %i\n", age);
myInt score = 99;
printf("score = %i\n", score);
}
typedef和巨集定義的區別
一般情況下如果要給資料型別起一個名稱建議用typedef, 不要用define
typedef int myInt;
#define Integer int
typedef char * String;
#define MY_STRING char *
const關鍵字
const修飾的變數內容不可改變,變數變為常量。
int const num = 10;
printf("num = %i\n", num);
//num = 55;//編譯報錯,不能改變變數的值
printf("num = %i\n", num);
const修飾的指標變數,指標變數指向的地址的內容可以改變,但是指標儲存的地址不能變。
char const * name = "lnj";
printf("name = %s\n", name);
name = "lk";
printf("name = %s\n", name);
const可以寫在char這個資料型別的左右都可,習慣寫在最前面。
終於學完了基礎部分,以後多加練習,有些重要的知識點要回頭反覆敲程式碼,不然記不住,同時進度繼續,下一步學oc。