C++学习之第一天

一、选择题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

二、简答题

  1. const关键字与宏定义的区别是什么?

答:
1.用宏定义的常量,在预处理阶段会被直接展开,在编译阶段没有检查机制,如果有bug,在运行的时候才会检查出来。
2.用const关键字定义的常量,在程序的编译阶段会进行语法检查,如果有bug,在编译阶段就能实现。

 2.malloc的底层实现是怎样的?free是怎么回收内存的?

        参考:c/c++中malloc的底层实现原理_ypshowm的博客-CSDN博客_c++ malloc

3.new/delete与malloc/free的区别与联系是什么?

答:

相同点:

(1)都是用来申请堆空间内存的。

(2)都要成对使用,否则会出现内存泄漏问题。

不同点:

(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;

猜你喜欢

转载自blog.csdn.net/weixin_49278191/article/details/120982641