【C语言进阶】假期测评②

简介: 【C语言进阶】假期测评②

day10

1. int类型字节数

求函数返回值,传入 -1 ,则在64位机器上函数返回( )

int count = 0;
int x = -1;
while (x)
{
    
    
    count++;
    x = x >> 1;
}
printf("%d", count);

A: 1 B: 2 C: 32 D: 死循环,没结果

【答案解析】C
x=x&(x-1)这个表达式执行一次就会将x的2进制中最右边的1去掉,在x变成0之前,表达式能执行几次,就去掉几个1,
所以这个代码实现了求一个有符号整数二进制补码中1的个数的功能,我们知道-1的补码是全1,而int类型4个字节32位(即使在64位机器上也是32位),

2. 算数右移

读代码选结果( )

int count = 0;
int x = -1;
while (x)
{
    
    
    count++;
    x = x >> 1;
}
printf("%d", count);

【答案解析】D
此题一个关键,==有符号数右移运算高位是补符号位的,负数的符号位是1,所以x永远不会变为0,是个死循环==

3. 关系运算符优先级

以下程序运行后的输出结果是( )

int main()
{
    
    
    int a = 1, b = 2, m = 0, n = 0, k;
    k = (n = b < a) && (m = a);
    printf("%d,%d\n", k, m);
    return 0;
}

A: 0,0 B: 0,1 C: 1,0 D: 1,1

【答案解析】A
k=(n=b<a)&&(m=a);
这部分的执行顺序如下:先执行n=b<a部分,
其中,关系运算符优先级高于赋值运算符,所以先算b<a,得到0,n=0赋值运算的结果将作为括号内表达式的结果,
即(n=b<a)&&(m=a)转换成(0)&&(m=a),
&&运算前表达式为假,则后面的括号(m=a)不运算,m值还是0,
最后,&&的结果是0,即k=0

4. 按位相加进位操作(重点)

在这里插入图片描述
【答案解析】:
十进制相加思想: 15+07 , 先计算不考虑进位的相加结果 12 (因为 5+7 的不考虑进位的结果是 2 ,遇 10 进位嘛),然后计算进位 5+7 进位是 10 ,则 10 与 12 再次相加,得到 22 ,进位为 0 ,则计算到此结束。
这里使用二进制求和完成,思想类似,但是二进制计算相加和进位不需要使用 + 符号
二进制相加思想:与十进制相同,先计算不考虑进位的相加结果( 0+0 得 0 , 1+1 进位得 0 , 1+0 得 1 ),使用异或可以取得; 然后计算相加的进位结果(同 1 的位置左移一位即可),使用相与后左移取得。

==二进制不断进位,直到无法再进位为止,计算完毕==

示例:
5 0101 + 7 0111
不考虑进位的相加结果 0101^0111 -> 0010
相加的进位 0101&0111 -> 0101 因为进位左移得到 1010
1010 + 0010
不考虑进位的相加结果 1010 ^ 0010 -> 1000
相加的进位 1010 & 0010 -> 0010 因为进位左移得到 0100
1000 + 0100
不考虑进位的相加结果 1000 ^ 0100 -> 1100
相加的进位 1000 & 0100 -> 0000 进位为0结束运算

int Add(int num1, int num2) {
    
    
    while (num2 != 0) {
    
    //进位不为0则持续与相加结果进行相加
        int tmp = num1 ^ num2;//得到每位相加不考虑进位的数据
        num2 = (num1 & num2) << 1;//同1的位相加则会进位
        num1 = tmp;
    }
    return num1;
}

5. 常规遍历 + 原地标记数组思想(重点)

在这里插入图片描述
==常规遍历思路:开辟一个新空间在对应出现的下标做标记,再遍历一遍找到未标记的数据即可== 空间复杂度:O(N)

