我是靠谱客的博主 魁梧鸭子,最近开发中收集的这篇文章主要介绍C/C++笔试面试常见题目2,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

第一、转int到str

//使用函数sprintf将整形数字格式化
char* int2str(int nSrc,char*sDest)
{
if(sDest == NULL)
return NULL;
char nSrc_arry[64] = {0};
sprintf(nSrc_arry,"%d",nSrc);
memcpy(sDest,nSrc_arry,strlen(nSrc_arry));
return sDest;
}
//先逆序保存起来,在从尾部取出数据保存在str中
int int2str(int num, char * str)
{
int sign, count;
char buf[12] = {0};
sign = num<0 ? -1:1;	//标志位
num *= sign;
//将这个整数逆序存放在数组中
for(count=0; num; num/=10, count++)
{
buf[count] = num%10 + 48;
}
if(sign == -1)
buf[count++] = '-';
//逆序去除数据存入到str中
while(count)
{
*(str++) = buf[count-1];
count--;
}
return 0;
}
//不适用任何的库函数实现
char* int2str(int nSrc,char*sDest)
{
if(sDest == NULL)
return NULL;
//处理负数
if(nSrc < 0)
{
*sDest = '-';
sDest++;
nSrc *= -1;
}
//计算这个整数有多少个整数位
int n_bit = 0;
int m = nSrc;
char *p_sDest = sDest;
do
{
n_bit++;
m = m/10;
}
while(m!=0);
//填充这个整数位到sDest中
int x_src = nSrc;
*(p_sDest+n_bit) = '';
do
{
*(p_sDest+n_bit-1) = x_src%10 + '0';
n_bit--;
x_src = x_src/10;
}
while(x_src != 0);
return p_sDest;
}

以上三种方法中,第三种比较繁琐。另外出题者的意图不是很清楚,后续再做研究。先做出来再说。

第二、C标准库函数的安全性问题

 C里操作字符串很高效,但也很麻烦。
1. char * strcpy ( char * destination, const char * source );
最常用的函数,但是却不安全,原因在于,一是要destination有足够的空间,二是要保证source和destination指向的空间没有overlap。
2. int sprintf ( char * str, const char * format, ... );
也许要问,这个怎么用于字符串拷贝呢?可以这么用 sprintf(dest, "%s", src); 但是要调用者保证dest有足够的内存存放src。
3. char * strncpy ( char * destination, const char * source, size_t num );
比起strcpy,多了个长度的控制。从source拷贝num个字符到destination。如果source里不够num字符怎么办呢?会补充0。
一个典型的用法是:
char buf[MAX];
strncpy(buf, src, MAX-1);
这段代码的本意是,一个长为MAX的buf,最多也就放MAX-1个字符,最后一个位置放‘'。因此最多能从src里拷贝MAX-1个字符,如果src里没这么多,剩余的填充0就是了。
但是这样做就安全了么?不是,如果src刚好MAX-1个字符。注意到strncpy只复制了MAX-1个字符,最后一个位置未知,有潜在的隐患。下段代码可以诠释:
#define MAX 4
char buf[MAX];
char* src="123";
// solution 1. memset(buf, 0, MAX);
strncpy(buf, src, MAX-1);
// solution 2. buf[MAX-1] = '';
printf("%sn", buf);
有两个办法可以解决:1. 调用strncpy之前memset为0,有点浪费。2. 在strncpy之后对最后一个字符赋值为0。
都可以,但不够优雅。
4. int snprintf( char *buffer, int buff_size, const char *format, ... );
用作字符串拷贝的用法:
char buf[MAX];
snprintf(buf, sizeof(buf), "%s", src);
即安全,又简洁。
你可能会关心:如果src的长度大于dest(buf)呢?这个是另外一个问题,这里需要的是安全的字符串拷贝,在C语言里,如果一个字符串指针指向的内存没有结尾字符'',是非常危险的。
snprintf会把buf的最后一个位置保留为''。
关于返回值:如果当前buf够用,返回实际写入的字符数;如果不够用,返回将要写入的字符数。换句话说,返回值就是传入的字符数目。
假设当前的buf[4].
待写入    实际写入    返回值
12           12     2  够用
123        123   3  够用
1234     123   4  不够用
12345  123   5  不够用
sprintf/snprintf的另外一个用法:
itoa不是ANSI C或C++的一部分,可以变相的用sprintf来代替:
sprintf(str,"%d",value)  转换为十进制数值。
sprintf(str,"%x",value)  转换为十六进制数值。
sprintf(str,"%o",value)  转换为八进制数值。
总结:
一方面是不够容纳的问题 ,另一个是内存重叠的问题;
复制的时候检查目标内存的大小;对于C字符串警惕字符串结束标识‘’是否存在;
C标准库不安全的API可能引入漏洞,所以微软strsafe为之做了改进;

