首页 > 其他分享 >面试基础概念题(keep updating)

面试基础概念题(keep updating)

时间:2023-05-04 20:23:18浏览次数:39  
标签:vtable 函数 void Derived keep 面试 const data updating

1、const的作用有哪些,谈一谈你对const的理解?
(1)const起一个限制作用,限制修改,防止被修饰的成员的内容被改变。使用const关键字修饰的变量可以认为有只读属性。

(2)const 关键字修饰函数形参时,可以保护输入的参数。(如 ,字符串拷贝函数 : char *strcpy(char *strDest, const char *strSrc) , 其中const char *strSrc 为常量指针,const 修饰的是 *strSrc , 所以 *strSrc代表的内容不能被修改。)

(3)const修饰函数的返回值的时候,可以保护指针指向的内容或者引用的内容不被修改。(比如:const char * function(void);)

(4)const 修饰指针变量的时候 , 可以构成常量指针与指针常量两种容易混淆的概念。

2、描述char、const char、char* const、const char* const的区别?
A: char 定义普通字符,const char 定义的字符可以认为有只读属性,char *const 修饰的变量为指针常量,const修饰的是那个指向该字符的指针,所以该指针不能被改变指向,但是可以改变该指针当前所指的字符内容。

const char * const (常量指针常量), 既不能改变指针所指的内容,也不能改变指针的指向。

3、指针常量和常量指针有什么区别?
指针常量:char * const p; // 指的是p这个指针被声明为常量,不能改变指针的指向,但是可以改变指针指向内存中的内容。
常量指针: char const * p;// 指的时p指针指向的内容被声明为常量,不能改变指针指向的内容,但是可以改变指针的指向。

4、static的作用是什么,什么情况下用到static?
1)在函数内部,static 关键字可以用来控制函数的作用域。静态局部变量的生命周期和全局变量相同,但是其作用域被限制在了函数内部。
2)在文件级别(也就是在函数外部),static 关键字可以用来限制变量或函数的作用域,这种变量被称为静态全局变量。静态全局变量的生命周期和全局变量相同,但是其作用域被限制在了当前文件内部。
3)在类中,static 关键字可以用来声明静态成员变量和静态成员函数。静态成员变量是属于类的变量,而不是属于类的实例的变量。静态成员函数是属于类的函数,而不是属于类的实例的函数。静态成员变量和静态成员函数可以通过类名访问,也可以通过对象名访问。

5、全局变量与局部变量的区别?
全局变量和局部变量都是存储数据的方式,但其作用域和生命周期不同。全局变量在整个程序中都可以访问,生命周期也是整个程序的运行周期。而局部变量只在其定义的函数内部可用,生命周期也只在函数执行时存在。

6、宏定义的作用是什么?
宏定义是一种预处理指令,只是简单的文本替换,预处理器只是简单地将代码中出现的宏名替换为宏定义中的内容,可以在程序中定义一个常量或者一个简单的函数。宏定义可以提高程序的可读性,很重要的一个作用就是避免程序中出现大量的魔法数字或者重复代码。

7、内存对齐的概念?为什么会有内存对齐?
内存对齐是指在内存中分配空间时,按照一定规则将数据排列在内存中的过程。在现代计算机中,内存对齐是为了提高数据访问的效率。
1)CPU对内存的读取不是连续的,而是分成一块一块大小来读,块的大小只能是1、2、4、8、16...字节。
2)当读取操作的数据不对齐,CPU需要两次总线周期来访问内存,数据对齐的情况下只需要一次,因此不对齐的话性能会有折扣。
具体对齐规则如下:
//1、数据成员本身要对齐,每个数据成员都要对齐
//2、结构体也要对齐,最后结构体的大小是结构体中最大数据成员的整数倍。
//3、结构体里面还有结构体的时候,里面的结构体要对齐,并且里面结构体也要满足对齐规则(要按照最大数据成员的整数倍开始对齐。)
除此之外,对齐的进行还跟对齐系数(pragma pack)相关,比如64位系统默认对齐系数为8,32位系统默认对齐系数只有4。通过预编译(#pragma pack(n), n= 1,2,4,8,16)指令可以改变对齐系数。

8、inline 内联函数的特点有哪些?它的优缺点是什么?
内联函数是一种特殊的函数,它在编译时会将函数体直接嵌入到调用代码中,而不是通过函数调用来执行。内联函数的特点有:
1)编译器会将内联函数的代码直接嵌入到调用代码中,减少了函数调用的开销,从而提高了程序的执行效率。
2)内联函数通常是在头文件中定义的,这样可以让编译器在编译时直接将函数的代码嵌入到调用代码中,避免了多次链接相同的函数代码。
3)内联函数的调用方式和普通函数相同,对程序员来说是透明的,只需要在函数声明前加上 inline 关键字即可。

