diff --git "a/algorithm notebook/\344\272\214\345\217\211\346\240\221\347\232\204\351\201\215\345\216\206.org" "b/algorithm notebook/\344\272\214\345\217\211\346\240\221\347\232\204\351\201\215\345\216\206.org" index 627b1bf..d62e6a9 100644 --- "a/algorithm notebook/\344\272\214\345\217\211\346\240\221\347\232\204\351\201\215\345\216\206.org" +++ "b/algorithm notebook/\344\272\214\345\217\211\346\240\221\347\232\204\351\201\215\345\216\206.org" @@ -2,7 +2,9 @@ 先序+中序、后序+中序、层序+中序 都可唯一确定一棵二叉树 -* 代码目录结构 +* 测试代码 + +[[../images/bitree.tar.gz][bitree.tar.gz]] #+BEGIN_EXAMPLE ├── queue.h @@ -13,309 +15,304 @@ 其中,stack.h 是顺序栈一节的代码内容。queue.h 是循环队列一节的代码内容 -* tree.h +* 二叉树数据结构 #+BEGIN_SRC c -#ifndef TREE_H -#define TREE_H -typedef struct node -{ - int data; - struct node *lchild; - struct node *rchild; -}node, *BiTree; -#endif + typedef struct node + { + int data; + struct node *lchild; + struct node *rchild; + }node, *BiTree; #+END_SRC -* tree.cpp - +* 递归遍历 #+BEGIN_SRC c -#include -#include "tree.h" -#include "stack.h" -#include "queue.h" - -void visit(BiTree t) -{ - if(t) - printf("node is %d\n", t->data); - else - printf("node is null"); -} - -// 先序遍历二叉树 -void PreOrder(BiTree T) -{ - if(T) - { - visit(T); - PreOrder(T->lchild); - PreOrder(T->rchild); - } -} - -// 中序遍历二叉树 -void InOrder(BiTree T) -{ - if(T) - { - InOrder(T->lchild); - visit(T); - InOrder(T->rchild); - } -} - -// 后续遍历二叉树 -void PostOrder(BiTree T) -{ - if(T) - { - PostOrder(T->lchild); - PostOrder(T->rchild); - visit(T); - } -} - -// 从左向右层次遍历二叉树(需要借助一个队列) -void LevelOrder(BiTree T) -{ - if(!T) - return; - queue Q; - init_queue(Q); - enqueue(Q, T); - BiTree t; - while(!is_empty(Q)) - { - dequeue(Q, t); - visit(t); - if(t->lchild) - enqueue(Q, t->lchild); - if(t->rchild) - enqueue(Q, t->rchild); - } -} - -// 非递归先序遍历(借助栈) -void PreOrder2(BiTree T) -{ - stack S; - init_stack(S); - BiTree t = T; - while(t || !is_empty(S)) - { - if(t) - { - visit(t); - push(S, t); - t = t->lchild; - } - else - { - pop(S, t); - t = t->rchild; - } - } -} - -// 非递归中序遍历(借助栈) -void InOrder2(BiTree T) -{ - stack S; - init_stack(S); - BiTree t = T; - while(t || !is_empty(S)) - { - if(t) // 不断把左孩子压栈 - { - push(S, t); - t = t->lchild; - } - else // 访问栈顶元素,转向右孩子 - { - pop(S, t); - visit(t); - t = t->rchild; - } - } -} - -// 非递归后序遍历(借助栈) -// 非递归后序遍历和先序、中序遍历不同的是需要记录最近一次访问的结点 -void PostOrder2(BiTree T) -{ - stack S; - init_stack(S); - BiTree t = T; - BiTree tmp = NULL; // 表示最近一次访问的结点,用于防止从右子树返回时再次访问右子树 - while(t || !is_empty(S)) - { - if(t) // 不断进入左子树 - { - push(S, t); - t = t->lchild; - } - else - { - get_top(S, t); - // 如果右子树存在且首次访问右子树,就进入右子树 - if(t->rchild && t->rchild != tmp) - { - t = t->rchild; - push(S, t); - t = t->lchild; - } - else // 访问当前子树的根结点 - { - pop(S, t); - visit(t); - tmp = t; // 记录最近一次访问的结点(如果该结点是其父结点的右孩子,防止返回到父结点时再次进入该结点) - t = NULL; - } - } - } -} - -int main(int argc, char** argv) -{ - node a, b, c, d, e; - a.data = 1; - b.data = 2; - c.data = 3; - d.data = 4; - e.data = 5; - a.lchild = &b; - a.rchild = &c; - b.lchild = &d; - b.rchild = &e; - c.lchild = NULL; - c.rchild = NULL; - d.lchild = NULL; - d.rchild = NULL; - e.lchild = NULL; - e.rchild = NULL; - - PreOrder(&a); - putchar(10); - InOrder(&a); - putchar(10); - PostOrder(&a); - putchar(10); - - LevelOrder(&a); - putchar(10); - - PreOrder2(&a); - putchar(10); - InOrder2(&a); - putchar(10); - PostOrder2(&a); - - return 0; -} + // 先序遍历二叉树 + void PreOrder(BiTree T) + { + if(T) + { + visit(T); + PreOrder(T->lchild); + PreOrder(T->rchild); + } + } + + // 中序遍历二叉树 + void InOrder(BiTree T) + { + if(T) + { + InOrder(T->lchild); + visit(T); + InOrder(T->rchild); + } + } + + // 后续遍历二叉树 + void PostOrder(BiTree T) + { + if(T) + { + PostOrder(T->lchild); + PostOrder(T->rchild); + visit(T); + } + } #+END_SRC -* queue.h - +* 非递归遍历 +** 层序遍历(借助队列) #+BEGIN_SRC c -#include "tree.h" -#define N 1024 -#define elem_type BiTree - -typedef struct queue -{ - elem_type data[N]; - int front, rear; -}queue; - -void init_queue(queue &q) -{ - q.rear = q.front = 0; -} - -bool is_empty(queue &q) -{ - if(q.rear == q.front) - return true; - else - return false; -} - -bool enqueue(queue &q, elem_type &x) -{ - if((q.rear+1) % N == q.front) - return false; - q.data[q.rear] = x; - q.rear = (q.rear+1) % N; - return true; -} - -bool dequeue(queue &q, elem_type &x) -{ - if(q.rear == q.front) - return false; - x = q.data[q.front]; - q.front = (q.front+1) % N; - return true; -} - -int queue_length(queue &q) -{ - return (N+q.rear-q.front)%N; -} + // 从左向右层次遍历二叉树(需要借助一个队列) + void LevelOrder(BiTree T) + { + if(!T) + return; + queue Q; + init_queue(Q); + enqueue(Q, T); + BiTree t; + while(!is_empty(Q)) + { + dequeue(Q, t); + visit(t); + if(t->lchild) + enqueue(Q, t->lchild); + if(t->rchild) + enqueue(Q, t->rchild); + } + } #+END_SRC -* stack.h - +** 先序遍历(借助栈) #+BEGIN_SRC c -#include "tree.h" -#define N 1024 -#define elem_type BiTree -typedef struct -{ - elem_type data[N]; - int top; -}stack; - -void init_stack(stack &s) -{ - s.top = -1; -} - -bool is_empty(stack &s) -{ - if(s.top == -1) - return true; - else - return false; -} + // 非递归先序遍历(借助栈) + void PreOrder2(BiTree T) + { + stack S; + init_stack(S); + BiTree t = T; + while(t || !is_empty(S)) + { + if(t) + { + visit(t); + push(S, t); + t = t->lchild; + } + else + { + pop(S, t); + t = t->rchild; + } + } + } +#+END_SRC -bool push(stack &s, elem_type &x) -{ - if(s.top == N-1) - return false; - s.data[++s.top] = x; - return true; -} +** 中序遍历(借助栈) +#+BEGIN_SRC c + // 非递归中序遍历(借助栈) + void InOrder2(BiTree T) + { + stack S; + init_stack(S); + BiTree t = T; + while(t || !is_empty(S)) + { + if(t) // 不断把左孩子压栈 + { + push(S, t); + t = t->lchild; + } + else // 访问栈顶元素,转向右孩子 + { + pop(S, t); + visit(t); + t = t->rchild; + } + } + } +#+END_SRC +** 后序遍历(借助栈) +#+BEGIN_SRC c + // 非递归后序遍历(借助栈) + // 非递归后序遍历和先序、中序遍历不同的是需要记录最近一次访问的结点 + void PostOrder2(BiTree T) + { + stack S; + init_stack(S); + BiTree t = T; + BiTree tmp = NULL; // 表示最近一次访问的结点,用于防止从右子树返回时再次访问右子树 + while(t || !is_empty(S)) + { + if(t) // 不断进入左子树 + { + push(S, t); + t = t->lchild; + } + else + { + get_top(S, t); + // 如果右子树存在且首次访问右子树,就进入右子树 + if(t->rchild && t->rchild != tmp) + { + t = t->rchild; + push(S, t); + t = t->lchild; + } + else // 访问当前子树的根结点 + { + pop(S, t); + visit(t); + tmp = t; // 记录最近一次访问的结点(如果该结点是其父结点的右孩子,防止返回到父结点时再次进入该结点) + t = NULL; + } + } + } + } +#+END_SRC -bool pop(stack &s, elem_type &x) -{ - if(s.top == -1) - return false; - x = s.data[s.top--]; - return true; -} +* morris traversal(非递归、不借助栈) +空间复杂度 O(1) +时间复杂度 O(n) +morris traversal 用到了线索二叉树的思想,在 morris 方法中不需要为每个节点额外分配指针指向其前驱(predecessor)和后继节点(successor),只需要利用叶子节点中的左右空指针指向某种顺序遍历下的前驱结点或后继结点就可以了。 +** 中序遍历 +[[../images/bitree1.jpg]] +#+BEGIN_SRC c + void InOrder3(BiTree T) + { + BiTree cur = T, prev = NULL; + while (cur) + { + if (!cur->lchild) // 1. 左子树为 NULL,访问当前结点,遍历右子树;或者换句话说,步骤 2 改动了二叉树,通过步骤 1 来把当前结点回溯到后继结点 + { + visit(cur); + cur = cur->rchild; + } + else + { + // 在左子树中找到当前结点的前驱结点 + prev = cur->lchild; + while (prev->rchild && prev->rchild != cur) + prev = prev->rchild; + + if (!prev->rchild) // 2. 改动二叉树:使前驱结点的右孩子指向当前结点 + { + prev->rchild = cur; + cur = cur->lchild; + } + else // 3. 恢复二叉树:使前驱结点的右孩子恢复为 NULL。此时左子树已经遍历过了,所以需要恢复当前结点的前驱结点的右孩子为 NULL,并开始遍历右孩子 + { + prev->rchild = NULL; + visit(cur); + cur = cur->rchild; + } + } + } + } +#+END_SRC -bool get_top(stack &s, elem_type &x) -{ - if(s.top == -1) - return false; - x = s.data[s.top]; - return true; -} +** 先序遍历 +[[../images/bitree2.jpg]] +#+BEGIN_SRC c + void PreOrder3(BiTree T) + { + BiTree cur = T, prev = NULL; + while (cur) + { + if (!cur->lchild) + { + visit(cur); + cur = cur->rchild; + } + else + { + prev = cur->lchild; + while (prev->rchild && prev->rchild != cur) + prev = prev->rchild; + + if (!prev->rchild) + { + visit(cur); + prev->rchild = cur; + cur = cur->lchild; + } + else + { + prev->rchild = NULL; + cur = cur->rchild; + } + } + } + } +#+END_SRC -int stack_length(stack &s) -{ - return s.top+1; -} +** 后序遍历 +[[../images/bitree3.jpg]] +#+BEGIN_SRC c + void reverse(BiTree from, BiTree to) // reverse the tree nodes 'from' -> 'to'. + { + if (from == to) + return; + BiTree x = from, y = from->rchild, z; + while (true) + { + z = y->rchild; + y->rchild = x; + x = y; + y = z; + if (x == to) + break; + } + } + + void printReverse(BiTree from, BiTree to) // print the reversed tree nodes 'from' -> 'to'. + { + reverse(from, to); + + BiTree p = to; + while (true) + { + visit(p); + if (p == from) + break; + p = p->rchild; + } + + reverse(to, from); + } + + void PostOrder3(BiTree T) + { + struct node dump; + dump.lchild = T; + BiTree cur = &dump, prev = NULL; + while (cur) + { + if (!cur->lchild) + { + cur = cur->rchild; + } + else + { + prev = cur->lchild; + while (prev->rchild && prev->rchild != cur) + prev = prev->rchild; + + if (!prev->rchild) + { + prev->rchild = cur; + cur = cur->lchild; + } + else + { + printReverse(cur->lchild, prev); // call print + prev->rchild = NULL; + cur = cur->rchild; + } + } + } + } #+END_SRC diff --git "a/algorithm notebook/\345\276\252\347\216\257\351\223\276\350\241\250.org" "b/algorithm notebook/\345\276\252\347\216\257\351\223\276\350\241\250.org" index ddad6db..e96c45a 100644 --- "a/algorithm notebook/\345\276\252\347\216\257\351\223\276\350\241\250.org" +++ "b/algorithm notebook/\345\276\252\347\216\257\351\223\276\350\241\250.org" @@ -115,7 +115,7 @@ p2 = h2; } - int offset = len1-len2; + int offset = len1 < len2 ? len2-len1 : len1 - len2; for(int i = 0; i < offset; ++i) p1 = p1->next; while(p1 && p2 && p1 != p2) diff --git a/images/bitree.tar.gz b/images/bitree.tar.gz new file mode 100644 index 0000000..e486c6b Binary files /dev/null and b/images/bitree.tar.gz differ diff --git a/images/bitree1.jpg b/images/bitree1.jpg new file mode 100644 index 0000000..c25c73a Binary files /dev/null and b/images/bitree1.jpg differ diff --git a/images/bitree2.jpg b/images/bitree2.jpg new file mode 100644 index 0000000..4e5a0c4 Binary files /dev/null and b/images/bitree2.jpg differ diff --git a/images/bitree3.jpg b/images/bitree3.jpg new file mode 100644 index 0000000..4a68dd8 Binary files /dev/null and b/images/bitree3.jpg differ diff --git "a/images/\345\244\251\347\251\272\345\215\253\345\243\253.PNG" "b/images/\345\244\251\347\251\272\345\215\253\345\243\253.PNG" new file mode 100644 index 0000000..c898565 Binary files /dev/null and "b/images/\345\244\251\347\251\272\345\215\253\345\243\253.PNG" differ diff --git "a/others notebook/gdb \350\260\203\350\257\225\345\244\232\347\272\277\347\250\213\346\255\273\351\224\201.org" "b/others notebook/gdb \350\260\203\350\257\225\345\244\232\347\272\277\347\250\213\346\255\273\351\224\201.org" new file mode 100644 index 0000000..8001d34 --- /dev/null +++ "b/others notebook/gdb \350\260\203\350\257\225\345\244\232\347\272\277\347\250\213\346\255\273\351\224\201.org" @@ -0,0 +1,77 @@ +#+TITLE: gdb 调试多线程死锁 + +** 死锁示例代码 +#+BEGIN_SRC c + #include + #include + #include + + void* alive_thread(void *arg) + { + while(1) + { + usleep(1000*1000); + } + } + + void* dead_thread(void *arg) + { + pthread_mutex_t mutex; + pthread_mutex_init(&mutex, NULL); + usleep(1000*1000); + fprintf(stderr, "timeout we will start dead lock\n"); + pthread_mutex_lock(&mutex); + pthread_mutex_lock(&mutex); + } + + int main(void) + { + pthread_t alive_pid; + pthread_create(&alive_pid, NULL, alive_thread, NULL); + pthread_t dead_pid; + pthread_create(&dead_pid, NULL, dead_thread, NULL); + void *ret1 = NULL; + pthread_join(alive_pid, &ret1); + void *ret2 = NULL; + pthread_join(dead_pid, &ret2); + + return 0; + } +#+END_SRC + +** 编译调试 + +#+BEGIN_SRC bash +$ gcc main.c -o main -g -lpthread +$ ./main +timeout we will start dead lock +#+END_SRC + +先不要杀死该进程,打开另一个终端进行 gdb 调试 + +#+BEGIN_SRC bash + $ ps -ef | grep main | grep -v grep + root 22895 22671 0 15:30 pts/0 00:00:00 ./main + $ gdb main 22895 + ... + (gdb) info threads + Id Target Id Frame + 3 Thread 0x7f03a291a700 (LWP 22896) "main" 0x00007f03a29da06d in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6 + 2 Thread 0x7f03a2119700 (LWP 22897) "main" 0x00007f03a2ce789c in __lll_lock_wait () from /lib/x86_64-linux-gnu/libpthread.so.0 + ,* 1 Thread 0x7f03a310b700 (LWP 22895) "main" 0x00007f03a2ce2148 in pthread_join () from /lib/x86_64-linux-gnu/libpthread.so.0 + (gdb) thread 2 + [Switching to thread 2 (Thread 0x7f03a2119700 (LWP 22897))] + #0 0x00007f03a2ce789c in __lll_lock_wait () from /lib/x86_64-linux-gnu/libpthread.so.0 + (gdb) bt + #0 0x00007f03a2ce789c in __lll_lock_wait () from /lib/x86_64-linux-gnu/libpthread.so.0 + #1 0x00007f03a2ce3065 in _L_lock_858 () from /lib/x86_64-linux-gnu/libpthread.so.0 + #2 0x00007f03a2ce2eba in pthread_mutex_lock () from /lib/x86_64-linux-gnu/libpthread.so.0 + #3 0x000000000040084c in dead_thread () + #4 0x00007f03a2ce0e9a in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0 + #5 0x00007f03a2a0e36d in clone () from /lib/x86_64-linux-gnu/libc.so.6 + #6 0x0000000000000000 in ?? () +#+END_SRC + +第一个线程运行到 pthread_join,显然是主线程 +第二个线程运行到 __lll_lock_wait(),显然是 dead_thread() 函数中第二个 pthread_mutex_lock(&mutex) 加锁时在等待锁。通过 thread 2 跳转到该线程,bt 查看堆栈,显然发生死锁 +第三个线程运行到 nanosleep(),显然是 alive_thread() 函数中无限休眠 diff --git "a/others notebook/gdb \350\260\203\350\257\225\346\256\265\351\224\231\350\257\257.org" "b/others notebook/gdb \350\260\203\350\257\225\346\256\265\351\224\231\350\257\257.org" new file mode 100644 index 0000000..ad6eaf5 --- /dev/null +++ "b/others notebook/gdb \350\260\203\350\257\225\346\256\265\351\224\231\350\257\257.org" @@ -0,0 +1,80 @@ +#+TITLE: gdb 调试段错误 + +** 段错误产生原因 + +段错误是指非法访问内存产生的错误 + +*** 访问不存在的内存地址 +#+BEGIN_SRC c + #include + #include + void main() + { + int *ptr = NULL; + *ptr = 0; + } +#+END_SRC + +*** 访问系统保护的内存地址 +#+BEGIN_SRC c + #include + #include + void main() + { + int *ptr = (int *)0; + *ptr = 100; + } +#+END_SRC + +*** 访问只读的内存地址 +#+BEGIN_SRC c + #include + #include + #include + void main() + { + char *ptr = "test"; + strcpy(ptr, "TEST"); + } +#+END_SRC + +*** 栈溢出 +#+BEGIN_SRC c + #include + #include + void main() + { + main(); + } +#+END_SRC + +** gdb 调试 +*** 以栈溢出为例直接使用 gdb 调试 +#+BEGIN_SRC bash +$ gcc segfault.c -g -o segfault +$ ./segfault +Segmentation fault (core dumped) +$ gdb segfault +... +(gdb) run # 使用 run 命令运行后,会提示段错误出现在文件中的位置,如下所示 +Starting program: /home/he/Desktop/GDB/segfault + +Program received signal SIGSEGV, Segmentation fault. +main () at segfault.c:5 +5 main(); +#+END_SRC + +*** 使用 core 文件调试段错误 +#+BEGIN_SRC bash +$ gcc segfault.c -g -o segfault +$ ulimit -c # 显示 core 文件的大小 +$ ulimit -c unlimited # 把 core 文件设为最大,只对本次有效 +$ ./segfault # 运行程序生成 core 文件,设生成了 core.7963 +Segmentation fault (core dumped) +$ gdb segfault core.7963 +... +Core was generated by 'n./segfault'. +Program terminated with signal 11, Segmentation fault. +#0 main () at test2.c:12 +12 main(); +#+END_SRC diff --git a/others notebook/others.org b/others notebook/others.org index 6ce7b6b..931ec82 100644 --- a/others notebook/others.org +++ b/others notebook/others.org @@ -16,3 +16,7 @@ - [[./打印目录下的文件.org][打印目录下的文件]] - [[./offsetof和container_of.org][小技巧:offsetof 和 container_of 根据结构体成员计算结构体地址]] - [[./signal和sigaction处理逻辑.org][连续到来多个信号时,signal 和 sigaction 的处理逻辑]] +- [[./跨域问题.org][跨域问题]] +- [[./面试题:N 个线程循环打印.org][面试题:N 个线程循环打印]] +- [[./gdb 调试段错误.org][gdb 调试段错误]] +- [[./gdb 调试多线程死锁.org][gdb 调试多线程死锁]] diff --git "a/others notebook/\350\267\250\345\237\237\351\227\256\351\242\230.org" "b/others notebook/\350\267\250\345\237\237\351\227\256\351\242\230.org" new file mode 100644 index 0000000..6eab4ce --- /dev/null +++ "b/others notebook/\350\267\250\345\237\237\351\227\256\351\242\230.org" @@ -0,0 +1,5 @@ +#+TITLE: 跨域问题 + +参考 +- [[http://www.ruanyifeng.com/blog/2016/04/cors.html][跨域资源共享 CORS 详解--阮一峰]] +- [[https://segmentfault.com/a/1190000015597029][不要再问我跨域的问题了]] diff --git "a/others notebook/\351\235\242\350\257\225\351\242\230\357\274\232N \344\270\252\347\272\277\347\250\213\345\276\252\347\216\257\346\211\223\345\215\260.org" "b/others notebook/\351\235\242\350\257\225\351\242\230\357\274\232N \344\270\252\347\272\277\347\250\213\345\276\252\347\216\257\346\211\223\345\215\260.org" new file mode 100644 index 0000000..16eee7d --- /dev/null +++ "b/others notebook/\351\235\242\350\257\225\351\242\230\357\274\232N \344\270\252\347\272\277\347\250\213\345\276\252\347\216\257\346\211\223\345\215\260.org" @@ -0,0 +1,88 @@ +#+TITLE: 面试题:N 个线程循环打印 + +** 问题 +阿里多线程面试题 +#+BEGIN_QUOTE +通过 N 个线程顺序循环打印从 0 至 100,如给定 N = 3 则输出: +thread0: 0 +thread1: 1 +thread2: 2 +thread0: 3 +thread1: 4 +... +#+END_QUOTE + +** 代码 +#+BEGIN_SRC c + #include + #include + #include + + #define N 7 // 线程数 + #define M 101 // 打印数字的总数 + + sem_t sem[N]; + + void * func(void *arg) + { + int id = (int)arg; // 线程编号 + int offset = id < (M%N) ? 1 : 0; + for(int i = 0; i < M/N + offset; ++i) + { + sem_wait(&sem[id]); + printf("thread %d: %d\n", id, i*N + id); + sem_post(&sem[(id+1)%N]); // 下一个信号灯灯值加一,唤醒下一个线程 + } + } + + int main() + { + sem_init(&sem[0], 0, 1); // 初始化,只有第一个信号灯中有一个访问资源 + for(int i = 1; i < N; ++i) + sem_init(&sem[i], 0, 0); + + pthread_t thread[N]; + for(int i = 0; i < N; ++i) + { + int arg = i; + pthread_create(&thread[i], NULL, &func, (void*)arg); + } + + for(int i = 0; i < N; ++i) + pthread_join(thread[i], NULL); + + for(int i = 0; i < N; ++i) + sem_destroy(&sem[i]); + + return 0; + } +#+END_SRC + +** 执行结果 +#+BEGIN_SRC shell +$ gcc test.c -o test -std=c99 -pthread +$ ./test +thread0: 0 +thread1: 1 +thread2: 2 +thread3: 3 +thread4: 4 +thread5: 5 +thread6: 6 +thread0: 7 +thread1: 8 +thread2: 9 +thread3: 10 +thread4: 11 +thread5: 12 +thread6: 13 +thread0: 14 +... +thread3: 94 +thread4: 95 +thread5: 96 +thread6: 97 +thread0: 98 +thread1: 99 +thread2: 100 +#+END_SRC diff --git a/qu/qu.org b/qu/qu.org index be22ac7..88cede1 100644 --- a/qu/qu.org +++ b/qu/qu.org @@ -2,3 +2,4 @@ - [[./ShadowsocksR.org][ShadowsocksR 配置]] - [[./redis 面试题.org][redis 面试题]] +- [[./天空卫士 面试题.org][天空卫士 面试题]] diff --git "a/qu/\345\244\251\347\251\272\345\215\253\345\243\253 \351\235\242\350\257\225\351\242\230.org" "b/qu/\345\244\251\347\251\272\345\215\253\345\243\253 \351\235\242\350\257\225\351\242\230.org" new file mode 100644 index 0000000..ec32162 --- /dev/null +++ "b/qu/\345\244\251\347\251\272\345\215\253\345\243\253 \351\235\242\350\257\225\351\242\230.org" @@ -0,0 +1,44 @@ +#+TITLE: 天空卫士 面试题 + +* 岗位描述 +[[../images/天空卫士.PNG]] + +* 面试题 +- 如何判断一个单链表有环,设置2个指针,一个快一个满 + +- 二叉树先序遍历、中序遍历、后续遍历,根据2个确定一个 + +- 线程互斥锁、条件锁的生产者消费者 + +- gdb 调试全面掌握 + +- 排序 + +- 父子进程之间的最优通信方式 + +- 哈希 + +- pthread_self() pthread_... + +- epoll ET LT + +- 父进程如何获取退出状态 + +- 写个 Deamon + +- EAGAIN EINTR send recv read write + +- 惊群 + +- errno 为全局变量,为什么多线程不用加锁 + 在支持线程的环境中,多个线程共享进程地址空间,每个线程都有属于它自己的局部 errno 以避免一个线程干扰另一个线程。 + 对于 errno 应当知道两条规则。 + 1. 如果没有出错,则其值不会被一个例程清除。因此,仅当函数的返回值指明出错时,才检验其值。 + 2. 任一函数都不会将 errno 值设置为 0,在 中定义的所有常量都不为 0。 + +- __VA_ARGS__ 是一个可变参数的宏,这个可宏是新的C99规范中新增的,目前似乎 gcc 和 VC6.0 之后的都支持(VC6.0 的编译器不支持)。宏前面加上 ## 的作用在于,当可变参数的个数为 0 时,这里的 ## 起到把前面多余的 "," 去掉的作用。 +- __FILE__ 宏在预编译时会替换成当前的源文件名 +- __LINE__ 宏在预编译时会替换成当前的行号 +- __FUNCTION__ 宏在预编译时会替换成当前的函数名称 + +- recv read 返回0 diff --git "a/redis notebook/redis\346\272\220\347\240\201\351\230\205\350\257\273\350\256\241\345\210\222.org" "b/redis notebook/redis\346\272\220\347\240\201\351\230\205\350\257\273\350\256\241\345\210\222.org" index c7de647..efb1369 100644 --- "a/redis notebook/redis\346\272\220\347\240\201\351\230\205\350\257\273\350\256\241\345\210\222.org" +++ "b/redis notebook/redis\346\272\220\347\240\201\351\230\205\350\257\273\350\256\241\345\210\222.org" @@ -9,7 +9,7 @@ * 底层数据结构实现 ** DONE sds - +文件:sds.c 和 sds.h **数据结构** #+BEGIN_SRC c typedef char *sds; // sds 是一个 char* 指针,指向 sdshdr.buf @@ -31,6 +31,37 @@ - 惰性空间释放。sds 字符串缩短时,并不会主动释放多余空间,只是记录在 sdshdr.free 中。当然,sds 也提供了相应的 API,真正的释放 sds 的未使用空间 ** DONE 双端链表 +文件:adlist.c 和 adlist.h + +**数据结构** +#+BEGIN_SRC c + #define AL_START_HEAD 0 // 从表头向表尾进行迭代 + #define AL_START_TAIL 1 // 从表尾到表头进行迭代 + + typedef struct listNode { + struct listNode *prev; + struct listNode *next; + void *value; + } listNode; + + typedef struct list { + listNode *head; + listNode *tail; + void *(*dup)(void *ptr); // 节点值复制函数 + void (*free)(void *ptr); // 节点值释放函数 + int (*match)(void *ptr, void *key); // 节点值对比函数 + unsigned long len; // 链表所包含的节点数量 + } list; + + typedef struct listIter { + listNode *next; // 当前迭代到的节点 + int direction; // 迭代的方向 + } listIter; +#+END_SRC + +**redis 双端链表特点** +- redis 的链表是无环链表。因为链表表头结点的前置结点和表尾结点的后置结点都指向 NULL +- 通过为链表设置不同类型的特定函数(dup、free、match 函数),redis 的链表可以用于保存各种不同类型的值 ** DONE 字典