C/C++面试题之语言基础篇(一)

2024-02-20 13:12:10 浏览数 (1)

准备C/C 面试需要深入研究语言基础知识,掌握控制流结构、函数、指针和标准库,同时准备好回答各种与C/C 编程相关的问题,这将有助于你在面试中脱颖而出。希望这些建议对你有所帮助,祝你面试顺利!

简述下C 语言的特点和对象的特点

C 特点:

  1. 面向对象
  2. 封装和数据隐藏:把相关的数据封装成一个“类”组件
  3. 继承和重写:是子类自动共享父类属性和方法,这是类之间的一种关系
  4. 多态:增强软件的灵活性和重用性,通过virtual关键字

对象特点:

  1. 状态:用来描述对象的基本特征
  2. 行为:为用来描述对象的功能
  3. 标识:指对象在内存中都有一个唯一的地址值用来和其他对象区分开来

OOP 三大特点和继承中三个修饰符

c的三大特点是:

  1. 封装:封装是将数据和操作数据的方法封装在一个单元(类)内部,然后通过公共接口暴露有限的访问权限。
  2. 继承:继承允许一个类继承另一个类的属性和方法。
  3. 多态:多态允许不同类的对象对相同的消息(方法调用)作出不同的响应。

继承中的三个修饰符

  1. public:公有继承,派生类继承基类的公有成员,这些成员在派生类中仍然是公有的。
  2. protected:保护继承,派生类继承基类的保护成员,这些成员在派生类中变为保护或私有的。
  3. private:私有继承,派生类继承基类的私有成员,这些成员在派生类中变为私有的,不能被外部访问。

C语言和C 的区别

与C语言区别:

  1. C语言是面对过程的,而C 是面对对象的。C和C 都有结构的概念,但是在C语言中结构只有成员变量没有成员方法,而在C 结构中,它可以有自己的成员变量以及成员方法。
  2. C语言中结构的成员是默认是公共的,而在C 中没有加限定符则默认是私有的
  3. C 中有bool类型,而C语言可没有bool类型
  4. 定义常量C语言使用#define,而C 使用const
  5. 在C中,强制类型转换通常例如:(int)float_variable 在C 中,有四种类型转换操作符:static_cast<double>(float_variable)
  6. malloc/free和new/delete区别:

6.1 malloc()与free ()是C语言的标准库函数,new/delete是C 的运算符,所以new/delete不 需要头文件进行声明; 6.2 new/delete可以调用构造函数和析构函数;

new 和 malloc 的区别

  1. new 是运算符,而 malloc 是库函数
  2. new 返回的是指针,不需要进行类型转换;malloc 返回的是void *(无类型指针)。需要手动将其转换为适当的类型
  3. new 会调用构造函数来初始化对象(如果是自定义类的对象),而 malloc 不会执行构造函数。

void *

void * 是通用指针类型,被称为"无类型指针"。可以用来指向任何数据类型的内存地址。使用 void * 指针时需要谨慎,必须进行类型转换

示例(int 型转换):

代码语言:javascript复制
int * intPtr=static_case<int *>(ptr);

因为不提供类型检查,可能导致运行时类型错误。

代码语言:javascript复制
#include <iostream>
using namespace std;

int main()
{
    int x = 42;
    double y = 3.14;
    char c = 'A';

    void *ptr; // 声明一个无类型指针

    ptr = &x; // 指向整数
    //cout << *ptr << endl; //报错

    int *intPtr = static_cast<int *>(ptr);//类型转换语句
    cout << "*intPtr = "<<*intPtr << endl; // 42

    ptr = &y; // 指向双精度浮点数
    double *doublePtr = static_cast<double *>(ptr);
    cout << "*doublePtr = "<<*doublePtr << endl; // 3.14

    ptr = &c; // 指向字符
    char *charPtr = static_cast<char *>(ptr);
    cout << "*charPtr = "<<*charPtr << endl; // 'A'
}

C 中 struct 和 class 的区别

换个问法:说说C 结构体和C结构体的区别

  1. struct默认为public类型,而class默认为private类型
  2. struct只有public类型,而class支持public、protect以及private
  3. struct不支持成员函数,而class支持
  4. struct无法做到继承和重写,而class可以

的区别">include头文件的顺序以及双引号""和尖括号<>的区别