内联函数的优点:
1)提高程序的执行效率且减少了代码体积:代码会被直接嵌入到调用代码中,避免了函数调用的开销,从而提高了程序的执行效率,减少了代码体积。
2)方便调试:内联函数的代码在调用处展开,可以方便地进行调试。

内联函数的缺点:
1)增加代码体积:内联函数的代码会被直接嵌入到调用代码中,如果内联函数的代码比较长,会使得代码体积增大,从而降低程序的性能。
2)编译时间增加:内联函数的代码会被直接嵌入到调用代码中,如果内联函数的使用比较频繁,会增加编译时间
3)可读性降低:内联函数的代码会被直接嵌入到调用代码中,使得代码可读性降低,从而增加了代码维护的难度

9、如何用C 实现 C++ 的面向对象特性(封装、继承、多态)

1)封装:在 C 中,可以通过结构体和函数指针来实现类似于封装的效果。
首先,定义一个结构体来表示一个对象的数据:

typedef struct {
    int data;
} Object;

然后,定义一组函数来操作这个结构体,这些函数可以看作是对象的行为:

void Object_init(Object *obj, int data) {
    obj->data = data;
}

int Object_get_data(Object *obj) {
    return obj->data;
}

void Object_set_data(Object *obj, int data) {
    obj->data = data;
}

这些函数可以通过函数指针来封装到一个结构体中:

typedef struct {
    void (*init)(Object *, int);
    int (*get_data)(Object *);
    void (*set_data)(Object *, int);
} Object_vtable;

typedef struct {
    Object_vtable *vtable;
    Object data;
} Encapsulation;

void Encapsulation_init(Encapsulation *encap, int data) {
    encap->vtable->init(&encap->data, data);
}

int Encapsulation_get_data(Encapsulation *encap) {
    return encap->vtable->get_data(&encap->data);
}

void Encapsulation_set_data(Encapsulation *encap, int data) {
    encap->vtable->set_data(&encap->data, data);
}

现在,我们就可以使用 Encapsulation 结构体来实现类似于封装的效果了:

int main() {
    Object_vtable obj_vtable = {
        .init = Object_init,
        .get_data = Object_get_data,
        .set_data = Object_set_data
    };
    Encapsulation encap = {
        .vtable = &obj_vtable
    };
    Encapsulation_init(&encap, 42);
    printf("%d\n", Encapsulation_get_data(&encap));  // 输出 42
    Encapsulation_set_data(&encap, 100);
    printf("%d\n", Encapsulation_get_data(&encap));  // 输出 100
    return 0;
}

2)继承
在 C 中,可以通过结构体嵌套和函数指针来实现类似于继承的效果。
首先,定义一个基类的结构体和函数指针:

typedef struct {
    int base_data;
} Base;

typedef struct {
    void (*base_method)(Base *);
} Base_vtable;

然后,定义一个派生类的结构体,它嵌套了一个基类的结构体,并且定义了一个自己的数据和方法:

typedef struct {
    Base_vtable *base_vtable;
    int derived_data;
} Derived;

void Derived_method(Derived *obj) {
    printf("Derived_method: base_data=%d, derived_data=%d\n", obj->base_vtable->base_method, obj->derived_data);
}