int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {
    
    
    int* tmp = (int*)malloc(4 * numsSize);
    int* error_array = (int*)malloc(4 * numsSize);
    int count = 0;
    assert(tmp);
    memset(tmp, 0, numsSize*4);
    for (int i = 0; i < numsSize; i++)
    {
    
    
        tmp[nums[i] - 1]++;
    }
    for (int i = 0; i < numsSize; i++)
    {
    
    
        if (tmp[i] == 0)
        {
    
    
            error_array[count] = i + 1;
            count++;
        }
    }
    *returnSize = count;
    return error_array;
}

在这里插入图片描述
```c
int findDisappearedNumbers(int nums, int numsSize, int* returnSize){

int i,t=0;
intp= (int)malloc(numsSizesizeof(int));
for( i=0;i0) //原地标记数组元素,记得加abs
nums[abs(nums[i])-1]
= -1;
}
for( i=0;i0)
p[t++]=i+1;
}
*returnSize=t;
return p;
}

### day11
# 6. 算数左移和右移(重点)
> 下面函数的输出结果是( )
```c
void func()
{
int k = 1^(1 << 31 >> 31);
printf("%d\n", k);
}

A: 0 B: -1 C: -2 D: 1

【答案解析】C
(1 << 31 );左移31位,并在右侧填充0,得到0x80000000,即符号位为1,其他为0,即-2147483648
类似于char a = -128 //1000 0000 详见博客:数据的存储
这里1<<31 为`//1000 0000 0000 0000 0000 0000 0000 0000` 也就是-2147483648 int k = 1^(1 << 31 >> 31);注意,这里在右移的时候,符号位保持为1,右移后填充1,结果为0xFFFFFFFF,
// 1111 1111 1111 1111 1111 1111 1111 1111 即-1
0x00000001^0xFFFFFFFF,即0xFFFFFFFE(-2)

7. sizeof 执行时间

如下代码的输出结果是( )

#include <stdio.h>
int main()
{
    
    
    int i = 1;
    sizeof(i++);
    printf("%d\n", i);
    return 0;
}

A: 1 B: 4 C: 2 D: 8

【答案解析】A
一般表达式的运算是在运行时执行的,==而sizeof是一个编译阶段就执行的运算符,在其内的任何运算都不执行,只推测出其中表达式结果的类型求其大小,故前后i的值不变。==

day12

8. 连续运算符的表达式

请阅读以下程序,其运行结果是( )

int main()
{
    
    
    char c = 'A';
    if ('0' <= c <= '9') printf("YES");
    else printf("NO");
    return 0;
}

A: YES B: NO C: YESNO D: 语句错误

【答案解析】A
'0'<=c<='9'并非判断x大于等于字符0,小于等于字符9,而是先执行'0'<=c,使用这个表达式的结果再和'9'比较,'0'的ASCII码值是48,'A'的ASCII码值是'65',故'0'<c是真值1,1无疑是小于字符'9'的,最终是真

9. 找规律:异或

下列程序的输出结果是什么( )

#include<stdio.h>
int main()
{
    
    
    int n = 1001;
    int ans = 0;
    for (int i = 1; i <= n; ++i)
    {
    
    
        ans ^= i % 3;
    }
    printf("%d", ans);
    return 0;
}

A: -2 B: 0 C: 1 D: 2

【答案解析】B
i % 3 的值按1、2、0循环,可推算出ans按1、3、3、2、0、0循环,(关键点:ans异或后值的规律) 循环进行1001次,而1001%6=5,也就是ans按规律得
到的第5个数为最终结果,故ans=0

day13

10. unsigned类型进位

如果 x=2014 ,下面函数的返回值是( )

int fun(unsigned int x)
{
    
    
    int n = 0;
    while (x + 1)
    {
    
    
        n++;
        x = x | (x + 1);
    }
    return n;
}

A: 20 B: 21 C: 23 D 25

【答案解析】C
==unsigned int 的二进制位是32位 ==
这个作用是对整型中0的个数进行统计,x=x|(x+1);的作用是每次循环把x的二进制中从右往左数的最后一位0变成1,直道变
成全1的时候x+1就溢出为全0,循环结束。2014的二进制是0000 0000 0000 0000 0000 0111 1101 1110,所以结果是23

