[学习笔记-C++篇]day17-18 牛客C++面经page1

只能说越学越觉得自己还需要学很多。

前10天打基础,4天入门基本题,现在要把算法入门一下。

有太多的所谓的学习计划了,连参考都不知道参考什么。

slow down。

一部分时间看面经。一部分时间算法入门为切入,对某种算法专项训练(做出过的题)。预计11天,到月底结束。下个月开始剑指offer。

链接:算法入门
c++校招面试题目合集


1.面试题第1页

1.1

void test1()
{
    char string[10];
    char* str1 = "01234567891";
    strcpy( string, str1 );
}

str可以被拷贝到string,但是长度增加了2(包含\0),如果string后面接着有内容,会被覆盖,存在数组越界

1.2

void test2()
{
 char string[10], str1[10];
 int i;
 for(i=0; i<10; i++)
 {
 str1  = 'a';
 }
strcpy( string, str1 );
}

1)不能将字符赋值给指针
2)strcpy遇到\0才会停止,如果没遇到过,会出现越界访问非法内存的错误

1.3

void test3(char* str1)
{
 if(str1 == NULL){
        return ;
 }
 char string[10];
 if( strlen( str1 ) <= 10 )
 {
 strcpy( string, str1 );
 }
}

strlen不统计\0字符,只统计数组中元素个数,所以应改为strlen(str1)<10

>1.4

写出完整版的strcpy函数
void strcpy(char * strdest,const char *strsrc)
{
	assert(strdest!=NULL && strsrc!=NULL );//注意!!!这里2个指针都不能为空
	while((strdest++=strsrc++)!='\0');
}

更高要求:

char * strcpy( char *strDest, const char *strSrc )  
{ 
 assert( (strDest != NULL) && (strSrc != NULL) ); 
 char *address = strDest;  
 while( (*strDest++ = * strSrc++) != ‘\0);  
 return address; 
} 

>1.5

void GetMemory( char *p )
{
 p = (char *) malloc( 100 );
}
void Test( void ) 
{
 char *str = NULL;
 GetMemory( str ); 
 strcpy( str, "hello world" );
 printf( str );
}

1)这里函数是要改变指针p的值,那么不能只传p,而要在函数形参部分说明传&pchar * &p)或者**pchar **p
2)最后要用free释放内存,不然会造成内存泄露

>1.6

char *GetMemory( void )
{ 
 char p[] = "hello world"; 
 return p; 
}
void Test( void )
{ 
 char *str = NULL; 
 str = GetMemory(); 
 printf( str ); 
}

1)一般来说,函数是可以返回局部变量的,但不能返回局部变量的地址,包括指向局部变量的指针也是不能返回的。如果真要返回,必需定义为static,存放在静态数据区,这样是可以返回的。static char p[]="hello world"
2)此外,语句中p是数组名,存储在栈中,"hello world"存储在文字存储区,该语句只能保存字符串的一个副本,函数结束p就被回收了,副本就消失了,所以要用char * p[],按值返回存储的地址
这两种都可以

>1.7

void GetMemory( char **p, int num )
{
 *p = (char *) malloc( num );
}
void Test( void )
{
 char *str = NULL;
 GetMemory( &str, 100 );
 strcpy( str, "hello" ); 
 printf( str ); 
}

形参是字符串指针的指针,但函数中对字符串指针进行赋值,在子函数要判断num是否大于0,是否成功申请内存,if(*p==NULL)。且主函数最后要free释放内存,free(str);,不然会有野指针,还要str = NULL;

1.8

void Test( void )
{
 char *str = (char *) malloc( 100 );
 strcpy( str, "hello" );
 free( str ); 
 ... //省略的其它语句
}

1)没有判断是否成功申请内存,if (*str==NULL)
2)释放内存后置str为空

小总结

参考链接:https://www.nowcoder.com/questionTerminal/8d409a9fa63d4971b207126dbd8bb99f
来源:牛客网

malloc函数以及free函数的注意事项:
1)声明malloc要保证形参大于等于0
2)依据数据类型强制转换malloc的分配内存类型,在前面加上type *
3)要判断是否成功申请内存,if(*p==NULL)
4)要成对使用mallocfree
5)释放内存后要令指针为空,p==NULL

>1.9

swap( int* p1,int* p2 )
{
 int *p;
 *p = *p1;
 *p1 = *p2;
 *p2 = *p;
}

1)要说明void无返回值
2)指针没有初始化就是一个野指针,这里可以不用指针,直接int p;p=*p1;...

