以下是我对c/c++一些笔试题中常见的题目,若有错误的或者遗漏的知识点,还请指点,定会做出修改!
1、new、delete、malloc、free关系
delete会调用对象的析构函数,new调用构造函数;malloc与free是库函数,new与delete是c++运算符
2、delete与delete[]区别
对于普通数据类型,delete和delete[]功能是相同的,对于自定义的复杂数据类型,delete和delete[]不能互用,delete[]用于删除一个数组、delete用于删除一个指针,delete[]会调用每一个成员的析构函数,内部数据类型没有析构函数,所以没有问题,如果你在用delete时没有括号,则delete认为指向单个对象,否则就认为指向一个数组<
3、c++有哪些性质
封装、继承、多态
4、多态、虚函数、纯虚函数
多态 :不同对象接收相同消息时产生的不同动作,c++的多态具体表现在运行和编译两个方面:1、在程序运行时的多态通过继承和虚函数来体现;2、在程序编译时的多态体现在函数和运算符重载上
虚函数 :在基类中冠以关键字virtual的成员函数,它提供了一种接口界面,在派生类中对基类的虚函数重新定义
纯虚函数:在基类中为其派生类保留一个函数名,以便派生类根据需要对它进行定义,作为接口而存在,纯虚函数不具备函数功能,一般不嫩美国直接调用
5、子类析构要调用父类的析构函数吗?
析构函数调用的次序是先派生类的析构,后基类的析构;定义一个对象时先调用基类的析构函数,然后调用派生类的构造函数
6、什么是引用,使用和声明要注意哪些问题?
引用是某个变量的别名,声明引用的时候要对其初始化,引用不是一种数据类型,因此不占用内存单元,系统也不给引用分配内存单元,因此不能建立数组的引用
7、将引用作为参数有哪些特点
1. 传递引用给函数参数与传递指针的效果是一样的
2. 使用引用传递的参数在内存中并没有产生副本,而使用一般的变量传递函数参数时,给形参分配存储单元,形参变量是实参的副本,如果传递的是对象,将还调用拷贝构造函数
3.如果要提高程序的运行效率又要保持传递的数据不发生变化,就可以使用const &
8、将引用作为函数返回值类型的格式、好处和要遵循的规则
格式:类型标识符 & 函数名 (形参) {函数体}
好处:在内存中不产生被返回值的副本(注意:正式因为这一点,返回一个局部变量的引用时不可取的,因为随着局部变量的生存期结束,相应的引用也会失效)
注意事项
(1)不能返回局部变量的引用
(2)不能返回内部new分配的内存的引用,当函数返回时,引用无所指,导致new的内存无法释放,造成内存泄露
(3)可以返回成员类的引用,但最好是const
9、结构体和联合体有什么区别?
在任何时刻,联合体中只存放了一个被选中的成员,所有成员共用一块地址空间,而结构成员的存放地址不同;对联合体的成员赋值,将会对其他成员重写,原来成员的值就不存在了
10、重载(overload)和重写(override)的区别?
重载:允许多个同名函数,函数的参数类型和数量不同
重写:子类重新定义父类虚函数的方法
实现原理
重载:编译器根据不同的参数列表,对同名函数的参数做修饰,编译器修饰过后的函数与原来不同,也就是说,函数在编译期间就已经确定了,是静态的,因为重载与多态无关
重写:和多态相关,当子类重新定义父类的虚函数后,父类指针根据给他的不同的子类指针,动态调用属于子类的函数
11、mian函数执行以前,还会执行什么代码?
全局对象的构造函数
12、描述内存分配方式以及他们的区别?
(1) 从静态存储区分配:内存在程序编译的时候就已经分配号了,在整个运行期间都存在,例如:全局变量、static变量
(2) 在栈上创建创建: 函数内部变量和参数都是在栈上创建的,函数执行结束时,都会被释放
(3) 从堆上分配:也称动态内存分配,程序在运行的时候用malloc或者new,程序在任何时候用free或者delete释放
13、请说出const与#define相比,有何优点?
const作用:定义常量,修饰函数参数,修饰函数返回值三个作用;被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性
const常量有数据类型,而宏常量没有数据类型,编译器可以对前者进行安全类型检查,而对后者只进行字符替换
14、关键字const是什么含义?
说const代表着常数,基本上就挂了,可以说是只读
(1) 关键字const的作用是为给读你代码的人传递有用的信息,实际上,声明一个参数为常量是为了告诉我们这个参数的目的
(2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码
(3) 合理的使用关键字const可以使编译器很自然地保护哪些不希望被改变的参数,防止其被无意的修改代码
const int a;//代表a是一个常数
int const a;//代表a是一个常数
const int *a;//a是一个指向常整数的指针(整数时不可以变的,但是指针可变)
int *const a;//a是一个指向整数的常指针(指针指向的整数可变,但是指针不可变)
int const * a const;//a是一个指向常整数的常指针
15、在c/c++程序中被编译后的函数,为什么要加extern"c"
c++语言支持函数重载,c语言不支持函数重载,函数被c++编译器编译后的名字与c语言不同,为了解此类名字匹配的问题,c++提供了c链接叫喊制定符号extern"c"
16、解释c++中静态函数和静态变量
(1) 静态数据成员在编译时期创建并初始化,在该类的任何对象之前就存在,不属于任何对象,而非静态数据成员属于对象所有
(2) 类静态成员属于整个类,不属于某个对象,由该类所有对象共享
(3) 可以通过类名访问,也可以通过对象访问静态数据
(4) 静态成员的意义,不在与信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装
(5) 静态成员函数只能访问静态数据,因为静态成员不属于对象,没有this指针
17、c语言中关键字static的作用?
(1) 在函数体,一个被声明为静态的变量在这一函数中被调用过程中维持其值不变
(2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其他函数访问
(3) 在模块内,一个被声明为静态的函数只可被这一模块内的其他函数调用。那就是这个函数被限制在声明它的模块的本地范围内使用
很多人可能会忽略第三点,但是第三点也是极其重要的一点。
18、struct和class的区别
struct的成员默认时共有的,而类的成员默认时私有的。
19、分别写出bool,int,float,指针类型的变量a与“零”的比较语句
bool : if(!a) || if(a)
int : if(a == 0)
float : if(a<0.000001 && a>-0.000001)
pointer : if(a != NULL) or if(a == NULL)
20、类成员函数的重载、覆盖和隐藏区别?
a.成员函数被重载的特征:
(1) 在同一个类中
(2) 函数名字相同
(3) 参数不同相同
(4) virtual 关键字可有可无
b.覆盖是指派生类覆盖基类函数:
(1) 分别位于派生类和基类中
(2) 函数名字相同
(3) 参数相同
(4) 基类必须有virtual关键字
c.隐藏是指派生类的函数屏蔽了与其同名的基类函数:
(1) 如果派生类的函数与基类的函数同名,但是参数不同,此时无论有无virtual关键字,基类函数将被隐藏(不要与重载混淆)
(2) 如果派生类的函数与基类函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时基类函数被隐藏
21、如何打印出当前源文件的文件名以及源文件的当前行号?
cout << FILE;
cout << LINE;
_FILE_和_LINE_是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的
22、.h头文件ifndef/define/endif的作用?
答:防止该头文件被重复引用:例如,假如a.c调用了n.h,也调用了b.c,但是b.c中也调用了n.h,那这样一来,a.c中就调用了两次n.h,如果做了宏,第一次调用之后就,不需要再第二次调用了
23、画出OSI的七层网络构图和TCP/IP的五层结构图
应用层:为应用程序提供服务
表示层:处理再两个通信系统中交换信息的表示方式
会话层:负责维护两个结点间绘画连接的建立、管理和终止,以及数据交换
传输层:向用户提供可靠的服务端 UDP TCP协议
网络层:通过路由选择算法为分组通过通信电子网选择最适当的路径,以及实现拥塞控制、网络互联等功能,数据传输 单元是分组,IP地址,路由器,IP协议
数据链路层:在物理层提供的服务基础上,数据链路层在通信的实体间建立数据链路连接,传输以帧为单位的数据包
物理层:传输比特流。传输单元是比特。调制解调器。
24、引用与指针有什么区别
(1) 引用必须被初始化,指针不必
(2) 引用初始化以后不能改变,指针可以改变所指的对象
(3) 不存在指向空值的引用,但存在指向空值的指针
25、交换两个数的宏定义
#define SWAP(a,b) (a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);
26、在c语言库函数中将一个字符转换成整数的函数:atol
int main()
{
long l;
char *str = "98765432";
l = atol(str);
printf("string = %s integer = %ld\n", str, l);
return 0;
}
27、用预处理指令#define声明一个常数,用以表明一年中有多少个秒?
#define YEAR (60*60*24*365)UL
1、#define语法的基本知识(例如:不能以分号结束,括号的使用)
2、懂得预处理将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际值
3、这个表达式将使用16位机的整型数溢出,因此要用到长整型符号L,告诉编译器这个常数是长整型数
4、如果在表达式中用到UL(表示无符号长整形),好的起点
28、写一个标准宏,返回两个数中较小的一个
#define MIN(A,B) ((A) < (B) ? (A) : (B))
29、下面代码的输出是什么,为什么?
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts(">6"):puts("<=6");
答案:">6"
当表达式中存在有符号类型和无符号类型时,所有的操作数都自动转换为无符号类型,因此-20变成了一个非常大的整数
30、用递归算法判断数组a[N]是否为一个递增数组
bool fun(int a[],int n) {
if(n == 1) {
return true;
}
if(n == 2) {
return a[n-1]>a[n-2];
}
return fun(a,n-1)&&(a[n-1]>=a[n-1]);
}
31、单链表的建立,把’a’–‘z’ 26个字母插入到表中,并且倒叙
方法一:
typedef struct Node
{
int data;
Node *next;
}*p;
void mian(void)
{
char c;
for(c=122;c>=97;c++) {
(*p).data = c;
p = p->next;
}
(*p).next = NULL;
}
32、判断字符串是否为回文
bool lsSymmetry(const char *p) {
assert(p != NULL);
const * q = p;
int len = 0;
while(*q != '\0') {
len++;
q++;
}
bool bSign =true;
q = p + len -1;
if (len > 0) {
if(*p++ != *q--) {
bSign = false;
break;
}
}
if (bSign==true) {
printf("yes\n");
} else {
printf("no\n")
}
return bSign;
}
33、用变量a给出下面的定义
a) 一个整型数
int a;
b) 一个指向整数的指针
int *a;
c) 一个指向指针的指针,它指向的指针是指向一个整数
int **a;
d) 一个有10个整数的数组
int a[10];
e) 一个有10个指针的数组,该指针是指向一个整型数的
int *a[10];
f) 一个指向10个整型数组的指针
int(*a)[10];
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
int (*a)(int)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
int (*a[10])(int)
34、c++中调用c语言
/*c语言头文件:cExample.h*/
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);
#endif
/*c语言实现文件:cExample.h*/
int add(int x,int y) {
return x+y;
}
/*c++实现文件*/
extern "C"
{
#include "cExample.h"
}
int main(int argc,char* argv[]) {
add(2,3);
return 0;
}
35、查找错误1
void test1() {
char string[10];
char* str = "0123456789";
strcpy(string,str);
}
// 答案:数组越界;strcpy是以’\0‘判断结束复制的,当str的元素赋值完后还会有一个默认的'\0'
36、查找错误2
void test() {
char string[10],str[10];
int i;
for(int i=0;i<10;i++) {
str = 'a';
}
strcpy(string,str);
}
/*答案:string数组会越界,strcpy可能会一直复制下去,因为str中没有'\0'
,但是strcpy赋值以'\0'为结束符
*/
37、查找错误3
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)函数的形参为字符串指针,在函数内部
//修改形参并不能真正的改变传入的形参的值,执行完之后,str还是null
注意:你要修改什么变量,形参就要带入变量的地址,本题修改的是指针,所以带入指针的地址
38、查找错误4
char* GetMemory(void) {
char p[] = "hello world";
return p;
}
void Test(void) {
char *str = NULL;
str = GetMemory();
printf(str);
}
//p[]数组为函数内部的局部自动变量,在函数返回后,内存已经被释放
39、查找错误5
char *GetMemory(char **p,int num) {
*p = (char *)malloc(num);
}
void Tset(void) {
char *str = NULL;
GetMemory(&str,100);
strcpy(str,"hello");
printf(str);
}
//在*p后面做一个判断会更好
if(*p == NULL) {
....
}
40、查找错误7
void Test(void) {
char *str = (char*)malloc(100);
strcpy(str,"hello");
free(str);
}
//答案:1、未判断str指针;2、free之后,应该把str = NULL;
41、完成以下程序
#include <stdio.h>
#define N 8
int main(){
int i,j,k;
for(i=1;i<=N;i++) {
for(j=1;j<=i+1;j++) {
printf("*");
for(k=1;k<=i;k++) {
printf(".");
}
}
printf("\n");
return 0;
}
}
42、编写strcat函数
char * __cdecl strcat(char *dst,const char *src) {
char *p = dst;
while(*p)
p++;
while(*p++ = *src);
return dst;
}
43、冒泡排序
void sort(int a[],int n)
{
for(i=0;i<n-1;i++) {
for(j=0;j<n-i-1;j++) {
if(a[j] > a[j+1]) {
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
44、斐波那契数列1、1、2、3、5…求第十项,可以用递归
int main()
{
printf("the 10th is %d",Phoneponatch(10));
return 0;
}
int Phoneponatch(int N) {
if(N == 1) {
return 1;
}
if (N == 2) {
return 1;
}
if(n > 2) {
return Phoneponatch(N-1) + return Phoneponatch(N-2);
}
}
45、如何判断一个单链表是有环的(不能用标志位,最多只能用两个额外指针)
struct node {
char val;
node* next;
}
/*
false:无环
true:有环
思路:两个指针,一个每次递增一步,一个每次递增两步
有环的话会重合
*/
bool check(const node* head) {
if(head == NULL){
return false;
}
node *low=head;
node *fast=head->next;
while(fast != NULL && fast->next != NULL) {
low = low->next;
fast = fast->next->next;
if(low==fast) {
return false;
}
}
return false;
}
46、写出一个函数找出一个正数数组中的第二大的数
int find_sec_max(int data[],int count) {
int maxnumber = data[0];
int sec_max = -32767;
for(int i=0;i<count;i++) {
if(data[i] > maxnumber) {
sec_max = maxnumber;
maxnumber = data[i];
}else {
if(data[i] > sec_max) {
sec_max = data[i];
}
}
}
return sec_max;
}
47、编写string的构造函数,析构函数,拷贝构造函数、赋值函数
class String
{
public:
String(const char *str = NULL);//通用构造函数
String(const String &another);//拷贝构造函数
String & operator = (const String &rhs);//赋值函数
char *mdata;
}
/*通用构造函数*/
String::String(const char *str) {
if(str == NULL) {//不要忘记了字符串为空的可能性
mdata = new char[1];
mdata[0] = '\0';
}else {
mdata = new char[strlen(str) + 1];
strcpy(mdata,str);
}
}
/*拷贝构造函数*/
String::String(const String &another) {
mdata = new char[strlen(another.mdata) + 1];
strcpy(mdata,another.mdata);
}
/*赋值函数*/
String &String::operator = (const String &rhs) {
if(this == &rhs) {
return *this;
}
delete []mdata;
mdata = mew char[strlen(rhs.mdata) + 1];
strcpy(mdata,rhs.mdata);
return *this;
}
/*析构函数*/
String::~String() {
delete []mdata;
mdata = NULL;
}
48、已知链表的头结点head,写一个函数把链表逆序
struct Node
{
int data;
Node *next;
};
typedef struct Node Node;
Node *receivehead(Node *head) {
if(head == NULL || head->next == NULL) {
return head;
}
Node *p1 = head;
Node *p2 = p1->next;
Node *p3 = p2->next;
p1->next = NULL;
while(p3 != NULL) {
p2->next = p1;
p1 = p2;
p2 = p3;
p3 = p3->next;
}
p2->next = p1;
head = p2;
return head;
}
49、两个链表有序,合并之后依然有序
Node *Merge(Node *head1,Node *head2) {
if(head1 == NULL) {
return head2;
}
if(head2 == NULL) {
return head1;
}
Node *head = NULL;
Node *p1 = NULL;
Node *p2 = NULL;
if(head1->data < head2->data) {
head = head1;
p1 = head1->next;//第一位已经有了,从第二位开始比
p2 = head2;
}
if(head2->data < head1->data) {
head = head2;
p1 = head1;
p2 = head2->next;//第一位已经有了,从第二位开始比
}
Node *pcurrent = head;
while(p1 != NULL && p2 != NULL) {
if(p1->data < p2->data) {
pcurrent->next = p1;
pcurrent = p1;
p1 = p1->next;
}
if(p1->data > p2->data) {
pcurrent->next = p2;
pcurrent = p2;
p2 = p2->next;
}
}
if (p1 != NULL) {
pcurrent->next = p1;
}
if (p2 != NULL) {
pcurrent->next = p2;
}
return head;
}
50、不调用c/c++的字符串库函数,编写strcpy
char *strcpy(char* strDest,const *strSrc) {
if ((strDest == NULL) || strSrc == NULL) {
return NULL;
}
char *str = strDest;
while((*strDest++=*strSrc++)!='\0');
*strDest = '\0';
return str;
}
51、查找一个数组中第二大的数
int find_sec_max(int data[],int count) {
int maxnumber = data[0];
int sec_max = -32767;
for(int i=0;i<count;i++) {
if(data[i] > maxnumber) {
sec_max = maxnumber;
maxnumber = data[i];
}else {
if(data[i] > sec_max) {
sec_max = data[i];
}
}
}
return sec_max;
}
52、删除字符串中重复的字符,并且保留以前的先后顺序
int GetResult(const char *input,char *output) {
int OutCnt=0;
int i,j,find;
if(!output || !input) {
return -1;
}
for(int i=0;input[i]!='\0';++i) {
for(j=0,find=0;j<OutCnt;j++) {
if(input[i] == output[j]) {
find = 1;
}
if(find==0) {
output[j]=input[i];
OutCnt++;
}
}
}
output[OutCnt]='\0';
return 0;
}