11. 表达式优先级

若有定义 int a[8]; ,则以下表达式中不能代表数组元素 a[1] 的地址的是( )
A: &a[0]+1
B: &a[1]
C:&a[0]++
D: a+1

【答案解析】C
==D选项a计算时是首元素地址,再加1,就是a[1]的地址==,AB明显对,==C选项a[0]先和++结合,形成一个表达式,不能对表达式
取地址,会报错==

12. 找规律:兔子出生

在这里插入图片描述
【答案解析】
在这里插入图片描述
这道题的关键在于寻找数字之间的规律,如果细心的同学会发现这其实是一个斐波那契数列。
可以看出新兔子的规律是:1 1 2 3 5 8... (都是前两个数之和)
第 n 个月的兔子数量实际上就是第 n-1 个斐波那契数。

#include <stdio.h>
int main()
{
   
   
int n;
while(~scanf("%d", &n)) {
   
   
int num1 = 1, num2 = 1, ret = 0;
for (int i = 2; i < n; i++) {
   
   
ret = num1 + num2;
num1 = num2;
num2 = ret;
}
printf("%d\n", ret);
}
return 0;
}

day14

13. 字符串当中的' \0 '

1、有以下函数,该函数的功能是( )

int fun(char *s)
{
    
    
char *t = s;
while(*t++);
return(t-s);
}

A: 比较两个字符的大小
B: 计算s所指字符串占用内存字节的个数
C: 计算s所指字符串的长度
D: 将s所指字符串复制到字符串t中

【答案解析】B
循环在t为0时停止,同时t++,*t最后会停在字符串结束的'\0'之后的一个位置
==t作为尾部指针减去头部指针就是整个字符串占用内存的字节数,包含\0在内;而c答案字符串长度不包括最后的\0==

14. 指针的移动 -- 后置++

若有

float a[3]={
    
    1.5,2.5,3.5},*pa=a;*(pa++)*=3;

则 *pa 的值是( )
A: 1.5 B: 2.5 C: 3.5 D: 4.5

【答案解析】B
*pa=a中指针pa指向a[0]; ==pa++是后置加加,返回值仍是pa;==
*(pa++)取pa指向的地址的值;*(pa++)*=3将该值变为原来的3倍,也就是数组a的第一个值为4.5;
由于pa++之后pa指针移动了sizeof(float)个字节,所以pa指向a[1],所以值为2.5

15. 对空指针的解引用

以下程序运行后的输出结果是( )