1.10

分别给出BOOL,intfloat,指针变量 与“零值”比较的 if 语句(假设变量名为var)
int val;
if(val==0)

bool val;
if(!val)

# const float EPSINON = 0.00001;   
# if ((x >= - EPSINON) && (x <= EPSINON)

int * val;
if(val==NULL)

浮点型变量并不精确,所以不可将float变量用==!=与数字比较,应该设法转化成>=<=形式。

>1.11

以下为Windows NT下的32位C++程序,请计算sizeof的值

void Func ( char str[100] )
{
 sizeof( str ) = ?
}
void *p = malloc( 100 );
sizeof ( p ) = ?

1)数组名指代数组数据结构,直接使用sizeof()就指代数据结构,如char a[10]; cout << sizeof(a) << endl;
2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;不能使用a++
3)数组名作为函数形参时,沦为普通指针。Windows NT 32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof( str ) 、sizeof ( p ) 都为4。

1.12

写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。另外,当你写下面的代码时会发生什么事?  
least = MIN(*p++, b);  
#define MIN(a,b) ((a)<(b)?(a):(b)) 

1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。
2)防止宏的副作用,在运算过程中,p会自增改变量,加入一开始*p更小,返回*p的时候已经自增到*p+2

1.13

为什么标准头文件都有类似以下的结构?

#ifndef __INCvxWorksh
#define __INCvxWorksh 
#ifdef __cplusplus
extern "C" {
#endif 
/*...*/ 
#ifdef __cplusplus
}
#endif 
#endif /* __INCvxWorksh */

头文件中的编译宏是为了防止重复引用
#ifndef __INCvxWorksh
#define __INCvxWorksh
#endif
这些是预处理(还有include/define等)中的条件编译。

//参考链接:https://www.nowcoder.com/questionTerminal/a314892082e649df8ee6294e012345b6
来源:牛客网
1)情况1:
#ifdef _XXXX
...程序段1...
#else
...程序段2...
#endif
这表明如果标识符_XXXX已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。
2)情况2:
#ifndef _XXXX
...程序段1...
#else
...程序段2...
#endif
这里使用了#ifndef,表示的是if not def。和#ifdef相反的状况(如果没有定义了标识符_XXXX,那么执行程序段1,否则执行程序段2)。
3)情况3:用于注释
#if 常量
...程序段1...
#else
...程序段2...
#endif
这里表示,如果常量为真(非0,随便什么数字,只要不是0),就执行程序段1,否则执行程序段2。

C编译和C++编译产生的名字是不一样的,加上extern "C"编译器就会按照C语言的方式进行编译,这样C语言中就可以调用C++的函数了。

>1.14

编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefg” 函数头是这样的:
//pStr是指向以'\0'结尾的字符串的指针
//steps是要求移动的n

void LoopMove ( char * pStr, int steps ) 
{ 
 //请填充... 
} 
void LoopMove(char * pStr, int steps)
{
	char tmp;
	int st=steps%strlen(pStr);
	strcpy(tmp,pStr+strlen(pStr)-st);
	strcpy(tmp+st,pStr);//这个时候超出了一部分
	*(tmp+strlen(pStr))='\0';
	strcpy(pStr,tmp);
}

注意,strcpy拷贝的时候,第1个参数是拷贝到的指针,第2个参数是被拷贝的内容,也是指针。拷贝的时候会自动检索到\0才停止。就是说strcpy是不会自己给定拷贝范围的,只给拷贝起始指针。

1.15

已知WAV文件格式如下表,打开一个WAV文件,以适当的数据结构组织WAV文件头并解析WAV格式的各项信息。

将WAV文件格式定义为结构体WAVEFORMAT:
参考链接:https://www.nowcoder.com/questionTerminal/84550dd3f61240eaa046a30cb2571768
来源:牛客网

typedef struct tagWaveFormat
{ 
char riffFlag[4];
uint32 fileLen;
char vaveFlag[4];
char fmtFlag[4];
char tranferB[4];
uint16 formatType;
uint16 channels;
uint16  samplf;
uint32 wavef;
uint16 dataNum;
uint16 dataWei;
char dataFlag[4];
uint32 dataLen;
 }WAVEFORMAT;

通过内容设定变量类型,字节数确定变量长度(占几个字节)
有个疑问,char类型的话,会默认是字符数组吗,所以会给个长度,而int类型都是整型变量,不是数组
这边不定类型给的是charint16是2字节(16位),int32是4字节。单数第3个虽然也没有说类型,但是长2个字节,且表示样本的数据位数,所以用int16