对于#include <a.h>,编译器从标准库路径开始搜索a.h 对于#include "a.h",编译器从用户的工作路径开始搜索a.h

导入C函数的关键字是什么,C 编译时和C有什么不同?

  1. 关键字:在C 中,导入C函数的关键字是extern,表达形式为extern “C” extern是C/C 语言中的一个关键字,用于声明一个变量或函数具有外部链接性,即这些变量或函数可以被其他文件访问。
  2. 编译区别:由于C 支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时一般只包括函数名

简述C 从代码到可执行二进制文件.exe的过程

有四个过程,预编译、编译、汇编、链接

C 函数只声明,不定义会在哪步报错

实际的错误通常发生在链接阶段,当链接器尝试将所有编译单元(源文件)组合在一起时。如果在链接过程中找不到函数的定义,链接器会生成一个"未定义的引用"错误。

static关键字的作用

  1. 隐藏。当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,故使用static在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。
  2. static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。
  3. static的第三个作用是默认初始化为0.和全局变量一样存储在静态数据区。

说说静态变量什么时候初始化?

对于C语言的全局和静态变量,初始化发生在代码执行之前,属于编译期初始化。 对于C :全局或静态对象对象首次用到时才进行构造。

静态局部变量,全局变量,局部变量的特点

  1. 首先从作用域考虑: 全局变量:全局作用域,可以通过extern作用于其他非定义的源文件。 静态全局变量 :全局作用域 文件作用域,所以无法在其他文件中使用。 局部变量:局部作用域,比如函数的参数,函数内的局部变量等等。 静态局部变量 :局部作用域,只被初始化一次,直到程序结束。
  2. 从所在空间考虑:除了局部变量在栈上外,其他都在静态存储区。
  3. 生命周期: 局部变量在栈上,出了作用域就回收内存;而全局变量、静态全局变量、静态局部变量都在静态存储区,直到程序结束才会回收内存。

内联函数和宏函数的区别

区别:

  1. 宏定义不是函数,无返回值、参数列表等。而内联函数本质上是一个函数,比如有返回值、参数列表等
  2. 宏函数是在预编译期做简单字符串替换 ;而内联函数则是在编译期进行代码插入
  3. 宏定义是没有类型检查的,无论对还是错都是直接替换;而内联函数在编译的时候会进行类型的检查

说说内联函数和函数的区别,内联函数的作用。

  1. 关键字不同: 内联函数使用 inline 关键字进行声明,而普通函数不需要。
  2. 编译时展开: 内联函数在编译时展开,而普通函数则在运行时进行函数调用。
  3. 是否需要寻址:内联函数不需要寻址,而普通函数需要寻址;
  4. 复杂程序不同:内联函数要求代码简单,不能包含switch、while语句以及递归。

内联函数的作用:提高程序的性能。

说说运算符i 和 i的区别

先看到实现代码:

代码语言:javascript复制
int i = 5;
int a = i  ; // a = 5, i = 6

int i = 5;
int b =   i; // b = 6, i = 6

赋值顺序不同: i 是先加后赋值;i 是先赋值后加;

说说const和define的区别

查看链接:const与define详解

C 多态与虚函数

查看链接:虚函数实现多态详解

简述C 有几种传值方式,区别是什么?

传参方式有这三种:值传递、引用传递、指针传递

值传递:形参即使在函数体内值发生变化,也不会影响实参的值;

代码语言:javascript复制
void foo(int x) {
    x = x * 2; // 修改的是副本,不会影响原始数据
}

引用传递:形参在函数体内值发生变化,会影响实参的值;

代码语言:javascript复制
void bar(int &x) {
    x = x * 2; // 修改原始数据
}

指针传递:在指针指向没有发生改变的前提下,形参在函数体内值发生变化,会影响实参的值;

代码语言:javascript复制
void baz(int *x) {
    *x = *x * 2; // 修改原始数据
}

堆和栈的区别

C 有几种构造函数

默认构造函数:默认构造函数没有参数,它用于创建对象的默认初始化。如果你没有为类定义构造函数,并且没有提供初始化值,编译器会自动生成一个默认构造函数。

代码语言:javascript复制
class MyClass {
public:
    MyClass() {
        // 默认构造函数
    }
};

带参数的构造函数 :带参数的构造函数接受一个或多个参数,用于初始化对象的成员变量。

