C/C++面试题

以下是我对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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值