c基础
##
- 数组不支持变量定义数组大小,如下:未初始化数组错误。只能通过编译时常量
#define
定义
size_t maxlineLen = 128;
char line[maxlineLen] = {0};
-
为啥有
puts
没有gets
?被废除了,不安全。可以使用
fgets
,getline
这类限制大小。一般使用后者不保留\n
-
读写函数中有哪些外部分配,有哪些内部分配?
内部分配的:
getline
外部分配的:
fgets
-
一直
strtok
会发生什么?- 最后返回
null
- 原始字符串破坏了,替换为
\0
- 最后返回
-
fork
子进程分支如果没有exit
会发生什么?是必须的嘛?是的,会发生未定义行为,且会进入父亲代码。
时间
CPU时间:程序占用CPU时间。clock_t
,long
毫秒级。
正常时间:
- 微秒级:
struct timeval
;gettimeofday()
-
纳秒级:
struct timespec
;clock_gettime()
- 毫秒级:
time_t
,long
;struct tm
场景:
-
时间戳转换为字符串?
ctime(time_t *)
:固定格式,带换行。快速使用。- 如果要转为指定格式字符串,要先转为
struct tm
。
-
time_t
和struct tm
转换:- 时间戳转为tm结构:
localtime()
gmtime()UTC的
- 时间戳转为tm结构:
-
tm
转字符串:strftime()
-
屏幕刷新频率?
- 使用usleep实现,0.1s, 0.2秒感知用户输入
重载冗杂
sizeof并不是一个函数,而是一个操作符。
- 操作数是变量:不必加括号。 sizeof *p 获取p指针指向对象的字节数
- 操作数是类型:必须加括号。sizeof(int) *p。 这里含义是什么? 强制转换int型之后 sizeof?还是int长度乘以p? 实践:是后者。
Int main(){
chara = ‘5’;
int*p = &a;
int q = sizeof(int) * p;
printf(“%d”, q);
return0;
}
结构体
柔性数组必须作为结构体最后一个字段。
问题,redis ziplist 压缩列表希望柔性数组后面再来一个zlend末尾标记,这就会冲突!
typedef struct {
unsigned int zlbytes; // 压缩列表总字节数
unsigned int zltail; // 到最后一个节点的偏移量
unsigned short zllen; // 压缩列表中节点数
unsigned char entries[]; // 节点数据(变长)
unsigned int zlend;
} ziplist;
解决:额外检测zlend字段,创建内存时候,加上zlend字节。但是结构不包含
向前声明!
- 如果
A
里放B*
,只声明struct B;
就够了 - 如果
A
里放B b;
,就得先完整定义struct B {}
两种写法
struct ConnectionListener;
typedef struct {
int fd;
struct ConnectionListener *listener;
// 其他字段
} Connection;
typedef struct ConnectionListener {
int port;
char *bindAddr;
Connection *conn;
} ConnectionListener;
typedef struct ConnectionListener;
typedef struct {
int fd;
ConnectionListener *listener;
// 其他字段
} Connection;
struct ConnectionListener {
int port;
char *bindAddr;
Connection *conn;
} ;
强制结构体/变量对齐。
场景:现在pool结构后面,要跟数据区域。但是整个内存要求8字节对齐,pool结构lock属性尺寸不能保证是8字节对齐的。
typedef struct {
// meta
uint32_t magic;
uint32_t version;
uint32_t cap;
uint32_t used;
//
FreeBlock freeListHead; // 空闲链表头
//
pthread_mutex_t lock; // 内存池锁
// data
} Pool;
__attribute__((aligned(8)))
是编译器的扩展语法,能够强制结构体/变量对齐。
这个叫编译器属性,不是C语言语法或者宏,一般我们通过宏展开添加#define ALIGN8 __attribute__((aligned(8)))
``attribute((aligned(8))) struct pool {};`
int x __attribute__((aligned(16)))
字符串
string
类函数是原地修改,内部分配吗?
除了strdup
内部会分配,其余不会主动malloc。
strtok, strcat
都会原地修改dest
一些结构体内有
char*
属性,什么时候该直接赋值,什么时候该dump:
基本上都要dump~
char* buf; c->buf = "aaaa";
后者常量字符串是编译时候生成在只读段的,这就是正常的指针指向。
IO
snprintf
返回打印的字符数。
printf
行缓冲行为。默认遇到\n
或者缓冲区满才会刷新的标准输出。程序推出前会自动fflush(stdout)
。
下面代码, printf不会打印。因为行缓冲。
int main(int argc, char const *argv[])
{
PoolContext ctx = {NULL,-1};
pool_create(&ctx);
printf("??");
signal(SIGINT, sigintHandler); // CTRL+C
void* ptr= pool_alloc(16, &ctx);
uint64_t* cp = (uint64_t*)ptr;
*cp = 0XFFFFFFFFFFFFFFFF; // 测试写入
// pool_free(ptr, &ctx);
while (!stop) {
sleep(1);
}
puts("stopped");
pool_close(&ctx);
pool_cover(&ctx);
pool_destroy(&ctx);
return 0;
}
GLIB
glib是跨平台的C工具库。相关地,glibc(GNU C)是linux的标准C库,linux运行基础。
C标准分为语法和实现。
-
C标准库:就是一组标准头文件,规定实现含义。
-
C运行库:各平台自己实现,并添加一些扩展。比如LINUX的glibc和MS的MSVCRT
glib有6个主要模块:
GObject
:面向对象特性:继承、信号、属性等GIO
:IO抽象(文件、流、网络)GLib
:基础工具。高级数据结构、内存管理、字符串处理、时间等GModule
: 跨平台动态加载模块接口?GThread
: 跨平台线程API和线程池支持。GMainLoop
:事件循环机制。
GTree
平衡二叉搜索树
随机
整数:
int x = rand() % 100; // 0~99
浮点:
double r = rand() / (double)RAND_MAX; // [0, 1)
double r2 = 90.0 + r * 20.0; // [90.0, 110.0)
时间种子:
srand((unsigned(time(NULL))
随机字符:
char c = 'A' + rand() % 26;
布尔值:
bool b = rand() & 1;
ncurses
关于界面刷新和用户按键。
用户按键按下是有时长的, 如果1秒界面刷新,即意味着只有间隔1s才检测。 如果0.1秒,那就是每秒检测10次。显然会流畅很多。
安全信号处理
volatile sig_atomic_t stop = 0;
sig_atomic_t 类型表示 对于数据的访问不会被信号中断。
volatile 用于告知编译器,该变量的值可能会在编译器无法察觉的情况下被修改(例如,由信号处理程序修改)。防止编译器不必要优化,每次都从内存读取,不可使用缓存。
signal(SIGINT, sigintHandler); // CTRL+C
while (!stop) {
}
// 资源清理
指针加减
void*
不允许加减,比如内存池通过offset + base指针定位需要转为char*