开发岗笔试基础题总结

const和指针

const int * a // a是一个指针,指向一个const int类型的内存, a本身可以修改指向别的变量,但是a所指向的内存中的数据不能修改。
int const * a // 和第一种相同
int * const a // const修饰的是a, a是一个int *型的变量。也就是说a本身不能修改,即指向确定的内存,但所指向的内存中的数据可以修改。
const int * const a // a以及a所指向的内存中的值都不能被修改。

主要看const后边修饰的是什么,

  • 第一种const修饰的是int * a, 那就是*a不能被改变, 而a可以改变。
  • 第二种const修饰的是* a, 同样*a不能改变,即指向的内存中的值不能改变。
  • 第三种const修饰的是a,即a本身不能被修改,也就是说指向确定的内存空间,而*a内存中的值可以被改变。

结构体的内存对齐

#include <iostream>
#include <cstddef>

using namespace std;

struct a{
        char a;
        int b;
        short c;
        double d;
};

struct b{
        char a;
        short b;
        int c;
        float d;
};

int main()
{
        cout<<"sizeof(struct a) : "<<sizeof(struct a)<<" sizeof(struct b) : "<<sizeof(struct b)<<endl;
        cout<<"sizeof(short) : "<<sizeof(short)<<endl;
        cout<<"sizeof(int) : "<<sizeof(int)<<endl;
        cout<<"sizeof(long int) : "<<sizeof(long int)<<endl;
        cout<<"sizeof(long long int) : "<<sizeof(long long int)<<endl;
        cout<<"sizeof(flaot) : "<<sizeof(float)<<endl;
        cout<<"sizeof(double) : "<<sizeof(double)<<endl;


        printf("offset of b in struct a : %lun", offsetof(struct a, b));
        printf("offset of d in struct a : %lun", offsetof(struct a, d));
        printf("offset of b in struct b : %lun", offsetof(struct b, b));

        return 0;
}
程序在64位机器上的输出结果为
sizeof(struct a) : 24 sizeof(struct b) : 12
sizeof(short) : 2
sizeof(int) : 4
sizeof(long int) : 8
sizeof(long long int) : 8
sizeof(flaot) : 4
sizeof(double) : 8
offset of b in struct a : 4
offset of d in struct a : 16
offset of b in struct b : 2

结构体的内存对齐有以下

  1. 结构体变量的起始地址能够被其最宽的成员大小整除
  2. 结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节
  3. 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节

struct a 来说,

  • 第一个成员是char类型的,占用一个字节,偏移地址为0。
  • 第二个成员是int类型,本身占用4个字节,原本偏移为1,但是1不能被4整除,因此在a后边需要补充3个字节,这样b的起始地址偏移就变成了4,能够被自身大小整除,因此b的起始地址偏移为4。
  • 第三个成员是short类型,本身占用2个字节,偏移为8,能够被本身大小整除。
  • 第四个成员是double类型,本身占用8个字节,偏移为10,不能被本身大小整除,前一个c后边需要补充6个字节,也就是说double d的起始地址偏移为16。
  • 最后,整个结构体占用24个字节,最宽的成员 大专栏  开发岗笔试基础题总结double d占8个字节,24能被8整除,所以sizeof (struct a) = 24

使用定义在 stddef.h 的宏offsetof(type, member) ,可以查看成员相对与结构体类型的偏移值

一道关于内存计算的题

#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
	int array[2019]={0};
	array[19] = 2019;

	int offset = (unsigned long)((short *)array+2019) - (unsigned long)array + *(unsigned char *)(array+19);

	cout<<offset<<endl;

	printf("%pn",  2019);

	printf("%pn", array);
	cout<<(unsigned long)(array)<<endl;
	cout<<(unsigned long)((short *)array+2019)<<endl;
	cout<<(unsigned long)array+*(unsigned char *)(array+19)<<endl;
	cout<<(int)*(unsigned char *)(array+19)<<endl;

    return 0;
}
输出结果如下
4265
0x7e3						// 2019的十六进制,小端存储模式,所以在内存中应为 e3 07, 故*(unsigned char *)(array+19) = 0xe3 = 227
0x7ffeec55a800  // 64位机器输出的地址是64位的
140732863457280 // array的起始地址
140732863461318 // short占用两个字节 (unsigned long)((short *)array+2019)相当于array向后偏移了2*2019=4038个字节,因此地址值为array+4038
140732863457507 // 
227

C++ 对象的存储方式

#include <iostream>
#include <string>

using namespace std;

class A{
public:
        A(){}
        ~A(){}

        void p(const string & s)
        {
                cout<<s<<endl;
        }
};

int main()
{
        A * p = NULL;
        p->p("hello worldn");
}

此程序能够正常编译运行输出hello world

c++类方法存储在代码段,因此同一个类的所有方法公用代码段,p->p(“hello worldn”);访问的是代码段,所以不会出现段错误或者core dump。

红黑树的性质

红黑树是每个节点都带有颜色属性的二叉查找树。在二叉查找树强制一般要求外,对于任何有效的红黑树增加额外的要求。

  • 节点是红色或者黑色;
  • 根是黑色的;
  • 所有叶子节点都是黑色的(叶子是NIL节点);
  • 每个红色节点必须有两个黑色的子节点(或:从每个叶子节点到根的所有路径上不能有两个连续的红色节点);
  • 从任一节点到其每个叶子节点的所有简单路径都包含相同数目的黑色节点。

这些约束确保了红黑树的关键特性:从根到叶子的最长可能路径不多于最短的可能路径的两倍长。保证红黑树大致是平衡的。保证红黑树在最坏的请胯下也是高效的。

二叉查找树,也叫二叉搜索树、有序二叉树或者排序二叉树,是一颗空树或者具有以下性质的二叉树:

  • 若任一节点的左子树不为空,则左子树上所有节点的值均小于它的根节点的值;
  • 若任一节点的右子树不为空,则右子树上所有节点的值均大于它的根节点的值;
  • 任意节点的左右子树也分别为二叉查找树;
  • 没有键值相等的节点。

TCP三次握手和四次挥手

死锁

死锁:多个进程由于互相等待对方持有的资源而造成的谁都无法执行的情况叫死锁。

死锁产生的四个必要条件

  • 互斥使用
  • 不可抢占
  • 请求和保持
  • 循环等待

死锁处理方法

  • 死锁预防:破坏死锁产生的条件;
    • 一次性申请所有资源,不会占有资源还去申请其他资源
      • 缺点1:需要预知未来,编程困难
      • 缺点2:许多资源分配后很长时间后才使用,资源利用率低
    • 对资源进行排序,资源申请必须按照顺序进行,不会出现环路等待
      • 缺点:仍然造成资源浪费
  • 死锁避免:检测每个资源请求,如果会造成死锁就拒绝分配相应资源;
    • 银行家算法
      • 请求出现时:首先假装分配,然后调用银行家算法,如果进程序列无法运行下去就拒绝。
      • 时间复杂度为, m为资源个数, n为进程的个数。
  • 死锁检测+恢复:检测到死锁出现时,让一些进程回滚从而让出资源;
    • 银行家算法每次申请都需要执行, 效率低:发现问题再处理
      • 定时检测或者发现资源利用率低时检测。
      • 选择哪些进程回滚?
      • 如何实现回滚?
  • 死锁忽略 (windows, linux采用的方式)
    • 代价最小
    • PC重启影响小
    • 死锁出现概率小

Linux I/O多路复用

聊聊IO多路复用之select、poll、epoll详解

epoll例程

原文地址:https://www.cnblogs.com/lijianming180/p/12230904.html