第三、可重入和线程安全的关系

1、概念比较
(1)一个函数对于多个线程是可重入的,则这个函数是线程安全的。
(2)一个函数是线程安全的,但并不一定是可重入的。线程安全强调“排队”,或者说“同步”;
(3)可重入性要强于线程安全性。即可重入必线程安全。
(4)可重入函数,是线程安全函数的一种。特点:当它们被多个线程调用时,不会引用任何共享数据,也就是不引用静态或全局变量。
(5)一个线程安全的函数通过“锁”来保护共享的资源不被并发地访问。
2、可重入函数:
(1)不为连续的调用持有静态数据。
(2)不返回指向静态数据的指针;所有数据都由函数的调用者提供。
(3)使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
(4)如果必须访问全局变量,记住利用互斥信号量来保护全局变量。
(5)绝不调用任何不可重入函数。
3、不可重入函数:
(1)函数中使用了静态变量,无论是全局静态变量还是局部静态变量。
(2)函数返回静态变量。
(3)函数中调用了不可重入函数。
(4)函数体内使用了静态的数据结构;
(5)函数体内调用了malloc()或者free()函数;
(6)函数体内调用了其他标准I/O函数。printf不可重入
(7)函数是singleton中的成员函数而且使用了不使用线程独立存储的成员变量 。
4、在大部分情况下,不可重入的函数可以修改成可重入的函数,这需要修改函数的对外接口。
很多不可重入的函数返回一个指向静态数据的指针。要把这样的不可重入函数改写为可重入函数时,有两种解决办法:
(1)返回从堆中分配的空间的地址。在这种情况下,调用者必须负责释放堆中的空间。这种办法的优点是不必修改函数的外部接口,但是不能向后兼容。现存的单线程的程序使用修改后的函数会导致内存泄露(因为它们没有释放空间)。
(2)由调用者提供空间。尽管函数的外部接口需要改变,仍然推荐这种方法。
5、使一个函数变成线程安全的
在一个多线程的程序中,所有的被多线程调用的函数多必须是线程安全的(或可重入的)。注意,不可重入的函数一般都是线程“不安全”的,然而,将它们改写成可重入的同时,一般就会将它们变成线程安全的。
“锁”住共享资源
使用静态数据或者其它任何共享资源(如文件、终端等)的函数,必须对这些资源加“锁”以实现对它们的串行访问,这样才能成为线程安全的函数。例如:
在一个使用线程库的多线程程序中,应该使用信号量来串行化共享资源的访问,或者其它“锁”。 
总的来说,如果一个函数在重入条件下使用了未受保护的共享的资源,那么它是不可重入的。

第四、IO多路复用问题
说到底就是一个select的使用问题,select观察文件描述符的读写状态,设定超时时间即可。
第五、网络编程中的状态机问题
就是说这个netclient目前的状态,是已经断开、已经连接、密码错误、还是被服务器踢开等。来决定这个类的使用。

第六、打印完数

求2000以内的所有“完数”。所谓“完数”是指一个数恰好等于它的因子值和。要求:所有的完数放在一个数组中,并输出所有完数。
IsWanshu(int Num)
{
int i = 2;
int total = 1;
for(;i<Num;i++)
{
if(Num%i == 0)
total += i;
if(total == Num)
{
printf("%dn",Num);
return 0;
}
}
return 0;
}
int main(void)
{
int i = 0;
for(;i < 2000;i++)
{
IsWanshu(i);
}
return EXIT_SUCCESS;
}

第七、抽象类的使用

class CAbstract

{
    virtual int fun() = 0;
}
CAbstract是一个抽象类,抽象类不能实例化,所以:
(1)作为该对象作为参数是不可以的,int fun(CAbstract A)错误
(2)作为函数的返回值对象是不可以的,CAbstract fun();错误
(3)定义抽象类的对象是错的,CAbstract obj;错误
抽象类的使用都是作为指针和引用方式出现,例如:
int fun1(CAbstract *A)
{
   return 0;
}
或者
int fun2(CAbstract &A)
{
   return 0;
}
或者定义CAbstract *p;