>1.16

编写类String的构造函数、析构函数和赋值函数,已知类String的原型为:
class String 
{  
 public:  
 String(const char *str = NULL); // 普通构造函数  
 String(const String &other); // 拷贝构造函数  
 ~ String(void); // 析构函数  
 String & operator =(const String &other); // 赋值函数  
 private:  
 char *m_data; // 用于保存字符串  
}; 
class String 
{  
 public:  
 String(const char *str = NULL); // 普通构造函数  
 String(const String &other); // 拷贝构造函数  
 ~ String(void); // 析构函数  
 String & operator =(const String &other); // 赋值函数  
 private:  
 char *m_data; // 用于保存字符串  
}; 

String::String (const char *str)
{
	if(str==NULL)
	{
		m_data=new char[1];// 得分点:对空字符串自动申请存放结束标志'\0'的空
		*m_data='\0';
	}
	else
	{
		int len=strlen(str);
		m_data=new char[len+1];
		strcpy(m_data,str);
	}
	m_data=str;
}

String::String (const String &other)
{
	int len=strlen(other.m_data);
	m_data=new char[len+1];
	strcpy(m_data,other.m_data);
}

String::~String(void)
{
	delete [] m_data;
}

String & String::operator =(const String &other) // 得分点:输入参数为const型
{ 
 if(this == &other)   //得分点:检查自赋值
 return *this; //这是类指针
 delete [] m_data;     //得分点:释放原有的内存资源
 int length = strlen( other.m_data ); 
 m_data = new char[length+1];  
 strcpy( m_data, other.m_data ); 
 return *this;         //得分点:返回本对象的引用
}

>1.17

请说出staticconst关键字尽可能多的作用

static
1)在模块中定义static变量,作用范围是模块中的所有函数,模块外的函数不可使用
2)在函数体中定义static变量,作用范围是整个函数体,函数体外不能使用。且在第一声明的时候分配内存,后面调用的值是前一次的值;
3)在类中定义static变量,作用范围是类的函数,且对所有类的对象只有一个拷贝;
4)在模块中定义static函数,作用范围是整个模块;
5)在类中定义static成员函数,则不能在该函数中使用this指针,只能使用static变量。
const
1)定义const变量,在定义的时候就要初始化,此后值不会改变
2)可用于指定指针或指针指向的数据;
3)可用于指向函数的形参,表明为输入参数,在函数中值不会改变
4)可用于类的成员函数,表明该函数为常函数,不会改变类的成员变量
5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”

>1.18

请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1
int checkCPU() 
{ 
 { 
 union w 
 {  
 int a; 
 char b; 
 } c; 
 c.a = 1; 
 return (c.b == 1); 
 } 
} 

链接:https://www.nowcoder.com/questionTerminal/a903ebe93add411d9c94b114f5fabb36
来源:牛客网
union 联合体是共用内存区域,也就是说int 和 char一起公用4byte.并且union一定是从低地址开始存放,所以char b对应最低内存区域。如果是大端存储,int的1存在最高位,其他全为0,小端存储时1存在最低位,所以只要判断b是否为0即可

1.19

写一个函数返回1+2+3++n的值(假定结果不会超过长整型变量的范围)
int sum(int n)
{
	return (1l + n) * n / 2;
}

一定要注意,long要加在1前面,或者用1l

>1.20

说一下C++和C的区别

设计思想上:C++是面向对象的语言,而C是面向过程的结构化编程语言
语法上:
C++具有封装、继承和多态三种特性
C++相比C,增加多许多类型安全的功能,比如强制类型转换、
C++支持范式编程,比如模板类、函数模板等

封装:将客观事物封装抽象为类,且具备一定访问权限。
继承:可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:允许将子类类型的指针赋值给父类类型的指针。实现多态,有二种方式,方法覆盖,方法重载。

参考链接:继承、封装、多态的概念与区别

1.21 static关键字的作用

  1. 全局静态变量
    在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。
    静态存储区,在整个程序运行期间一直存在。
    1)初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
    2)作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。
  2. 局部静态变量
    在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。
    内存中的位置:静态存储区
    1)初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
    2)作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;
  3. 静态函数
    在函数返回类型前加static,函数就定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。
    函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;
    warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰;
  4. 类的静态成员
    在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存储一处,供所有对象共用
  5. 类的静态函数
    静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
    在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值