1.static关键字的几个用处

(1). 修饰局部变量,正常的局部变量在处理作用域以后就会销毁,使用static修饰的局部变量生命周期边长,当走完整程序后才会销毁(作用域不变,出了作用域依旧无法被识别)’

void test()
{
    // 普通局部变量
    int a = 0;
    // static修饰局部变量
    static int b = 0;

    a++;
    b++;
    cout << "a = " << a << " , b = " << b << endl;
}

int main()
{
    test(); // a=1  b=1
    test(); // a=1  b=2
    test(); // a=1  b=3
  //b的生命周期变长了,所以b会不断变大,但作用域不变
cout << "b=" << b;//未定义标识符
    return 0;
}

(2). 修饰全局变量,正常全部变量在任何.cpp文件中都可见,static修饰后,它的链接属性由外部链接编程内部链接,使得这个全局变量只能在本文件中可见
(3). 修饰函数,用static修饰函数后,会改变函数的链接属性,由外部链接变成内部链接,与修饰全局函数一样。

2.对指针的理解

指针就是地址,它是变量的地址,存储这个地址的变量,叫做指针变量,指针可分为以及指针,二级指针,数组指针,函数指针等

3.什么是大小端

在操作系统中,从左向右地址是从小到大的,如果是大端存储,那么数据的从高地址位开始存储,否则是小端的话就从低地址开始存储

  int i = 1;
   char* ret = (char*)i;
   if (ret == 0) {
       //大端
   }
   else {
       //小端
   }

5.为什么要有结构体内存对齐规则

1.cpu不是按字节读内存的,cpu读取内存是按照固定块读的一般是4字节或者是8字节,如果地址没对齐,存到了中间块中,cpu可能会读取两次数据然后拼接数据,导致效率低下(本质是以空间换时间)

2.部分硬件需要,还有就是方便底层编译器管理内存
对齐规则:
每个成员的偏移量必须是自身大小的整数倍
整体的结构体总大小必须是最大偏移量的整数倍
最大对齐数:结构体中基本数据类型中最大的。

struct s{
    char a;//1字节
	int i;//4字节
	char b;//1字节
};
//这里最大对齐数是4,所以a占1字节,偏移量为0,i占4字节,i最大偏移量为4,所以a需要占3字节填充,然后
//i占4字节,(4-7)字节就是i,b占1字节,此时总字节是9字节(1+3+4+1),但是结构体的大小必须是最大对齐数的整数倍,所以需要再填充3字节,所以总大小是12字节

6.程序从编译到运行的整个过程

1.预处理阶段:头文件展开,去掉注释,条件编译,宏替换,形成.i文件还是c语言(把.h 文件里的所有内容,复制粘贴到.c 中,.h 不生成目标文件,只在预处理阶段展开到 .c 中,随 .c 一起编译成 .o。)

2.编译阶段:将c语言翻译成汇编语言,形成.s文件
3.汇编阶段:将汇编语言编译成二进制文件.o文件
4.链接阶段:.o文件+系统库,形成可执行文件

7.extern关键字的作用

声明一个变量或者函数,指明它是在其他文件中定义的,提醒编译器去其他文件中找定义。

8.volatile关键字的作用

volatile这个关键字的作用是告诉编译器当前的变量可能会被修改,不能从寄存器中拿这个变量的值要从内存中拿。

9.讲一下#define和const int定义宏的区别

  1. 首先#define在预处理阶段就进行处理,而const int是在编译阶段进行处理的
  2. 第二个就是#define就是进行简单的文本替换,不会对类型安全进行检查,而const int会进行安全检查
  3. 第三个就是#define调试可能会比较困难,且可读性不佳,const int定义的是真正的变量,容易调试

10.指针常量和常量指针

  1. 指针常量重点在于常量,所以int *const p=&a;这里就代表这它的指向就不能变量,而它的值只要是常量即可,
  2. 常量指针就是const int*p ;这种就代表着它的指向可以发生变化,而它的值就不能变化。指针就是地址,所以它的值就是地址,地址就不能变化。但是指向是初始化,可以改变

11.谈malloc和calloc还有realloc的区别

  1. 首先说malloc和calloc的区别:
    Malloc分配的内存是没有初始化的,分到的内存都是乱码,分配好的内存必须要初始化,而calloc分配的内存可以不初始化,分配的内存初始都是0
  2. 第二个malloc传参需要申请的字节数,而calloc传参为元素个数和每个元素的大小(即字节数)
  3. Realloc就是在已经分配好的空间做大小调整,参数是期望调整的总字节数,自动释放原来的内存,不用手动释放

讲一下malloc的原理

  1. 首先Malloc内部会维护一个空闲内存块链表,如果找到就直接切一块给你,找不到的话,就会向操作系统申请扩大内存空间,
  2. 然后申请成功以后就返回申请的内存的起始地址,(这里只分配,不初始化)
  3. free时不直接还给操作系统而是直接直接把内存标记为空闲状态,放回malloc的空闲链表。
    这里简单演示一下
class MyVector {
public:
    MyVector() :
        _arr(nullptr),
        _size(0),
        _capacity(0)
    {
        
    }
	void push_back(int value) {
		if (_size >= _capacity) {
			//使用realloc扩容,自动释放原来的内存,不用手动释放
            int newCapacity = _capacity == 0 ? 1 : _capacity * 2;
			int* newArr = (int*)realloc(_arr, sizeof(int) * newCapacity);
            if (newArr != NULL) {
                _arr = newArr;
                _capacity = newCapacity;
            }
		}
		_arr[_size++] = value;
	}
    void reverse(int newSize) {
        if (_capacity >= newSize) {
            return;
        }
        /*int* newCapacity = (int*)malloc(sizeof(int) * newSize);
        if (_arr != nullptr) {
            memcpy(newCapacity, _arr, sizeof(int)*_size);
            free(_arr);
        }
        _arr = newCapacity;
        free(newCapacity);
        _capacity = newSize;*/
        //使用calloc扩容
        int* newCapacity = (int*)calloc(newSize, sizeof(int));
		if (_arr != nullptr) {
			memcpy(newCapacity, _arr, sizeof(int) * _size);
			free(_arr);
		}
		_arr = newCapacity;
		_capacity = newSize;
    }
    
private:
    int* _arr;
    int _size;
    int _capacity;
};
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