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;  }