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