单选
1.单选
使用printf函数打印一个double类型的数据,要求:输出为10进制,输出左对齐30个字符,4位精度。以下哪个选项是正确的?
A. %-30.4e
B. %4.30e
C. %-30.4f
D. %-4.30f
解析
- double数据类型,10进制:是用f而不是e
- e:按指数类型进行输出
- %m.nf
- 左对齐:要输出左对齐,在m前面加个负号
- 输出的字符占30个字符宽度:m就是30
- 4位精度:小数点后保留4位,n就是4
- %-30.4f
- 选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和
*
的相对位置
const在*
的左边:常量指针
const在*
的右边:指针常量
- 常量指针,无初始化
- 常量指针,指向变量i的地址
- p2指针的指向由i指向j,因为是常量指针,指针指向可以发生改变
- 指针常量,指向变量i的地址
- p3解引用修改值,指针常量,可以通过指针解引用修改指向的值
- 对指针p2解引用进行赋值,这是一个常量指针,不能通过解引用改变所指空间的值,错
- 给p3指针赋值为j变量,改变了p3指针的指向,是指针常量,指针本身是一个常量,指针的指向是不能发生改变的,错
- 选C
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和acy是两个字符数组,初始化的方式不同
- szx和szy都是指针,两个指针的指向都是一个常量字符串
A:定义的时候是在栈上开辟的,内容是可以修改的
B:都是指针指向同一个常量字符串,所以指向的是同一个地址
C:两个初始化不同,acx是字符串初始化,acy是字符数组初始化,acx后面由\0占4个字节,acy后面没有\0占3个字节
D:szx是一个指针,内容被修改就是指szx的指向发生了改变,常量字符串abc没有发生改变,szy的指向没有发生改变,所以szy的内容不会发生改变, - 选D
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
解析
int *b = a;
指针b指向数组的首元素*b += 2;
先对b进行解引用,获取到数组首元素,然后进行+=2,也就是给1+2=3以后赋给数组首元素*(b + 2) = 2;
先给指针b+2,向后偏移两个指针类型大小,因为是int类型数组,向后偏移8个字节,再解引用,获取到数组第三个元素,将它改为2b++;
将指针b向后偏移1个指针类型大小,向后偏移4个字节,指向数组第二个元素*b
对指针类型解引用进行打印,获取数组第二个元素,2*(b + 2)
,向后偏移两个指针类型的大小,也就是8个字节,指向4- 选C
5.单选
下列关于C/C++的宏定义,不正确的是()
A. 宏定义不检查参数正确性,会有安全隐患
B. 宏定义的常量更容易理解,如果可以使用宏定义常量的话,要避免使用const常量
C. 宏的嵌套定义过多会影响程序的可读性,而且很容易出错
D. 相对于函数调用,宏定义可以提高程序的运行效率
解析
A:宏定义是没有类型安全检测的
B:宏定义没有类型安全检测,并且在预处理阶段进行宏替换,无法进行调试,所以要尽量使用const常量
C:宏定义可能会导致运算符优先级的问题,很容易导致出错
D:宏定义相对于函数调用,没有创建函数栈帧和函数压栈的开销,可以提高程序的运行效率
- 选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);
解析
- 定义了一个整形数组和一个char类型数组
- 函数名是sss,函数返回值是void,第一个参数是char类型数组,第二个参数是int类型数组
- 函数调用时,参数是数组类型的话,在调用的时候,需要传入数组名就可以,也就是数组的地址
- A:写反了
- B:函数参数不需要写数据类型
- C:不应该+方括号
- D:正确
- 选D
7.单选
用变量a给出下面的定义:一个有10个指针的数组,该指针指向一个函数,该函数有一个整形参数并返回一个整型数()
A. int *a[10];
B. int (*a)[10];
C. int (*a)(int);
D. int (*a[10])(int);
解析
- 方括号运算符的优先级高于
*
号的优先级
A:指针数组:a先和方括号结合,表示是一个数组,数组里存放的是int类型的指针
B:数组指针:因为加了括号,a先和*
结合,是一个指针,指针指向的是一个int类型的数组,数组大小为10
C:函数指针:a先和*
结合,表示是一个指针,后边括号里是int,表示这是一个函数指针,指针指向一个函数,该函数有一个int参数,参数值类型也是int
D:函数指针数组:a先和方括号结合,表示是一个数组,再和*
结合,表示是一个指针,数组里存放的是函数指针,函数指针指向一个函数,该函数有一个整形参数,并返回一个整形数 - 选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]
E.
++count[a[i]]
'A'+i
count[a[i]]
解析
- 这是一个函数的定义,第一个参数是一个字符数组,输入的大写字母;第二个参数是数组的大小
- 定义一个count数组,大小为26,用来计数A~Z26个字母所对应的出现次数,数组元素中存放的是每个字母出现的次数
- 第二个for循环,去遍历count这个数组,去统计每个字符出现了多少次
- 将字符和出现次数打印出来
- if语句是用来控制分号的
A:a[i]-'Z'
数组里存放的是大写字母A~Z,这里的ASCII码值是<=Z的ASCII码值,所以这里的计算结果<=0,因为数组下标不能小于0,所以A错误
B:'A'-a[i]
最后的大小<=0,因为count数组下标不能小于0,所以B错
C:i的值是取决于n的,n是字符数组的大小,字符数组有可能比count数组大,可能会越界,所以不满足题意
D:'Z'-a[i]
,是字符Z的话,Z-Z等于0,count[0]
存的是大写字母Z出现的次数,倒序存储
E:a[i]
保存的是大写字母A~Z,ASCII码值肯定超过26,肯定会产生越界 - 选D
9.单选
在32位cpu上选择缺省对齐的情况下,有如下结构体定义:
struct A
{
unsigned a : 19;
unsigned b : 11;
unsigned c : 4;
unsigned d : 29;
char index;
};
则sizeof(struct A)的值为()
A. 9
B. 12
C. 16
D. 20
解析
- 冒号后面的数字表示变量所占多少比特位,去计算它所占的空间有多大
- a变量是一个unsigned类型,首先会开辟一个4字节空间(32bit),因为是位段,19个bit位会保存在开辟的4个字节里面
- b变量是一个unsigned类型,占11个比特位,如果空间足够的话,会放在之前开辟的空间,19+11=30
- c变量是一个unsigned类型,因为之前的空间已经用了30个bit,只剩2个bit,放不下了,只能在开4个字节空间,32个bit位,将c变量放进去,占4个bit位
- d变量占29个比特位,因为上一个开辟的4个字节的空间只剩28个bit位,是放不下的,只能重新开辟空间,也是4个字节,32个bit位,将29个bit位放进去
- 最后一个是一个char类型,不同于unsigned类型,需要再开一个1个字节的空间,8个bit位,将index变量放进去
- 因为有内存对齐的原因,所以结构体最终的整个大小,是最大宽度的整数倍,也就是4字节的整数倍
3*4+1=13
,比这个大也就是16个字节
4字节(32):19+11
4字节(32):4
4字节(32):29
1字节(8):
13->16字节
- 选C
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
解析
- 给了个整形数组,里面的元素是1,2,3,4
- 定义了一个指针ptr,利用数组给它赋值,对数组名进行取地址,变成一个数组指针,指向数组首元素,
- +1向后偏移指针类型的大小,也就是整个数组类型大小,指向数组最后一个元素的下一个位置
- ptr-1:往前偏移一个指针类型大小,也就是4个字节,因为ptr指针类型是int类型,指向数组最后一个元素,解引用获取到这个数组的最后一个值4
- 选A
编程
1.编程题
倒置字符串_牛客题霸_牛客网 (nowcoder.com)
技能
枚举 模拟 字符串 栈
题目
- 描述
将一句话的单词进行倒置,标点不倒置。比如 I like beijing. 经过函数后变为:beijing. like I - 输入描述:
每个测试输入包含1个测试用例: I like beijing. 输入用例长度不超过100 - 输出描述:
依次输出倒置之后的字符串,以空格分割 - 示例1
输入:
I like beijing.
输出:
beijing. like I
解析
I like beijing.
分两步走
- 先将整体字符串进行逆置
gnijieb ekil I ‘\0’ - 每个单词进行逆置
单词与单词之间是一个空格,但是最后一个单词后面是一个\0
对于C++- s.begin():开始,指向g
- s.end():结尾,指向\0
- 每个单词逆置的时候分别定义两个指针,一个start,一个end
- start和end之间就是一个区间,表示的就是一个单词
- 刚开始start指向s.begin(),end也指向start,然后让end走到空格,此时start和end之间就是一个单词
思路
- 第一步,先将这个单词逆置,逆置后变成beijing
- 下一步逆置下一个单词,将start指向end的下一个,start=end+1,end再等于start,end再往后走遇到空格,再形成一个空间,对ekil进行逆置,变成like
- 下一步,start=end+1,指向I,end等于I的地方,end往后走,如果和s.end()相遇,说明又是一个单词,把I逆置一下,还是I
- 完成题意
注意
reverse里面是`[start, end)`,不包含end
代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
//从标准输入中获取一行字符串,并存储在字符串变量 s 中
string s;
getline(cin, s);
//1.进行整体的逆置,即将整个字符串 s 逆序排列
reverse(s.begin(), s.end());
//定义迭代器start,初始化为字符串的起始位置
auto start = s.begin();
//循环处理字符串中的每个单词
while(start != s.end())
{
//定义迭代器 end,初始化为 start
auto end = start;
//移动end直到找到空格或者到达字符串末尾
while(end != s.end() && *end != ' ')
{
end++;
}
//对单词进行局部逆置,即将单词内部的字符逆序排列
reverse(start, end);
//如果还有下一个单词,将start移动到下一个单词的起始位置
if(end != s.end())
{
start = end + 1;
}
else
{
start = end;
}
}
// 输出经过处理后的字符串
cout << s << endl;
}
2.编程题
排序子序列_牛客笔试题_牛客网 (nowcoder.com)
技能
贪心
题目
- 牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为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
解析
- 非递减就是
a[i]<=a[i+1]
,递减就是a[i]>a[i+1]
- 非递增就是
a[i]>=a[i+1]
,递增就是a[i]<a[i+1]
1,2,3,4,5:递增排列
9,8,7,6,5:递减排列
1,2,3,3,4,5,8,8:非递减排列
9,8,7,7,6,5,5,2,1:非递增排列
思路
- i下标从0开始,它每次比较i下标和i+1下标的值,如果
if(a[i]<a[i+1])
,即将进入非递减排序序列,
if(a[i]==a[i+1])
,不用关心,因为不管是非递增还是非递减都有相同的数字
if(a[i]>a[i+1])
,即将进入非递增排序序列 - 循环条件,i<n,
- 定义一个计数器count,来记录一下,如果出现i的值和i+1的值的关系发生变化,count++,一直到i全部走完的时候,count就是最后结果
i==i+1
的情况,不管是在非递增还是非递减里面,都属于本身的情况的一种,只需要让i继续往后++就行
代码
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> a;
a.resize(n);
for(int i = 0; i < n; i++)
{
cin >> a[i];
}
//数组当中已经存在数字
int i = 0;
int count = 0;
while(i < n)
{
//进入非递减序列
if(a[i] < a[i+1])
{
//注意i的值不能一直加,这里i+1会越界
while(i < n && a[i] <= a[i+1])
{
i++;
}
count++;
i++;
}
else if (a[i] == a[i+1])
{
i++;
}
else
{
while(i < n && a[i] >= a[i+1])
{
i++;
}
count++;
i++;
}
}
cout << count << endl;
}
为了避免越界情况的产生
a.resize(n+1);
a[n] = 0,放零是因为提供的数据全是正整数,放0没有影响
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 从输入中获取整数 n,表示数组的大小
int n;
cin >> n;
// 创建一个大小为 n+1 的整数向量 a,并将最后一位初始化为 0
vector<int> a;
a.resize(n+1);
a[n] = 0;
// 循环读取 n 个整数到数组 a 中
for(int i = 0; i < n; i++)
{
cin >> a[i];
}
//数组当中已经存在数字
// 初始化迭代器 i 为 0,用于遍历数组
int i = 0;
int count = 0;
// 循环遍历数组
while(i < n)
{
//进入非递减序列
if(a[i] < a[i+1])
{
while(i < n && a[i] <= a[i+1])
{
i++;
}
count++;
i++;
}
else if (a[i] == a[i+1])
{
i++;
}
else
{
while(i < n && a[i] >= a[i+1])
{
i++;
}
count++;
i++;
}
}
// 输出统计结果
cout << count << endl;
}