char str[] = “http://www.ibegroup.com/”
char *p = str ;
int n = 10;
请计算
sizeof (str ) = ?(1)
sizeof ( p ) = ?(2)
sizeof ( n ) = ?(3)
void Foo ( char str[100]){
请计算
sizeof( str ) = ?(4)
}
void *p = malloc( 100 );
请计算
sizeof ( p ) = ?(5)
(1)17 (2)4 (3) 4 (4)4 (5)4
( 1 ) 可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型
栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上。
更多完整一线互联网大厂面试题,视频资料,XV关注零声学院领取!
1)信号量机制:
一个信号量只能置一次初值,以后只能对之进行p操作或v操作。
由此也可以看到,信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点。
2)自旋锁:
旋锁是为了保护共享资源提出的一种锁机制。
调用者申请的资源如果被占用,即自旋锁被已经被别的执行单元保持,则调用者一直循环在那里看是否该自旋锁的保持着已经释放了锁. 自旋锁是一种比较低级的保护数据结构和代码片段的原始方式,可能会引起以下两个问题;
(1)死锁
(2)过多地占用CPU资源
3)管程:
信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量
和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。
4)会合:
进程直接进行相互作用
5)分布式系统: 由于在分布式操作系统中没有公共内存,因此参数全为值参,
而且不可为指针。
优缺点:
信号量(Semaphore)及PV操作
优:PV操作能够实现对临界区的管理要求;实现简单;允许使用它的代码休眠,持有锁的时间可相对较长。
缺:信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点。信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。
加重了程序员的编码负担;核心操作P-V分散在各用户程序的代码中,不易控制和管理;一旦错误,后果严重,且不易发现和纠正。
自旋锁:
优:旋锁是为了保护共享资源提出的一种锁机制; 调用者申请的资源如果被占用,即自旋锁已经被别的执行单元保持,则调用者一直循环在那里看是否该自旋锁的保持者
已经释放了锁; 低开销;安全和高效;
缺:自旋锁是一种比较低级的保护数据结构和代码片段的原始方式,可能会引起以下两个问题;
(1)死锁
(2)过多地占用CPU资源
传统自旋锁由于无序竞争会导致“公平性”问题
管程:
优: 集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,
易于保证正确性。
缺:如果一个分布式系统具有多个CPU,并且每个CPU拥有自己的私有内存,它们通过一个局域网相连,那么这些原语将失效。而管程在少数几种编程语言之外又无
法使用,并且,这些原语均未提供机器间的信息交换方法。
会合:
进程直接进行相互作用
分布式系统: 消息和rpc
由于在分布式操作系统中没有公共内存,因此参数全为值参,而且不可为指针
void GetMemory(char **p, int num){
*p = (char *)malloc(num);
}
void Test(void){
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
main() {
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d", *(a+1), *(ptr-1));
}
请问输出的结果是?
2,5
解释:
(a+1)就是a[1],(ptr-1)就是a[4],执行结果是2,5。&a+1不是首地址+1,系统会认为加
一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)。 int ptr=(int)(&a+1);则
ptr实际是&(a[5])也就是a+5
原因如下:
&a是数组指针,其类型为int()[5];而指针加1要根据指针类型加上一定的值,不同类型
的指针+1之后增加的大小不同;a是长度为5的int数组指针,所以要加5sizeof(int)。所
以ptr实际是a[5]。但是prt与(&a+1)类型是不一样的(这点很重要),所以ptr-1只会减去
sizeof(int*).a&a的地址是一样的,但意思不一样,a是数组首地址,也就是a0的地址,
&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,
即a[5].
(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型
Linux内核的五大模块 1.进程调度模块 2.内存管理模块 3.文件系统模块 4.进程间通信模块 5.网络接口模块
进程调度模块
用来负责控制进程对CPU 资源的使用。所采取的调度策略是各进程能够公平合理地访问CPU, 同时保证内核能及时地执行硬件操作。
内存管理模块
用于确保所有进程能够安全地共享机器主内存区, 同时, 内存管理模块还支持虚拟内存管理方式, 使得Linux 支持进程使用比实际内存空间更多的内存容量。并可以利用文件系统, 对暂时不用的内存数据块交换到外部存储设备上去, 当需要时再交换回来。
文件系统模块
用于支持对外部设备的驱动和存储。虚拟文件系统模块通过向所有的外部存储设备提供一个通用的文件接口,隐藏了各种硬件设备的不同细节。从而提供并支持与其它操作系统兼容的多种文件系统格式。
进程间通信模块
用于支持多种进程间的信息交换方式
网络接口模块
提供对多种网络通信标准的访问并支持许多网络硬件
建立连接
HTTP 和 HTTPS 都需要在建立连接的基础上来进行数据传输,是基本操作
当客户在浏览器中输入网址的并且按下回车,浏览器会在浏览器 DNS 缓存,本地 DNS 缓存,和 Hosts 中寻找对应的记录,如果没有获取到则会请求 DNS 服务来获取对应的 ip
当获取到 ip 后,tcp 连接会进行三次握手建立连接
tcp 的三次挥手和四次挥手
过程简图
三次挥手(建立连接)
第一次:建立连接时,客户端发送 SYN 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;
第二次:服务器收到 SYN 包,向客户端返回 ACK(ack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RCVD 状态;
第三次:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据,也就是 ESTABLISHED 状态。
三次握手保证了不会建立无效的连接,从而浪费资源。
四次挥手(断开连接)
第一次: TCP 客户端发送一个 FIN,用来关闭客户到服务器的数据传送。
第二次:服务器收到这个 FIN,它发回一个 ACK,确认序号为收到的序号加 1。和 SYN 一样,一个 FIN 将占用一个序号。
第三次:服务器关闭客户端的连接,发送一个 FIN 给客户端。
第四次:客户端发回 ACK 报文确认,并将确认序号设置为收到序号加 1。
HTTP 请求过程
建立连接完毕以后客户端会发送响应给服务端
服务端接受请求并且做出响应发送给客户端
客户端收到响应并且解析响应响应给客户
在使用 HTTPS 是需要保证服务端配置正确了对应的安全证书
客户端发送请求到服务端
服务端返回公钥和证书到客户端
客户端接收后会验证证书的安全性,如果通过则会随机生成一个随机数,用公钥对其加密,发送到服务端
服务端接受到这个加密后的随机数后会用私钥对其解密得到真正的随机数,随后用这个随机数当做私钥对需要发送的数据进行对称加密
客户端在接收到加密后的数据使用私钥(即生成的随机值)对数据进行解密并且解析数据呈现结果给客户
SSL 加密建立
整个空间按顺时针方向组织,0和2^32-1在零点中方向重合。
结构是一种将数据集合成组的方法,类是一种同时将函数和数据都集合成组的方法。结构和类在表面上的唯一区别是:类中的成员在默认情况下是私有的,而结构中的成员在默认情况下是公用的。
class foo
{
private:
int data1;
public:
void func();
};
可以写成:
class foo
{
int data1;
public:
void func();
};
因为在类中默认的是私有的,所以关键字private就可以不写了。
如果想用结构完成这个类所作的相同的事,就可以免去关键字public,并将公有成员放置在私有成员之前:
struct foo
{
void func();
private:
int data1;
};