Derived_vtable Derived_vtable_instance = {
    .base_vtable = &Base_vtable_instance,
    .derived_method = Derived_method
};

现在,我们就可以定义一个 Derived 对象,并且调用它的方法了:

int main() {
    Base_vtable Base_vtable_instance = {
        .base_method = NULL
    };
    Derived_vtable Derived_vtable_instance = {
        .base_vtable = &Base_vtable_instance,
        .derived_method = Derived_method
    };
    Derived obj = {
        .base_vtable = &Derived_vtable_instance,
        .derived_data = 42
    };
    obj.base_vtable->base_method = (void (*)(Base *))Derived_method;
    obj.base_data = 100;
    obj.base_vtable->base_method((Base *)&obj);  // 输出 Derived_method: base_data=100, derived_data=42
    return 0;
}

这里需要注意的是,如果派生类的方法需要访问基类的数据或方法,可以通过基类的指针来实现。

3)多态
在 C 中,可以通过函数指针和虚函数表来实现类似于多态的效果。
首先,定义一个基类的结构体和虚函数表:

typedef struct {
    void (*virtual_method)(void *);
} Base;

typedef struct {
    void (*virtual_method)(void *);
} Base_vtable;

然后,定义一个派生类的结构体,它嵌套了一个基类的结构体,并且定义了一个自己的虚函数表:

typedef struct {
    Base base;
    int derived_data;
} Derived;

void Derived_method(Derived *obj) {
    printf("Derived_method: derived_data=%d\n", obj->derived_data);
}

Derived_vtable Derived_vtable_instance = {
    .virtual_method = (void (*)(void *))Derived_method
};

现在,我们就可以定义一个 Derived 对象,并且调用它的虚函数了:

int main() {
    Derived obj = {
        .base = {
            .virtual_method = (void (*)(void *))Derived_method
        },
        .derived_data = 42
    };
    obj.base.virtual_method((void *)&obj);  // 输出 Derived_method: derived_data=42
    return 0;
}

这里需要注意的是,如果派生类的虚函数需要访问派生类的数据,可以通过派生类的指针来实现。此外,虚函数表必须在每个对象中都存在,这会增加内存的开销。

10、memcpy怎么实现让它效率更高?
memcpy 是一个 C 标准库函数,用于在内存之间复制指定数量的字节。为了实现更高效的 memcpy,可以采用以下几种方法:

1)使用平台特定的指令集。现代的 CPU 都支持一些特定的指令集,例如 SSE、AVX 等。这些指令集可以对数据进行并行操作,从而提高复制速度。

2)使用编译器优化。现代的编译器都可以对代码进行优化,使得代码更加高效。例如,可以使用 -O2 或 -O3 选项来启用编译器优化。另外,可以使用 restrict 关键字来告诉编译器,指针之间没有重叠,从而使得编译器可以进行更好的优化。

3)使用多线程。在多核 CPU 上,可以使用多线程来并行复制数据,从而加快复制速度。例如,可以将数据划分为多个块,每个线程负责复制一个块,然后将结果合并。需要注意的是,多线程的实现需要考虑线程同步和数据竞争等问题。参考代码如下:

#include <pthread.h>

typedef struct {
    void* dest;
    const void* src;
    size_t n;
} memcpy_arg;

void* memcpy_worker(void* arg) {
    memcpy_arg* m_arg = (memcpy_arg*)arg;
    memcpy(m_arg->dest, m_arg->src, m_arg->n);
    return NULL;
}

void* my_memcpy(void* dest, const void* src, size_t n) {
    const size_t num_threads = 4;  // 使用 4 个线程
    size_t i, block_size = n / num_threads;
    pthread_t threads[num_threads];
    memcpy_arg args[num_threads];

    for (i = 0; i < num_threads; i++) {
        args[i].dest = (char*)dest + i * block_size;
        args[i].src = (const char*)src + i * block_size;
        args[i].n = (i == num_threads - 1) ? n - i * block_size : block_size;
        pthread_create(&threads[i], NULL, memcpy_worker, &args[i]);
    }

    for (i = 0; i < num_threads; i++) {
        pthread_join(threads[i], NULL);
    }

    return dest;
}

