C语言常考及易错题整理
选择题
全局、局部和静态变量
- 执行下面程序,正确的输出是:
int x=5,y=7;
void swap()
{
int z;
z=x;
x=y;
y=z;
}
int main()
{
int x=3,y=8;
swap();
printf("%d,%dn",x, y);
return 0;
}
答案解析:
正确答案:3,8
swap函数调用时用的是全局变量,主函数中定义的变量只在主函数中有效,因为主函数也是一个函数,它与其他函数是平 行关系;输出语句这里,考虑局部优先的原则
- 如下函数的f(1)的值为:
int f(int n)
{
static int i = 1;
if(n >= 5)
return n;
n = n i;
i ;
return f(n);
}
答案解析:
正确答案: 7
此题注意静态局部变量的使用,static
改变了i
的生命周期,第一次调用函数:i
初值是1,递归第二次调用函数时,i
还是第一 次那个变量,值已经变成了2,再一次调用函数时i
就是3,依次类推
- 以下程序的输出结果为:
#include <stdio.h>
int i;
void prt()
{
for (i = 5; i < 8; i )
printf("%c", '*');
printf("t");
}
int main()
{
for (i = 5; i <= 8; i )
prt();
return 0;
}
答案解析:
正确答案:***
全局变量i
,在main()
中修改为5,第一次在prt()
中执行循环输出三次'*'
,i
被修改为8,执行一次print("t")
,回到主函数后i
变为9,i<=8
为假,循环结束;
#define与typedef
test.c
文件中包括如下语句,文件中定义的四个变量中,是指针类型的变量为:【多选】
#define INT_PTR int*
typedef int* int_ptr;
INT_PTR a, b;
int_ptr c, d;
答案解析:
正确答案:acd
因为#define
是宏定义,仅仅是直接替换,INT_PTR a, b
; 进行宏替换后代码是这样的:int *a, b
;这里的int *
是a的类型,b的类型是int
,故此次b只是int
类型
而typedef
是把该类型定义一个别名,别名是一个独立的类型了,使用这个类型创建的变 量都是这个类型的。
所以 a,c,d
才是指针类型
- 以下程序结果输出是什么
#include <stdio.h>
#define N 2
#define M N 1
#define NUM (M 1)*M/2
int main()
{
printf("%dn",NUM);
return 0;
}
答案解析:
正确答案:8
宏只是替换,替换后NUM的样子是(2 1 1)*2 1/2
,计算得8
转义字符
- 以下程序段输出结果是什么:
#include<stdio.h>
int main()
{
char s[] = "\123456123456t";
printf("%dn", strlen(s));
return 0;
}
答案解析:
正确答案:12
这里考查转义字符,注意:\
表示字符''
,123
(ASCII码为83)表示字符'S'
,t
表示制表符,这些都是一个字符
操作符
- 下面代码段的输出是:
#include <stdio.h>
int main()
{
int a=3;
printf("%dn",(a =a-=a*a));
return 0;
}
答案解析:
正确答案:-12
a =a-=a*a
等价于a=a (a=a-a*a)
,即先计算``a=a-a*a,所以此时a的值为3-3*3=-6,再计算-6 (-6)=-12赋值给
a,所以
a`的值 为-12,也就是整个表达式的值,就是-12
循环
- 我们知道C语言的 break 语句只能跳出离它最近的一层循环,可是有时候我们需要跳出多层循环,下列跳出多层循环的做法正确的是【多选】( ) A: 将程序写成函数用return结束函数,便可跳出循环
B: 修改外层循环条件例如
代码语言:javascript复制for( int i = 0 ; i < MAX1 ; i )
{
for( int j = 0 ; j < MAX2 ; j )
{
if( condition )
{
i = MAX1;
break;
}
}
}
C:在外层循环设置判断条件例如
代码语言:javascript复制for( ; symbol != 1 && condition2 ; )
{
for( ; symbol != 1 && condition3 ; )
{
if( condition1 )
symbol = 1 ;
}
}
D: 在外层循环后面加入break例如
代码语言:javascript复制for( ; condition2 ; )
{
for( ; condition3 ; )
{
if( condition1 )
symbol = 1 ;
}
if( symbol == 1 )
break ;
}
答案解析:
正确答案:ABCD
此题旨在整理跳出多层循环的方法,每个选项都是正确的,代码为伪代码,condition
代表逻辑表达式
- 执行下面的程序段,语句3的执行次数为( )
or(i = 0; i <= n-1; i ) // (1)
for(j = n; j > i; j--) // (2)
state; // (3
答案解析:
正确答案:n(n 1)/2
外循环有n次,当i=0,内循环为n次,当i=1,内循环为n-1次,当i=2时,内循环为n-2次,以此类推,总次数为 n (n-1) (n-2) … 2 1,就是个等差数列,等于n(n 1)/2
- 对于下面说法:正确的是()
t=0;
while(printf("*"))
{
t ;
if (t<3)
break;
}
A: 其中循环控制表达式与0等价 B: 其中循环控制表达式与’0’等价 C: 其中循环控制表达式是不合法的 D: 以上说法都不对
答案解析:
正确答案:B
因print(“*”)
函数的返回值是字符串中字符的个数,即为1。所以while
后面的条件恒为真,所以循环控制表达式与'0'
是等 价的(字符'0'
不是0
)。
- 设变量已正确定义,以下不能统计出一行中输入字符个数(不包含回车符)的程序段是( )
A: n=0;while(ch=getchar()!='n') n ;
B: n=0;while(getchar()!='n') n ;
C: for(n=0;getchar()!='n';n );
D: n=0;for(ch=getchar();ch!='n';n );
答案解析:
正确答案:D
对于for
循环,其中第一项初始化表达式只执行一次,因此ch
只从输入流中取一个字符,之后就再不会取字符,因此会死循环
- 若运行以下程序时,从键盘输入
ADescriptor<回车>
,则下面程序的运行结果是( )
#include <stdio.h>
int main()
{
char c;
int v0=0,v1=0,v2=0;
do
{
switch(c=getchar())
{
case'a':case'A':
case'e':case'E':
case'i':case'I':
case'o':case'O':
case'u':case'U':v1 = 1;
default:v0 = 1;v2 =1;
}
}while(c!='n');
printf("v0=%d,v1=%d,v2=%dn",v0,v1,v2);
return 0;
}
答案解析:
正确答案:12 4 12
代码switch
语句中没有break
,则每次找到入口进入后,顺序执行到代码块结束为止。例如当c
为'A'
时,从case 'A'
进入,先 后执行v1 =1;v0 =1;v2 =1;
,而当c
为'p'
时,从default
进入,先后执行v0 =1;v2 =1;
,容易看出最终v0
和v2
是相等的
其他
- 对于下面说法:正确的是()
A. 对于 struct X{short s;int i;char c;}
,sizeof(X)
等于sizeof(s) sizeof(i) sizeof(c)
B. 对于某个double
变量 a
,可以使用 a == 0.0
来判断其是否为零
C: 初始化方式 char a[14] = "Hello, world!";
和char a[14]; a = "Hello, world!";
的效果相同
D: 以上说法都不对
答案解析:
正确答案:D
A选项,没有考虑内存对齐。
B选项,考察double
类型的比较,由于浮点数存在误差,不能直接判断两个数是否相等,通常采用比较两数之差的绝对值是否小于一个很小的数字(具体的可自己设定这样一个数,作为误差)来确定是否相等。
C选项,a
为数组首地址是常量不能改变,
所以A,B,C都是错的,选择D
编程题
计算日期到天数转换
根据输入的日期,计算是这一年的第几天。 保证年份为4位数且日期合法。 输入描述:输入一行,每行空格分割,分别是年,月,日。 输出描述:输出是这一年的第几天
这道题简单解法其实将每个月的天数枚举出来,然后根据当前月份向前累加满月的天数,然后再加上当前月所在的天数。最终考虑平闰年的 2 月份区别是否增加一天。 其中需要注意的是平年和闰年的判断,而且是闰年的月份大于 2 的时候,也就是 2 月走完,总天数才能加 1 (比 如 2000年2月18日 ,虽然是闰年,但是 2月 都没走完那是不能加上闰年多出的一天的).
代码语言:javascript复制#include <stdio.h>
int is_leap_year(int year) {
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0){
return 1;
}
return 0;
}
int main()
{
int month_day[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int year, month, day;
while (scanf("%d %d %d", &year, &month, &day)==3){
int total_day = day;//先把当前月份天数加上
if (is_leap_year(year) && month > 2) {
//若闰年,且月份大于2月,则在平年基础上 1
total_day = 1;
}
for (int i = month - 1; i > 0; i--) {
total_day = month_day[i];//向前累加每月的天数即可
}
printf("%dn", total_day);
}
return 0;
}
柯尼希定理
验证尼科彻斯定理,即:任何一个整数m的立方都可以写成m个连续奇数之和。 例如: 1^3=1 2^3=3 5 3^3=7 9 11 4^3=13 15 17 19 输入一个正整数m(m≤100),将m的立方写成m个连续奇数之和的形式输出。 数据范围:1≤m≤100
这道题的关键在于知道规律后,能够找到第 n 个数据立方的起始奇数,从这个起始奇数开始,组成连续的n个奇数项之和的表达式即可。
代码语言:javascript复制等差数列和为m^3,项数为m,公差d为2,求首项a1 :
ma1 (m ( m -1 ) / 2 ) * 2 = m^3
最终得到起始奇数:a1= m * (m - 1) 1
代码语言:javascript复制#include <stdio.h>
int main()
{
int m;
while(scanf("%d", &m)==1){
int start = m * (m - 1) 1;//找到对应m^3的起始奇数
char buf[10240] = {0};
//sprintf(buf, format, ...) 与printf用法类似,格式化字符串但是不用于打印而是放到一个buf中
sprintf(buf, "%d", start);//先将起始奇数转换成为字符串存入buf中
for (int i = 1; i < m; i ) {
//然后将紧随随后的m-1个奇数数字转换为字符串,按照指定格式放入buf中
//%s %d, 要求先有一个字符串,然后是 符号,然后是个数字的格式,对应是buf原先的数据,和奇数
sprintf(buf, "%s %d", buf, start =2);
}
printf("%sn", buf);
}
return 0;
}
旋转数组的最小数字
描述 有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
暴力破解:遍历数组找出最小值即可
更优思想:采用二分查找,这个题主要分析三种旋转情况 [1, 2, 3, 4, 5],使用中间值与右端进行比较。
1. 中间大于右边 [3, 4, 5, 1, 2],这种情况下,最小数一定在右边;则left = middle 1
2.中间等于右边 [1, 0, 1, 1, 1], 这个是[0, 1, 1, 1, 1] 旋转过来的,这时候需要缩小范围 right–;,注意不能是 left ,因为是非降序数组,所以要缩小右边范围,符合我们的判断规则。 3. 中间小于右边 [5, 1, 2, 3, 4], 这种情况下,最小数字则在左半边;则right = middle
说白了,最小数左右两边都非递减排列,并且左排序数组大于等于右边的值
代码语言:javascript复制int minNumberInRotateArray(int* nums, int numsLen ) {
int right = numsLen - 1;
int left = 0;
while (left < right) {
int mid = (right - left) / 2 left;
if (nums[mid] > nums[right])
left = mid 1;
else if (nums[mid] < nums[right])
right = mid;
else
right--;
}
return nums[left];
}
错误的集合
集合
s
包含从1
到n
的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。 给定一个数组nums
代表了集合S
发生错误后的结果。 请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
重复的数字在数组中出现 2 次,丢失的数字在数组中出现 0 次,其余的每个数字在数组中出现 1 次。由此可见,重复的数字和丢失的数字的出现次数的奇偶性相同,且和其余的每个数字的出现次数的奇偶性不同。如果在数组的 n 个数字后面再添加从 1 到 n 的每个数字,得到 2n 个数字,则在 2n 个数字中,重复的数字出现 3 次,丢失的数字出现 1 次,其余的每个数字出现 2 次。根据出现次数的奇偶性,可以使用异或运算求解。
用 x 和 y 分别表示重复的数字和丢失的数字。考虑上述 2n 个数字的异或运算结果 xor
,由于异或运算 ⊕ 满足交换律和结合律,且对任何数字 a 都满足 a⊕a=0 和 0⊕a=a,因此 xor=x⊕x⊕x⊕y=x⊕y,即 x 和 y 的异或运算的结果。
令 lowbit=xor & (−xor)
,**则 lowbit
为 x 和 y 的二进制表示中的最低不同位,**可以用 lowbit
区分 x
和 y
。
得到 lowbit
之后,可以将上述 2n 个数字分成两组,第一组的每个数字 a 都满足 a & lowbit=0
,第二组的每个数字 b
都满足 b & lowbit!=0
。
int* findErrorNums(int* nums, int numsSize, int* returnSize) {
int* newnums = (int*)malloc(2 * sizeof(int));
*returnSize = 2;
int xor = 0;
for (int i = 1; i <= numsSize; i ) {
xor ^= i ^ nums[i - 1];
}
int lowbit = xor&(-xor);
int num1 = 0;
int num2 = 0;
for (int i = 0; i < numsSize; i ) {
if ((lowbit & nums[i]) == 0)
num1 ^= nums[i];
else
num2 ^= nums[i];
}
for (int i = 1; i <= numsSize; i ) {
if ((lowbit & i) == 0)
num1 ^= i;
else
num2 ^= i;
}
// 要求第一个是重复的数,第二个是丢失的整数
for (int i = 0; i < numsSize; i ) {
if (num1 == nums[i]) {
newnums[0] = num1;
newnums[1] = num2;
return newnums;
}
}
newnums[0] = num2;
newnums[1] = num1;
return newnums;
}
整数转换
整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。
示例1:
代码语言:javascript复制 输入:A = 29 (或者0b11101), B = 15(或者0b01111)
输出:2
示例2:
代码语言:javascript复制 输入:A = 1,B = 2
输出:2
即求A与B异或的值中1的个数, 通过n&(n - 1)
可以去掉一个数的二进制表示的最右边的1
int convertInteger(int A, int B) {
unsigned int temp = A ^ B;
int count = 0;
while (temp) {
temp &= (temp - 1);
count ;
}
return count;
}
注意这里需要用unsigned int
来存储两数异或的结果,因为如果用int
类型,当两数异或结果为
10000000000000000000000000000000
即int
类型能表示的负数的最小值,-231
此时再实施减一操作会超出范围,所以要转换为unsigned int
类型
密码检查
小明同学最近开发了一个网站,在用户注册账户的时候,需要设置账户的密码,为了加强账户的安全性,小明对密码强度有一定要求:
- 密码只能由大写字母,小写字母,数字构成;
- 密码不能以数字开头;
- 密码中至少出现大写字母,小写字母和数字这三种字符类型中的两种;
- 密码长度至少为8
现在小明受到了n个密码,他想请你写程序判断这些密码中哪些是合适的,哪些是不合法的。 输入描述: 输入一个数n,接下来有n(n≤100)行,每行一个字符串,表示一个密码,输入保证字符串中只出现大写字母,小写字母和数字,字符串长度不超过100。 输出描述: 输入n行,如果密码合法,输出YES,不合法输出NO
代码语言:javascript复制#include <stdio.h>
#include <string.h>
int main()
{
int n;
while(scanf("%d", &n)==1) {
for (int i = 0; i < n; i ) {
char password[101] = {0};
int upper = 0, lower = 0, digit = 0, other = 0;
scanf("%s", password);//捕捉输入的密码
if (strlen(password) < 8) {//密码长度小于8
printf("NOn");
continue;
}
if (password[0] >= '0' && password[0] <= '9') {//密码以数字开头
printf("NOn");
continue;
}
char *ptr = password;
while(*ptr != '