2018 C筆試題

⼀、單項選擇題(15⼩題,每⼩題2分,共30分)

 解析:【傳值調用】,實參和形參之間傳遞的是“地址”


 

 解析:【優先順序】:算術運算符 > 關係運算符  >   賦值運算符   

    【結核性】:賦值運算符(從右至左)   算數運算符和關係運算符(從左至右)

      相當於: ( k > i ) <  j   , ( i < j ) == ( j < k  )   


解析:指針數組是指數組的每一個元素都是一個指針變數的數組

           相當於: point[2] 一樣

例如:


 

解析:數組定義時,常量表達式中不允許包含變數


 

解析:【函數遞歸】,相當於 fib (2) + fib (1) 


 

解析:【do 循環】


 


 

解析:【數組傳參】:傳過去的是地址,實參和虛參都改變


 

 題目有錯!!!

 

解析:【運算優先順序】:“ * ”和“ ++ ”  高於 ” = “,“ * ”和“ ++ ”優先順序相同,結合性:從右往左

    *p1 += *p2++  相當於: *p1 = *p1 + *(p2++)

    *p2 =  p1++ 相當於: *p1 =  (p1++)


解析:【運算優先順序】+ 【結合性】

    i = 3 / 2 + 7 / 2 == 5   相當於: i =  ( ( (3 / 2) + (7 / 2) )  ==  5 )

    j = 45 % 11 + (((7 > 8) ? 15: 14) == 14   相當於:j = (45 % 11) + ((((7 > 8) ? 15: 14) == 14 )


 

 解析:!x是邏輯非操作,當x為0時,值為真,否則值為假。 在這裡等效於x==0 ,即x = 8,while (!x) 為假,退出循環

例如:

 


 

解析:(i * j) / k + 6 – 15 % k    相當於   ((i * j) / k) + 6 – (15 % k)   :  “+”和“-”  結合性從左向右


 解析: while()是循環語句。while(expr)表示當邏輯表達式expr為真時,循環運行循環體,直到expr值為假或遇到break語句時退出 

由 316 = 13 * i +  11 * j 得:k 一定得是11的倍數

 例:

 


 

 解析:【字元數組的定義】:

char cc[ ]  = {‘1′,’2′,’3′,’4′,’5’}, 數組長度為5,字元串長度為5

char cc[ ]  =”12345“,數組長度為6,字元串長度為5,自動在字元串結尾加上一個終止符 ,

[strlen()]函數:測試字元串長度,不包括


 

解析: 【指針與結構數組】:指向結構 的 指針 要先定義,後賦值

p 指向結構第一個位元組,*p 相當於 stu 

  第一種方式:(*p).num    【由於結構成員引用符 “.”的優先順序比間接運算符 “ *” 高,故必須加括弧

  第二種方式:p->num 

  第二種方式:stu.num     三種方式等價!!!


⼆、指出程式段中的錯誤:分析原因並修改。(每題15分,共30分)

改正後:

int main()  {      char *src = "HELLO,UESTC";    //指向字元串常量的指針      char *dest = NULL;      int len = strlen(src);      dest = (char *)malloc(len + 1);      char *d = dest;      char *s = &src[len - 1];      while ( len-- != 0 )          *d++ = *s--;      *d = '';      printf("%sn", dest);   // 使用printf()輸出時,%s  表示輸出一個字元串,src指向字元串的第一個字元,然後src自動加1      free(dest);      return 0;  }

知識點:

【指向字元串常量的指針】:不是將字元串存放在指針src中,而是將指向字元串的指針賦給指針變數,src指針指向字元串的首地址


改正後:

void fun()  {      char data[50] = "welcome to CHENGDU and UESTC";      char *point;      char array[200];      int i = 0, length = 0;      point = array;      while (data[length] != '')          length++;      for (; i < length; i++)      {          *point = *data;          point++;      }      puts(data);  }


三、問答題(共70分)

 1、C語⾔的單詞符號除關鍵字外,還包括其他哪四類?存儲類型關鍵字是哪4個?(每個1分,共8分)

答:  標識符【只能由字母、常量、下劃線、數字組成,且第一個字元必須是字母或者下劃線】、運算符、常量和分隔符【空格、製表符、換行符】。
       存儲類型關鍵字:anto、extern、register、static

 2、函數中,如何使⽤與局部變數同名的全局變數?(3分)

答:使⽤::(作⽤域區分符)(3分)

 3、如何使⽤1個表達式將float類型變數f的值四舍五⼊ 轉換為long類型的值?(3分)

答:(long)(f + 0.5) (3分)

 4、參數傳遞的⽅式分別是什麼?(6分)

數組作為函數參數有三種形式:
1. 實參是數組元素;  傳(數據)值(2分)
2. 形參是指針,實參是數組; 傳地址值(2分)
3. 函數的形參和實參都是數組。 傳地址(2分)

 5、 C程式運⾏時,不對數組進⾏越界檢查,可能導致什麼問題?(5分)

答:編譯通過,但可能導致運⾏出錯(訪問不可訪問的存儲單元)(3分)或訪問和修改
其他⾮數組元素的數據。(2分)

 6、C語⾔的隱式類型轉換髮⽣在哪4種情況下?轉換規則分別是什麼?(6分)

混合運算: 級別低的類型向級別⾼的類型值轉換。 1分
將表達式的值賦給變數: 表達式的值向變數類型的值轉換。 1分
實參向函數形參傳值: 實參的值向形參的值進⾏轉換。 2分
函數返回值: 返回值向函數返回類型的值進⾏轉換。 2分

 7、程式⽚段為: (14分)

當程式執⾏進⼊fun函數時,請列出各個數據(包括常量、變數)在記憶體中對應存儲區的名稱和數據的存儲順序以及所佔⽤的存儲空間的位元組數。

假設整數佔2個位元組,字元佔2個位元組,指針佔4個位元組;⽽記憶體按2個位元組進⾏編址。存儲區名稱1分,其他每3個1分

int n = 2018;  void main() {  char * p=”COMPUTER”, *q;  int mm, arr[2018];  char ch2;  ...  fun(mm);  ...  }  void fun(int nn) {  int mm = 10;  static int snum;  ...  }  

 

 8、下⾯的程式的功能是什麼?(5分)

void main ()  {      int d, i, j, k, flag1, flag2;      scanf("%d", &d);      for (i = 1; i <= 100; i++)      {          j = i;          flag1 = 0;
    // 該數是否含有 d while ((j > 0) && (!flag1)) { k = j % 10; j = j / 10; if (k == d) flag1 = 1; }
    // 該數的平方是否含有 d if (flag1) { j = i * i; flag2 = 0; while ((j > 0) && (!flag2)) { k = j % 10; j /= 10; if (k == d) flag2 = 1; } if(flag2) printf("%-5d %-5dn", i, i * i); } } }

功能:輸⼊數字d=0~9,找1~100中滿⾜條件的數:該數的本⾝及它的平⽅中都含有數字d。

 9、采⽤Eratasthenes篩選法求2-200之間的素數   鏈接:https://www.cnblogs.com/pam-sh/p/12384776.html      請對第6、7、8、9⾏的程式碼進⾏修改,使得程式執⾏效率得到提⾼。(8分)

void main()  {      int prime[201] = {0}; //用於存儲200以內的數是否已篩去      int d, i,k;      for (d = 2; d < 200; d++)          if (prime[d] == 0)              for (k = d + 1; k <= 200; k++)                  if (k % d == 0) prime[k] = 1;      for (i = 2; i <= 200; i++)          if(prime[i]==0)              printf("%dt", i);  }  

// 改為:  for (d=2; d < sqrt(200); d++)      if (prime[d] == 0)          for (k = 2*d; k<=200; k = k+d)   //篩去d的所有倍數              prime[k]=1; 

要點:

為提高篩選效率:

  一個合數n必有一個不大於sqrt(n)的正因子,故一個數若是沒有小於sqrt(n)的正因子,則說明它是一個素數

10、為提⾼程式執⾏效率,C語⾔除了提供指針、宏定義、位運算、不檢查數組下標外,簡述C語⾔還採取了其他哪些措施及原因(12分)

主要從 數據類型 類型檢查 邏輯運算的處理 等⽅⾯分析。
例如: 暫存器變數 ⽆布爾類型 ⽆⼦界類型
a&&b(只有a成⽴才計算b) 數組做參數是采⽤傳地址⽅式 等


 四、程式填空(每空2.5分,共50分)

 1、快排找第K⼩

low = 0; // 該空為填空  high = n - 1; // 該空為填空  do  {      do      {          i = low;          j = high; // 該空為填空          t = a[i]; // 該空為填空      }      while(i < j);   // 該空為填空      a[i] = t; // 該空為填空      if(i == k)          return t; // 該空為填空      if(i > k)          high = i - 1; // 該空為填空      if(i < k)          low = i + 1; // 該空為填空  }while(low < high);  return a[low]; // 該空為填空  

 此題錯誤!!! 

 改正後:還是有錯!!!

low = 0;  high = n - 1;  do  {      i = low;      j = high;      t = a[i];      do      {          while (a[j--] > t) ;          while (a[i++] < t) ;          if(i < j) swap(a[j], a[i]);      }while(i < j);
// 折半查找 if(i == k) return t; if(i > k) high = i - 1; if(i < k) low = i + 1; }while(low < high); return a[low];

程式碼:

const int n = 5;    void main()  {      int a[5] = {3,1,4,6,2};      int low,high,k;      low = 0;      high = n - 1;      printf("請輸入查找第K小的數:n");      scanf("%d",&k);      find(a,low,high,k);  }    int find(int a[],int low,int high,int k)  {      int t = a[low];      int i = low;      int j = high;      while(low < high)      {          while(low < high && a[high] >= t)              --high;          a[low] = a[high];          while(low < high && a[low] <= t)              low++;          a[high] = a[low];      }      a[low] = t;      // 折半查找        if(low == k)      {          return a[low]);          return 0;      }      else if(low > k)          return find(a,i,low - 1,k);      else          return find(a,low + 1,j,k - low);  }

2、⼀個包含9個數的⾮降序數列存儲在數組中,現在插⼊⼀個數到合適位置,使序列保持⾮降序(插⼊的數⼤於第⼀個數,⼩於第九個數)。

 

#define N 10    void main()  {      int a[N] = {1,2,4,8,16,32,64,128,256};      int m,i,d;      scanf("%d",&d);
//找到插入點 for(i = 0; i < 9 ; i++) //這裡填N-1也行 if( d < a[i] ) { m = i ; break ; }
// m之後的往後移 for(i = 8; i >= m; i--) //這裡填N-2也行 a[i + 1] = a[i];
// 插入 a[m] = d;
// 列印 for(i = 0;i < N;i++) printf("%dt",a[i]); }

3、凱撒密碼

這道題是⼀道加密解密問題,⼤致的意思就是通過主函數輸⼊兩個字元串,第⼀個字元串作為加密解密的⽬標
字元串,第⼆個字元串通過函數轉換為整型數據作為解密加密的key,這道題也不難推導,以上是我在⽹絡上找到
的⼀個相似版本。
凱撒加密(Caesarcipher)是⼀種簡單的消息編碼⽅式:它根據字母表將消息中的每個字母移動常量位k。
舉個例⼦如果k等於3,則在編碼後的消息中,每個字母都會向前移動3位:
a會被替換為d;b會被替換成e;依此類推。字母表末尾將回卷到字母表開頭。
於是,w會被替換為z,x會被替換為a。

程式碼:

#include <stdio.h>  #include <stdlib.h>    int main ()  {      char small_letter[26] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',                               'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'                              };      char big_letter[26] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',                             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'                            };      char text[1000], result[1000];      int c, count = 0, k, p,i;      char function;      printf("Insert Text:");      c = getchar();      while (1)   // 讀取字元串      {          if (c == 'n')              break;          text[count] = c;          printf("%c", text[count]);          count++;          c = getchar(); //讀取下一個字元      }      printf("n");      printf("Encrypt or Decrypt? E or D :");      scanf("%c", &function);      // 加密      if (function == 'E')      {          printf("Insert Key :" );          scanf("%d", &k);          for (i = 0; i < count; i++)          {              if (text[i] >= 'A' && text[i] <= 'Z')              {                  result[i] = big_letter[((text[i] - 'A') + k) % 26];  // 向前挪k位              }              //找出加密後字元在字元數組裡的對應位置              else if (text[i] >= 'a' && text[i] <= 'z')              {                  result[i] = small_letter[((text[i] - 'a') + k) % 26];  // 向前挪k位              }              else                  result[i] = text[i];              printf("%c", result[i]);          }      }      // 解密      else      {          printf("Insert Key :" );          scanf("%d", &k);          for (i = 0; i < count; i++)          {              if (text[i] >= 'A' && text[i] <= 'Z')              {                  p = ((text[i] - 'A') - k);                  //  解碼涉及 字母表末尾將回卷到字母表開頭                  while (p < 0)                      p += 26;                  result[i] = big_letter[p];  // 向後挪k位              }          //找出解密後字元在字元數組⾥里里的對應位置          //這裡要注意不要讓它超出範圍(下表位置為負數)              else if (text[i] >= 'a' && text[i] <= 'z')              {                  p = ((text[i] - 'a') - k);                  while (p < 0)                      p += 26;                  result[i] = small_letter[p];  // 向後挪k位              }              else                  result[i] = text[i];              printf("%c", result[i]);          }          printf("n");      }      return 0;  }  

五、程式設計(共20分) 

 輸⼊若⼲個整數(以0結束)如何逆序構建雙向鏈表

程式碼:

#include <stdio.h>  #include <stdlib.h>  //雙鏈表結構定義  typedef struct DNode  {      int data;      struct DNode *pre;      struct DNode *next;  } DNode;    int main()  {      DNode *head, *s;      int t;      // 定義頭節點      head = (DNode*)malloc(sizeof(DNode));      head->pre = NULL;      head->next = NULL;      // 輸入      scanf("%d", &t);      while (t != 0)      {          s = (DNode*)malloc(sizeof(DNode));          s->data = t;          s->next = NULL;          s->pre = NULL;          if (head->next == NULL)          {              head->next = s;              s->pre = head;          }          else          {   // 頭插法【逆序】              s->next = head->next;              head->next->pre = s;              head->next = s;              s->pre = head;          }          scanf("%d", &t);      }      s = head->next;      while (s)      {          printf("%d ", s->data);          s = s->next;      }      return 0;  }