第八、关于虚函数的使用

#include <iostream>

#include <stdio.h>
using namespace std;
class A
{
public:
A()	{doSth();}
virtual void doSth(){printf("I am A!n");}
};
class B:public A
{
public:
virtual void doSth(){printf("I am B!n");}
};
int main()
{
A*pa = new B();
B*pb = new B();
pa->doSth();
pb->doSth();
delete pa;
delete pb;
return 0;
}
//结果:
I am A!
I am A!
I am B!
I am B!
//结论:父类的构造函数调用的是父类的doSth函数,而不是子类的doSth函数。
//
这道题只是关于构造函数的,子类的构造函数是默认的,没有打印信息
//
覆盖,虚函数实现多态。pa指向子类的空间,调用的是子类的doSth函数

第九、string类的实现

这个实现比较简单,这里是从网友哪里获取的代码,修改了一些编译中出现的bug,还有按照自己的书写习惯

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
class CMyString
{
friend std::ostream& operator<<( std::ostream& os, const CMyString& str);
private:
char* m_pData; //
私有变量保存字符串
public:
CMyString( const char* str = NULL ); // 构造函数
CMyString( const CMyString& str ); // 拷贝构造函数
~CMyString( void ); // 析构函数
CMyString& operator=( const CMyString& str ); // 赋值运算符
CMyString operator+( const CMyString& str ); // 字符串连接
bool operator==( const CMyString& str ); // 判断相等
char operator[]( int idx ); // 数组索引
int getLength(); // 返回长度
};
CMyString::CMyString( const char* str ) // 构造函数
{
if ( str == NULL )
{
this->m_pData = NULL;
}
else
{
this->m_pData = new char[ strlen( str ) + 1 ];
strcpy( this->m_pData, str );//''
}
}
CMyString::CMyString( const CMyString& str )// 拷贝构造函数
{
if ( str.m_pData == NULL)
{
this->m_pData = NULL;
}
else
{
this->m_pData = new char[ strlen( str.m_pData ) + 1 ];
strcpy( this->m_pData, str.m_pData );
}
}
CMyString::~CMyString( void )// 析构函数
{
if ( this->m_pData)
{
delete[] this->m_pData;
this->m_pData = NULL;
}
}
CMyString& CMyString::operator=( const CMyString& str)
{
if ( this != &str )
{
delete[] this->m_pData;
if ( !str.m_pData )
{
this->m_pData = 0;
}
else
{
this->m_pData = new char[ strlen( str.m_pData ) + 1 ];
strcpy( this->m_pData, str.m_pData );
}
}
return *this;
}
CMyString CMyString::operator+( const CMyString& str )
{
CMyString newString;
if ( !str.m_pData )
{
newString = *this;
}
else if ( !this->m_pData )
{
newString = str;
}
else
{
newString.m_pData = new char[ strlen( this->m_pData ) + strlen( str.m_pData ) + 1 ];
strcpy( newString.m_pData, this->m_pData );
strcat( newString.m_pData, str.m_pData );
}
return newString;
}
bool CMyString::operator==( const CMyString& str )
{
if ( strlen(this->m_pData) != strlen( str.m_pData ) )
{
return false;
}
else
{
return strcmp( this->m_pData, str.m_pData ) ? false : true;
}
}
char CMyString::operator[]( int idx)
{
if ( idx > 0 && idx < (int)strlen( this->m_pData ) )
return this->m_pData[idx];
return 0;
}
int CMyString::getLength()
{
return strlen(this->m_pData);
}
std::ostream& operator<<( std::ostream& os, const CMyString& str )
{
os<< str.m_pData;
return os;
}
int main()
{
CMyString str1("i love ");
CMyString str2("u baby");
std::cout << "str1: " << str1 << std::endl;
std::cout << "str2: " << str2 << std::endl;
CMyString str3 = str1 + str2;
std::cout << "str3: " << str3 << std::endl;
CMyString str4 = str2;
str1 = str2;
std::cout << "str4: " << str4 << std::endl;
std::cout << "str1: " << str1 << std::endl;
std::cout << "str3 length: " << str3.getLength() << std::endl;
std::cout << "str3[3]= " << str3[3] << std::endl;
std::cout << ( str1 == str3 ) << std::endl;
return 0;
}



最后

以上就是魁梧鸭子为你收集整理的C/C++笔试面试常见题目2的全部内容,希望文章能够帮你解决C/C++笔试面试常见题目2所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部