#include <stdio.h>
int main()
{
    
    
    int a[] = {
    
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, * p = a + 5, * q = NULL;
    *q = *(p + 5);
    printf("%d %d\n", *p, *q);
    return 0;
}

A: 运行后报错
B: 6 6
C: 6 11
D: 5 10

【答案解析】A
指针q初始化为NULL,接着又解引用指针q,是错误的,对NULL指针是不能解引用的

16. 指针赋值

5、以下叙述中正确的是( )
A: 即使不进行强制类型转换,在进行指针赋值运算时,指针变量的基类型也可以不同
B: 如果企图通过一个空指针来访问一个存储单元,将会得到一个出错信息
==C: 设变量p是一个指针变量,则语句p=0;是非法的,应该使用p=NULL;==
D: 指针变量之间不能用关系运算符进行比较

【答案解析】B
==A 选项描述不正确,不同类型指针一般不可以直接赋值;==
C选项中,p=NULL;和p=0;是等价的

17. 珠玑妙算

在这里插入图片描述
【答案解析】
在这里插入图片描述

int* masterMind(char* solution, char* guess, int* returnSize){
   
   
    int flag1,flag2,flag3,flag4,flag5,flag6,flag7,flag8;
    flag1=flag2=flag3=flag4=flag5=flag6=flag7=flag8=0;
    for(int i;i<4;++i)
    {
   
   
        switch(solution[i])
        {
   
   
        case'R':
            flag1++;
            break;
        case'Y':
            flag2++;
            break;
        case'G':
            flag3++;
            break;
        case'B':
            flag4++;
            break;
        }
    }
    for(int i;i<4;++i)
    {
   
   
        switch(guess[i])
        {
   
   
        case'R':
            flag5++;
            break;
        case'Y':
            flag6++;
            break;
        case'G':
            flag7++;
            break;
        case'B':
            flag8++;
            break;
        }
    }
    int x1=flag1<flag5?flag1:flag5;
    int x2=flag2<flag6?flag2:flag6;
    int x3=flag3<flag7?flag3:flag7;
    int x4=flag4<flag8?flag4:flag8;
    int falseGuess=x1+x2+x3+x4;
    int count=0;
    for(int i=0;i<4;++i)
    {
   
   
        if(solution[i]==guess[i])
        {
   
   
            count++;
        }
    }
    falseGuess-=count;
    int*ret = (int*)calloc(2, sizeof(int));
    *returnSize=2;
    ret[0]=count;
    ret[1]=falseGuess;
    return ret;
}

day15

18. void* 指针的用法

2、关于指针下列说法正确的是【多选】( )
A: 任何指针都可以转化为void
B: void
可以转化为任何指针
C: 指针的大小为8个字节
D: 指针虽然高效、灵活但可能不安全

【答案解析】ABD
==void*类型的指针可以转换成任何指针,任何指针也可以转换成void*类型==

19. 二分思想

在这里插入图片描述

在这里插入图片描述

int findPeakElement(int* nums, int numsLen ) 
{
   
   
    int left = 0;
    int right = numsLen - 1;
    while(left < right)
    {
   
   
        int mid = ((right - left) >> 1) + left; //防止直接相加发生溢出
        if(nums[mid] < nums[mid + 1])
            left = mid + 1;
        else
            right = mid;
    }
    return left;
}

day16

20. strcpy参数

请指出以下程序的错误【多选】( )

void GetMemory(char** p, int num)
{
    
    
    if (NULL == p && num <= 0)//1
        return;
    *p = (char*)malloc(num);
    return;
}
int main()
{
    
    
    char* str = NULL;
    GetMemory(&str, 80); //2
    if (NULL != str)
    {
    
    
        strcpy(&str, "hello"); //3
        printf(str); //4
    }
    return 0;
}

A: 1 B: 2 C: 3 D: 4

【答案解析】AC
第1处两种情况之一成立都是要返回的,应该用或,此处用与错误。在语句GetMemory(&str,100);中传入str的地址,在语句char*str=NULL;中str初始化为空指针,但是str指针变量也有地址,所以参数char**p里面的p保存的是指针变量str的地址,所以调用GetMemory函数之后,动态开辟的空间的地址存放在了str中,在函数返回之后没有释放内存,但是这不会导致程序错误,只会导致内存泄漏。==第3处用&str是错的,应该直接用str,是刚申请下来的空间首地址,可以用来接收字符串的copy。==

21. 大小端字节序

请问下列代码的输出结果有可能是哪些【多选】( )

#include <stdio.h>
typedef union
{
    
    
    int a;
    struct
    {
    
    
        short b;
        short c;
    };
}X;
int main()
{
    
    
    X x;
    x.a = 0x20150810;
    printf("%x,%x\n", x.b, x.c);
    return 0;
}

A: 2015,810 B: 50810,201 C: 810,2015 D:`20150,810

【答案解析】AC
对于0x20150810
==如果按照大端模式存储:从低地址到高地址:20 15 08 10 输出从低地址到高地址:20 15 08 10==
==如果按照小端模式存储:从低地址到高地址:10 08 15 20 输出从高地址到低地址:08 10 20 15==
此数以int类型赋值给联合体x.a,而以结构成员b和c分开访问,分别拿到低地址的2个字节和高地址的2个字节,
大端下是2015和810,小端下是810和2015

22. unsigned char 范围

请问下列程序的输出是多少()

#include<stdio.h>
int main()
{
    
    
    unsigned char i = 7;
    int j = 0;
    for (; i > 0; i -= 3)
    {
    
    
        ++j;
    }
    printf("%d\n", j);
    return 0;
}

A: 2 B: 死循环 C: 173 D: 172

【答案解析】C
本题就是找规律,计算什么时候能遇到0
unsigned char 8位数据位,范围在0-255,==所以-2(11111110)时,变成254==;同理-1(11111111)时,变成255;最
后减到0时,不满足循环条件,for停止。刚好173次。
7 4 1 = = > 共(7-1)/3+1=3次(1-3=-2,即254,继续循环)
254 251 ... 5 2 = = > 共(254-2)/3+1=85次(2-3=-1,即255,继续循环)
255 252 ... 6 3 = = > 共(255-5)/3+1=85次(3-3=0,退出循环) 所以总共173次

23. 寻找数学规律

在这里插入图片描述

【答案解析】:
暴力破解:将 x 和 y 分别遍历 [1, n] ,进行判断当 x % y > k 时统计计数 count++ 即可,但是这样的话当 n 的值非
常大的时候循环次数将非常恐怖,需要循环 n^2 次。

更优解法: 假设输入 n=10 , k=3 ;
当 y <=k 时,意味着任何数字取模y的结果都在 [0, k-1]之间,都是不符合条件的。
当 y = k+1=4 时,x符合条件的数字有 3,7
当 y = k+2=5 时,x符合条件的数字有 3,4,8,9
当 y = k+3=6 时,x符合条件的数字有 3,4,5,9,10
当 y = k+n时,
x小于y当前值,且符合条件的数字数量是:y-k个,
x大于y当前值,小于2y的数据中,且符合条件的数字数量是:y-k个
从上一步能看出来,在y的整数倍区间内,x符合条件的数量就是 (n / y) `
` (y - k)个
n / y 表示有多少个完整的 0 ~ y区间, y - k 表示有每个区间内有多少个符合条件的数字
最后还要考虑的是6...往后这种超出倍数区间超过n的部分的统计
n % y 就是多出完整区间部分的数字个数,其中k以下的不用考虑,则符合条件的是 n % y - (k-1) 个
这里需要注意的是类似于9这种超出完整区间的数字个数 本就小于k的情况,则为0
最终公式:(n / y) * (y - k) + ((n % y < k) ? 0, (n % y - k + 1));

#include <stdio.h>
int main()
{
   
   
    long n, k;
    while (~scanf("%ld %ld", &n, &k)) 
    {
   
   
        if (k == 0) 
        {
   
   
            printf("%ld\n", n * n);//任意数对的取模结果都是大于等于0的
            continue;
        }
        long count = 0;
        for (long y = k + 1; y <= n; y++) 
        {
   
   
            count += ((n / y) * (y - k)) + ((n % y < k) ? 0 : (n % y - k + 1));
        }
        printf("%ld\n", count);
    }
    return 0;
}

在这里插入图片描述

相关文章
|
2天前
|
存储 C语言
C语言进阶---------作业复习
C语言进阶---------作业复习
|
2天前
|
存储 Linux C语言
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-2
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
2天前
|
自然语言处理 Linux 编译器
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-1
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
2天前
|
存储 编译器 C语言
C语言进阶第十课 --------文件的操作-1
C语言进阶第十课 --------文件的操作
|
2天前
|
存储 程序员 C语言
C语言进阶第九课 --------动态内存管理-2
C语言进阶第九课 --------动态内存管理
|
2天前
|
编译器 C语言
C语言进阶第九课 --------动态内存管理-1
C语言进阶第九课 --------动态内存管理
|
2天前
|
C语言
C语言进阶第八课 --------通讯录的实现
C语言进阶第八课 --------通讯录的实现
|
2天前
|
存储 算法 C语言
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
|
2天前
|
存储 人工智能 搜索推荐
【C语言进阶】 假期测评①
【C语言进阶】 假期测评①
38 2
|
2天前
|
存储 测试技术 C语言
【C语言进阶】 假期测评③
【C语言进阶】 假期测评③
45 1