代码语言:javascript复制
class MyClass {
public:
    MyClass(int x, double y) {
        // 带参数的构造函数
    }
};

拷贝构造函数:拷贝构造函数用于在创建一个对象时,将另一个对象的值复制到新对象中。它通常以引用参数传递另一个对象。

代码语言:javascript复制
class MyClass {
public:
    MyClass(const MyClass& other) {
        // 拷贝构造函数
    }
};

什么是拷贝构造函数

它是单个参数的构造函数,其参数是与它同属一类的对象的(常)引用;类定义中,如果未提供自己的拷贝构造函数,C 提供一个默认拷贝构造函数,该默认拷贝构造函数完成一个成员到一个成员的拷贝,属于浅拷贝

深拷贝和浅拷贝区别

浅拷贝(默认拷贝函数)︰将原对象或原数组的引用直接赋给新对象,新数组,新对象/新数组只是原对象的一个引用。 深拷贝∶创建一个新的对象和数组,将原对象的各项属性的"值”(数组的所有元素)拷贝过来,是“值",深拷贝会在堆内存Q中另外申请空间来储存数据,从而解决了指针悬挂问题。 注意:当数据成员中有指针时,必须要用深拷贝

左值引用和右值引用区别和目的

左值是能够出现在表达式左边的值如变量,右值是只能出现在等号右边的值,如常量。

  1. 左值可以取地址,右值不可。
  2. 左值引用表达式结束后对象依然存在。右值引用赋值表达式结束后对象会被销毁
  3. 左值引用后可以利用别名修改左值对象;右值引用绑定的值不能修改

目的

  1. 左值引用目的是为了传递和操纵数据
  2. 右值引用的目的是为了实现完美转发(传递参数的数据类型 左右值属性) 移动语义
  • 完美转发(Perfect Forwarding):右值引用允许在函数参数中精确传递参数的值类别(左值或右值)。
  • 移动语义(Move Semantics):移动语义允许将资源从一个对象移动到另一个对象,而不是进行昂贵的数据复制。
代码语言:javascript复制
template <typename T>
void wrapper(T&& arg) {
    // 在这里使用 std::forward 来实现完美转发
    some_function(std::forward<T>(arg));
}

void some_function(int& x) {
    // 处理左值引用
}

void some_function(int&& x) {
    // 处理右值引用
}

std::move 是什么

std::move用于将左值强制转换为右值引用,然后原来的x变成空串。

示例中,source 最初指向”hello,world!“。使用 std::move 后,source 的内容被移动到了 destination,而 source 变成了一个空字符串。

代码语言:javascript复制
#include <iostream>
#include <string>
using namespace std;

int main() {
    string x = "Hello, World!";
    string y;

    y = std::move(x);

    cout << "x: " << x << endl; // 输出为空字符串
    cout << "y: " << y << endl; // 输出"Hello, World!"

    return 0;
}

strcpy存在什么安全问题?

strcpy 是标准C库函数,用于将一个字符串复制到另一个字符串中。然而它不检查源字符串的长度,可能会导致缓冲区溢出,引发安全漏洞。

结构体的比较,怎么判断两个对象是否相等

需要逐个比较它们的成员变量(字段)

继承关系中的子类能访问父类哪些成员变量以及成员函数

在继承关系中,子类(派生类)通常可以访问父类(基类)的以下成员变量和成员函数,具体访问权限取决于这些成员的访问控制修饰符:

  1. 公有成员(public):子类可以访问父类的公有型成员变量和函数。
  2. 受保护成员(protected):子类可以访问父类的受保护成员变量和函数。
  3. 私有成员(private):子类不能访问父类的私有成员变量和函数。
  4. 构造函数和析构函数:如果基类的构造函数和析构函数是公有的,子类可以直接访问它们;如果基类的构造函数和析构函数是受保护或私有的,子类仍然可以访问它们,但只能在子类的构造函数和析构函数内部调用它们,不能在类外部使用。

c 中A类访问B类中的私有成员的解决方法

  1. 使用友元函数或友元类:B类中声明A类为友元,或者在A类中声明B类为友元
  2. 提供公有成员函数或接口:如果希望B类的私有成员被A类间接访问,可以在B类中提供一些公有成员函数或接口,以允许A类通过这些函数来访问B类的私有成员。

数组和链表的区别

0 人点赞