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