程序内存详解
动态内存
C语言的一切操作都是基于内存的
变量和数组都是内存的别名
内存分配由编译器在编译期间决定 定义数组的时候必须指定数组长度 数组长度是在编译期就必须确定的
malloc所分配的是一块连续的内存
malloc以字节为单位,并且不带任何的类型信息
free用于将动态内存归还系统
void* malloc (size_t size);void free(void* pointer);
malloc和free是库函数,而不是系统调用malloc实际分配的内存可能会比请求的多不能依赖于不同平台下的malloc行为(可能被改动)当请求的动态内存无法满足是,malloc返回NULL当free参数为NULL时,函数直接返回
一道面试题:malloc(0)的返回?
1234567891011#include <stdio.h>#include <malloc.h>int main(){ int* p = (int *)malloc(0); printf("p = %p, siz ...
函数深度分析
函数声明与定义
声明的意义在于告诉编译器程序单元的存在
定义则明确指示程序单元的意义
C语言通过extern进行程序单元的声明
一些程序单元在声明时可以省略extern
严格意义而言声明和定义完全不同
声明是向编译器介绍名字–标识符。它告诉编译器“这个函数或变量在某处可找到,它的模样象什么”。
而定义是说:“在这里建立变量”或“在这里建立函数”。它为名字分配存储空间。无论定义的是函数还是变量,编译器都要为它们在定义点分配存储空间。
12345678910111213141516171819202122232425262728#include <stdio.h>#include <malloc.h>extern int g_var; //在其他C文件定义了,编译器编译到这一行知道这是个别名,但是不会为这个别名分配一块内存extern struct Test;int main(){ extern void f(int i, int j); extern int g(int x); struct Test* p = NULL; ...
bool类型和引用
C++中的布尔类型C语言根本没有具体的bool类型存在,C语言往往使用整型0和1来代替bool类型。功能上可以,但是显得不那么严谨。
C++在C语言的基本类型系统之上增加了bool
C++中的bool可取的值只有true和false
理论上bool只占用一个字节,内部实现仍然使用整型,一个byte。
注意:true代表真值,编译器内部用1来表示false代表非真值,编译器内部用0来表示
bool类型只有true (非0 )和false ( 0 )两个值C++编译器会将非0值转换为true,0值转换为false。为了兼容C语言,bool类型还支持数学运算
运行实例1234567891011121314151617181920212223242526272829303132#include <stdio.h>int main(int argc, char *argv[]){ bool b = false; int a = b; printf("sizeof(b) = %d\n", (int)sizeof(b)); // ...
const深度解析
C语言中const
const修饰的变量是只读的, 本质还是变量
const修饰的局部变量在栈上分配空间
const修饰的全局变量在全局数据区分配空间
const只在编译期有用,在运行期无用
const修饰的变量不是真的常量,它只是告诉编译器该变量不能出现在赋值符号的左边。
1234567891011121314151617#include <stdio.h>int main(){ const int cc = 1; int *p = (int *)&cc; printf("cc = %d\n",cc); *p = 2; printf("cc = %d\n",cc); return 0;}
通过指针修改const局部变量的值不会报错并且可以修改成功
1234fengyun@ubuntu:~/share$ gcc test.c -o testfengyun@ubuntu:~/share$ ./testcc = 1cc = 2
在现代C语言编译器中,修改cons ...
编译链接
编译模块
预编译
处理所有的注释,以空格代替
将所有的#define删除,并且展开所有的宏定义
处理条件编译指令#if, #ifdef, #elif, #else, #endif
处理#include ,展开被包含的文件
保留编译器需要使用的#pragma指令
预处理指令示例: gcc-E file.c -o file.i
范例test.c与test.h如图所示
执行gcc -E test.c -o test.i预处理过后,可以观察到
注释消失了。
include头文件test.h里的东西(两个全局变量)原封不动的复制到test.i中了。
宏消失了并且宏直接展开全部被替换了。
多了#号开头的内容,这些内容是作为传递给后续的编译器的输入内容。
编译1.对预处理文件进行词法分析,语法分析和语义分析(详情请见编译原理)
词法分析:分析关键字,标示符,立即数等是否合法
语法分析:分析表达式是否遵循语法规则
语义分析:在语法分析的基础上进一步分析表达式是否合法
2.分析结束后进行代码优化生成相应的汇编代码文件 编译指令示例: gcc -S file.c -o file. ...
服务器设计
服务器设计原则总述我们写的是:通用的服务框架:将来,稍加改造甚至不用改造就可以把它直接应用在很多的具体开发工作中;
我们的工作重点就可以聚焦在业务逻辑上; 相当于你自带框架【自带源码】入职;甚至你可以挑战高级程序员/主程序这种职业;
收发包格式问题提出游戏服务器:第一条命令出拳【1abc2】,第二条加血【1def2|30】;
1abc21def2|30,服务器如何识别出这是两条命令?
TCP粘包、缺包Nagle优化算法TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。(一个连接会设置MSS参数,因此,TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据)。Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。
Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。
Nagle算法的规则(可参考 ...
epoll介绍及原理
epoll技术简介epoll概述(1)I/O多路复用:epoll就是一种典型的I/O多路复用技术:epoll技术的最大特点是支持高并发;传统多路复用技术select,poll,在并发量达到1000-2000,性能就会明显下降;epoll,kqueue(freebsd)epoll,从linux内核2.6引入的,2.6之前是没有的;(2)epoll和kquene技术类似:单独一台计算机支撑少则数万,多则数十上百万并发连接的核心技术;epoll技术完全没有这种性能会随着并发量提高而出现明显下降的问题。但是并发没增加一个,必定要消耗一定的内存去保存这个连接相关的数据;/并发量总还是有限制的,不可能是无限的;(3)10万个连接同一时刻,可能只有几十上百个客户端给你发送数据,epoll只处理这几十上百个客户端;(4)很多服务器程序用多进程,每一个进程对应一个连接;也有用多线程做的,每一个线程对应 一个连接;epoll事件驱动机制,在单独的进程或者单独的线程里运行,收集/处理事件;没有进程/线程之间切换的消耗,高效(5)适合高并发,融合epoll技术到项目中,作为大家将来从事服务器开发工作的立身之本 ...
TCP状态转换图
TCP状态转换重复bind同一端口出错12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576#include <stdio.h>#include <ctype.h>#include <unistd.h>#include <sys/types.h>#include <arpa/inet.h>#include <sys/socket.h>#include <stdlib.h>#include <string.h>#include <errno.h>#define SERV_PORT 9000 //本服务器要监听的端口号,一般1024以下的端口很多都是属于周知端口,所以我们一般采用1024之后的数字做端口号int main(int argc, char * ...
套接字连接队列解析
listen():监听端口,用在 TCP连接 中的 服务器端 角色;listen()函数调用格式: int listen(int sockfd, int backlog);要理解好backlog这个参数,我们需要先谈一谈 “监听套接字 队列”的话题;
监听套接字的队列对于一个调用listen()进行监听的套接字,操作系统会给这个套接字 维护两个队列;
a)未完成连接队列 【保存连接用的】
当客户端 发送tcp连接三次握手的第一次【syn包】给服务器的时候,服务器就会在未完成队列中创建一个跟这个 syn包对应的一项,其实,我们可以把这项看成是一个半连接【因为连接还没建立起来呢】,这个半连接的状态会从LISTEN变成SYN_RCVD状态,同时给客户端返回第二次握手包【syn,ack】。这个时候,其实服务器是在等待完成第三次握手。
b)已完成连接队列 【保存连接用的】 当第三次握手完成了,这个连接就变成了ESTABLISHED状态,每个已经完成三次握手的客户端 都放在这个队列中作为一项;
backlogbacklog曾经的含义:已完成队列和未完成队列二者条目之和不能超过 backlog; ...
I/O模型
阻塞与非阻塞I/O阻塞和非阻塞主要是指调用某个系统函数时,这个函数是否会导致我们的进程进入sleep()【卡在这休眠】状态而言的;
阻塞I/O我调用一个函数,这个函数就卡在在这里,整个程序流程不往下走了【休眠sleep】,该函数卡在这里等待一个事情发生,只有这个事情发生了,这个函数才会往下走;这种函数,就认为是阻塞函数;accept();【listenfd套接字阻塞,因此accept阻塞】这种阻塞,并不好,效率很低;一般我们不会用阻塞方式来写服务器程序,效率低;
非阻塞I/O:不会卡住,充分利用时间片,执行更高;
非阻塞模式的两个鲜明特点:(1)不断的调用accept(),recvfrom()函数来检查有没有数据到来,如果没有,函数会返回一个特殊的错误标记来告诉你,这种标记可能是EWULDBLOCK,,也可能是EAGAIN;如果数据没到来,那么这里有机会执行其他函数,但是也得不停的再次调用accept(),recvfrom()来检查数据是否到来,非常累;因此我采用了epoll技术!!!!不用来回调用了。(2)如果数据到来,那么就得卡在这里把数据从内核缓冲区复制到用户缓冲区,所以复制 ...