🌈一、选择题
❤第1题:关于重载函数,( )说明是正确的。
A: 函数名相同,参数类型或个数不同 B: 函数名相同,返回值类型不同
C: 函数名相同,函数内部实现不同 D: 函数名称不同
答案:A
答案解析:
为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个函数完成不同的功能。这就是重载函数。
- 必须不同点:参数列表不同(指参数的个数、类型或者顺序)。
- 必须相同点:函数名,函数的返回类型,函数内部实现。
❤第2题:关于引用以下说法错误的是( )。
A: 引用必须初始化,指针不必
B: 引用初始化以后不能被改变,指针可以改变所指的对象
C: 不存在指向空值的引用,但是存在指向空值的指针
D: 一个引用可以看作是某个变量的一个“别名”
E: 引用传值,指针传地址
F: 函数参数可以声明为引用或指针类型
答案:E
答案解析:
A选项:引用是小名,你要先知道别人的大名才能起小名,而指针是这个人的身份证号,所以说引用必须初始化,指针不必。没毛病。
B选项:这个选项有歧义,引用不可以改变指向,但是可以改变初始化对象的内容。引用之所以不可以改变指向,就是因为引用的本质是常量指针。
1.&a=b; &a=c;这样就是不对的
2.x=2, &y=x, y=5 这样写就是对的
C选项:你给一个不存在的人起小名不是扯淡吗,但是空值也有地址,所以可以用指针。
E选项:引用也需要传地址,引用的两大作用做参数,做返回值。更关键的是引用的本质是常量指针,那必须用到地址。所以E选项是一定错误的。
F选项:C语言时候指针就可以是形参类型,更何况引用类型的参数呢?F选项没一点问题。
❤第3题:以下程序输出的是( )。
#include <iostream>
using namespace std;
int main(void)
{
const int a = 10;
int * p = (int *)(&a);
*p = 20;
cout<<"a = "<<a<<", *p = "<<*p<<endl;
return 0;
}
A: 编译阶段报错运行阶段报错 B: a = 10, *p = 10
C: a = 20, *p = 20 D: a = 10, *p = 20
E: a = 20, *p = 10
答案:D
答案解析:
先定义了一个const整型常量a, 并且初始化为10,又创建一个指针变量p, 存放的是a的地址。
接下来解引用p并且并且赋值为20。 所以是a = 20, *p = 20。但是阿里的笔试题会这么简单吗?
- 这个问题我们先不解释,先说一下另一个问题为啥要是(int*)(&a)这个不算解引用吗?
其实前面那个int*可以理解为类型转换。&a是int const *类型的,a的值不能被更改,但是把他转化为int*类型就可以是p和a指向的对象可以改变了。
之所以选D选项涉及到常量折叠。
原来我们使用过#define PI 3.1415, 用到Π时就用3.1415代替掉,这个是一个意思。因为使用 const 编译器会优化,对于以后见到a,编译器立马用10替代,而 p 是新定义的一个指针变量,开始p通过 int p = (int )(&a),获得了栈区常量a的地址,即p指向这个地址,但是因为 const 导致编译器优化,直接在符号表中 用10代替a了,所以导致打印 a 还是10,但是原来a的地址还是原来那个没有改变的,所以通过p指向对象的改变,原来地址的值就变成20了。
就比如林冲被发配到沧州之前脸上被烙了一个奸字,别人见到他脸上的奸字后不再喊他的名字,而是叫他凡人,但是它家还是在京城这一点没有变化。所以虽然p改变了这个地址代表的数字,但是a的身份已经改变了,不再受到地址的限制了。
所以a=10, *p=20。
❤第4题:
在 Windows 32 位操作系统中,假设字节对齐为 4 ,对于一个空的类 A , sizeof(A) 的值()。
A: 0 B: 1 C: 2 D: 4
答案:B
答案解析:
C语言中空结构体的大小是0,但是C++中空类的大小不是0,而是1。
因为空类也能被实例化,但是他的大小是0的话,那如果一个空类实例化很多对象是,内存地址咋样分辨他的一个个实例呢?所以,为了实现每个实例在内存中都有一个独一无二的地址,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址,所以空类所占的内存大小是1个字节。
❤第5题:有如下类的定义:
int F1();
static int F2();
class TestClass
{
public:
int F3();
static int F4();
};
在所描述的函数中,具有隐含 this 指针的是哪个( )。
A: F1 B: F2
C: F3 D: F4
答案:C
答案解析:
F1:全局变量,F2:静态全局变量,F3局部变量(类的成员函数),F4:静态局部变量(成员函数)。
隐藏的this指针只存在于类的非静态成员函数中。
所以F1, F2全部排除。
- 有的说static函数属于类不属于对象(对象不分配内存),this是对象的指针(指向的是对象分配的内存)两种不是一个系统的管不到。(我最倾向与这种。)
- 还有一种是:static成语是在实例没有创建前就存在的,而类的其他成员必须是在实例创建后才存在的。
接下来我们复习一下引用和指针的区别:
- 引用是一个变量的别名,指针存放的是地址(引用是小名,指针是身份证号)。
- 引用在定义时必须初始化,指针不必初始化(要说清是谁的小名,有身份证就一定知道这人).
- 没有空引用,但是有空指针。【之所以有空指针就是因为我们设置一个指针后,这个指针里的内容是不可预知的,即不知道它指向内存中的哪个空间(即野指针),它有可能指向的是一个空白的内存区域,为了防止出现意外,所以设置了空指针。之所以没有空引用是因为引用必须被初始化】。
- 引用在初始化时引用一个实体后,就不能再引用其他实体,因为其本质是一个指针常量,无法改变指向。而指针可以在任何时候指向任何一个同类型实体。
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是空地址空间所占字节个数(32位平台下占用4个字节,64位占用8字节)。
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
- 访问实体方式不同,指针需要显示解引用,引用则由编译器自己处理。
🌈编程题
🍄第1题 :反转字符串中的单词|||
解题思路:
这个题的思路很是简单,这个逆序不是全部逆序,而是把一个单词的顺序逆序,但是这个单词的位置顺序没有改变,所以我们以空格为界限,运用双指针以单个单词为单位进行左右交换逆序,重复循环直到交换完所有单词就完成了。
class Solution {
public:
string reverseWords(string s) {
int i = 0;
while(i<s.length())
{
int start = i; //记录头个字符, 必须是从i开始不是从0开始。
while(i < s.length() && s[i] != ' ')
{
i++;
}
int left = start;
int right = i-1; //此时i指向的是空格前面的字符
while(left<right)
{
swap(s[left], s[right]);
left++;
right--;
}
while(i < s.length() && s[i] == ' ')
{
i++;
}
}
return s;
}
};
特别注意一点,这个start记录的是每一个单词的首字母,比如我们逆序完一个字符后,这个start就记录第二个字符的首字母,所以start不是从0开始的,而是跟着i走的。
🍄第2题:连续字符
解题思路:
- 这个题属实很简单,找两个计数板就可以了,例如ans计数板记录在已经遍历的字符中,所出现的最长的连续字符个数。如果后面又出现更长的连续字母,就更新ans计数板。
- cnt计数板记录当前连续字母的个数。如果前后字母不一样,就更新cnt为1,重新记录后面那个字母。
class Solution {
public:
int maxPower(string s) {
int ans = 1; //记录的是当前最大重复字符数
int cnt = 1; //记录重复字符的个数,记录完后归1
for (int i = 1; i < s.length(); ++i) {
if (s[i] == s[i - 1])
{
++cnt;
ans = max(ans, cnt);
}
else
{
cnt = 1; //前后出现的数不一样,重新记录
}
}
return ans;
}
};