#我是小白之牛客网学习# C++面试题(自学整理阶段-转自牛客网)
分析代码问题
1.分析下面代码有什么问题?
void test1() { char string[10]; char* str1 = "0123456789"; strcpy( string, str1 ); }
答:string只有10字节空间,字符串str1还要包括结束符“\n”,故共有11个字符,strcpy把从src地址开始且含有’\0’结束符的字符串复制到以dest开始的地址空间,返回值的类型为char*。(原型声明:char strcpy(char dest, const char *src);),故会发生数组越界,程序崩溃…
2.分析下面代码有什么问题?
void test2() { char string[10], str1[10]; int i; for(i=0; i<10; i++) { str1 = 'a'; } strcpy( string, str1 ); }
答:首先,代码根本不能通过编译。因为数组名str1为 char *const类型的右值类型,根本不能赋值。
再者,即使想对数组的第一个元素赋值,也要使用 *str1 = ‘a’;
其次,对字符数组赋值后,使用库函数strcpy进行拷贝操作,strcpy会从源地址一直往后拷贝,直到遇到’\0’为止。所以拷贝的长度是不定的。如果一直没有遇到’\0’导致越界访问非法内存,程序就崩了。
修改方案为:
void test2() { char string[10], str1 [10]; int i; for(i = 0; i<9; i++) { str1[i] = 'a'; } str1[9] = '\0'; strcpy(string, str1); }
3.指出下面代码有什么问题?
void test3(char* str1) { if(str1 == NULL){ return ; } char string[10]; if( strlen( str1 ) <= 10 ) { strcp 4000 y( string, str1 ); } }
答:a. sizeof()会统计后面的\0,strlen不会;
b.要将str1拷贝到string,string为10,str1长度最多为10,而strlen不计\0,所以str1长度最多为9,不能等于10。改为:strlen(str1 ) < 10
4. 写出完整版的strcpy函数
void strcpy( char *strDest, const char *strStr) //将源字符串加const,表明其为输入参数,加2分 { assert( (strDest != NULL) && (strStr != NULL) ); //对源地址和目的地址加非0断言,加3分 char *address = strDest; while( ( *strDest++ = *strStr++) != '\0' ); return address; //为了实现链式操作,将目的地址返回,加3分 }
5.下面代码有什么问题
void GetMemory( char *p ) { p = (char *) malloc( 100 ); } void Test( void ) { char *str = NULL; GetMemory( str ); strcpy( str, "hello world" ); printf( str ); }
答(转):传入中GetMemory(char *p)函数的形参为字符串指针,在函数内部修改形参不能真正改变传入形参的实参值,执行完
char *str = NULL; GetMenmory(str);
后的str仍然为NULL;
1:传入形参并不能真正改变形参的值,执行完之后为空;
2:在函数GetMemory中和Test中没有malloc对应的free,造成内存泄漏。
修改:
#include <iostream> using namespace std; //传值调用 void GetMemory (char **p) { *p = (char *) malloc( 100 ); //malloc-向系统申请分配size个字节的内存空间,返回类型为void*类型 } void GetMemory_1 (char *&p) { p = (char *) malloc ( 100 ); } int main() { char *str = NULL; char *str1 = NULL; GetMemory( &str ); GetMemory_1( str1 ); strcpy(str,"hello world"); strcpy(str1,"hello world1"); cout<<str<<endl; cout<<str1<<endl; free(str); //函数结束时需要用free()将内存块释放 free(str1); return 0; }
6.下面代码有什么问题
链接:https://www.nowcoder.com/questionTerminal/13a4daacf9114ffea3bff132c8834db3 来源:牛客网 char *GetMemory( void ) { char p[] = "hello world"; return p;} void Test( void ) { char *str = NULL; str = GetMemory(); printf( str ); }
答:
char p[] = "hello world"; return p;
的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。
char p[] = “hello world”,相当于char p[12],strcpy(p, “hello world”),p是一个数组名,属于局部变量,存储在栈中,“hello world”存储在文字存储区,数组p存储的是“hello world”的一个副本,当函数结束时,p被回收,副本也就消失了(确切的说p指向的栈存储区被取消标记,可能随时被系统修改),而函数返回的p指向的内容也变得不确定,文字存储区的 " hello world" 未改变。
可以修改为:1). char *p = “hello world”; return p; 这里p直接指向文字存储区的“hello world”,函数按值返回p存储的地址,所以有效。
2).static char p[] = “hello world”; return p; static 指出数组p为静态数组,函数结束也不会释放,所以有效。
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 ); }
答(转):1). 传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句 *p = (char *) malloc( num ); 后未判断内存是否申请成功,应加上:
if ( *p == NULL) { perror("malloc fail"); // 进行申请内存失败处理 return -1; } // 同时应该考虑num>0
2).未释放堆内存,动态分配的内存在程序结束前没有释放,应该调用free,把malloc生成的内存释放掉,释放后记得str=NULL,否则可能会导致野指针出现;
3).printf(str) 改为printf("%s",str),以字符串形式打印输出变量str后光标换行,否则可使用格式化,字符串攻击
可修改为:
void GetMemory(char **p, int num){ if(num <= 0) printf("申请的内存空间要大于零!\n"); *p = (char*) malloc(num); if(*p == NULL){ printf("申请内存失败\n"); return -1; } } void test(){ char *str = NULL; GetMemory(str,100); strcpy(str,"Hello world"); printf("%s\n",str); free(str); str = NULL; }
8.下面代码有什么问题
swap( int* p1,int* p2 ) { int *p; *p = *p1; *p1 = *p2; *p2 = *p; }
答(转):1.需要一个返回值void
2. 在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。
该程序应该改为:
void swap( int* p1, int* p2) { int p; p = *p1; *p1 = *p2; *p2 = p; }
问答题
1.分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var)
【解答】
BOOL型变量:if( !var )
int型变量: if(var == 0)
float型变量:
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
指针变量:if(var == NULL)
【剖析】
考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==
0),而int型变量也可以写成if(!var),指针变量的判断也可以写成if(!var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。
一般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!var),表明其为“逻辑”判断;
如果用if判断一个数值型变量(short、int、long等),应该用if(var== 0),表明是与0进行“数值”上的比较;
而判断指针则适宜用if(var== NULL),这是一种很好的编程习惯。 浮点型变量并不精确,所以不可将float变量用“==”
或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。如果写成if (x == 0.0),则判为错,得0分。
2.以下为Windows NT下的32位C++程序,请计算sizeof的值
void Func ( char str[100] ) { sizeof( str ) = ? } void *p = malloc( 100 ); sizeof ( p ) = ?
参考答案:
sizeof( str ) = 4
sizeof ( p ) = 4
剖析:
Func ( char str[100] )函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
数组名的本质如下:
(1)数组名指代一种数据结构,这种数据结构就是数组;
例如:
char str[10]; count << sizeof(str) << endl;
输出结果为10,str指代数据结构char[10]。
(2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;
char str[10]; str++; // 编译出错,提示str不是左值
(3)数组名作为函数形参时,沦为普通指针
Windows NT 32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof( str ) 、sizeof ( p ) 都为4。
3.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。另外,当你写下面的代码时会发生什么事?
least = MIN(*p++, b);
首先学习下C++中的宏定义(转):https://www.geek-share.com/detail/2678774487.html
参考答案:
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
MIN(*p++, b)会产生宏的副作用
剖析:
这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。
程序员对宏定义的使用要非常小心,特别要注意两个问题:
(1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答:
#define MIN(A,B) (A) <= (B) ? (A) : (B) #define MIN(A,B) (A <= B ? A : B )
都应该判为0分;
(2)防止宏的副作用
宏定义#define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:
((*p++) <= (b) ? (*p++) : (b))
这个表达式会产生副作用,指针p会作2次++自增操作。(不要用改变变量的调用方法,来调用宏定义,会有副作用)
除此之外,另一个应该判0分的解答是:
#define MIN(A,B) ((A) <= (B) ? (A) : (B));
这个解答在宏定义的后面加**“;”**,显示编写者对宏的概念模糊不清
4.编写一个函数,作用是把一个char组成的字符串循环右移n个。
比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefg” 函数头是这样的:
//pStr是指向以’\0’结尾的字符串的指针
//steps是要求移动的n
void LoopMove(char *pStr, int steps) { //请填充..... }
答(转):
#include <stdio.h> #include <string.h> void LoopMove(char *pStr, int steps) { int len = strlen(pStr); int st = steps % len; //取余 //字符串长度为0,或不需移动,或移动步数小于等于0,返回,也可报错。 if (len == 0 || st == 0|| step <=0) return; char temp[100] = {0}; memcpy(temp,pStr+len-tr,tr); //以上为例,hi memcpy(temp+tr,pStr,len-sr); mencpy(pStr,temp,len); } int main() { char s[138]="abcdefghi"; LoopMove(s,2); printf("%s\n",s); return 0; }
输出结果为:hiabcdefg
作者:YLD10
链接:https://www.nowcoder.com/questionTerminal/64492230b659496dbae4957dd16d20b9
来源:牛客网memcpy(目标地址, 源地址, 拷贝的长度)。
例如 pStr=“123456”,steps=2,那么 len=6,st=2。
第一个memcpy 中,pStr+len-st=“56”,st=2 所以就是把 “56” 这两个字符拷贝给 temp,temp="56"两个字符,即 temp[0]=‘5’,temp[1]=‘6’。
第二个 memcpy 中,temp+st=temp[2] 所在的地址,pStr=“123456”,len-st=4,也就是说把 pStr 的前 4 个字符拷贝到从 temp[2] 开始的地址里,即 temp[2]=‘1’,temp[3]=‘2’,temp[4]=‘3’,temp[5]=‘4’,即 temp="561234"六个字符。
第三个 memcpy 就是把 temp 里面的 6 个字符拷贝到从 pStr 起的连续 6 个 char 空间里头,因为第 7个空间里至始至终都没有人动过,所以第 7 个空间里头还有 ‘\0’,所以 pStr=“561234” 字符串。这样就能实现循环右移了。
5.编写类String的构造函数、析构函数和赋值函数
编写类String的构造函数、析构函数和赋值函数,已知类String的原型为:
class String{ public: String(const char*str = NULL); //普通构造函数 String(const String &other); //拷贝构造函数 ~ String(void); //析构函数 String & operator = (const String &other); //赋值函数 private; char *m_date; //用于保存字符串 };
参考答案:
String::String(const char *str) { if(str == NULL) // 对m_date加NULL判断 { m_date = new char[1]; *m_date = '\0'; // 对空字符串自动申请存放结束标志'\0'的空 } else { int length = strlen(str); m_date = new char[length+1]; strcpy(m_date, str); } // String的析构函数 String::~String( void ) { delete [] m_date; //或delete m_date } //拷贝构造函数 Sring::String(const String &other) // 输入参数为const型 { int length = strlen(other.m_date); m_date = new char[length+1]; strcpy(m_date, other.m_date); } // 赋值函数 String& String::operater = (const String &other) //输入参数为const { if(this == &other) // 检查自赋值 return *this; delete [] m_date; // 释放原有的内存资源 int length = stlen( other.m_date); my_date = new char[length+1]; strcpy(m_date, other.m_date); return *this; // 返回本对象的引用 } }
6.请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1
解答:
int checkCPU() { { union w { int a; char b; } c; c.a = 1; return (c.b == 1); } }
剖析:采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节;Big-endian模式对操作数的存放方式是从高字节到低字节。
联合体union的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。如果谁能当场给出这个解答,那简直就是一个天才的程序员。
例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
而在Big-endian模式CPU内存中的存放方式则为:
7.写一个函数返回1+2+3+…+n的值(假定结果不会超过长整型变量的范围)
int Sum(int n) { return ((long)1+n)*n / 2; //或return (1l + n) * n / 2; } int Sum2(int n) { long sum = 0; for(i=1;i<=n;i++){ sum += i; } return sum; }
- C++面试题整理之一
- Linux下的C和C++面试题(10/19自己整理小结)
- 全面整理的C++面试题
- 全面整理的C++面试题
- 全面整理的C++面试题
- c++面试题整理(二)
- 全面整理的C++面试题
- C++面试题整理--持续更新
- 全面整理的C++面试题
- C/C++面试题(整理)
- [分类整理I]微软等100题系列V0.1版:c/c++基础面试题集锦
- C/C++常见面试题整理
- c++面试题整理(三)
- 全面整理的C++面试题(转载)
- C/C++面试题(整理)
- 整理的C、C++面试题
- C++ 面试题整理
- 全面整理的C++面试题
- [分类整理I]微软等100题系列V0.1版:c/c++基础面试题集锦
- [C/C++]整理郝斌精品C语言自学教程15课