C語言編程常見技巧(問題???)


本文章根據《算法競賽入門經典(第二版)》一書整理。。。

 

    第一章 程序設計入門

  •   printf 語句控制輸出小數位數或總長度

      printf("%.3f\n",8.0/5.0)    //小數位數為3。

      printf("%.*f\n",3,8.0/5.0)    //用3來代替* ,用來動態指定小數位數

      printf("%3d",xxx)      //指定輸出寬度為3

      printf("%-3d",xxx)      //左對齊,郵編填補空格

      printf("%03d",xxx)      //指定寬度,並用0填補左邊空缺

 

  •   求多位數各個位上的數

    while(x > 0){

      y  =  x % 10;    //y分別為個位、十位、百位等

      x /= 10;

    }

 

  •   交換變量a,b的值  

    方法一:借用第三方變量

      int a,b,temp;

      temp = a;

      a = b;

      b = temp;

    方法二:不借用第三方變量

      1)使用加減法(和不變)//限制:只有定義了加減法的數據類型才可以使用。

        int a,b;

        a = a +b;

        b = a - b;

        a = a - b;

      2)使用異或運算(^)

        a = a ^ b;  

                        b = a ^ b;

                        a = a ^ b;

        原理: x^x == 0;  x^0 == x;

        首先用a保存了a^b的值,再用b = a ^ b = (a^b)^b=a^b^b=a^(b^b)=a^0=a,這樣實現了b = a;

        接着又用a = a^b = (a^b)^b(第一個b還是原來的b,而第二個b已經是a的值)= (a^b)^a = a^a^b = 0^b = b,這樣實現交換。

 

  •   C語言中,0為假,其余皆為真。

 

    第二章  循環結構程序設計

 

  •   四舍五入

    floor(x + 0.5)

  

  •   continuebreak

    continue進行下一次循環。break則是跳出當前循環體。

 

  • 整數溢出

    int型一般為32位整數,取值范圍為-2^31~2^31-1,即-2147483648~2147483647

 

  •   while  (先判斷后計算)

 

  •        do  while (先計算一次,后判斷)  

 

  •   scanf判斷輸入結束:

    windows:輸入完畢后,先按Enter鍵,再按Ctrl+Z鍵,最后在按Enter鍵。之所以這么復雜是因為scanf語句的輸入格式中空格,TAB,回車符都不意味着輸入結束。

    Linux:輸入完畢后按下Ctrl+D即可。

    while(scanf("%s",&a) == 1 )    //保證輸入不為空(有沒有==1,意義不大吧。。。。)

    while(~scanf("%d",&m,&n))  等同於:while (scanf("%d%d",&m,&n)!=EOF)  

      //參考:https://www.cnblogs.com/woaiheniunai/p/6394672.html

      //EOF定義為-1。~是按位取反,-1十六進制補碼表示為0x ffffffff,f是二進制的1111,取反后就全部變成0了,於是while結束。    //限制:僅用於輸入數字(字母、符號容易形成死循環)。

      while(scanf("%d",&a) == 1 && n)    //保證輸入不為空且不是0。

    while(scanf("%d",&s) == 1)  按下Ctrl+z +回車  即可結束輸入。但while(scanf("%d",&s)  != '\n')或while(scanf("%d",&s)  != EOF)是不可以。。。未解決。  

  •   文件讀取

    輸入輸出重定向:詳見https://blog.csdn.net/xavierdarkness/article/details/80638641

      freopen("input.txt","r",stdin);

      freopen("output.txt","w",stdout);

    文件讀取(不使用重定向)(較靈活)

      FILE *fin,*fout;

      fin = fopen("data.in","rb");

      fout = fopen("data.out","wb");

    不同:freopen不是指針類型的,寫起來簡單,自然,但不可同時讀寫文件和輸入輸出。

       fopen則需使用文件指針,且在使用fopen時,應使用配套的fsacnf和fprintf進行輸入輸出,普通的scanf和printf依然能在命令行界面操作。

       如果在fopen下想使用標准輸入輸出,只需賦值“fin = stdin;fout = stdout”即可,此時不在調用fopen和fclose。

  •   浮點數溢出

    在除法中(1/n^2)此時,需注意溢出,此時要使用連除(1/n/n)而不是直接除以其平方和(1/(n*n))

 

    第三章   數組和字符串

  •   數組聲明:

    數組在程序中的聲明位置很重要:參考:https://zhidao.baidu.com/question/397529275.html

    全局變量(在main函數外聲明)在靜態存儲區分配內存,局部變量(main函數內聲明)是在棧上分配內存空間的,
    VC堆棧默認是1M,int a[1000000]的大小是4*1000000,將近4M,遠遠大於1M,編譯連接的時候不會有問題,但運行是堆棧溢出
