C 初探
C vs Java
1. 類型, 運(yùn)算符與表達(dá)式
1.1 基本數(shù)據(jù)類型
char, int, float, double, short int, long int ? (可以省略int)
signed 和 unsigned 可以限定 char 類型或任何整形
類型轉(zhuǎn)換
1.2 常量
整型常量: 默認(rèn)為 int, ?L 表示 long 型, U 表示 unsigned 型, UL 表示 unsigned long 型
浮點(diǎn)型常量: 默認(rèn)為 double, F 表示 float 型, L 表示 long double 型
整型常量還可以用八進(jìn)制, 十六進(jìn)制表示: 前綴 0 表示八進(jìn)制, 前綴 0x 表示十六進(jìn)制
八進(jìn)制, 十六進(jìn)制也可以用 L 表示 long 型, U表示 unsigned 型 e.g: 0XFUL?
?
字符常量: 將一個(gè)字符括在單引號(hào)中, 如 'x'. '\0' 表示值為0的字符, 即 null
轉(zhuǎn)移字符列表
| 轉(zhuǎn)義字符 | 意義 | ASCII碼值(十進(jìn)制) |
| \a | 響鈴(BEL) | 007 |
| \b | 退格(BS) ,將當(dāng)前位置移到前一列 | 008 |
| \f | 換頁(FF),將當(dāng)前位置移到下頁開頭 | 012 |
| \n | 換行(LF) ,將當(dāng)前位置移到下一行開頭 | 010 |
| \r | 回車(CR) ,將當(dāng)前位置移到本行開頭 | 013 |
| \t | 水平制表(HT) (跳到下一個(gè)TAB位置) | 009 |
| \v | 垂直制表(VT) | 011 |
| \\ | 代表一個(gè)反斜線字符''\' | 092 |
| \' | 代表一個(gè)單引號(hào)(撇號(hào))字符 | 039 |
| \" | 代表一個(gè)雙引號(hào)字符 | 034 |
| \0 | 空字符(NULL) | 000 |
| \ddd | 1到3位八進(jìn)制數(shù)所代表的任意字符 | 三位八進(jìn)制 |
| \xhh | 1到2位十六進(jìn)制所代表的任意字符 | 二位十六進(jìn)制 |
e.g: '\007', '\x7' 均表示響鈴
?
字符串常量: e.g: "hello, world"
字符串常量就是字符數(shù)組, 字符串的內(nèi)部使用一個(gè)空字符'\0'結(jié)尾
注意字符常量與僅含一個(gè)字符的字符串常量的區(qū)別: 'x' 與 "x"
?
Consts:
const int days_in_week = 7; /*相當(dāng)于final in Java*/
const 常用在函數(shù)的 prototype 中,說明函數(shù)的功能
void foo(const struct fraction* fract); ? // 表明foo函數(shù)并不會(huì)改變fract指針指向的內(nèi)容
?
枚舉常量: 枚舉是一個(gè)常量整型值的列表 (名字與常量值之間建立聯(lián)系)
e.g: enum boolean {NO, YES} ;
在沒有顯示說明的情況下, 第一個(gè)枚舉名的值為0, 然后依次遞推. 如果指定了部分枚舉名的值, 則依最后一個(gè)遞推.
e.g: enum months { JAN = 1, FEB, MAR, APP, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
?
1.3 運(yùn)算符
?
?
2. 函數(shù)與程序結(jié)構(gòu)
C 不支持函數(shù)定義的嵌套
參數(shù)傳遞傳遞的是 value (好好理解這句話:傳遞普通數(shù)據(jù),傳遞指針,傳遞指針的指針)
作用域規(guī)則:
storage classes in C
C語言不像Java, Python, 不同文件中的變量都已 文件名.變量名 訪問. C 語言中不同文件不能定義相同名稱的外部變量.(無法通過編譯).?
如果要在外部變量的定義之前使用該變量, 或外部變量的定義與變量的使用不再同一個(gè)源文件中, 則必須在相應(yīng)的變量聲明中強(qiáng)制性地使用關(guān)鍵字 external.
注意區(qū)分 聲明 與 定義. 變量聲明用于說明變量的屬性, 而變量定義還將引起存儲(chǔ)器的分配. 因此外部變量的定義必須指定數(shù)組的長度, 但聲明不一定.
// 定義 int sp; double val[MAXVAL];// 聲明 extern int sp; extern double val[];?
靜態(tài)變量(static):
用static來聲明一個(gè)變量的作用有二:
(1) 外部變量用static來聲明,則該變量的作用只限于本文件模塊.(這種不同文件可以使用相同變量名)
(2) 對于局部變量用static聲明,則是為該變量分配的空間在整個(gè)程序的執(zhí)行期內(nèi)都始終存在(但只初始化一次)
對于外部變量和靜態(tài)變量來說, 初始化表達(dá)式必須是常量表達(dá)式, 而自動(dòng)變量沒有這要求.
?
宏定義 (#define)
#define 名字 替換文本
#define forever for(;;); // 無限循環(huán) #define square(x) (x) * (x);?注意可能傳入的參數(shù)有副作用(比如含有自增運(yùn)算符), 以及計(jì)算次序.
?
?
C 語言中 可以返回 any type?except arrays and functions. 但可以返回 pointer to array or pointer to function.
?
?
3. 指針與數(shù)組
Pointer:
Pointer: A variable that contains the address of a variable
int *x;
- Tells compiler that variable x is address of an int
x = &y;
- Tells compiler to assign address of y to x
- & called the "address operator" in this context
z = *x;
- Tells compiler to assign value at address in x to z
- * called the "dereference operator" in this context
pointers can point any kind of data, normally a pointer only points to one type
but void * is a type that can point to anything
?
x = (*p)++ ? x = *p; *p = *p + 1x = *p++ ? (*p++) ? *(p)++ ? *(p++) ? x = *p; p = p + 1
x = *++p ? p = p + 1; x = *p
盡量只用 *p++ , (*p) ++
?
?通過不同類型的 cast, 可以確切控制指針的位置
// assume p is a pointer point to a int p = p + 12; // move 48 bytes p = (int *)( ((char *)p) + 12); // move 12 bytes?
Array:
int ar[2]; int ar[] = {1, 2}; char string[] = "abc"; // String in C is just an array of characters?數(shù)組表示的字符串可能并不以 '\0' 結(jié)尾
數(shù)組的大小必須是常量,比如 int ar[n] 就不行,要想使用變量的數(shù)組,要在 heap 中分配
?
Array variable is a "pointer" to the first element, so array variable almost identical to pointers?
ar[0] sames as *ar ar[2] sames as *(ar+2)Q: 如果數(shù)組中的元素不是一個(gè)byte大小會(huì)怎么樣? A:?因?yàn)槁暶髦羔槙r(shí)說明了 type, 所以 +1 就是增加相應(yīng)類型的位數(shù)
?
當(dāng)把數(shù)組名傳遞給一個(gè)函數(shù)時(shí), 實(shí)際上傳遞的是該數(shù)組第一個(gè)元素的地址. ?即?int strlen(char s[]) ?等價(jià)于 int strlen(char *s) ?而且更習(xí)慣于后一種形式(因?yàn)榍逦谋砻媪藗魅氲氖侵羔?
?
Array vs Pointer
?
指向指針的指針
int n = 3; int *pn = &n; // pointer to n int **ppn = &pn; // pointer to address of n?
e.g:
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp; }void swap(int **a, int **b) {int *temp = *a;*a = *b;*b = temp; }?e.g:
void inc_ptr(int *p) {p = p + 1; }int A[3] = {50, 60, 70}; int *q = A; inc_ptr(q); printf("%d\n", *q);// output: 50因?yàn)檫@里傳遞的是值, 所以函數(shù)內(nèi)改變只是改變了拷貝. 要修改q的指向, 需要傳遞q的指針
void inc_ptr(int **p) {*p = *p + 1; }int A[3] = {50, 60, 70}; int *q = A; inc_ptr(&q); printf("%d\n", *q);// output: 60?
指針數(shù)組 ?
e.g: char *name[] = { "Illegal month", "Jan", "Feb", "Mar" };
注意區(qū)分指針數(shù)組與二維數(shù)組的區(qū)別(兩者在內(nèi)存上的分配不同, 指針數(shù)組更自由, 其每一行的長度可以不同)
?
函數(shù)指針
int *f(); // f 是一個(gè)函數(shù), 它返回一個(gè)指向 int 類型的指針 int (*f) (); // f 是一個(gè)指向函數(shù)的指針, 該函數(shù)返回一個(gè) int 類型的對象C 中函數(shù)不能穿入函數(shù)做參數(shù),但可以傳入函數(shù)指針做參數(shù)。(所以要支持Callback(回調(diào))就傳入函數(shù)指針)
ps: 函數(shù)做指針時(shí),不必用 & 和 *,當(dāng)然用也沒錯(cuò)
int strcmp_wrapper(void * pa, void * pb) {return strcmp((const char *)pa, (const char *)pb); }int (*fp)(void *, void *) = strcmp_wrapper; // 或 &strcmp_wrapperint ret = fp(str1, str2); // 或 (*fp)(str1, str2)?
函數(shù)指針應(yīng)用舉例
以標(biāo)準(zhǔn)庫中的qsort為例, qsort的signature為
void qsort(void* arr, int num, int size, int (*fp)(void* pa, void* pb))qsort 使用舉例
int arr[] = {10, 9, 8, 1, 2, 3, 5}; /* callback */ int asc(void* pa, void* pb) {return (*(int *)pa - *(int *)pb); }/* callback */ int desc(void* pa, void* pb) {return (*(int *)pb - *(int *)pa); }qsort(arr, sizeof(arr)/sizeof(int), sizeof(int), asc); //&asc也行 qsort(arr, sizeof(arr)/sizeof(int), sizeof(int), desc); //&desc也行?
?
4. Structures?、union、typedef、bit fields
ps:C structure assignment is not a "deep copy". All members are copied, but not things pointed to by members.
?
Structure Member Alignment, Padding
(將structure 中的元素由 size 從大到小或從小到大排列可以減小總大小)
?
Bit Fields 用于Structure中, 目的是節(jié)省空間
/* define simple structure */ struct {unsigned int widthValidated;unsigned int heightValidated; } status1;/* define a structure with bit fields */ struct {unsigned int widthValidated : 1;unsigned int heightValidated : 1; } status2;int main( ) {printf( "Memory size occupied by status1 : %d\n", sizeof(status1));printf( "Memory size occupied by status2 : %d\n", sizeof(status2));return 0; }?
?
5. C Memory Management
?
static, global 變量儲(chǔ)存在 static data 區(qū)
局部變量 儲(chǔ)存在 stack 區(qū), 并且當(dāng)函數(shù)返回時(shí)釋放
?
C 內(nèi)存管理是指管理 Heap
內(nèi)存管理有如下幾個(gè)函數(shù)
| 1 | void *calloc(int num, int size); This function allocates an array of?num?elements each of which size in bytes will be?size. |
| 2 | void free(void *address); This function release a block of memory block specified by address. |
| 3 | void *malloc(size_t num); This function allocates an array of?num?bytes and leave them initialized. |
| 4 | void *realloc(void *address, int newsize); This function re-allocates memory extending it upto?newsize. |
切記檢查 malloc, calloc, realloc 的返回值是否為 NULL
?
e.g:??沒有cast也行,但編譯器會(huì)給出warnning
int *ip ip = (int *) malloc(sizeof(int)); typedef struct { ... } TreeNode; TreeNode *tp = (TreeNode *) malloc(sizeof(TreeNode)); free((void *) tp);?
ps:如果內(nèi)存不夠時(shí), malloc() 返回 NULL 指針。
free 內(nèi)存時(shí),必須保證傳遞給 free() 是 malloc() 返回的指針 (不能做修改)
?
一個(gè)實(shí)例:Binary Tree
typedef struct node {int key;struct node *left;struct node *right; } Node;Node *root = NULL;Node *create_node(int key, Node *left, Node *right) {Node *np;if ( (np = (Node *) malloc(sizeof(Node))) == NULL ) {printf("Memory exhausted!\n");exit(1);} else {np->key = key;np->left = left;np->right = right;return np;} }void insert(int key, Node **tree) {if ( (*tree) == NULL ) {(*tree) = create_node(key, NULL, NULL); return;}if (key <= (*tree)->key)insert(key, &((*tree)->left));elseinsert(key, &((*tree)->right)); }?
Memory Leak:more mallocs than frees
int *pi; void foo() {pi = malloc(8*sizeof(int));free(pi); }void main() {pi = malloc(4*sizeof(int));foo(); }?
?
?
6. I/O
Standard I/O
// 注意 IO 函數(shù)的返回值類型是 int int putchar(int); int getchar(int);int printf(char* format, arg1, arg2,...) int scanf(char* format, arg1. arg2,...) // 注意 scanf 的arg必須是地址或指針(所以傳遞數(shù)字時(shí)常加&, 傳遞字符串則沒事)?
?File I/O
FILE* fopen(char name[], char model[]) int fclose(FILE* fp);fopen 返回一個(gè)指向文件流的指針(如果不存在則返回 NULL )
Standard I/O 其實(shí)就是 FILE* (stdin, stdout)
?
int getc(FILE* fp); // 從文件流中讀取一個(gè)字符返回讀取值或EOF(注意返回值類型為 int)
getchar() 就是 getc(stdin)
?
char[] fgets(char line[], int maxlen, FILE* fp) // 讀取一行(包括換行符)??返回字符串的指針或EOF
?
int putc(int c, FILE* fp); int fputs(char line[], FILE* fp);?
main 函數(shù)參數(shù)輸入
int main(int argc, char* argv[])args: 參數(shù)數(shù)量 argv[]:參數(shù)
注意參數(shù)包含了程序名(第一個(gè)) ?
?
?
?
2015-09-19
?
轉(zhuǎn)載于:https://www.cnblogs.com/whuyt/p/4791206.html
總結(jié)
- 上一篇: 条款14:在资源管理类中心copying
- 下一篇: 360电话面试