嗨翻C語言筆記(部分引用)

1.       編譯:gcc cards.c  -o cards

另外-o表示優化級別-o2 -o3  -ofast級別依次升高,相對應的編譯速度也依次變慢
運行:  ./cards(類linux中)
           cards(windows)
合併執行 gcc cards.c -o cards && ./cards
2.     *a取內容 &a取地址

字符串指針
    I. 函數傳遞的情況下,eg: void fortune(char msg[])
                                             {printf("%s\n",msg);             //輸出字符串的內容,字符串輸出時給出首地址就行不是*msg
                                              pintg("%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 num
     scanf("%i",&num)
    char name[40];
    scanf("%s",name) //可用eg:%39s限制輸入過多越界,程序崩潰。
    另外:scanf 和 fgets
   scanf數值字符串都行,但不適合用於輸入字符串中有空格之類的。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);
           else 
            fprintf(file3,"%s\n",line); 
 
       }
   
 fclose(file1);
 fclose(file2);
 fclose(file3);
 return 0;
}

3.  const 和static
    由上引出了const 是常量值,以前自己總是和static靜態混。另外還有一個可以定義用於多個文件的extern關鍵字。
把任何不想改變的對象聲明爲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()運行子進程。p424
printf>>fprintf(stdout,"這是標準輸出!")
也可以通過fprintf(stderr,"錯誤信息:%s\n",變量名)//把錯誤信息過濾,單獨輸出到屏幕
所以和上面的一塊用就可以把正常的數據輸出到文件,錯誤的數據在屏幕上提示,當然也可以輸出到某個文件中。P124
II. 用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”);//把兩個內容寫入到output
fscanf(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//這兩個表示方式等價

10. unsigned int low_pass:1;//表示只佔一位的int型,注意要規定用幾位前面必須是unsigned,

通常多個這樣的計算機會放一起,能組成一個字節就組成一個字節,節約空間
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()註冊 ,處理函數P462

16.三目運算符 return (x==1)?2:3  //真時返回2,假時返回3

17.位運算, ~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";           //就包含着段代碼
#else
char *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中的函數add
int 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 6
9 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 9
2 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)所以數組從零開始



 

 

 

 

 

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章