C语言面试高频(二)
1.结构体和共用体的区别
1.定义
结构体:
- 成员在内存中独立存储,每个成员占用独立的内存空间。
- 内存占用是成员之和,每个成员都占用独立的空间。
- 成员可以同时被访问,通过成员名字来访问。
- 适合存储和处理多个不同类型的数据,如员工信息、图形对象等。
共用体:
- 成员共享同一块内存空间,只能存储一个成员的值。
- 内存占用是最大成员的大小,所有成员共享该空间。
- 成员只能同时访问其中的一个,存取时要明确指定。
- 适合存储和处理只使用其中一种类型的数据,可以节省内存空间或进行数据类型转换。
2.举例(便于理解)
#include <stdio.h> struct MyStruct { int x; float y; }; union MyUnion { int a; float b; }; int main() { struct MyStruct myStruct; union MyUnion myUnion; printf("结构体的大小: %zu 字节\n", sizeof(myStruct)); printf("共用体的大小: %zu 字节\n", sizeof(myUnion)); return 0; } 结构体的大小: 8 字节 共用体的大小: 4 字节
2.简述C++有几种传值方式之间的区别⭐
值传递(pass by value):参数以值的方式传递给函数,函数内部对参数的修改不会影响到原始数据。函数会创建参数的副本,在函数的作用域内使用副本进行操作。常用于传递简单类型的参数。
#include <iostream> void modifyValue(int x) { x = 10; } int main() { int num = 5; modifyValue(num); std::cout << "原始值: " << num << std::endl; return 0; } 输出结果:原始值: 5
引用传递(pass by reference):参数以引用的方式传递给函数,函数内部对参数的修改会影响到原始数据。函数直接操作原始数据,没有创建副本。可以用于传递任意复杂类型的参数,比如对象或容器。
#include <iostream> void modifyValue(int& x) { x = 10; } int main() { int num = 5; modifyValue(num); std::cout << "修改后的值: " << num << std::endl; return 0; } 输出:修改后的值: 10
指针传递(pass by pointer):参数以指针的方式传递给函数,函数内部可以通过指针来访问和修改原始数据。函数通过指针间接操作原始数据,需要注意空指针和指针的解引用。可以用于传递需要动态分配内存的参数,或者需要返回多个值的情况。
#include <iostream> void modifyValue(int* x) { *x = 10; } int main() { int num = 5; modifyValue(&num); std::cout << "修改后的值: " << num << std::endl; return 0; } 输出:修改后的值: 10
3.数组指针与指针数组的区别⭐
数组指针(pointer to an array):数组指针是指向数组的指针变量。数组指针的类型声明中,指针符号 * 出现在数组名之前,用于表示数组指针的类型。数组指针可以指向整个数组,而不仅仅是数组的第一个元素。
#include <stdio.h> int main() { int arr[5] = {1, 2, 3, 4, 5}; int (*ptr)[5]; // 声明一个指向包含5个整数的数组的指针 ptr = &arr; // 数组指针指向数组 printf("数组元素通过指针访问: "); for (int i = 0; i < 5; i++) { printf("%d ", (*ptr)[i]); // 使用指针访问数组元素需要解引用 } return 0; } 数组元素通过指针访问: 1 2 3 4 5
指针数组(array of pointers):指针数组是一个数组,数组的元素都是指针。指针数组的类型声明中,指针符号 * 出现在数组名的后面,用于表示指针数组的类型。指针数组的每个元素指向一个独立的内存块,可以指向不同的变量或数据。
#include <stdio.h> int main() { int num1 = 1, num2 = 2, num3 = 3, num4 = 4, num5 = 5; int* arr[5]; // 声明一个包含5个整型指针的指针数组 arr[0] = &num1; arr[1] = &num2; arr[2] = &num3; arr[3] = &num4; arr[4] = &num5; printf("指针数组元素的值: "); for (int i = 0; i < 5; i++) { printf("%d ", *arr[i]); // 解引用指针数组的元素以获取其值 } return 0; } 指针数组元素的值: 1 2 3 4 5
4.指针函数与函数指针的区别⭐
指针函数(function returning a pointer):指针函数是一种返回指针类型的函数。指针函数的返回类型是一个指针,指向特定类型的数据。调用指针函数时,会返回一个指向函数计算结果的指针。
#include <stdio.h> int* addIntegers(int a, int b) { int* result = (int*)malloc(sizeof(int)); *result = a + b; return result; } int main() { int* sum = addIntegers(5, 3); printf("和: %d\n", *sum); free(sum); return 0; } 输出:和: 8
函数指针(pointer to a function):函数指针是指向函数的指针变量。函数指针的类型声明中,指针符号 * 出现在函数名之前,用于表示函数指针的类型。函数指针可以用于直接调用指向的函数,或者作为参数传递给其他函数。
#include <stdio.h> int addIntegers(int a, int b) { return a + b; } int main() { int (*ptr)(int, int); // 声明一个指向以两个整数为参数并返回整数的函数的指针变量 ptr = addIntegers; // 将函数地址赋值给函数指针 int sum = ptr(5, 3); // 通过函数指针调用函数 printf("和: %d\n", sum); return 0; } 输出:和: 8
5.原码、反码、补码的定义
原码:最高位表示符号,其余位表示数值的绝对值。正数的原码就是二进制表示,符号位为0。负数的原码符号位为1,数值位根据绝对值的二进制表示。
反码:正数的反码与原码相同,负数的反码是对原码除符号位外的每一位取反(0 变为 1,1 变为 0)。
补码:正数的补码与原码和反码相同,负数的补码是对反码加 1。
6.内存分布模型⭐
上图是比较经典的内存分布的模型图,下面将对上图中的不同的组成部分进行详细解释(从低地址到高地址)注:必须知道组成结构但是具体的含义只需要理解。
- 代码段:存放程序的机器指令(即二进制代码)。通常是只读的,因为程序的指令在执行过程中不应该被修改。
- 数据段:存放已初始化的全局变量和静态变量。这些变量在程序开始运行时已经赋予了初始值。
- BSS 段:存放未初始化的全局变量和静态变量。它们在程序开始运行时会自动初始化为0或者空指针。
- 堆区:动态分配的内存空间,用于存放程序运行时动态申请的内存。(程序员可以通过函数(如malloc、calloc等)或者操作系统提供的接口来申请和释放堆内存,堆从低地址向高地址增长。)
- 栈区:存放函数的局部变量、函数参数值以及函数调用和返回时的相关信息。栈区是按照“先进后出”的原则进行管理,内存的分配和释放是自动进行的,栈从高地址向低地址增长。是一块连续的空间。
- 共享区:也称为文件映射或共享内存,用于实现不同进程之间的内存共享。
7.malloc和calloc的区别
malloc
- malloc 分配的内存是未初始化的,其中的字节内容是不确定的(可能是随机值)。
- 如果内存分配失败,malloc 返回一个空指针 NULL,可以通过检查返回值来判断是否分配成功。
calloc
- calloc 分配的内存会被初始化为全0。
- calloc 在分配失败时会自动抛出错误(异常),可以使用异常处理机制来捕获和处理错误。
8.malloc的底层原理⭐
1.结论:
- 当开辟的空间小于 128K 时,调用 brk()函数,malloc 的底层实现是系统调用函数 brk(),其主要移动指针 _enddata(此时的 _enddata 指的是 Linux 地址空间中堆段的末尾地址,不是数据段的末尾地址)
- 当开辟的空间大于 128K 时,mmap()系统调用函数来在虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空间来开辟。
2.具体实现
- 当调用 malloc(size) 时,它首先计算需要分配的内存块大小,
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本人2022年毕业于山东大学,目前就职intel。打算把之前校招时做的笔记通过专栏发出来,本专栏适合于C/C++、嵌入式方向就业的同学,本篇面经总结数千篇面经的知识集合,实时更新全网最新的嵌入式/C++最新内容,囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构、数据库等一系列知识点,在我看来这些是求职者在面试中必须掌握的知识点。最后呢祝各位能找到自己合适的工作。