另外-o表示優化級別-o2 -o3 -ofast級別依次升高,相對應的編譯速度也依次變慢2. *a取內容 &a取地址
運行: ./cards(類linux中)
cards(windows)
合併執行 gcc cards.c -o cards && ./cards
字符串指針I. 函數傳遞的情況下,eg: void fortune(char msg[]){printf("%s\n",msg); //輸出字符串的內容,字符串輸出時給出首地址就行不是*msgpintg("%i"),sizeof(msg));} // 結果爲指針的大小(msg[]實際爲指針,只是接了個數組地址,指針本身並不能說明數組長度),不是字符串的大小,在32位系統中佔4個字節,64位系統中佔8個字節。因:4*8位=32,8*8位=64但是不做這種傳遞的字符數組變量(不用指針),可以用sizeof取其大小的,eg:char s[]="How big is it?";char *t=s;sizeof(s)=15; //一個字母佔一個字節 sizeof(t)=4或8另外,傳遞數組時 void fortune(char msg[])變成char msg[4]之類的可以通過編譯,但本身並無任何實際意義,函數中可以用msg[5]等,表示給這種標識無意義。II.int numscanf("%i",&num)char name[40];scanf("%s",name) //可用eg:%39s限制輸入過多越界,程序崩潰。另外:scanf 和 fgetsscanf數值字符串都行,但不適合用於輸入字符串中有空格之類的。printf字符串遇到空格可沒事。fgets輸入字符串時更適合,但必須限定長度。eg: char food[5]; fgets(food,5,stdin)//賦值給food,長度爲5或者用sizeof(food),stdin ,表示標準輸入來自鍵盤。
scanf也可以通過,[^\n]來讀字符串,但是[^\n]具體什麼意思(只說是表示這一行剩下的數據都給他,但是由下圖,需要兩次回車才能結束輸入,和140頁的例子我由於[^\n]忘了加\n導致只能讀入第一行數據,所以有點不懂了,用時先試試吧)
III,指向字符串字面值的指針變量,不能用來修改字符串的內容。
eg: char *cards="JQK";//不能用cards[0]等做改值操作因其儲存在常量區,可在前加const,這樣編譯器會提示錯誤信息,read-only。
但字符數組就可以, char cards[]="JQK";
一個C一行一行操作文件的例子
#include<stdio.h>
#include<stdlib.h>#include<string.h>int main(){char line[80];FILE *in=fopen("spooky.txt","r");FILE *file1=fopen("ufos.csv","w");FILE *file2=fopen("disappearances.csv","w");FILE *file3=fopen("other.csv","a");//a表示追加模式,若原已有此文件,則在原有數//數據後繼續寫入,而若w模式,若原有文件,則抹掉原數據,寫入新數據while(fscanf(in,"%79[^\n]\n",line)==1){//把\n去掉就只讀一行數據了。是讀一行還是顯示時到\n停止了//while(fscanf(in,"%79s",line)==1){//這個也行,所以別用[^\n了]if(strstr(line,"UFO"))fprintf(file1,"%s\n",line);else if(strstr(line,"Disappearance"))fprintf(file2,"%s\n",line);elsefprintf(file3,"%s\n",line);}fclose(file1);fclose(file2);fclose(file3);return 0;}
3. const 和static
由上引出了const 是常量值,以前自己總是和static靜態混。另外還有一個可以定義用於多個文件的extern關鍵字。
把任何不想改變的對象聲明爲const是一種良好的習慣,還有一種這樣的define N 12,注意後面別加冒號
把任何不想改變的對象聲明爲const是一種良好的習慣,還有一種這樣的define N 12,注意後面別加冒號
const作用:
聲明常量,它不能被修改,它存放在常量區
static作用:1限制範圍(此例左中只能在counter函數中訪問,所以在主函數用s承接,右例中則都可以訪問,這是用它聲明全局變量的注意點。注意左中,雖然每次調用函數,但是count並不是每次都被重新賦值爲1)
2設定變量存儲區域(靜態存儲區域)(C#中類定義爲static表示不能用此類實例化,所以只能通過名字作用域引用,而函數定義爲static是爲了防止函數重名,重新定義,加入static後函數就定了,若再加入同名函數的不同方法則會報錯。類中變量成員定義爲static則爲爲此類建一個成員,而非爲每個實例,詳細見C++數據結構筆記9)
4.語句本身有值
eg:t=3,此語句本身就是值3,所以有f=t=3,得f=3;
scanf()函數可返回成功讀取的數據條數。eg: while(scanf("%f","%f","%i",&a,&b,&c)==3)做判斷條件。fgets返回的是指針。
5,Ctrl+D可停止程序
6.重定向輸入輸出
I.在終端中,可用<重定向標準輸入,>重定向標準輸出。eg: ./geo2json < a.csv > output.json //運行程序geo2jsion,從a.csv中讀入,輸出到output.json中。另:在類linux中看錯誤,輸入,echo $? 另補:C代碼中system(cmd);可執行命令行p400,但用此容易被注入,不安全所以有了exec(),但此會終止當前程序,於是又引出了fork()+exec()運行子進程。p424printf>>fprintf(stdout,"這是標準輸出!")也可以通過fprintf(stderr,"錯誤信息:%s\n",變量名)//把錯誤信息過濾,單獨輸出到屏幕所以和上面的一塊用就可以把正常的數據輸出到文件,錯誤的數據在屏幕上提示,當然也可以輸出到某個文件中。P124II. 用C語言代碼重定向輸入輸出,即重定向輸入輸出數據流#include<stdlib.h>File *in_file=fopen("input.txt","r");//從input裏讀數據,我試過若是無此文件,自己代碼不檢查錯誤,編譯不報錯。但打開失敗返回0(一般語句失敗返回0或-1,)File *out_file=fopen("output.txt","w");//r、w、a、三種模式,r是讀,w是寫,a是追加,w和a若原無此文件都可新建,另外,a表示追加模式,若原已有此文件,則在原有數據後繼續寫入,而若w模式,若原有文件,則抹掉原數據,寫入新數據寫入: fprintf(outfile,"內容 %s和 %s",“內容1”,“內容2”);//把兩個內容寫入到outputfscanf(in_file,"%9s",變量);fclose(in_file);fclose(out_file);注意:用完必須關閉數據流。例子在P140頁,不過不用再去糾結那個[^\n]\n了,直接用S吧。
7.管道"|",一個程序的輸出直接作爲另一個程序的輸入
(./ber|./geo)<in.csv > output.json //實際in.csv>>ber處理>>結果給geo處理>>輸出到output中.p131
8.由P186想編C可這樣借鑑C#的三層架構
同類函數全局變量聲明在一個.h文件中再用一個.c文件在其中把相應函數具體定義,並include...h這樣其他文件就可以通過include....h,使用....c中的函數,最表層的執行放在最上層。另外:共享函數,共享變量需eg: extern int a(頭文件中比較適合,是外部變量引用)
9.P239和236結構體和類實例一樣若傳遞函數用,函數名(class/struct T)這種,副本改值不影響原始(按值傳遞),但可以這樣改變初始值,eg:C結構體改變其初始值,用結構體指針指向保存數據的地址。另外,上面說的是類的實例,若整個類的初始值需要改變則涉及到類中static變量的定義,看看C++數據結構筆記9.
再另外,對應按值傳遞,這種指針傳遞,還有按引用傳遞,詳細見C++數據結構筆記14,
eg:void happy_birth(turtle *t) //turtle是已經定義好的一個結構體的名字{(*t).age=(*t).age+1;}(*t).age 和t->age//這兩個表示方式等價
通常多個這樣的計算機會放一起,能組成一個字節就組成一個字節,節約空間C語言默認函數類型是 Int,不是void,C++、C#也是但是C99新標準規定主函數必須返回int,不能默認那種
11.數組的長度是固定的,爲了保存可變數量的數據,可用鏈表,鏈表插值比數組方便的多。
12.malloc()函數可用於申請堆空間,因函數內變量一般放在棧空間,結構體等數據較多時數據較大可申請堆空間,當然用完還是必須歸還,free()。
13.C中函數參數數量可變,//P344
eg: void pint(tin a, ...){} //
14.Linux 中輸入 ps -ef查看進程, windows中taskmgr
15.進程:P431
文件描述符對應數據流進程含有正在運行的程序,還有棧和堆數據空間等# 數據流0 鍵盤 //標準輸入1 屏幕 //標準輸出2 屏幕 //標準輸出。。。。FILE *my_file=fopen();操作系統會遍歷描述符表尋找空項,把新文件註冊在其中如果不知道描述符號fileno()返回int a=fileno(my_file);dup2(4,3)複製數據流4的到3的數據流上,dup2(fileno(my_file),3);
進程的管理
進程內部有符號表,相互通信pipe,而進程和系統相當於僱員和老闆的關係,老闆給出信號,僱員可以有自己的處理方式(寫自定義處理函數),也有默認的方式,有此開明也有強制措施,eg:kill -kill,當然僱員也可以給自己發出信號(raise()),讓自己做什麼,這是自願做的。eg: alarm(5); //倒計時5秒,發出SIGALRM信號,然後>寫自定義處理函數>用sigaction包裝>sigaction()註冊 ,處理函數P46216.三目運算符 return (x==1)?2:3 //真時返回2,假時返回317.位運算, ~a, a中所有位取反 a&b,a|b,a^b a中的位“與”,“或”,“異或”b中的位<<位左移(值增加) >>位右移(值減小)<<可用來快速將某個整型值乘以2的冪,但小心溢出18.逗號分割表達式for(i=0;i<10;i++,j++)
19. 預處理指令
#define AS 7 //程序中所有出現AS的地方被替換爲7帶參數的:#define AS(x) ((x)+1) //程序中所有出現AS(x)的地方被替換爲。。帶條件的#ifdef SPANISH //如果SPANISH這個宏存在char *s="ang"; //就包含着段代碼#elsechar *s="wngla"; //否則就包含着段代碼#endif
20.limits.h 可以查看某類型數據數值的上下限 eg: INT_MIN,INT_MAX等表示int 型最小值最大值
程序中include後直接用變量名就好。
或者c++中climits, include<climits>// include<limits>
21.函數指針
C語言中函數名也是指針變量,指向函數地址,因無函數類型,所以不能function *f這樣eg:把函數int ns(char *s)傳遞給函數 void find(), find應這樣定義void find(int (*match)(char*)) //因match指向函數地址所以加*號,後面()是參數的類型{ 裏面用時,match(char *s)就表示傳來的函數} //定義那樣定,但是這可以用也可以不用(*match)(char *s)eg:#include<stdio.h>#include<windows.h>typedef int(*lpAddFun)(int, int);//這樣就是聲明一個函數指針變量。前面加typedef就是定義函數指針類型。int main(){lpAddFun addFun;//函數指針addFun=(lpAddFun)GetProcAddress(hDll,"add");//addFun相當於承接了dll中的函數addint result=addFun(2,3);……}調用: find(ns)也可以寫成find(&ns); 函數指針是唯一可以省略*和&編譯器也能識別的指針。另外區別返回值類型,eg: void* lots(){} //是返回空指針(空指針可以被任意類型的指針接受)是返回值類型,不是函數類型,P510
22rand() 產生僞隨機數
若要增加隨機性,可用srand初始化隨機種子,但是這裏是想記一小技巧,比如說想產生0到9的數,而rand產生的是0到RAND_MAX之間的,我可以這樣。int a=rand()%10; 巧用取餘符號,將隨機數限定。同理0到100的可以%100,等等,方便快捷。但真正規範的是int j=(int)(n*rand()/(RAND_MAX+1.0))產生一個0到n之間的隨機數,這是採用一個0~1的數乘以n的思想,分母加1,取不到n了。若1到n之類,則可採用平移的思想 int j=1+(int)(n*rand()/(RAND_MAX+1.0))rand()和srand()的用法首先我們要對rand()&srand有個總體的看法:srand初始化隨機種子,rand產生隨機數,下面將詳細說明。rand(產生隨機數)表頭文件: #include<stdlib.h>定義函數 :int rand(void)函數說明 :因爲rand的內部實現是用線性同餘法做的,他不是真的隨機數,只不過是因爲其週期特別長,所以有一定的範圍裏可看成是隨機的,rand()會返回一隨機數值,範圍在0至RAND_MAX 間。在調用此函數產生隨機數前,必須先利用srand()設好隨機數種子,如果未設隨機數種子,rand()在調用時會自動設隨機數種子爲1。rand ()產生的是假隨機數字,每次執行時是相同的。若要不同,以不同的值來初始化它.初始化的函數就是srand()。返回值:返回0至RAND_MAX之間的隨機整數值,RAND_MAX的範圍最少是在32767之間(int),即雙字節(16位數)。若用unsigned int 雙字節是65535,四字節是4294967295的整數範圍。0~RAND_MAX每個數字被選中的機率是相同的。範例:/* 產生介於1 到10 間的隨機數值,此範例未設隨機數種子,完整的隨機數產生請參考srand()*/#include<stdlib.h>main(){int i,j;for(i=0;i<10;i++){j=1+(int)(10.0*rand()/(RAND_MAX+1.0));printf("%d ",j);}}執行:9 4 8 8 10 2 4 8 3 69 4 8 8 10 2 4 8 3 6 //再次執行仍然產生相同的隨機數srand(設置隨機數種子)表頭文件:#include<stdlib.h>定義函數:void srand (unsigned int seed);函數說明:srand()用來設置rand()產生隨機數時的隨機數種子。參數seed必須是個整數,通常可以利用geypid()或time(0)的返回值來當做seed。如果每次seed都設相同值,rand()所產生的隨機數值每次就會一樣。範例/* 產生介於1 到10 間的隨機數值,此範例與執行結果可與rand()參照*/#include<time.h>#include<stdlib.h>main(){int i,j;srand((int)time(0));for(i=0;i<10;i++){j=1+(int)(10.0*rand()/(RAND_MAX+1.0));printf(" %d ",j);}}執行:與rand範例比較5 8 8 8 10 2 10 8 9 92 9 7 4 10 3 2 10 8 7又或:用"int x = rand() % 100;"來生成 0 到 100 之間的隨機數這種方法是不或取的,比較好的做法是: j=(int)(n*rand()/(RAND_MAX+1.0))產生一個0到n之間的隨機數int main(void){int i;time_t t;srand((unsigned) time(&t));printf("Ten random numbers from 0 to 99\n\n");for(i=0; i<10; i++)printf("%d\n", rand() % 100);return 0;}除以上所說的之外,補充一點就是srand這個函數一定要放在循環外面或者是循環調用的外面,否則的話得到的是相同的數字。MSDN中的例子。// crt_rand.c// This program seeds the random-number generator// with the time, then displays 10 random integers.//#include <stdlib.h>#include <stdio.h>#include <time.h>int main( void ){int i;// Seed the random-number generator with current time so that// the numbers will be different every time we run.//srand( (unsigned)time( NULL ) );// Display 10 numbers.for( i = 0; i < 10;i++ )printf( " %6d\n", rand() );printf("\n");// Usually, you will want to generate a number in a specific range,// such as 0 to 100, like this:{int RANGE_MIN = 0;int RANGE_MAX = 100;for (i = 0; i < 10; i++ ){int rand100 = (((double) rand() /(double) RAND_MAX) * RANGE_MAX + RANGE_MIN);printf( " %6d\n", rand100);}}總結:我們知道rand()函數可以用來產生隨機數,但是這不是真真意義上的隨機數,是一個僞隨機數,是根據一個數,我們可以稱它爲種了,爲基準以某個遞推公式推算出來的一系數,當這系列數很大的時候,就符合正態公佈,從而相當於產生了隨機數,但這不是真正的隨機數,當計算機正常開機後,這個種子的值是定了的,除非你破壞了系統,爲了改變這個種子的值,C提供了 srand()函數,它的原形是void srand( int a) 功能是初始化隨機產生器既rand()函數的初始值,即使把種子的值改成a; 從這你可以看到通過sand()函數,我們是可以產生可以預見的隨機序列,那我們如何才能產生不可預見的隨機序列呢?我們可能常常需要這樣的隨機序列,是吧。利用srand((unsign)(time(NULL))是一種方法,因爲每一次運行程序的時間是不同的,對了,你知道time() 函數的功能是返回從1970/01/01到現在的秒數的吧,可能這個起始時間不正確,你查一下對不對吧,C還提供了另一個更方便的函數, randomize()原形是void randomize(),功能是用來始初rand() 的種子的初始值,而且該值是不確定的,它相當於srand((unsign)(time(NULL)) 不過應注意的是randomize()的功能要通過time來實現所以在調用它時頭文件要包含time.h罷了
23.數組名和指針
定義數組a,指針pa
數組做右值時轉換成指向首元素的指針,但做左值仍然表示整個數組的存儲空間,而不是首元素的存儲空間。所以像a++,a=pa+1,都是錯誤的(第一個a++容易與指針自加混,指針自加是可以的,加1相當於加上自身指向類型的長度),而&a是合法的
另:a[2]=*(a+2)所以數組從零開始