需要注意的是,多线程的实现可能会导致额外的开销,因此在数据比较小的情况下,多线程实现可能不如单线程实现效率高。

标签:vtable,函数,void,Derived,keep,面试,const,data,updating
From: https://www.cnblogs.com/serendipitymaker/p/17372392.html

相关文章

  • 面试题——python后端开发
    Python和Java、PHP、C、C#、C++等其他语言的对比?python语言,是面向对象、直译式计算机程序设计语言,python语法简洁清晰,具有丰富和强大的类库。Python是完全面向对象的语言。函数、模块、数字、字符串都是对象。并且完全支持继承、重载、派生、多继承,有益于增强源代码的复用性......
  • 面试题 02.07(Java). 链表相交(简单)
    题目:本题与:力扣160相交链表一致给你两个单链表的头节点 headA和headB,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null。图示两个链表在节点c1开始相交:题目数据 保证 整个链式结构中不存在环。注意,函数返回结果后,链表必须 保持其原始结构......
  • 毕业生进入社会,JAVA工程师面试经验汇总
    Java工程师是高度需求的技术岗位之一,面试过程非常重要。以下是一些Java工程师面试经验:基础知识:面试官可能会问关于Java基础知识的问题,例如Java语言特性、集合框架、多线程等。在准备面试时,应该学习这些内容,并确保自己能回答相关问题。经验和项目:面试官通常会问你参与的项目和你遇......
  • Nginx 面试题总结大全
    转载请注明出处:1介绍下nginx特点与常用模块2nginx特点详细3反向代理和正向代理4负载均衡策略有哪些5Nginx如何实现动静分离? 6Nginx常用命令有哪些?7Nginx进程模型8nginx是四层协议还是七层的 9nginx如何自定义负载均衡......
  • 程序员面试金典---20
    井字游戏思路:朴素求解先判断横、竖、两个对角线如果前面判断无法得出结果,用函数countSpace判断是否有空格,有代表没有填满,输出Pending、否则输出Draw。代码:/***@param{string[]}board*@return{string}*/vartictactoe=function(board){if(board.lengt......
  • about-keeping-pets
    关于养宠物Datetime:2023-05-03T20:04+08:00Categories:Essay这是一篇很久之前就想要写的东西,但是优先级不高所以一直放着,今晚看完了《忠犬八公》,是国内翻拍的(此篇文章不是影评)。小时候我很想养一只什么,可以是猫,也可以是狗,还可以是鸟,我还记得有一篇小学课文就是讲一个小孩想......
  • TypeScript 面试题
    一、TypeScript是什么?JavaScript是一种解释型的脚本语言,基于对象,跨平台的特性,活跃于各大网站制作中。而TypeScript则是以JavaScript作为基础,并对其扩展的一种新的语言, 二、TypeScript的内置数据类型有哪些?数字类型:用于表示数字类型的值。TypeScript中的所有数字都存储为浮......
  • 2023前端面试题一
    元素类型空元素brhr块级元素divfooterheadersectionph1h6行内元素imginputspan元素类型转换display:block独占一行,可以设置宽高display:inline不独占一行,不可以设置宽高display:inline-block不独占一行,可以设置宽高样式导入方式link@import......
  • Axios 面试题
    一、Axios是什么?Axios是一个基于promise的网络请求库,可用于node.js和浏览器中。它是isomorphic的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生node.jshttp模块,而在客户端(浏览器端)则使用XMLHttpRequests。特性1.从浏览器创建XMLHttpRequests,从node.js创......
  • 面试官:您能说说序列化和反序列化吗?是怎么实现的?什么场景下需要它?
        序列化和反序列化是Java中最基础的知识点,也是很容易被大家遗忘的,虽然天天使用它,但并不一定都能清楚的说明白。我相信很多小伙伴们掌握的也就几句概念、关键字(Serializable)而已,如果深究问一下序列化和反序列化是如何实现、使用场景等,就可能不知所措了。在每次我作为面......