一、选择题1、在C++中执行以下4条语句后输出rad值为:(C)
static int hot=200;
int &rad=hot;
hot = hot + 100;
cout<< rad << endl;
A、100 B、200 C、300 D、400
二、简答题
-
const关键字与宏定义的区别是什么?
答:
1.用宏定义的常量,在预处理阶段会被直接展开,在编译阶段没有检查机制,如果有bug,在运行的时候才会检查出来。
2.用const关键字定义的常量,在程序的编译阶段会进行语法检查,如果有bug,在编译阶段就能实现。
2.malloc的底层实现是怎样的?free是怎么回收内存的?
参考:c/c++中malloc的底层实现原理_ypshowm的博客-CSDN博客_c++ malloc
3.new/delete与malloc/free的区别与联系是什么?
答:
相同点:
(1)都是用来申请堆空间内存的。
(2)都要成对使用,否则会出现内存泄漏问题。
扫描二维码关注公众号,回复: 13289820 查看本文章不同点:
(1)malloc申请堆空间是未进行初始化赋值的;用new申请的堆空间已是被初始化赋值的(调用构造函数来初始化)。
(2)malloc/free是C语言的标准库函数,new/delete是C++的运算符。
(3)malloc返回的是void *,需要进行强制转换才能使用;new返回的是创建对象的指针。
3.1 为什么new/delete覆盖了malloc/free的功能,c++中还要保留malloc/free?
答: C++中,new/delete本身底层实现也用了malloc和free。c++经常会对c程序就行调用,保留malloc/free从而向下兼容,因为C要用malloc/free来管理动态内存。
4.**区分以下概念:内存泄漏、内存溢出、内存踩踏、内存越界、野指针?(面试常考)**
答:
1.内存泄露:程序中已动态分配的的堆内存,由于某些原因无法释放或者未释放,造成的内存浪费。
2.内存溢出:你要分配的内存超出了系统能给你的,系统不能满足需求,于是产生了溢出
3.内存踩踏:访问了不合法的地址 ,就是访问了不属于自己的地址。
4.内存越界:你想系统申请一块内存,在使用的这块内存的时候,超过出了你申请的范围
5.野指针:1.指针变量未进行初始化;2.指针释放后未置空; 3.指针的操作超越变量的作用域。
5.引用与指针的区别是什么?并且将"引用"作为函数参数有哪些特点?在什么时候需要使用"常引用"?
一,引用和指针的区别:
1.指针变量是用于存储变量地址的变量;引用是变量的别名,本质上是一个指针常量, *const。2.引用定义时就必须初始化,引用一旦初始化,不能再引用到其他变量,具有依赖性;
指针变量定义时可以不进行初始化,可以是空指针,且在赋值之后,之后也可以改变指针指向。3.指针通过指针变量指向某个变量后,对它所指的变量需要进行间接操作。
引用就是变量本身的别名,对引用的操作就是对变量的操作。二.引用作为函数参数有什么特点?
1.引用作为函数的参数,在函数中,对形参的操作就是对实参的操作,;
2.在传参时,是不需要进行拷贝操作的,相当于传的是"自己本身"。三、常引用。
1.常引用声明方式:const 类型标识符 &引用名 = 目标变量名;
例如 const int &a = b; 的形式就是一个"常引用"2.常量引用的使用场景,用于修饰函数形参, 防止别人修改引用的值,防止误操作,提高安全性。
三、写出下面程序的运行结果。
1、第一题:
#include <iostream>
using std::cout;
using std::endl;
void f2(int &x, int &y)
{
int z = x;
x = y;
y = z;
}
void f3(int *x, int *y)
{
int z = *x;
*x = *y;
*y = z;
}
int main()
{
int x, y;
x = 10; y = 26;
cout << "x, y = " << x << ", " << y << endl;
f2(x, y);
cout << "x, y = " << x << ", " << y << endl;
f3(&x, &y);
cout << "x, y = " << x << ", " << y << endl;
x++;
y--;
f2(y, x);
cout << "x, y = " << x << ", " << y << endl;
return 0;
}
运行结果:
x, y = 10, 26//简单的值打印
x, y = 26, 10//引用作为函数的参数,引用传递实现两数对调,引用后主函数中x=26,y=10
x, y = 10, 26//指针作为函数的参数,地址传递实现两束对调, 再换回来
x, y = 25, 11//引用传递
2、以下代码输出的是__?
int foo(int x,int y)
{
if(x <= 0 ||y <= 0)
return 1;
return 3 * foo(x-1, y/2);
}
cout << foo(3,5) << endl;
运行结果:27
3、若执行下面的程序时,从键盘上输入5,则输出是()
int main(int argc, char** argv)
{
int x;
cin >> x;
if(x++ > 5)
{
cout << x << endl;
}
else
{
cout << x-- << endl;
}
return 0;
}
运行结果:
4、写出下面程序的结果:
int main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
运行结果:
2,5
//a+1,数组元素第二个值的地址
//&a+1,加 1是加整个数组的大小, ptr的初值为&p[4]+1,-1后就是&p[4]
5、假定 x = 9999,求下列函数的值
int func(x)
{
int countx = 0;
while(x)
{
countx++;
x = x&(x-1);
}
return countx;
}
运行结果:8
解析:
x&(x-1)的作用:
1.可以用于统计一个数的二进制有多少个1.
2.把x转成二进制数,x&(x-1)的结果就是消去x最右边的1
如201: 1100 1001
200: 1100 1000
结果:1100 1000--->把201的二进制数最右边的1去掉,结果就是200
199:1100 0111
结果:1100 0000---->把200的二进制最右边的1去掉,结果就是192
扩展:5.1 x&(-x)的作用.
x&(-x)结果为:x的二进制数保留最右边的1,其他全部置0。
如201&(-201) = 1, 1100 1001--->0000 0001--->真实值1
200&(-200) = 8,1100 1000--->0000 1000---->真实值8
5.2 x&(-x)的应用:有100个整数,其中有50个数出现了两次,2个数出现了一次, 找出出现了一次的那2个数。(大家使用10个数即可)
分析:
1.用0和数组中所有的元素进行异或,得到的是这两个数异或的结果
2.取异或结果的最最低位为1的数--split
3.用split去和数组中的元素作按位与操作,就能把两个不同的数分出来了
例子:
1.两个二进制数异或的结果如19和109:
19: 0001 0011
109: 0110 1101异或: 0111 1110 --->126--->最低位为1的值为2--->可以由126&(-126)得出
2.用2去和19和109作按位与,得出的结果只有0和非0(2本身),,因为2作为126的最低位为1的数,由19和109异或的来的,因此,用两数异或的结果的最低位为1的数去分别去和这两数作按位&,就能把不同的数分出来。
2:0000 0010
19:0001 0011
0000 0010----->2
2: 0000 0010
109:0100 1101
0000 0000---->0
代码实现:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void find2num(int arr[], int len)
{
int split = 0;
int num1 = 0;
int num2 = 0;
for (int i = 0; i < len; i++)
{
split ^= arr[i];//1.用0和数组中所有的元素进行异或,得到所有值异或的结果
}
split = split & (-split);//2.取异或结果的最最低位为1的数--split
//3.再用split去和数组中的元素作按位与操作,就能把两个不同的数分出来了
/*
分析
两个二进制数异或的结果如19和109:
19: 0001 0011
109: 0110 1101
异或结果:0111 1110 --->126--->最低位为1的值为2--->可以由126&(-126)得出
用2去和19和109作按位与,得出的结果只有0和非0(2本身),因为2作为126的最低位为1的数,由19和109异或的来的,因此,用两数异或的结果的最低位为1的数去分别去和这两数作按位&,就能把不同的数分出来。
2:0000 0010
19:0001 0011
0000 0010----->2
2:
109:0000 0010
0100 1101
0000 0000---->0 */
for (int i = 0; i < len; i++)
{
if (arr[i] &split)
{
num1 ^= arr[i];
}
else
num2 ^= arr[i];
}
printf("%d,%d\n", num1, num2);
}
//测试
int main()
{
int arr[10] = { 10,20,34,50,50,34,20,10,11,19 };
find2num(arr, 10);
system("pause");
return EXIT_SUCCESS;
}
5.3,5.2扩展,:有103个整数,其中有50个数出现了两次,3个数出现了一次, 找出出现了一次的那3个数。(大家使用9个数即可)
思路:1.利用循环移位,配合按位与和按位或特性,对三个数进行分堆,利用分堆思想,把三个数依次求出来。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void find2num(int arr[], int len, int num1,int num2)//思想:两两出现的数,有两个不重复,找出这两个数
{
//num1是找到的第一个不重复的值,num2是数组中某两个不同数异或的结果
num2 = num2 & (-num2); //二进制数,从右往左,保留第一个1,其余置0
int result1 = 0;
int result2 = 0;
for (int i = 0; i < len; i++)
{
if (arr[i] == num1)//剔除第一个已找到的数
continue;
if (arr[i] & num2)//异或的来的结果,通过按位与分出两个数
{
result1 ^= arr[i];
}
else
{
result2 ^= arr[i];
}
}
printf("第二个不重复的数为:%d\n", result1);
printf("第三个不重复的数为:%d\n", result2);
}
void find3num(int arr[],int len)
{
int split_flag = 1;//二进制位1,用循环移位来进行分堆
for (int i = 0; i < sizeof(int) * 8; i++)//循环移位,直到分堆成功或者溢出
{
int result1 = 0, result2 = 0;//一个结果保存第一个不重复的值,另一个保存另外两个异或后的结果
int count1 = 0, count2 = 0;//用来判断偶数堆
split_flag = split_flag << i;
for (int j = 0; j < len; j++)//分堆
{
if (split_flag&arr[j])
{
count1 += 1;
result1 ^= arr[j];
}
else
{
count2 += 1;
result2 ^= arr[j];
}
}
/*
分析这个循环可能得出的结果:
可能结果1:
1.奇数堆:可能结果为,三个不同的数都是放在同一堆--->这堆的个数肯定是为奇数;
2.偶数堆:偶数堆异或的结果肯定为0,
3.结论:分堆不成功,split_flag继续移位,进行下一次循环
可能结果2:
1.奇数堆:只有一个数不重复,其他的都是重复,异或的结果即找到了第一个不重复的数
2.偶数堆:偶数堆有两个数是不重复的,异或的结果肯定不为0,异或的结果为两个不重复的数异或的结果。
结论:可以通过判断偶数堆异或的结果是否为0来找到第一个不重复的值,再找其余两个
*/
//判断第一堆
if (count1 % 2 == 0 && result1 != 0)
//如果是偶数堆,并且异或的结果不为0,说明分堆成功,奇数堆result2的结果就是第一个不重复的值
{
printf("第一个不重复的数:%d\n", result2);
//printf("%d\n", result1); //result1就是其他两个数异或的结果
find2num(arr, len, result2,result1);
break;
}
//判断第二堆
if (count2 % 2 == 0 && result2 != 0)
{
//如果是偶数堆,并且异或的结果不为0,说明分堆成功,奇数堆result1奇数堆的结果就是第一个不重复的值
printf("第一个不重复的数:%d\n", result1);
//printf("%d\n", result2); //result2就是其他两个数异或的结果
find2num(arr, len, result1,result2);//找另外两个不重复的数
break;
}
}
}
int main()
{
int arr[] = { 10,33,44,55,55,44,33,10,50,50,123,123,888,777,190,190,999 };
int len = sizeof(arr) / sizeof(int);
find3num(arr, len);
system("pause");
return EXIT_SUCCESS;
}
四、有段代码写成了下边这样,如果在只修改一个字符的前提下,使代码输出20个hello?
for(int i = 0; i < 20; i--)
cout << "hello" << endl;
答:
for(int i = 0; i + 20; i--) // for(part1;part2;part3) 当part2 = 0时就会跳出
cout << "hello" << endl;