习题练习 C语言(暑期第三弹)
自我小提升!
前言
重要的事说三遍!
学习!学习!学习!
一、存储地址
二维数组X按行顺序存储,其中每个元素占1个存储单元。若 X[4][4] 的存储地址为 Oxf8b82140 , X[9][9] 的存储地址为 Oxf8b8221c ,则 X[7][7] 的存储地址为( )
A: Oxf8b821c4
B: Oxf8b821a6
C:Oxf8b82198
D: Oxf8b821c0
题目解析:
假设每行有n个元素:那x[9][9]元素的地址 - x[4][4]元素的地址 = 0x21c-0x140=5n+5(21c和140是地址末三位的十六进制数),这里n是43,假设x[7][7]的地址是z,x[7][7]元素的地址 - x[4][4]元素的地址 = 0x140 = 3n+3,z = 3n+3+140 =3*43+3+0x140 = 0x84+0x140 = 0x1c4,看地址的尾数,选择A
题目答案:
A
二、逗号表达式
以下逗号表达式的值为( )
(x= 4 * 5 , x * 5) , x + 5;
A: 25 B: 20 C: 100 D: 45
题目解析:
逗号表达式是从前到后依次计算子表达式,而其结果是最后一项的值,此题去掉括号后的表达式,和原表达式是等价的,先计算45并赋值给x,x变为20,中间x5并没有改变x的值,最后一项x+5值是25,也就是整个表达式的值
题目答案:
A
三、除自身以外数组的乘积
题目链接:OJ链接
提示:
2 <= nums.length <= 105
-30 <= nums[i] <= 30
保证数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
题目解析:
将乘积分为两次进行,第一次先将每个位置左边的数据乘积计算出来放到返回数组中,后边第二次循环
将对应位置右边的数据乘积计算出来与返回数组对应位置的左半边乘积相乘得到结果。
示例: 一个数组 int nums[] = {2, 3, 4} 。
int left = 1, right = 1;
计算左侧乘积:
第0个元素的左边乘积, arr[0] = left 然后计算第1位左侧乘积
left*=nums[0] -> left = 12
第1个元素的左边乘积, arr[1] = left 然后计算第2位左侧乘积
left=nums[1] -> left = 123
第2个元素的左边乘积, arr[2] = left 然后计算第3位左侧乘积 已经没必要了,因为第2元素是末尾元素了
一次循环完毕后,返回数组中每个元素存储的都是自己左侧元素的乘积。 arr[]中的值: [1, 2, 6]
计算右侧乘积:
第2个元素的右边乘积, arr[2] = right 然后计算第1位右侧乘积 right=nums[2] -> right =14
第1个元素的右边乘积, arr[1] = right 然后计算第0位右侧乘积 right=nums[1] -> right =143
第0个元素的右边乘积, arr[0] = right 然后计算第-1位右侧乘积 -1位已经不需要计算了
循环完毕后,返回数组中的每个元素都是其他元素的乘积了 arr[2]=1; arr[1]=4; arr[0]*=12
题目答案:
int* productExceptSelf(int* nums, int numsSize, int* returnSize){
*returnSize=numsSize;
static int answer[100000]={0};
int left=1,right=1;
for(int i=0;i<numsSize;i++){
answer[i]=left;
left*=nums[i];
}
for(int i=numsSize-1;i>=0;i--){
answer[i]*=right;
right*=nums[i];
}
return answer;
}
四、字节与二进制
求函数返回值,传入 -1 ,则在64位机器上函数返回( )
int func(int x)
{
int count = 0;
while (x)
{
count++;
x = x&(x - 1);//与运算
}
return count;
}
A: 死循环 B: 64 C: 32 D: 16
题目解析:
x=x&(x-1)这个表达式执行一次就会将x的2进制中最右边的1去掉,在x变成0之前,表达式能执行几次,就去掉几个1,所以这个代码实现了求一个有符号整数二进制补码中1的个数的功能,我们知道-1的补码是全1,而int类型4个字节32位,选C
题目答案:
C
五、符号计算
以下程序运行后的输出结果是( )
int main()
{
int a=1,b=2,m=0,n=0,k;
k=(n=b<a)&&(m=a);
printf("%d,%dn",k,m);
return 0;
}
A: 0,0 B: 0,1 C: 1,0 D: 1,1
题目解析:
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
题目答案:
A
六、不用加减乘除做加法
题目链接:OJ链接
题目解析:
十进制相加思想: 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) {
int tmp = num1 ^ num2;
num2 = (num1 & num2) << 1;
num1 = tmp;
}
return num1;
}
七、unsigned判断
关于代码的说法正确的是( )
#include <stdio.h>
int main()
{
int x = -1;
unsigned int y = 2;
if (x > y)
{
printf("x is greater");
}
else
{
printf("y is greater");
}
return 0;
}
A: x is greater B: y is greater C: 依赖实现 D: 随机
题目解析:
x是有符号数-1,内存中是全1,当有符号的x和无符号数进行比较时,x会隐式类型转换被当做无符号数,是一个很大的数,这时就选择A了
题目答案:
A
八、移位计算
下面函数的输出结果是( )
void func()
{
int k = 1^(1 << 31 >> 31);
printf("%dn", k);
}
A: 0 B: -1 C: -2 D: 1
题目解析:
(1 << 31 );左移31位,并在右侧填充0,得到0x80000000,即符号位为1,其他为0,即-2147483648
int k = 1^(1 << 31 >> 31);
注意,这里在右移的时候,符号位保持为1,右移后填充1,结果为0xFFFFFFFF,即-1,0x00000001^0xFFFFFFFF,即0xFFFFFFFE(-2)
题目答案:
C
九、sizeof宏
如下代码的输出结果是( )
#
include <stdio.h>
int main()
{
int i = 1;
sizeof(i++);
printf("%dn", i);
return 0;
}
A: 1 B: 4 C: 2 D: 8
题目解析:
一般表达式的运算是在运行时执行的,而sizeof是一个编译阶段就执行的运算符,在其内的任何运算都不执行,只推测出其中表达式结果的类型求其大小,故前后i的值不变。
题目答案:
A
十、移位计算
下面函数的输出结果是( )
void func()
{
int k = 1^(1 << 31 >> 31);
printf("%dn", k);
}
A: 0 B: -1 C: -2 D: 1
题目解析:
(1 << 31 );左移31位,并在右侧填充0,得到0x80000000,即符号位为1,其他为0,即-2147483648
int k = 1^(1 << 31 >> 31);
注意,这里在右移的时候,符号位保持为1,右移后填充1,结果为0xFFFFFFFF,即-1,0x00000001^0xFFFFFFFF,即0xFFFFFFFE(-2)
题目答案:
C
十一、移位计算
请阅读以下程序,其运行结果是( )
int main()
{
char c='A';
if('0'<=c<='9') printf("YES");
else printf("NO");
return 0;
}
A: YES B: NO C: YESNO D: 语句错误
题目解析:
‘0’<=c<=‘9’并非判断x大于等于字符0,小于等于字符9,而是先执行’0’<=c,使用这个表达式的结果再和’9’比较,‘0’的ASCII码值是48,‘A’的ASCII码值是’65’,故’0’<c是真值1,1无疑是小于字符’9’的,最终是真
题目答案:
A
十二、优先级判断
C 语言中,下列运算符优先级最高的是 ( )
A: !
B: %
C: >>
D: ==
题目解析:
单目运算符的优先级通常都比较高,具体情况可查阅运算符优先级表格
优先级表:
题目答案:
C
十三、单词倒排
题目链接:OJ链接
题目解析:
本题可以先将每两个空格之间的单句反转,最后逆序输出,就可以实现单词逆序排放啦
题目答案:
#include <stdio.h>
#include <string.h>
int main() {
char arr[10001];//接收字符串
while (gets(arr) > 0) {
char*dete=arr;
while(*dete!=''){//利用dete指针将字符串中的非字母符号变为空格
if((*dete >= 'a' && *dete <= 'z') || (*dete >= 'A' && *dete <= 'Z'))
dete++;
else{
*dete=' ';
dete++;
}
}
char*left=arr;//单句左指针
char*right=arr;//单句右指针
char*jb=arr;//循环结束判断指针
while(*jb!=''){
while(*right != ' ' && *right != '')//right指针找到单句右边的第一个空格或
right++;
jb=right;//将right的地址给到jb,如果为则说明字符串单句逆序完成
while(*left == ' ')//left指针找到单句左边第一个字母
left++;
char*moveleft=left;//moveleft指向单句左边第一个字母
char*moveright=right-1;//moveright指向单句右边第一个字母
while(moveleft<moveright){//单句逆序
char temp=*moveleft;
*moveleft=*moveright;
*moveright=temp;
moveleft++;
moveright--;
}
left=right;//让left继承right指针的位置
while(*right == ' ')//将right指针指向下一个单句的首字母位置
right++;
}
char*ptr=left-1;//结束单句逆序时left指针指向,则让ptr指向left-1,即最后一个单词的位置
while(ptr>=arr){//逆序输出
printf("%c",*ptr);
ptr--;
}
}
return 0;
}
总结
重要的事说三遍!
进步!进步!进步!