day2
一、选择题
1. 使用printf函数打印一个double类型的数据,要求:输出为10进制,输出左对齐30个字符,4位精度。以下哪个选项是正确的?
A %-30.4e
B %4.30e
C %-30.4f
D %-4.30f
#printf的格式控制
正确答案:C
2. 请找出下面程序中有哪些错误()
int main(){
int i = 10;
int j = 1;
const int *p1;//(1)
int const *p2 = &i; //(2)
p2 = &j;//(3)
int *const p3 = &i;//(4)
*p3 = 20;//(5)
*p2 = 30;//(6)
p3 = &j;//(7)
return 0;
}
A 1,2,3,4,5,6,7
B 1,3,5,6
C 6,7
D 3,5
const int *p1
:常量指针,指向常量int const *p2 = &i
:常量指针,指向常量int *const p3 = &i
:指针常量,指针本身是常量
(6):尝试改变常量指针指向的常量,错误。
(7):尝试改变指针常量,指针本身是常量,无法改变,错误。
正确答案:C
#指针常量和常量指针
const int* ptr
常量指针:指向常量的指针。
int* const ptr
指针常量:指针类型的常量。
可以这么理解:const保护它后面紧跟的那个
- const后面紧跟
*
:保护*
(解引用找到的的数据) - const后面紧跟
ptr
:保护ptr
(指针变量本身)
3. 下面叙述错误的是()
char acX[]="abc";
char acY[]={'a','b','c'};
char *szX="abc";
char *szY="abc";
A acX与acY的内容可以修改
B szX与szY指向同一个地址
C acX占用的内存空间比acY占用的大
D szX的内容修改后,szY的内容也会被更改
题中,
acX是双引号初始化的字符数组,自带\0做结尾
acY是花括号初始化的字符数组,不带\0做结尾
szX是字符指针,用字符串初始化,相当于把字符串的首元素地址赋给字符指针
szY是字符指针,用字符串初始化,相当于把字符串的首元素地址赋给字符指针
需要注意的是,这里的“abc”是常量字符串,所以szX和szY指向同一空间。
A:没问题
B:没问题,“abc”是常量字符串,两个指针都指向常量区的"abc"
C:没问题,acX自带\0做结尾,acY不带\0做结尾,前者多了一个’\0’
D:有问题,szX和szY根本不能修改。
正确答案:D
#字符指针和字符数组的初始化
字符数组的初始化:
- 双引号初始化,自带\0做结尾
- 花括号初始化,不带\0做结尾,如果是不完全初始化,未初始化的默认初始化为0
字符指针的初始化:
- 用常量字符串给字符指针初始化,相当于把字符串的首元素地址赋给字符指针
4. 在头文件及上下文均正常的情况下,下列代码的运行结果是()
int a[] = {1, 2, 3, 4};
int *b = a;
*b += 2;
*(b + 2) = 2;
b++;
printf("%d,%d\n", *b, *(b + 2));
A 1,3
B 1,2
C 2,4
D 3,2
b指向a的首元素,1+=2,对b+2解引用,找到3,3=2,b++,b指向2,打印*b(2)和b往后跨2个int的位置(4)。
正确答案:C
#指针加减整数
5. 下列关于C/C++的宏定义,不正确的是()
A 宏定义不检查参数正确性,会有安全隐患
B 宏定义的常量更容易理解,如果可以使用宏定义常量的话,要避免使用const常量
C 宏的嵌套定义过多会影响程序的可读性,而且很容易出错
D 相对于函数调用,宏定义可以提高程序的运行效率
#宏
本质是替换。
优点
- 代码可复用性高,效率高
缺点
- 代码可读性差,容易误用
- 没有类型的安全检查
- 无法调试(预处理期间直接替换)
带着这些了解,能轻松知道B之外的都是对的。B恰恰说反了,由于宏没有类型的安全检查,预处理后也会直接替换,所以不安全,更要尽量使用const常量。
正确答案:B
6. 有以下定义,正确的函数调用是()
int a[10];
char b[80];
函数声明为:
void sss(char[],int[]);
A sss(a,b);
B sss(char b[],int a[]);
C sss(b[],a[]);
D sss(b,a);
A:实参不对应
B:传了个类型过去,不合语法规范
C:实参不是char* 和 int*
D:显然正确
正确答案:D
#数组作参数
数组传参数一般不传本身,如果数组大,一来一去效率太低。所以都是传首元素地址,也能访问。
数组做参数:可以传同类型的数组或指针,形参拿到手里的都一样——都是首元素地址
7. 用变量a给出下面的定义:一个有10个指针的数组,该指针指向一个函数,该函数有一个整形参数并返回一个整型()
A int *a[10];
B int (*a)[10];
C int (*a)(int);
D int (*a[10])(int);
A:[]和a结合,a是一个数组,存放int*,所以是指针数组。
B:*和a结合,a是一个指针,指向int[10],所以是数组指针。
C:*和a结合,a是一个指针,指向有一个形参int,返回值为int的函数,所以是函数指针。
D::[]和a结合,a是一个数组,存放指针,它指向有一个形参int,返回值为int的函数,所以是函数指针数组。
正确答案:D。
#指针数组和数组指针
8. 以下 C++ 函数的功能是统计给定输入中每个大写字母的出现次数(不需要检查输入合法性,所有字母都为大 写),则应在横线处填入的代码为()
void AlphabetCounting(char a[], int n)
{
int count[26] = {}, i, kind = 10;
for (i = 0; i < n; ++i)
_________________;
for (i = 0; i < 26; ++i)
{
printf("%c=%d", _____, _____);
}
}
A
++count[a[i]-'Z']
'Z'-i, count['Z'-i]
B
-
++count['A'-a[i]]
-
'A'+i, count[i]
C
-
++count[i]
-
i, count[i]
D
-
++count['Z'-a[i]]
-
'Z'-i, count[i]
#hash映射和数组下标规则
统计给定输入中每个大写字母的出现次数,用到了hash映射的思想。第一个空,遍历给定输入,映射并记录出现次数;第二个空,找到 [对应的字符] 和其 [出现次数] 打印
A:++count[a[i]-‘Z’],i从0开始,a[i]-‘Z’,0-‘Z’ < 0,不符合下标的规则。
B:++count[‘A’-a[i]] ,i从0到n,‘A’-a[i] < ‘A’,无法找到大写字母,映射错误。
C:++count[i],i从0到n,下标直接定为i,无法找到大写字母,映射错误
D:++count[‘Z’-a[i]] ,i从0到n,‘Z’-a[i] = ‘Z’ / ‘Y’ / ‘X’…
count
元素:[Z, X, Y, …, A]
下标: 0 1 2 … 25
在看后面的空,可以实现“找到 [对应的字符] 和其 [出现次数] 打印”
正确答案:D
9. 在32位cpu上选择缺省对齐的情况下,有如下结构体定义,则sizeof(struct A)的值为()
struct A
{
unsigned a : 19;
unsigned b : 11;
unsigned c : 4;
unsigned d : 29;
char index;
};
A 9
B 12
C 16
D 20
unsigned a : 19:unsigned 开辟4bytes,有32bits用掉19bits,剩下13bits。
unsigned b : 11:和a类型相同,而且剩下13bits,足够存储,存储后一开始的4bytes剩下2bits。
unsigned c : 4:一开始的4bytes仅剩2bits,不够存储,重新开辟4bytes,存完剩下28bits 。
unsigned d : 29:上次的4bytes剩下28bits,不够存储,重新开辟4bytes,存完剩下3bits。
char index:类型和d不同,重新开辟1字节存储。
4bytes(32): 19 + 11 …2
4bytes(32): 4 …28
4bytes(32): 29 …3
1bytes(8):8 …0
总共13bytes,整个结构体的大小和结构体内最大对齐数(unsigned的4)对齐,对齐到4的最小整数倍:16。
正确答案:C
#位段
- 同类型可以放在一起,不同类型需要重新开辟字节
- 如果当前的n个字节剩下的比特位足够放,就直接放,反之重新按字节开辟(编译器决定)
10. 下面代码会输出()
int main()
{
int a[4]={1,2,3,4};
int *ptr=(int*)(&a+1);
printf("%d",*(ptr-1));
}
A 4
B 1
C 2
D 3
对于int arr[10]
sizeof(arr)
中的arr,表示整个数组&arr
中的arr,表示整个数组- 其他时候,arr始终表示数组首元素地址
&a是a的整体地址,&a+1跳过一整个a,这时候是野指针。
ptr本身是int指针,-1后退一个int,找到4。
正确答案:A
二、编程题
1. 排序子序列
描述
牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分为几段排序子序列.
如样例所示,牛牛可以把数组A划分为[1,2,3]和[2,2,1]两个排序子序列,至少需要划分为2个排序子序列,所以输出2
输入描述:
输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)
第二行包括n个整数A_i(1 ≤ A_i ≤ 10^9),表示数组A的每个数字。
输出描述:
输出一个整数表示牛牛可以将A最少划分为多少段排序子序列
示例1
输入
6
1 2 3 2 2 1
输出
2
思路
求 “连续的非递增或者非递减排序的子序列” 的个数。也可以说,求非递增/非递减的状态转换的次数。
如,有三个连续的排序子序列,状态就转化了两次。
非递增就是前后元素可以相等的递减,非递减就是前后元素可以相等的递增。
非递减:[1, 2, 2, 3] 非递增:[3, 2, 2, 1]
所以我们可以比较 a[i] 和 a[i+1] 来判断状态:
- a[i] > a[i+1]:状态切换至非递减
- a[i] == a[i+1]:状态不变
- a[i] < a[i+1]:状态切换至非递增
遍历每次状态切换时记录即可。但要注意,我们遍历的 i 是 [0, n-1],而我们比较的时候会访问i+1,所以这样直接遍历会越界。怎么解决?
由于题目说数组中的数组 >=1,所以我们的数组可以开n+1的空间,a[n]放上0,遍历结束的时候本来也是切换状态的时候,这样不仅不用遍历完后手动加一次,而且还能避免越界。
参考代码
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n = 0;
cin >> n;
vector<int> v(n + 1);
v[n] = 0; //最后一个位置放上0
for(int i = 0; i < n; ++i)
cin >> v[i];
int i = 0, cnt = 0;
while(i < n)
{
//当前是非递增状态
if(v[i] > v[i+1])
{
//找到下一次状态切换
while(i < n && v[i] >= v[i+1]) ++i;
++cnt;
}
//当前是非递减状态
else if(i < n && v[i] < v[i+1])
{
//找到下一次状态切换
while(i < n && v[i] <= v[i+1]) ++i;
++cnt;
}
//两种情况:
//1. 相等往后走
//2. 找到切换状态的点,再++i,跳到下一种状态
++i;
}
cout << cnt << endl;
return 0;
}
总结:
- 将题目要求转化成代码易实现的语言
- 题目要求:“连续的非递增或者非递减排序的子序列” 的个数
- 易实现:非递增/非递减的状态转换的次数
- 访问越界时视情况可以多开空间。
2. 倒置字符串
描述
将一句话的单词进行倒置,标点不倒置。比如 I like beijing. 经过函数后变为:beijing. like I
输入描述:
每个测试输入包含1个测试用例: I like beijing. 输入用例长度不超过100
输出描述:
依次输出倒置之后的字符串,以空格分割
示例1
输入:
I like beijing.
输出:
beijing. like I
思路
倒置单词,有个方法,反正前面的要往后走,后面的要往前走,我们直接逆置整体。每个单词都到了对应位置,但是单词内的字母顺序反了。我们只需要设置一个区间,找到[单词头, 空格或结尾),然后逆置。找的时候注意越界问题。
我们这里用的是C++算法库的reverse,传参要传左闭右开的迭代器。
参考代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
//reverse [当前单词的第一个有效字符, 空格的前一个字符]
string s;
getline(cin, s);
reverse(s.begin(), s.end());
int i = 0, begin = 0, end = 0;
while(i < s.size())
{
//找空格
while(i < s.size() && s[i] != ' ') ++i;
end = i - 1; //空格不管
reverse(s.begin() + begin, s.begin() + end + 1);
begin = end = i + 1;
i = begin;
}
cout << s << endl;
return 0;
}
今天的分享就到这里了
这里是培根的blog,期待与你共同进步!
下期见~