,程序異常終止。

 

  •   內存復制:#include <string.h>

    void *memcpy(void *dest, const void *src, size_t n);

    函數說明:從源src所指的內存地址的起始位置開始拷貝n個字節到目標dest所指的內存地址的起始位置中

 

    常用方法:把數組a全部復制到數組b中:memcpy(b,a,sizeof(a));

 

  •   數組置零:#include <string.h>

    void *memset(void *s, int ch, size_t n);

    函數說明:將s中當前位置后面的n個字節 (typedef unsigned int size_t )用 ch 替換並返回 s。

    常用方法:將數組a清零:memset(a,0,sizeof(a));

 

  •   開關問題

    一般使用j % i 來判斷幾倍關系,用 來取反模擬實現開啟、關閉。

 

  •   蛇形填數(1)

    eg:10 11 12 1

       9  16 13 2

       8  15 14 3

       7  6   5   4

    類似上述問題使用數組,模擬筆撞牆(是否越界、是否填過)的問題。

    檢驗是否填過以及碰壁:(碰壁條件 && !a[x][y])而不是判斷(a[x][y] == 0)。

 

    1、 gets功能為讀入一行,並將換行符(\n)轉換為字符串結束符(\0)。
    2、 scanf("%s",s);讀入時,遇到空白字符,包括空格,制表符,換行符時均會停止輸入。

      對於字符串數組scanf("%s",s)和scanf("%s",&s)是沒有區別的,s代表數組首地址,&s也是一樣。

      對於字符(int型或其他)來說參考以下:

        scanf函數要求第二個參數是一個指針類型的變量

        scanf("%d",a)----->表示a的定義為: int* a;
        scanf("%d",&a)---->表示a的定義為: int a;
       scanf("%d",s)和scanf("%s",&s)

    3、終止條件不同。

      gets只有遇到\n時才會結束輸入,而scanf遇到空格或制表符時,也會結束輸入。
      比如輸入"test string\n"。
      用gets得到的字符串為"test string", 二用scanf得到的是"test"。
    4、終止后,對終止字符處理不同。
      比如輸入為"test\nabcd"。
      執行gets后,\n不會留在緩沖區中,即這時調用getchar得到的字符是'a'。
      執行scanf后,\n會留在緩沖區,這時調用getchar得到的字符是'\n'。

 

    作用:把格式化的數據寫入某個字符串緩沖區。

     int sprintf( char *buffer, const char *format, [ argument] … );

     返回值:返回寫入buffer 的字符數(\n計算在內,\0不計算在內),出錯則返回-1. 如果 buffer 或 format 是空指針,且不出錯而繼續,函數將返回-1,並且 errno 會被設置為 EINVAL(不懂)

    用例:

      char buffer[200], s[] = "computer";
      int j;

      j  = sprintf( buffer,     "%s\n", s );    //此時buffer為“computer”,j為9(8+1)。 

 

    sizeof為運算符其值在編譯時即計算好了,參數可以是數組、指針、類型、對象、函數等。

    strlen(...)是函數,要在運行時才能計算。參數必須是字符型指針(char*)。當數組名作為參數傳入時,實際上數組就退化成指針了。

    

    char *strchr(const char* _Str,char _Val)

    功能:查找字符串_Str中首次出現字符_Val的位置。

    說明:返回首次出現_Val的位置的指針,返回的地址是被查找字符串指針開始的第一個與Val相同字符的指針,如果Str中不存在Val則返回NULL。

    

    char *fgets(char *buf, int bufsize, FILE *stream);  

    參數:

      *buf: 字符型指針,指向用來存儲所得數據的地址。
      bufsize: 整型數據,指明存儲數據的大小。

      *stream: 文件結構體指針,將要讀取的文件流。

    返回值:

    1. 成功,則返回第一個參數buf;
    2. 在讀字符時遇到 end-of-file,則eof指示器被設置,如果還沒讀入任何字符就遇到這種情況,則buf保持原來的內容,返回NULL;
    3. 如果發生讀入錯誤,error指示器被設置,返回NULL,buf的值可能被改變。

     注意:從文件結構體指針stream中讀取數據,每次讀取一行。讀取的數據保存在buf指向的字符數組中,每次最多讀取bufsize-1個字符(第bufsize個字符賦'\0'),如果文件中的該行,不足bufsize-1個字符,則讀完該行就結束。如若該行(包括最后一個換行符)的字符數超過bufsize-1,則fgets只返回一個不完整的行,但是,緩沖區總是以NULL字符結尾,對fgets的下一次調用會繼續讀該行。函數成功將返回buf,失敗或讀到文件結尾返回NULL。因此我們不能直接通過fgets的返回值來判斷函數是否是出錯而終止的,應該借助feof函數或者ferror函數來判斷。

 

    int fgetc(FILE *stream);

    功 能:從流中取字符。

    fgetc()從參數stream所指的文件中讀取一個字符,並把它作為一個字符返回。若讀到文件尾或出現錯誤時,它就返回EOF(-1),你必須通過ferror或feof來區分這兩種情況。

 

    int getc(FILE *stream);

    功 能:從流中取字符。

    返回值:從文件指針stream指向的文件流中讀取一個字符,並把它作為函數值返回給整型變量ch,並把位置標識符往前移動。

        如果讀取失敗或者到了文件結束標志返回EOF(-1)
 

    int getchar(void);

    參數:該函數沒有參數。
    返回值:函數的返回值為用戶輸入的第一個字符的ASCII碼,若出錯返回-1,且將用戶輸入的字符回顯到屏幕。如果用戶在按回車鍵之前輸入了不只一個字符,其他字符會保留在鍵盤緩沖區中,          等待后續getchar()調用讀取。也就是說,后續的getchat()調用不會等待用戶按鍵,而是直接讀取緩沖區中的字符,直到緩沖區的字符讀取完畢后,才等待用戶按鍵。

  

  •   鍵盤輸入錯位問題(字符串常量數組)UVa10082 WERTYU

    方法一:(原著)使用的是先使用for( i = 1; s[i] && s[i] != c;i++);來獲取當前輸入字符位置,然后減一就是正確位置。

    方法二:使用strchr獲取字符串位置。詳見https://www.cnblogs.com/BANLOONG/p/10389951.html

 

    int isalpha( int ch )

    功能:斷字符ch是否為英文字母

    返回值:若為英文字母,返回非0(小寫字母為2,大寫字母為1)。若不是字母,返回0。

 

    功能:用於檢查其參數是否為十進制數字字符。

    返回值:若參數c為阿拉伯數字0~9,則返回非0值,否則返回0。

 

    功能:判斷字符c是否為可打印字符(含空格)。

    返回值:當c為可打印字符(0x20-0x7e)時,返回非零值,否則返回零。

    注意:判斷字符c是否為可打印字符(含空格)說明:當c為可打印字符(0x20-0x7e)(ACSII中對應' ' 到 '~')時,返回非零值,否則返回零。0x7f(DEl)為控制字符,用iscntrl函數判斷。

 

    功能:用來判斷字符是否為ASCII碼的控制字符。

    返回值:若參數c 為ASCII 控制碼,則返回非 0 值,否則返回 0。

    注意:控制字符的ASCII碼值 0x00 ~ 0x1F,再加上 0x7f(DEL),控制字符在屏幕上顯示時不占位(看不見);

   注:將字符轉換為數字、字母的函數。
     #include <ctype.h>
    char r(char ch){
      if(isalpha(ch)) return ch - 'A';
      return ch-'0'+25;
    }
  •    回文(aba)+鏡像(5↔Z)問題 :UVa401 Palindromes

    用兩個標志位分別代替是否回文和是否鏡像,共四種結果,此時可以使用數組來保存這四種結果,輸出時由兩個標志位計算即可得出結果

    eg:char *msg[] = {"非回文","回文","鏡像","鏡像+回文"}

      p = 0;  //不是回文    m = 0;  //不是鏡像

      printf("%s",msg[m*2+p]);

    注意:字符數組順序需注意匹配結果

 

    給定答案序列和用戶猜的序列,統計有多少數字位置正確(A),有多少數字在兩個序列都出現過但位置不對(B)。

    //直接統計得A,為了求B,對於每個數字(1—9),統計二者出現的次數c1和c2,則min(c1,c2)就是該數字對B的貢獻,最后要減去A的部分

    同一數字c1和c2都存在(一次都沒有出現記為0),證明該數字在兩個序列中皆出現過,但此時的出現包括兩種情況(位置正確的(即A)和不正確的(即B)),而如下例eg1中的d=3,c1=1,c2=0,說明該數字在b[i]中沒有出現,當然不計入A或B中,同理,eg2中的d=6,c1=0,c2=2,說明該數字在a[i]中不存在,同樣不計入A或B。

    eg1:a[i]  1 3 5 5 5

        b[i]  5 5 2 6 6

    eg2:  a[i]  1 3 2 5 5

        b[i]  5 5 5 6 6

    在d=5時,eg1中c1為3,c2為2,c1>c2,在最好的情況下,b[i]中的2個5位置都正確,

          eg2中,c1為2,c2為3,c1<c2.在最好的情況下,也只有2個5滿足條件,因此總的來說,就是取較小值。(自己掰的)。

 1 for(;;){
 2     int A=0,B=0;
 3     用for循環直接比較得出A;  //偽代碼
 4     結束輸出;          //偽代碼
 5         for(int d=1;d<=9;d++){    //統計數字d(1~9)在答案序列a[i]和猜測序列b[i]中各出現次數。
 6         int c1=0,c2=0;
 7         for(int i=0;i<n;i++){
 8             if(a[i]==d) c1++;
 9             if(b[i]==d) c2++;
10             }
11         if(c1<c2) B+=c1;      //取兩序列中較小者,
12         else B+=c2;    
13         }
14     }

 

  •   環狀序列  UVa 1584,ACM/ICPC Seoul 2004 ,Circular Sequence

    任意字符串按字典序比較(選較小者):

1 int less(char *s, int p, int q){    // 比較兩個序列的字典序(p表示法是否比q表示法小)
2     int n = strlen(s);
3     for(int i = 0; i < n; i++ ){
4         if(s[(p+i)%n] != s[(q+i)%n])//意味着元素相同時候繼續 直到有不同的元素為止 
5         return s[(p+i)%n] < s[(q+i)%n];//不同時直接返回 大小關系
6         }
7     return 0;//到這一步 說明這兩個序列 元素一樣
8     } 

    注: “%n”可以保證整個字符全部取到。如下,p=2時,使用“%n”可以實現即使將“A”為首,認可讀到“A”前的“CT”,這樣讀取到的就是下列表格中的結果。

      eg:CTAC

      

------------------------------------------------------------------------------------------------------------23:13:18  2019-02-18

  •   判斷素數(質數)
1 int is_prime(int i){
2         for (int j = 2; j * j <= i; j++)
3             if(i % j == 0 )
4                 return 0;
5         return 1;
6     }
  •   求最大公約數(輾轉相除法)
 1 int max_n(int a,int b){
 2     if(a > b){
 3         if (a % b == 0)return b;
 4         else{
 5             a -=b;
 6             max_n(a,b);
 7         }
 8     } else{
 9         int temp = a;
10         a = b;
11         b = temp;
12         max_n(a,b);
13     }
14 }
  最小公倍數為二數之積除以最大公約數。

 

 

 

 


  
  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM