关于c:用C程序打印星形右直角三角形

解题思路用10*10网格 第一行9个空格(10-1行号)和一个星号(1行号) 第二行8个空格(10-2行号)和两个星号(2行号) 第三行7个空格(10-3行号)和三个星号(3行号) 第四行6个空格(10-4行号)和四个星号(4行号) 以此类推… 从法则来看,每行打印的都与行号无关 /*打印如下: * ** *** **** ***** ****** ******* ******** ******************* */#include <stdio.h>int main(){ int i; int j; for (i = 1; i <= 10; i++) { for (j = 0; j < 10 - i; j++) { printf(" "); } for (j = 0; j < i; j++) { printf("*"); } printf("\n"); } return 0;}

March 11, 2021 · 1 min · jiezi

关于c:用C程序打印数字左倒置直角三角形

解题思路用10*10网格 第一行10个数字(0行号) 第二行9个数字(1行号) 第三行8个数字(2行号) 第四行7个数字(3行号) 以此类推… 从法则来看,每行打印的都与行号无关 /* 打印如下:11111111112222222223333333344444445555556666677778889910 */#include <stdio.h>int main(){ int i; int j; int count = 1; for (i = 0; i < 10; i++) { for (j = 0; j < 10 - i; j++) { printf("%d", count); } printf("\n"); count++; } return 0;}

March 10, 2021 · 1 min · jiezi

关于c:用C程序打印星形左倒置直角三角形

解题思路用10*10网格 第一行10个星(0行号) 第二行9个星(1行号) 第三行8个星(2行号) 第四行7个星(3行号) 以此类推… 从法则来看,每行打印的都与行号无关 /* 打印如下:************************************ */#include <stdio.h>int main(){ int i; int j; for (i = 0; i < 10; i++) { for (j = 0; j < 10 - i; j++) { printf("*"); } printf("\n"); } return 0;}

March 7, 2021 · 1 min · jiezi

关于c:linux工具使用介绍iperf3网络质量测试工具介绍

March 6, 2021 · 0 min · jiezi

关于c:货拉拉女生跳车思考

请让我从多年前的一段个人经历讲起。 过后我住在离机场特地近的小区,相对间隔只有20公里。你可能认为我往返机场很不便,但理论体验恰恰相反,每次出差回京坐出租,其实是十分苦楚的过程。 过后没有叫车APP,出租车司机想在机场拉到一个乘客,往往要消耗几个小时的工夫排队。试想,如果司机接到的乘客间隔过近,在起步价只有10块钱的状况下,实际上司机就等于半天白干。 所以每次司机排到相似我这样一位乘客,他苦楚的表情,能够让人一年难忘。在乘客上车的第一分钟,少数司机就会开始叹气和埋怨,而后试图压服乘客,能不能多给点钱。 你当然能够回绝司机加价的提议,而这个回绝是齐全合乎规定的。 然而,这样做的结果,后续的事件有很大概率是司机胡作非为的踩刹车或者减速。或者拿出烟猛抽,齐全忽视你表白了不喜爱烟味的揭示。 为了防止麻烦,一些乘客在上车时立即说,徒弟,我晓得这趟活太近了,我给你加5块或者10块钱。 还有很多人,对规定是较真的,忽视司机的埋怨,甚至威逼去平台投诉。 在这种出租车计价体系中,如果一个喜爱埋怨的司机,碰到了一个喜爱较真的乘客,抵触就是必然的。而如果一个零碎规定人造会造成潜在抵触,放在一个更大的数据样本下,其实出小事就是早晚的事件。 正如过后出租车不喜爱拉机场的小活,货拉拉的司机也不喜爱40分钟的收费期待,而咱们须要认清的是,造成他们情绪不佳的根本原因,是计价零碎施加的,不是顾客,也不是司机。 心愿多赚一点的司机,较真节约的顾客,实际上是零碎不合理规定的牺牲品。 此次事件中,货车司机抄近路,不理睬顾客的停车诉求,和多年前机场猛踩油门的出租司机,也并没有什么两样。 货拉拉40分钟的收费期待,是平台吸引顾客的伎俩,但货车司机并不喜爱这条规定。在理论状况下,这条规定无时无刻不在引发买卖双方的对抗。在长沙,这种对抗,一方曾经付出了生命,另一方可能是牢狱之灾。 这里不得不再回到「科技向善」主题,奇客故事始终反复一个观点,科技互联网平台向善,不应该变成企业拿钱做慈悲。科技向善,应该是去思考经营一个「向善」的零碎,一个让生态各方都受害的零碎,而非一个平台通过“鱼肉宰割”弱势一方,利益最大化的零碎。 互联网平台有人造垄断性,有更大的数据样本,若规定具备哪怕一丁点“不够向善”的隐患,都可能后续引发劫难。 咱们不能说万分之一的概率就是能够漠视的概率,因为其背地或者就是生命的代价。 货拉拉作出抵偿了,要装摄像头了,要录音了,这些都很好,然而如果零碎没有「向善」的价值观,只去着眼于技术的规定,喜剧最初还会换一种形式产生。 向善的零碎才是要害。

March 5, 2021 · 1 min · jiezi

关于c:用C程序打印星形左直角三角形

解题思路用10*10网格 第一行一个星(1行号) 第二行两个星(2行号) 第三行三个星(3行号) 第四行四个星(4行号) 以此类推… 从法则来看,每行打印的都与行号无关。 /*打印如下:******************************************************* */#include <stdio.h>int main(){ int i; int j; for (i = 1; i <= 10; i++) { for (j = 1; j <= i; j++) { printf("*"); } printf("\n"); } return 0;}

March 5, 2021 · 1 min · jiezi

关于c:C语言二叉树的创建和遍历

二叉树的创立和遍历: #include<stdio.h>#include<stdlib.h>#define OK 1#define OVERFLOW -2typedef int Status;typedef char ElemType;typedef struct BiNode{ ElemType data; struct BiNode* lchild, * rchild;}BiTNode, *BiTree;Status Create_Tree(BiTree *T); //创立Status PreBiTree_Traverse(BiTree T); //结构和遍历思维:递归Status InBiTree_Traverse(BiTree T);Status PostBiTree_Traverse(BiTree T);int main(){ BiTree T; int n; printf("请输出字符(#示意空指针):"); Create_Tree(&T); printf("1.前序遍历 2.中序遍历 3.后序遍历\n"); printf("请输出遍历形式:"); scanf("%d", &n); switch (n) { case 1:PreBiTree_Traverse(T); break; case 2:InBiTree_Traverse(T); break; case 3:PostBiTree_Traverse(T); break; } return 0;}Status Create_Tree(BiTree *T)// 前序结构{ char ch; scanf("%c", &ch); if (ch == '#') //用#代表NULL,即虚结点 { *T = NULL; } else { *T = (BiTree)malloc(sizeof(BiTNode)); if (!*T) exit(OVERFLOW); (*T)->data = ch; Create_Tree(&(*T)->lchild); Create_Tree(&(*T)->rchild); } return OK;}Status PreBiTree_Traverse(BiTree T) //前序遍历{ if (T == NULL) return OVERFLOW; printf("二叉树的数据为:%c\n", T->data); PreBiTree_Traverse(T->lchild); PreBiTree_Traverse(T->rchild); return OK;}Status InBiTree_Traverse(BiTree T) //中序遍历{ if (T == NULL) return OVERFLOW; InBiTree_Traverse(T->lchild); printf("二叉树的数据为:%c\n", T->data); InBiTree_Traverse(T->rchild); return OK;}Status PostBiTree_Traverse(BiTree T) //后序遍历{ if (T == NULL) return OVERFLOW; PostBiTree_Traverse(T->lchild); PostBiTree_Traverse(T->rchild); printf("二叉树的数据为:%c\n", T->data); return OK;}

March 4, 2021 · 1 min · jiezi

关于c:用C程序打印星形镂空正方形

解题思路用5*5网格 在第一行和最初一行,在所有列中打印星号,应用这个条件(i==1||i==5)从第二行到倒数第二行(用红色标记),在第一列行和最初一列打印星号,应用这个条件(j==1||j==5)如果条件不满足,打印空格。 #include <stdio.h>int main(){ int i; int j; for(i=1;i<=5;i++){ for(j=1;j<=5;j++){ if (i==1||i==5){ printf("*"); }else if (j==1||j==5){ printf("*"); }else{ printf(" "); } } printf("\n"); } return 0;}

March 4, 2021 · 1 min · jiezi

关于c:C语言中fun1和fun2void的区别

在一次C语言实训中我发现老师在对无参函数的书写中写成fun2(void)的模式,这引起了我的好奇心,我只晓得fun1()和fun2(void)是两个无参函数,后者是C99中规定的,void的意思就是该函数不承受任何参数,然而我在应用的过程中除了对fun2传参会导致报错以外没有发现别的问题,所以就开始好奇为什么老师在实训时会特意在没有传参的函数里写上void。 通过谷歌搜寻失去以下论断 (C) The difference between int main() and int main(void)A common misconception for C programmers, is to assume that a function prototyped as follows takes no arguments:int foo();In fact, this function is deemed to take an unknown number of arguments. Using the keyword void within the brackets is the correct way to tell the compiler that the function takes NO arguments.参考文章:https://faq.cprogramming.com/... 然而我还是不能了解到底为什么要这样定义,最初在抖音的一个博主(抖抖代码)那里失去了解答 以下是博主演示的代码: #include<stdio.h>int main(void){ void fun1(); void fun2(void); fun1(); fun2(); return 0;}void fun1(){ printf("doudou \n");}void fun2(){ printf("douodu 2\n");}/*失去输入为:doudou douodu 2*/在没有传入任何参数的状况下两者都是被失常运行的。 ...

March 3, 2021 · 1 min · jiezi

关于c:读懂汇编代码

对于这样一份 C 代码: int add (int a, int b) { return a + b;}int main (void) { int a = 10; int b = 20; int c = add(a, b); return c;}先应用 gcc 编译 $ gcc -g -O0 hello.c -o hello而后应用 objdump 来查看反汇编只摘取其中后果中最重要的汇编代码,# 之后的内容为手动加的正文 $ objdump -j .text -S -I hello00000000004004ed <add>:int add (int a, int b) { 4004ed: 55 push %rbp # 将 rbp 寄存器的值压入栈 4004ee: 48 89 e5 mov %rsp,%rbp # 将 rsp 寄存器的值 挪动到 rbp 寄存器,栈底(rbp)挪动到原来的栈顶的地位(rsp) 4004f1: 89 7d fc mov %edi,-0x4(%rbp) # 将 edi 寄存器的值,挪动到 -0x4(绝对于 rbp 的地址) 4004f4: 89 75 f8 mov %esi,-0x8(%rbp) # 将 esi 寄存器的值,挪动到 -0x8(绝对于 rbp 的地址) return a + b; 4004f7: 8b 45 f8 mov -0x8(%rbp),%eax # 将 -0x8 的值挪动到 eax 4004fa: 8b 55 fc mov -0x4(%rbp),%edx # 将 -0x4 的值挪动到 edx 4004fd: 01 d0 add %edx,%eax # eax += edx} 4004ff: 5d pop %rbp # 从栈顶弹出一个值,放到 rbp 里 400500: c3 retq0000000000400501 <main>:int main (void) { 400501: 55 push %rbp 400502: 48 89 e5 mov %rsp,%rbp 400505: 48 83 ec 10 sub $0x10,%rsp int a = 10; 400509: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp) # 将整数 0xa 挪动到 -0x4(绝对于 rbp) int b = 20; 400510: c7 45 f8 14 00 00 00 movl $0x14,-0x8(%rbp) # 将整数 0x14 挪动到 -0x8(绝对于 rbp) int c = add(a, b); 400517: 8b 55 f8 mov -0x8(%rbp),%edx # 将 -0x8 挪动到 edx 40051a: 8b 45 fc mov -0x4(%rbp),%eax # 将 -0x4 挪动到 eax 40051d: 89 d6 mov %edx,%esi # esi = edx 40051f: 89 c7 mov %eax,%edi # edi = eax 400521: e8 c7 ff ff ff callq 4004ed <add> # 调用函数 add 400526: 89 45 f4 mov %eax,-0xc(%rbp) # 将 eax 挪动到 -0xc return c; 400529: 8b 45 f4 mov -0xc(%rbp),%eax # 将 -0xc 挪动到 eax} 40052c: c9 leaveq 40052d: c3 retq 40052e: 66 90 xchg %ax,%ax # nop

March 2, 2021 · 2 min · jiezi

关于c:用C程序程序打印数字正方形

解题思路用5*5网格,启动一个内部循环(行),将迭代5次。在内部循环下开始一个外部循环(列),迭代雷同的次数在外部循环中打印列号。 /*打印如下:1234512345123451234512345 */#include <stdio.h>int main(){ int i; int j; for (i = 1; i <= 5; i++) { for (j = 1; j <= 5; j++) { printf("%d", j); } printf("\n"); } return 0;}原文链接

March 1, 2021 · 1 min · jiezi

关于c:C语言中的-int-是什么

从 int* 和 int 说起“int**是什么” 这个问题其实不难。咱们能够递归剖析,先看下 int*是什么,嗯?如同还能够持续递归到 int 咱们都晓得,int是 C 的根底数据类型 整型,而多了个 * 的 int* 是 指向整型变量的指针,那么 int** 是什么就不言自明了,列个表: C语法释义int整型int*指向整型的指针int**指向指向整型的指针的指针看到这里,你对 int** 应该有了个初步的意识,但你可能感觉有点绕,没关系,上面咱们写一段代码看看: #include <stdio.h>int main(){ int i = 418; int* pi; // 依据下面的表格,咱们晓得 int* 是指向“整型”的指针, // 那么 pi 能够保留的是 int 类型的变量 i 的地址: pi = &i; int** ppi; // ppi 能够保留的是 int* 类型的变量 pi 的地址: ppi = &pi; // 祝贺你,当初你曾经晓得了怎么定义 int** 类型的变量和给它赋值 // 咱们先写到这里 return 0;}深刻思考如果定义有 int** p(为了不便,咱们暂且把 p 认为是 ppi 的别名),那么 p, *p, **p, p + 1, *p + 1, *(p + 1), **p + 1, *(*p + 1), **(p + 1) 别离是什么? ...

February 27, 2021 · 2 min · jiezi

关于c:递归的简单认识c语言

递归程序调用本身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或阐明中有间接或间接调用本身的一种办法,它通常把一个大型简单的问题层层转化为一个与原问题类似的规模较小的问题来求解,递归策略只需大量的程序就可形容出解题过程所须要的多次重复计算,大大地缩小了程序的代码。简而言之,递归就是利用调用本身的办法实现多次重复计算的形式。 设计思维:把问题分解成规模更小,但和原问题有着雷同解法的问题(大事化小) 分类:递归函数又能够分为尾递归和非尾递归函数。尾递归函数是指函数的最初一个动作是调用函数自身的递归函数,是递归的一种非凡情景。尾递归具备两个次要的特色: 调用本身函数(Self-called);计算仅占用常量栈空间(Stack Space)。长处:代码简洁,容易计算验证。 毛病:绝对于循环(迭代),效率低下,存在栈区限度问题(栈溢出)。 特点:后进先出,之后回归先进入的函数再执行下一步。 应用条件:一个问题可能分解成规模更小,且与原问题有着雷同解的问题;存在一个能让递归调用退出的简略进口。 设计条件:设置递归完结的限度条件(尽可能避免栈溢出);设计思路尽可能遵循在每次调用后一直迫近限度条件。 内存分区内存分区分为五种:栈区、堆区、动态区、常量区、代码区。 栈区:寄存函数的参数值(形参)、局部变量和函数调用申请等,由编译器主动调配和开释,通常在函数执行完后就开释了,其操作形式相似于数据结构中的栈。栈内存调配运算内置于CPU的指令集,效率很高,然而调配的内存量无限。 堆区:就是通过new、malloc、realloc和calloc调配的内存块,能够认为是动态分配的内存块,编译器不会负责它们的开释工作,须要用程序区开释。调配形式相似于数据结构中的链表。“内存透露”通常说的就是堆区。 动态区:全局变量和动态变量的存储地位,初始化的全局变量和动态变量在一块区域,未初始化的全局变量和未初始化的动态变量在相邻的另一块区域。程序完结后,由零碎开释。 常量区:常量存储在这里,不容许批改。代码区:寄存编写的代码,不调用。递归引起的栈溢出起因:咱们晓得,正确的递归就是在达到某个限度条件(较小调用次数)之前一直调用函数来实现目标,而每次调用函数都会向栈区申请内存且不开释(函数整体还未完结调用),一旦函数调用过多,栈区容量天然有余,栈溢出也就呈现了。 栈溢出常见谬误:1. 未设置限度条件,函数一直调用。2. 限度条件设置不合理,导致函数调用过多。3. 设计思路存在问题,函数调用完结不再迫近限度条件,导致函数过多调用。 迭代概念迭代是反复反馈过程的流动,其目标通常是为了迫近所需指标或后果。每一次对过程的反复称为一次“迭代”,而每一次迭代失去的后果会作为下一次迭代的初始值。目前对于c语言来说,迭代能够简略认为是循环构造。 递归与迭代递归是一种反复递推与回归过程的构造,而迭代是一种反复循环与更新状态的构造,两者为反复计算服务,实现的形式有所不同。 递归效率低下,循环验证麻烦。迭代能够转换为递归,但递归不肯定能转换为迭代。转换方法:将递归算法转换为非递归算法有两种办法,一种是间接求值(迭代),不须要回溯;另一种是不能间接求值,须要回溯。前者应用一些变量保留两头后果,称为间接转换法,后者应用栈保留两头后果,称为间接转换法。 间接转换法间接转换法通常用来打消尾递归(tail recursion)和单向递归,将递归结构用迭代构造来代替。(单向递归 → 尾递归 → 迭代) 间接转换法递归实际上利用了零碎堆栈实现本身调用,咱们通过应用栈保留两头后果模仿递归过程,将其转为非递归模式。 尾递归函数递归调用返回时正好是函数的结尾,因而递归调用时就不须要保留以后栈帧,能够间接将以后栈帧笼罩掉。

February 26, 2021 · 1 min · jiezi

关于c:三子棋小游戏C语言数组实现

一、游戏成果 二、游戏规则    ❶ 输出1进入页游戏     ❷ 输出0退出页游戏     ❸ 玩家先走     ❹ 玩家落子是抉择棋盘的坐标地位     ❺ 电脑是随机落子     ❻ 如果电脑连了三子则提醒电脑赢了,抉择要不要持续游戏 三、实现游戏的代码思路✪ 先构架游戏整体思路void game(){ char ret = 0; //数组寄存棋盘信息 char board[ROW][COL] = { 0 };//二维数组的初始化并没有这么简略 int row = ROW; int col = COL; //1.初始化棋盘 //肯定要初始化 InitBoard(board, ROW, COL); //2.打印棋盘 DisplayBoard(board, ROW, COL); //3.玩游戏,开始下棋了 while (1) { //玩家下棋 PlayerMove(board, ROW, COL); DisplayBoard(board, ROW, COL); //判断玩家是否赢了 ret = Iswin(board, ROW, COL); //如果不是游戏持续,那么就间接跳出循环 if (ret != 'D') { break; } //电脑下棋 ComputerMove(board, ROW, COL); DisplayBoard(board, ROW, COL); //判断电脑是否赢了 ret = Iswin(board, ROW, COL); //如果不是游戏持续,那么就间接跳出循环 if (ret != 'D') { break; } } if (ret == 'X') printf("玩家赢了\n"); else if (ret == 'O') printf("电脑赢了\n"); else printf("平局\n");}✪ 再分步写出相应所需函数1、菜单函数 ...

February 25, 2021 · 3 min · jiezi

关于c:C语言实现循环顺序队列的基本操作

#include<stdio.h>#include<stdlib.h>#define MaxSize 10#define TRUE 1#define ERROR 0typedef int Status;typedef int ElemType;typedef struct{ ElemType *base; //数组基地址,动静分配内存 int front; //头指针 int rear; //尾指针}Queue;Status InitQueue(Queue* Q); //初始化Status InsertQueue(Queue* Q, ElemType e);// 入队Status DeleteQueue(Queue* Q, ElemType* e); //出队Status LengthQueue(Queue* Q); //获取队长void DstoryQueue(Queue *Q); //销毁队int main(){ Queue Q; ElemType e; int n, a; if (InitQueue(&Q) == 1) printf("创立循环队列胜利!\n"); printf("输出插入队尾元素个数:"); scanf("%d", &n); printf("输出插入队尾元素:"); if (n != 0) //判断队空 { for (int i = 0; i < n; i++) { scanf("%d", &e); a = InsertQueue(&Q, e); } } else a = -1; if (a == 1) { printf("插入胜利!\n"); printf("队长为:%d\n", LengthQueue(&Q)); } if (a==0) printf("队已满!无奈持续插入了!\n"); if (DeleteQueue(&Q, &e) == 1) { printf("删除队首元素为:%d\n", e); } else printf("队为空!无奈持续删除了!\n"); printf("删除后队长为:%d\n", LengthQueue(&Q)); DstoryQueue(&Q); printf("销毁结束!"); return 0;}Status InitQueue(Queue* Q){ Q->base = (Queue*)malloc(sizeof(ElemType) * MaxSize); if (!Q->base) exit(0); Q->front = Q->rear = 0; return TRUE;}Status InsertQueue(Queue *Q, ElemType e){ if ((Q->rear + 1) % MaxSize == Q->front) return ERROR; Q->base[Q->rear] = e; Q->rear = (Q->rear + 1) % MaxSize; return TRUE;}Status DeleteQueue(Queue* Q, ElemType* e){ if (Q->front == Q->rear) return ERROR; *e = Q->base[Q->front]; Q->front = (Q->front + 1) % MaxSize; return TRUE;}Status LengthQueue(Queue* Q){ return ((Q->rear-Q->front+MaxSize)%MaxSize);}void DstoryQueue(Queue* Q){ free(Q->base); Q->base = NULL; Q->front = Q->rear = 0;}

February 24, 2021 · 1 min · jiezi

关于c:C语言实现顺序栈的基本操作举例

#include<stdio.h>#include<stdlib.h>#include<windows.h>#define MaxSize 100#define true 1#define error 0#define OK 1#define OVERFLOW -2typedef int ElemType;typedef struct { //程序栈构造(动静) ElemType* top; ElemType* base; int stacksize;}Sqstack;int InitStack(Sqstack* s); //初始化(在C语言中,形参和实参是单向传递,但可用指针(*)传递地址,让形参影响实参,达到双向传递)int EmptyStack(Sqstack* s);int Push(Sqstack* s,ElemType e); //插入元素(在c++中,能够用(&)援用型参数实现双向传递)int StackLength(Sqstack* s); //元素长度int StackPrint(Sqstack* s); //打印元素int Pop(Sqstack* s); //删除栈顶元素int DestroyStack(Sqstack* s); //销毁栈int main(){ int n; ElemType e, a; Sqstack *s; s = (Sqstack*)malloc(sizeof(Sqstack)); //敲黑板重点!指针要有地址!记住调配地址! InitStack(s); printf("请输出插入元素个数:"); scanf("%d", &n); printf("请输出插入元素:"); for (int i = 0; i < n; i++) { scanf("%d", &e); Push(s, e); } if (EmptyStack(s) == 1) printf("**啊!***空表**\n"); printf ("元素长度:%d\n",StackLength(s)); printf("打印元素为:"); StackPrint(s); printf("\n"); a = Pop(s); //我在这里翻了车,后面Pop(s)没赋值a,导致Pop(s)援用两次,引发谬误。 if (a == -2) printf("栈底溢出!\n"); else printf("删除栈顶元素为:%d\n", a); printf("删除后:"); StackPrint(s); printf("\n"); system("pause"); printf("销毁栈中.........\n"); Sleep(3000); system("cls"); if (DestroyStack(s) == 1) printf("销毁胜利!"); else printf("销毁失败!"); return 0;}int InitStack(Sqstack* s){ s->base = (ElemType*)malloc(sizeof(ElemType) * MaxSize); if (!s->base) return error; s->top = s->base; s->stacksize = MaxSize; return OK;}int EmptyStack(Sqstack* s){ if (s->base == s->top) return true; else return error;}int Push(Sqstack* s, ElemType e){ if (s->top - s->base >= MaxSize) return OVERFLOW; if (!s->base) return error; *(s->top) = e; s->top++; return OK;}int StackLength(Sqstack* s){ if (s->base == s->top) return error; else return ((s->top) - (s->base));}int StackPrint(Sqstack* s){ if (s->base == NULL) return error; if (s->base == s->top) printf("没有元素!"); ElemType* p; p = s->base; while (p <(s->top)) { printf("%d ", *p); p++; } return OK;}int Pop(Sqstack* s){ ElemType a; if (s->base == s->top) return OVERFLOW; else { a = *(s->top-1 ); s->top--; } return a;}int DestroyStack(Sqstack* s){ free(s); s->base = s->top = NULL; s->stacksize = 0; return OK;}

February 21, 2021 · 2 min · jiezi

关于c:为什么-Python-的-fstring-可以连接字符串与数字

本文出自“Python为什么”系列,归档在 Github 上:https://github.com/chinesehuazhou/python-whydo毫无疑问,Python 是一门强类型语言。强类型语言。强类型语言!(对于强弱类型话题,举荐浏览这篇 技术科普文) 这就意味着,不同类型的对象通常须要先做显式地类型转化, 而后能力进行某些操作。 上面以字符串和数字为例,看看强行操作会产生什么后果: >>> "Python猫" + 666Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: can only concatenate str (not "int") to str它报类型谬误了(TypeError),说字符串只能连贯(concatenate)字符串,不能连贯 int 类型。 这正是强类型语言的根本束缚。 然而,如果咱们先把数字“转化”成字符串类型,再执行“+”操作,就不会报错了: >>> "Python猫" + str(666)'Python猫666'下面的这个例子,对读者们来说,应该并不难理解。 由此,咱们要引出一个问题:如何在不作显式类型转化的状况下,进行字符串与数字类型的拼接呢? 在《详解Python拼接字符串的七种形式》这篇文章中,它梳理了七种拼接字符串的写法,咱们能够一一来试验一下。 几种字符串拼接形式:1、格式化类:%、format()、template 2、拼接类:+、()、join() 3、插值类:f-string 为了节俭篇幅,此处间接把能够顺利拼接的 4 种写法列举如下: >>> "%s %d" % ("Python猫", 666)'Python猫 666'>>> from string import Template>>> s = Template('${s1}${s2}')>>> s.safe_substitute(s1='Python猫',s2=666)'Python猫666'>>> "Python猫{}".format(666)'Python猫666'>>> num = 666>>> f"Python猫{num}"'Python猫666'第一种写法(即 % 格式化)来自古老的 C 语言,其中的“%d”是一个占位符,示意它将要接管一个整数,并格式化成字符串。 ...

February 19, 2021 · 1 min · jiezi

关于c:源代码下载地址linux系统源代码下载

linux源代码下载地址如下:https://mirrors.edge.kernel.o...

February 19, 2021 · 1 min · jiezi

关于c:源码下载地址c语言库函数代码下载位置

所有的c语言库函数代码下载地址分享: 上面这个地址就是C苦函数下载地址。http://ftp.gnu.org/gnu/glibc/

February 19, 2021 · 1 min · jiezi

关于c:ccc等等的听说了很多但只学了c有没有来科普一下的

集体了解:c++是基于c,然而面向对象,两者能够说是同源,C#新一些,个性不同,应用的中央也不同,c#优化了内存,然而是否是真的“优化”当然仁者见仁。c++简直齐全兼容c,他对指针的非凡操作能够说是十分独特的,长处毛病都在他,也给了他对底层操作的独特劣势。而感觉c#更像java[url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url]

February 18, 2021 · 1 min · jiezi

关于c:为什么我这代码还是超时

要求:工夫复杂度为O(n),空间复杂度为O(1)。一下是我的代码: include <stdio.h>int main(){int a,b,c,d,i;char ch;a=b=c=d=0;while((ch=getchar())!='\n'){if(ch=='a') a++;else if(ch=='b') b++;else if(ch=='c') c++;else if(ch=='d') d++;}for(i=0;i<a;i++) printf("a");for(i=0;i<b;i++) printf("b");for(i=0;i<c;i++) printf("c");for(i=0;i<d;i++) printf("d");printf("\n");return 0;}[url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url]

February 18, 2021 · 1 min · jiezi

关于c:运行时错误‘214746725980004005

目测是代码外面调用了ODBC,你的SQL server客户端没有装好或者ODBC数据源没设置好。[url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url]

February 18, 2021 · 1 min · jiezi

关于c:c-语句如何转VBNET

if (dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex + 1].Value is BuiltInType builtInType) {}这句如何转成VB.NET 啊[url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url][url]https://book.douban.com/douli...[/url][url]https://movie.douban.com/doul...[/url][url]https://www.douban.com/doulis...[/url][url]https://m.douban.com/doulist/...[/url]

February 18, 2021 · 1 min · jiezi

关于c:排序算法

前沿明天咱们来看看几种常见的排序算法 插入排序定义插入排序的算法形容是一种简略直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应地位并插入。 形容从第一个元素开始,该元素能够认为曾经被排序取出下一个元素,在曾经排序的元素序列中从后向前扫描如果该元素(已排序)大于新元素,将该元素移到下一地位反复步骤3,直到找到已排序的元素小于或者等于新元素的地位将新元素插入到该地位后反复步骤 2~5复杂度工夫复杂度O(N^2) 空间复杂度O(1) 复法void InsertionSort(int *arr, int size) { int i, j, tmp; for (i = 1; i < size; i++) { if (arr[i] < arr[i-1]) { //如果要大在前 改这个判断 和 arr[j] < temp tmp = arr[i]; for (j = i - 1; j >= 0 && arr[j] > tmp; j--) { arr[j+1] = arr[j]; } arr[j+1] = tmp; } } } 冒泡排序形容比拟相邻的元素。如果第一个比第二个大,就替换它们两个 复杂度工夫复杂度O(n^2) 空间复杂度O(1) 算法void BubbleSort(int *arr, int size) { int i, j, tmp; for (i = 0; i < size - 1; i++) { for (j = 0; j < size - i - 1; j++) { if (arr[j] > arr[j+1]) { tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } } } } 抉择排序定义抉择排序,是一种简略直观的排序算法。它的工作原理:首先在末排序列中找到最小(大)元素,寄存到排序序列的起始地位,直到全副待排序的数据元素排完。 ...

February 15, 2021 · 1 min · jiezi

关于c:深入-Python-解释器源码我终于搞明白了字符串驻留的原理

英文:https://arpitbhayani.me/blogs... 作者:arpit 译者:豌豆花下猫(“Python猫”公众号作者) 申明:本翻译是出于交流学习的目标,基于 CC BY-NC-SA 4.0 受权协定。为便于浏览,内容略有改变。 每种编程语言为了表现出色,并且实现卓越的性能,都须要有大量编译器级与解释器级的优化。 因为字符串是任何编程语言中不可或缺的一个局部,因而,如果有疾速操作字符串的能力,就能够迅速地进步整体的性能。 在本文中,咱们将深入研究 Python 的外部实现,并理解 Python 如何应用一种名为字符串驻留(String Interning)的技术,实现解释器的高性能。 本文的目标不仅在于介绍 Python 的外部常识,而且还旨在使读者可能轻松地浏览 Python 的源代码;因而,本文中将有很多出自 CPython 的代码片段。 全文提纲如下: (在 Python猫 公众号回复数字“0215”,下载高清思维导图) 1、什么是“字符串驻留”?字符串驻留是一种编译器/解释器的优化办法,它通过缓存一般性的字符串,从而节俭字符串解决工作的空间和工夫。 这种优化办法不会每次都创立一个新的字符串正本,而是仅为每个适当的不可变值保留一个字符串正本,并应用指针援用之。 每个字符串的惟一拷贝被称为它的intern,并因而而得名 String Interning。 Python猫注:String Interning 个别被译为“字符串驻留”或“字符串留用”,在某些语言中可能习惯用 String Pool(字符串常量池)的概念,其实是对同一种机制的不同表述。intern 作为名词时,是“实习生、实习医生”的意思,在此能够了解成“驻留物、驻留值”。查找字符串 intern 的办法可能作为公开接口公开,也可能不公开。古代编程语言如 Java、Python、PHP、Ruby、Julia 等等,都反对字符串驻留,以使其编译器和解释器做到高性能。 2、为什么要驻留字符串?字符串驻留晋升了字符串比拟的速度。 如果没有驻留,当咱们要比拟两个字符串是否相等时,它的工夫复杂度将回升到 O(n),即须要查看两个字符串中的每个字符,能力判断出它们是否相等。 然而,如果字符串是固定的,因为雷同的字符串将应用同一个对象援用,因而只需查看指针是否雷同,就足以判断出两个字符串是否相等,不用再逐个查看每个字符。因为这是一个十分广泛的操作,因而,它被典型地实现为指针相等性校验,仅应用一条齐全没有内存援用的机器指令。 字符串驻留缩小了内存占用。 Python 防止内存中充斥多余的字符串对象,通过享元设计模式共享和重用曾经定义的对象,从而优化内存占用。 3、Python的字符串驻留像大多数其它古代编程语言一样,Python 也应用字符串驻留来进步性能。在 Python 中,咱们能够应用is运算符,查看两个对象是否援用了同一个内存对象。 因而,如果两个字符串对象援用了雷同的内存对象,则is运算符将得出True,否则为False。 >>> 'python' is 'python'True咱们能够应用这个特定的运算符,来判断哪些字符串是被驻留的。在 CPython 的,字符串驻留是通过以下函数实现的,申明在 unicodeobject.h 中,定义在 unicodeobject.c 中。 PyAPI_FUNC(void) PyUnicode_InternInPlace(PyObject **);为了查看一个字符串是否被驻留,CPython 实现了一个名为PyUnicode_CHECK_INTERNED的宏,同样是定义在 unicodeobject.h 中。 ...

February 15, 2021 · 3 min · jiezi

关于c:C并发与多线程-11sharedfutureautomic

std::shared_future类模板template <class T> shared_future;template <class R&> shared_future<R&>; // specialization : T is a reference type (R&)template <> shared_future<void>; // specialization : T is void一个 shared_future 对象行为相似于 future 对象,除了它能够被赋值,而且一个以上的 shared_future 能够在他们的共享状态完结时共享所有权。它们还被容许一旦筹备好就屡次检索共享状态下的值。shared_future 对象能够从 future 对象隐式转换,也能够通过 future::share 显式取得。在这两种状况下,原 future 对象本身将生效。共享状态的生存期至多要继续到与之关联的最初一个对象被销毁为止。从 shared_future 中获取值(应用成员函数 get) 不会开释其对共享状态的所有权(与 future 不同)。因而,如果与 shared_future 对象相关联,则共享状态能够在最后取得它的对象(如果有的话)之后持续存在。成员函数形容get当共享状态就绪时,返回存储在共享状态中的值的援用(或引发其异样)valid查看 shared_future 对象是否与共享状态关联wait期待共享状态准备就绪wait_for期待共享状态在 rel_time 指定的工夫内准备就绪wait_until期待共享状态准备就绪,最多直到abs_time工夫点#include <iostream>#include <thread>#include <future>using namespace::std;int mythread(){ cout << "mythread begin" << endl; this_thread::sleep_for(chrono::microseconds(5000)); cout << "mythread end" << endl; return 5;}int main(){ cout << "main begin" << endl; future<int> result = async(launch::async, mythread); shared_future<int> result_s = result.share(); // 等价 // shared_future<int> result_s = async(launch::async, mythread); if (result_s.valid()) { cout << result_s.get() << endl; cout << result_s.get() << endl; cout << result_s.get() << endl; } cout << "main end" << endl; return 0;}输入: ...

February 14, 2021 · 2 min · jiezi

关于c:C语言中的指针和数组

指针是 C 语言中的一个特点,也是内存地址,是内存单元的编号,指针变量是用来寄存内存地址的变量,不同类型的指针变量所占用的存储单元长度是雷同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针当前,不仅能够对数据自身,也能够对存储数据的变量地址进行操作;个别把指针称为指针变量,指向的对象能够是变量或者数组等;指针指向数组时,它内容存储的是数组的首地址,所以数组和指针就产生了肯定的关系。那什么是数组呢?具备雷同类型的若干元素按有序的模式组织起来的一种汇合就叫做数组,上面会对指针、指针和数组相结合的一些用法进行剖析。 1、指针 1、1 定义 int * 类型的指针变量以及批改这个指针变量,其余类型的指针变量写法也相似 int * p; //p是变量的名字,int * 示意 p 变量寄存的是 int 类型变量的地址;它并不是示意定义了名叫 *p 的变量int i = 5;p = &i; //p 保留了 i 的地址,所以 p 指向 iint i2 = 6;p = &i2; //批改 p 的值不影响 i 的值int * p2 = &i;i = 9; //批改 i 的值不影响 p2 的值printf("%d\n",*p2); //*p2 输入为9,*p2 等同于 i 的值,p2 等同于 i 的地址;*p2 能够了解为以 p2 的内容为地址的变量,p2 的内容就是 i 的地址1、2 指针与指针变量是不同的概念 ...

February 12, 2021 · 3 min · jiezi

关于c:Linux函数学习setsockoptgetsockopt和getsockname函数

简介:setsockopt、getsockopt这两个函数获取套接字一些无关选项和设置套接字无关的套接字。getsockname是通过套接字获取套接字无关的一些信息,例如端口、协定等。 1:int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); sock:将要被设置或者获取选项的套接字。level:选项所在的协定层。optname:须要拜访的选项名。optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向蕴含新选项值的缓冲。optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为进口参数时,选项值的理论长度。对于setsockopt(),现选项的长度。对于level和optname具体参数如下level指定管制套接字的档次.能够取三种值:1)SOL_SOCKET:通用套接字选项.2)IPPROTO_IP:IP选项.3)IPPROTO_TCP:TCP选项. 返回阐明: 胜利执行时,返回0。失败返回-1,errno被设为以下的某个值 EBADF:sock不是无效的文件形容词EFAULT:optval指向的内存并非无效的过程空间EINVAL:在调用setsockopt()时,optlen有效ENOPROTOOPT:指定的协定层不能辨认选项ENOTSOCK:sock形容的不是套接字optval取得或者是设置套接字选项.依据选项名称的数据类型进行转换 选项名称 阐明 数据类型======================================================================== SOL_SOCKET------------------------------------------------------------------------SO_BROADCAST 容许发送播送数据 intSO_DEBUG 容许调试 intSO_DONTROUTE 不查找路由 intSO_ERROR 取得套接字谬误 intSO_KEEPALIVE 放弃连贯 intSO_LINGER 提早敞开连贯 struct lingerSO_OOBINLINE 带外数据放入失常数据流 intSO_RCVBUF 接收缓冲区大小 intSO_SNDBUF 发送缓冲区大小 intSO_RCVLOWAT 接收缓冲区上限 intSO_SNDLOWAT 发送缓冲区上限 intSO_RCVTIMEO 接管超时 struct timevalSO_SNDTIMEO 发送超时 struct timevalSO_REUSERADDR 容许重用本地地址和端口 intSO_TYPE 取得套接字类型 intSO_BSDCOMPAT 与BSD零碎兼容 int======================================================================== IPPROTO_IP------------------------------------------------------------------------IP_HDRINCL 在数据包中蕴含IP首部 intIP_OPTINOS IP首部选项 intIP_TOS 服务类型IP_TTL 生存工夫 int======================================================================== IPPRO_TCP------------------------------------------------------------------------TCP_MAXSEG TCP最大数据段的大小 intTCP_NODELAY 不应用Nagle算法 int========================================================================int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len); 性能:获取一个套接字的选项 参数: socket:文件描述符 level:协定档次 SOL_SOCKET 套接字档次 IPPROTO_IP ip档次 IPPROTO_TCP TCP档次 option_name:选项的名称(套接字档次) SO_BROADCAST 是否容许发送播送信息 SO_REUSEADDR 是否容许重复使用本地地址 SO_SNDBUF 获取发送缓冲区长度 SO_RCVBUF 获取接收缓冲区长度 SO_RCVTIMEO 获取接管超时工夫 SO_SNDTIMEO 获取发送超时工夫 option_value:获取到的选项的值 option_len:value的长度 返回值: 胜利:0 失败:-1int getsockname(int sockfd, struct sockaddr *localaddr,socklen_t *addrlen); getsockname能够取得一个与socket相干的地址。 服务器端能够通过它失去相干客户端地址。 而客户端也能够失去以后已连贯胜利的socket的ip和端口。 对于TCP连贯的状况,如果不进行bind指定IP和端口,那么调用connect连贯胜利后, 应用getsockname能够正确取得以后正在通信的socket的IP和端口地址。 而对于UDP的状况,无论是在调用sendto之后还是收到服务器返回的信息之后调用, 都无奈失去正确的ip地址:应用getsockname失去ip为0,端口正确。 2:在Tinyhttpd中有相似的例子,如下代码 ...

February 8, 2021 · 1 min · jiezi

关于c:树的解读

前沿前几期文章我介绍了链表,队列,栈这些都是线性构造的存储形式。明天咱们来看看非线性构造的树 定义业余的定义: 有且只有一个称为根的节点有若干个互不相交的子树,这些子树自身也是一颗树艰深的定义: 树是由节点和边组成每个节点只有一个父节点但能够有多个子节点但有一个节点例外,该节点没有父节点,此节点称为根节点专业术语: 深度:从根节点到最底层节点的层数称之为深度(根节点是第一层)叶子节点:没有子节点的节点非终端节点:理论就是非叶子节点(根节点既能够是叶子也能够是非叶子节点)度:子节点的个数称为度(一棵树看最大的)分类树的分类 个别树任意一个节点的子节点的个数都不受限制,子节点的程序能够更改也能够不能更改,能更改的树为无序个别树,不能更改的为有序个别树 二叉树 任意一个节点的子节点个数最多两个,且子节点的地位不可更改,即左子树和右子树的地位不可更改。 分类: 个别二叉树满二叉树:在不减少树的层数的前提下,无奈再多增加一个节点的二叉树就是满二叉树 齐全二叉树:如果只是删除了满二叉树最底层最左边的间断若干个节点,这样造成的二叉树就是齐全二叉树。 存储: 间断存储:间断存储用数组存储【实用于齐全二叉树,不是齐全二叉树的树补充为齐全二叉树】 长处 : 查找某个节点的父节点和子节点(也包含判断有没有子节点)方便快捷 毛病: 耗费内存空间过大 链式存储:链式存储两个指针域别离指向两个子节点,没有子节点的为空 长处:耗用内存空间小 毛病:查找父节点不不便 算法咱们来看看经典的二叉树的前中后序遍历 一、首先来看看先序遍历先拜访根节点再先序拜访左子树再先序拜访右子树//先序遍历伪代码void PreTraverseBTree(PBTNODE pT){ if (pT != NULL) { printf("%c\n", pT->data); PreTraverseBTree(pT->pLchild); PreTraverseBTree(pT->pRchild); }}先序遍历后果 A B C D E 二、再来看看中序遍历中序遍历左子树再拜访根节点再中序遍历右子树//中序遍历伪代码void MidTraverseBTree(PBTNODE pT){ if (pT != NULL) { MidTraverseBTree(pT->pLchild); printf("%c\n", pT->data); MidTraverseBTree(pT->pRchild); }}中序遍历后果 B A D E C 三、最初是后序遍历先后序遍历左子树再后序遍历右子树再拜访根节点//后序遍历伪代码void LastTraverseBTree(PBTNODE pT){ if (pT != NULL) { LastTraverseBTree(pT->pLchild); LastTraverseBTree(pT->pRchild); printf("%c\n", pT->data); }}后序遍历后果 B E D C A ...

February 6, 2021 · 1 min · jiezi

关于c:Linux函数学习getoptgetoptlonggetoptlongonly

简介:这几个函数是对相似于main函数那样传进来的参数进行解析。参数的指定由-key value -key --key value --key -key value1 value2 这几种类型,其中getopt能够解决前两种类型,getopt_long可能解决所有类型的参数解析,getopt_long_only相似于getopt_long,能够解决所有选项。具体细节再前面的局部进行介绍。 首先介绍getopt选项,他是绝对比较简单的。函数原型: int getopt(int argc, char * const argv[], const char *optstring);argc和argv就是main函数传进来的参数,在这里就不多说了。optstring:此参数是指定咱们要解析的参数内容。eg:abc:(注,getopt函数只能解决带一个冒号的optstring)optind:是下一次调用getopt函数该当解决参数的小标,也就是argv要解决参数的下表。optarg:是带有一个冒号的optstringopterr:此选项决定是否将谬误音讯打印到规范谬误中,如果是0的话,就不打印谬误了。上面是一个例子和对应的输入。 接下来解说getopt_long函数,后面说过,此函数可能解决所有的参数函数原型: int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);此函数多了两个参数第一个参数构造如下。struct option { const char *name; int has_arg; int *flag; int val; };name:此局部代表长选项的名称。has_arg:此构造有几个宏为其赋值。 no_argument(0):代表没有对应的值 required_argument(1) :代表肯定须要一个值。 optional_argument(2):代表有没有对应的值都能够。 flag:如果此选项不为空的话,那么将参数对应的val赋值给flag,否则返回。 val:就是咱们要返回后者付给flag对应的value。longindex:如果此选项不为空的话,那么它将指向绝对于longopts对应的下标。上面是我给出的一个例子:和一些输入。 getopt_long_only与上述getopt_long不同之处:前者不论是“-”还是“--”都视为长选项,如果找不到的话,才去短选项哪里查找:

February 6, 2021 · 1 min · jiezi

关于c:Linux学习tcpdump表达式及命令参数详解例子

1:tcpdump抓取各种类型的包。tcp规定只有一套,然而它有两种用处。一个是用于显示抓取到的包、另一个是用于抓包。当然有些options只能用于抓取包,有些options只能用于显示包。同时、tcpdump命令提供表达式,此项性能能够过滤掉咱们抓到大包,只去显示或存储咱们想要的包。废话不多说,上面开始介绍tcp。(这其中我也有许多参数弄不明确、心愿懂的人可能再评论区下方加以评论)。 首先、是总体看tcp这个命令都有哪些opitons 。 tcpdump [ -AbdDefhHIJKlLnNOpqStuUvxX# ] [ -B buffer_size ] [ -c count ] [ -C file_size ] [ -G rotate_seconds ] [ -F file ] [ -i interface ] [ -j tstamp_type ] [ -m module ] [ -M secret ] [ --number ] [ -Q in|out|inout ] [ -r file ] [ -V file ] [ -s snaplen ] [ -T type ] [ -w file ] [ -W filecount ] [ -E spi@ipaddr algo:secret,... ] [ -y datalinktype ] [ -z postrotate-command ] [ -Z user ] [ --time-stamp-precision=tstamp_precision ] [ --immediate-mode ] [ --version ] [ expression ]从上述能够看的进去、tcp有许多选项,我会将我不懂的中央标记进去、心愿各位大神可能加以探讨。我这里可能不是依照上述书写的程序定义。-i:指定咱们要抓取的接口。-D:列出咱们能够应用该命令抓取包的网络接口。如下图所示 ...

February 6, 2021 · 1 min · jiezi

关于c:16-最接近是三数之和

一、双指针法——C语言实现/** * 最靠近的三数和 * LeetCode第16题 * author: aliao language: C*/#include <stdio.h>#include <stdlib.h>int compare (const void *a, const void *b) { return (*(int *)a) - (*(int *)b);}int threeSumClosest(int *nums, int numsSize, int target){ // 先给数组升序排序 qsort(nums, numsSize, sizeof(int), compare); // 定义返回值 int best; for (int i = 0; i < numsSize; i++) { if (i > 0 && nums[i] == nums[i - 1]) { continue; } int head = i + 1; int tail = numsSize - 1; while (head < tail) { // 求和 int sum = nums[i] + nums[head] + nums[tail]; // 将第一个三数之和存为最靠近解best if (i == 0 && head == 1 && tail == numsSize - 1) { best = sum; } // 更新三元素之和 if (abs(sum - target) < abs(best - target)) { best = sum; } if (sum < target) { // 左指针左移 head++; // 左指针去重 while (nums[head] == nums[head - 1]) { head++; } } else if (sum > target) { // 右指针左移 tail--; // 右指针去重 while (nums[tail] == nums[tail + 1]) { tail--; } } else { return target; } } } return best;}int main (void) { int nums[] = {-1, 2, 1, -4}; int numsSize = 4; int target = 1; printf("%d\n", threeSumClosest(nums, numsSize, target));} ...

February 4, 2021 · 2 min · jiezi

关于c:递归的解读

前沿递归的思维是软件思维的根本思维之一,在树和图下面,简直全是用递归来实现的。计算机特地适宜用递归的思维来解决问题,然而咱们人类用递归的思维来思考问题就会感到非常困扰。接下来咱们一起探索递归吧。 定义一个函数本人间接或者间接调用本人 注:一个函数调用另外一个函数和他调用本人都是截然不同的。 递归满足的三个条件: 递归必须有一个明确的终止条件该函数解决的数据规模必须在递加这个转化必须时可解的函数的调用当在一个函数的运行期间调用另外一个函数时,在运行被调用函数之前,零碎须要实现三件事 将所有的理论参数,返回地址等信息传递给被调用函数。为被调用函数的局部变量(也包含形参)调配存储空间。将管制转移到被调用函数的入口。从被调函数返回主调函数之前,零碎也要实现三件事 保留被调函数的返回后果开释被调函数所占的存储空间按照被调函数保留的返回地址将管制转移到调用函数当有多个函数互相调用时,依照“后调用先返回”的准则 上诉函数之间信息传递和管制转移必须借助 “栈” 来实现,即零碎将整个程序运行所需的数据空间安顿在一个栈中,每当调用一个函数时,将在栈顶调配一个存储区,进行压栈操作,每当一个函数退出时,就开释它的存储区,就进行出栈操作,以后运行的函数永远都在栈顶地位。 算法咱们来看一题比拟经典的面试题,大家能够本人猜想下运算后果 #include <iostream>#include <cstdlib>int f(int num){ printf("%d\n", num); if (num <= 1) { return 1; } f(num-1); printf("%d\n", num); return 0;}int main() { f(3); return 0;}这道题目后果会是什么呢?他到底是怎么调用的呢? 我来画个调用图 咱们关注红色的调用箭头 首先运行主函数的f(3)而后调用 int f(3) 打印出 3而后调用 int f(2) 打印出 2而后调用 int f(1) 打印出 1,而后满足条件 return 1;完结 int (f1)之后回到 int f(2) 打印出 2之后回到 int f(3) 打印出 3最初回到主函数,程序完结所以最初打印的数是 3 2 1 2 3 ...

January 31, 2021 · 1 min · jiezi

关于c:进程之间的关系

简介:这解说过程之间的关系:父子过程、过程和过程组、会话。这节只须要理解即可。 1: 首先、解说一下终端登录。 在晚期的时候、咱们应用终端设备进行登录,然而因为终端设备连贯的无限,因而登录的用户也就是无限的。随着位映射图形终端变得可用,开发出了窗口零碎,它向用户提供了与主机零碎进行交互的新形式。在这里、我简略介绍一下过来有终端设备登录的流程(只是做为一个理解)。首先是init过程读取/etc/ttys文件(此文件保留各个终端设备的信息),为每一个终端创立一个空的环境。而后应用getty函数以读写的形式关上终端,并将规范输出、输入和谬误重置到该终端中,最初执行login程序进行登录。2:这里简略介绍一下过程组的概念。 每一个过程都属于一个过程组,每个过程组有相似与过程ID的过程组ID。过程组的存在不便咱们管理控制。在上一节中、咱们介绍回收过程的时候,能够期待指定过程组ID中的过程完结。有些时候,咱们可能须要很多个过程单干去实现某一项工作。这时候,咱们能够应用过程组的概念去会后过程组中的过程。上面我简略列举一个例子。多过程服务器:每当一个客户端到来的时候、咱们创立一个新的过程去执行相应的工作。当执行完工作后,子过程就会向父过程发送终止信号,这时候、我峨嵋你不分明是该过程组中的那个过程完结了。因而、咱们能够抉择去回收该过程组中的过程。咱们能够应用如下函数获取和设置过程ID。int setpgid(pid_t pid, pid_t pgid);此函数用于将指定过程ID设置为pgid,然而这个函数只能为本人或者对应的子过程设置过程组ID,同时、如果子过程应用了exec函数后就不能设置过程组ID。如果你想要父子过程同属一个过程组,那么请在fork之后应用此函数设置过程组ID。int getpgid(pid_t pid);获取指定过程ID的过程组ID。注:只有过程组中有一个过程存在,那么该过程组就存在。(因而、在这里咱们感觉过程组ID是这过程创立终止而随时变动的。3:会话 会话的概念倒是非常简略、他就是几个过程组的汇合。会话是没有会话ID的,通常咱们能够取得该过程的首个过程组的过程组ID。通常状况下、咱们能够fork一个子过程去调用如下的函数以保障改过程和其余过程没有什么关系(除了、父过程是init过程)。pid_t setsid(void);将调用该函数的过程设置为一个会话。在调用此函数后、改过程会产生如下状况。a)改过程成为一个会话的首过程。b)该过程是一个新的过程组的组长过程。c)如果改过程之前和管制终端有分割的话,那么当初改过程和管制终端将不会有什么分割。pid_t getsid(pid_t pid);此函数用于获取指定过程所处会话首个过程组的过程组ID。4:管制终端。 会话和过程组有一些其余的恶行:a:一个会话能够有一个管制终端。这通常是登录到其余的终端设备或伪终端设备。b:建设与管制终端连贯的过程称为伪终端过程。c:一个会话中能够分为前台过程组和后盾过程组。d:如果一个会话有一个管制终端,则它有一个前台过程组,会话中的其余过程组则为后盾过程组。e:信号会发送给前台过程组。如下两个函数用于告诉内核哪一个是前台过程组。pid_t tcgetpgrp(int filedes);//用于将filedes先关联的终端设备前台过程组的过程组ID。

January 31, 2021 · 1 min · jiezi

关于c:函数运行时在内存中是什么样子

在开始本篇的内容前,咱们先来思考几个问题。 咱们先来看一段简略的代码:void func(int a) { if (a > 100000000) return; int arr[100] = {0}; func(a + 1);}你能看出这段代码会有什么问题吗? 咱们在之前的文章《高性能高并发服务器是如何实现的》一中提到了一项关键技术——协程,你晓得协程的实质是什么吗?有的同学可能会说是用户线程,那么什么是用户态线程,这是怎么实现的?函数运行起来后是什么样子?这个问题看似没什么关联,但这背地有一样货色你须要了解,这就是所谓的函数运行时栈,run time stack。 接下来咱们就好好看看到底什么是函数运行时栈,为什么彻底了解函数运行时栈对程序员来说十分重要。 从过程、线程到函数调用汽车在高速上行驶时有很多信息,像速度、地位等等,通过这些信息咱们能够直观的感触汽车的运行时状态。 同样的,程序在运行时也有很多信息,像有哪些程序正在运行、这些程序执行到了哪里等等,通过这些信息咱们能够直观的感触零碎中程序运行的状态。 其中,咱们发明了过程、线程这样的概念来记录有哪些程序正在运行,对于过程和线程的概念请参见《看完这篇还不懂过程和线程你来打我》。 过程和线程的运行体现在函数执行上,函数的执行除了函数外部执行的程序执行还有子函数调用的管制转移以及子函数执行结束的返回。其中函数外部的程序执行乏善可陈,重点是函数的调用。 因而接下来咱们的视角将从宏观的过程和线程拉近到宏观下的函数调用,重点来讨论一下函数调用是怎么实现的。 函数调用的流动轨迹:栈玩过游戏的同学应该晓得,有时你为了实现一项主线工作不得不去打一些干线的工作,干线工作中可能还有干线工作,当一个干线工作实现后退回到前一个干线工作,这是什么意思呢,举个例子你就明确了。 假如主线工作西天取经A依赖干线工作收服孙悟空B和收服猪八戒C,也就是说收服孙悟空B和收服猪八戒C实现后能力持续主线工作西天取经A; 干线工作收服孙悟空B依赖工作拿到紧箍咒D,只有当工作D实现后能力回到工作B; 整个工作的依赖关系如图所示: 当初咱们来模仿一下工作实现过程。 首先咱们来到工作A,执行主线工作: 执行工作A的过程中咱们发现工作A依赖工作B,这时咱们暂停工作A去执行工作B: 执行工作B的时候,咱们又发现依赖工作D: 执行工作D的时候咱们发现该工作不再依赖任何其它工作,因而C实现后咱们能够会退到前一个工作,也就是B: 工作B除了依赖工作C外不再依赖其它工作,这样工作B实现后就能够回到工作A: 当初咱们回到了主线工作A,依赖的工作B执行实现,接下来是工作C: 和工作D一样,C不依赖任何其它其它工作,工作C实现后就能够再次回到工作A,再之后工作A执行结束,整个工作执行实现。 让咱们来看一下整个工作的流动轨迹: 仔细观察,实际上你会发现这是一个First In Last Out 的程序,人造实用于栈这种数据结构来解决。 再认真看一下栈顶的轨迹,也就是A、B、D、B、A、C、A,实际上你会发现这里的轨迹就是工作依赖树的遍历过程,是不是很神奇,这也是为什么树这种数据结构的遍历除了能够用递归也能够用栈来实现的起因。 A box函数调用也是同样的情理,你把下面的ABCD换成函数ABCD,实质不变。 因而,当初咱们晓得了,应用栈这种构造就能够用来保留函数调用信息。 和游戏中的每个工作一样,当函数在运行时每个函数也要有本人的一个“小盒子”,这个小盒子中保留了函数运行时的各种信息,这些小盒子通过栈这种构造组织起来,这个小盒子就被称为栈帧,stack frames,也有的称之为call stack,不论什么命名形式,总之,就是这里所说的小盒子,这个小盒子就是函数运行起来后占用的内存,这些小盒子形成了咱们通常所说的栈区。对于栈区具体的解说你能够参考《深刻了解操作系统:程序员应如何了解内存》一文。 那么函数调用时都有哪些信息呢? 函数调用与返回信息咱们晓得当函数A调用函数B的时候,管制从A转移到了B,所谓管制其实就是指CPU执行属于哪个函数的机器指令,CPU从开始执行属于函数A的指令切换到执行属于函数B的指令,咱们就说管制从函数A转移到了函数B。 管制从函数A转移到函数B,那么咱们须要有这样两个信息: 我从哪里来 (返回)要到去哪里 (跳转)是不是很简略,就好比你进来游览,你须要晓得去哪里,还须要记住回家的路。 函数调用也是同样的情理。 当函数A调用函数B时,咱们只有晓得: 函数A对于的机器指令执行到了哪里 (我从哪里来,返回)函数B第一条机器指令所在的地址 (要到哪里去,跳转)有这两条信息就足以让CPU开始执行函数B对应的机器指令,当函数B执行结束后跳转回函数A。 ...

January 31, 2021 · 1 min · jiezi

关于c:C语言实现随机抽取纸牌

利用数组实现从一副牌中随机抽取纸牌,供大家参考,具体内容如下一、我的项目要求本程序负责发一副规范纸牌,每张规范纸牌都有一种花色(梅花、方块、黑桃、红桃)和一个等级(2,3,4,5,6…K,A)。程序须要用户指明手机有几张牌,格局为:Enter number of cards in hand:____your hand: _二、原理1.应用库函数time函数返回以后工夫,用一个数示意,srand函数初始化C语言的随机数生成器。通过把time函数返回值传递给srand能够防止程序每次运行发同样的牌。rand函数产生随机数,通过%缩放。2.利用二维数组记录程序采纳in_hand二维数组对曾经抉择的牌进行记录,4行示意每种花色,13列示意每种等级。程序开始时,数组元素都为false,每随机抽取一张纸牌时,查看in_hand对应元素虚实,如果为真,则抽取其余纸牌,如果为假,记录到数组元素当中,揭示咱们这张牌曾经记录过了站长博客。三、我的项目代码我的项目的具体代码展现如下: include <stdio.h>include <ctype.h>include <stdbool.h>include <time.h>include <stdlib.h>define num_rates ((int) (sizeof(value)/sizeof(value[0])))define initial_balance 100.00define num_suits 4define num_ranks 13int main(){ bool in_handnum_suits = {false};int num_cards,rank,suit; const char rank_code[] = { '2','3','4','5','6','7','8','9', 't','j','q','k','a'};const char suit_code[] = { 'c','d','h','s'};printf("enter numbern");scanf("%d",&num_cards); printf("your handsn");while(num_cards>0){ suit = rand()%num_suits; rank = rand()%num_ranks; if(!in_handsuit){ in_handsuit = true; num_cards--; printf(" %c%c",rank_code[rank],suit_code[suit]); }}printf("n");return 0;}

January 30, 2021 · 1 min · jiezi

关于c:2021年最完整最强解决clion-mingw64中文乱码问题

首先,这篇文章是汇总了网上三种办法,并亲自测试的 测试环境零碎环境:window10 2004clion 2020.3.1编译器:mingw64 办法一——最愚昧的办法办法一是来自搜索引擎最多的解决办法,也是最差劲最没用最不举荐最应该被谩骂的办法参考链接:用Clion运行C++代码时输入中文乱码解决办法本人点进去看就好,没有述说意义 长处:能够解决中文乱码的问题毛病:脱裤子放屁 办法二——另辟蹊径参考链接:解决Windows平台的Clion控制台乱码问题大抵意思就是说把mingw换成cygwin长处:能够解决乱码问题(没有验证过)毛病:因为cygwin在windows下体验并不好,还是改编码格局吧,只是略微麻烦点。 办法三——副作用极大,导致clion无奈失常应用参考链接:Clion 中 的乱码问题正确解决方案(来自官网技术支持)大抵意思就是说,按下Ctrl+Shift+Alt+/,勾销默认选中的run.processes.with.pty 此办法为害人办法 长处:能够解决乱码问题毛病:某些状况下程序间接卡死 办法四——最完满的办法参考链接:Windows下CLion中文乱码最无效的解决形式 最无效的办法:c++在cmakelist.txt增加set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fexec-charset=GBK")c语言在cmakelist.txt增加CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fexec-charset=GBK"就完满解决了,此办法临时没有发现副作用

January 29, 2021 · 1 min · jiezi

关于c:C语言中extern详解

在C语言中,修饰符extern用在变量或者函数的申明前,用来阐明“此变量/函数是在别处定义的,要在此处援用”。 extern润饰变量的申明。举例来说,如果文件a.c须要援用b.c中变量int v,就能够在a.c中申明extern int v,而后就能够援用变量v。这里须要留神的是,被援用的变量v的链接属性必须是外链接(external)的,也就是说a.c要援用到v,不只是取决于在a.c中申明extern int v,还取决于变量v自身是可能被援用到的。这波及到c语言的另外一个话题--变量的作用域。可能被其余模块以extern修饰符援用到的变量通常是全局变量。还有很重要的一点是,extern int v能够放在a.c中的任何中央,比方你能够在a.c中的函数fun定义的结尾处申明extern int v,而后就能够援用到变量v了,只不过这样只能在函数fun作用域中援用v罢了,这还是变量作用域的问题。对于这一点来说,很多人应用的时候都心存顾虑。如同extern申明只能用于文件作用域似的。extern润饰函数申明。从实质上来讲,变量和函数没有区别。函数名是指向函数二进制块结尾处的指针。如果文件a.c须要援用b.c中的函数,比方在b.c中原型是int fun(int mu),那么就能够在a.c中申明extern int fun(int mu),而后就能应用fun来做任何事件。就像变量的申明一样,extern int fun(int mu)能够放在a.c中任何中央,而不肯定非要放在a.c的文件作用域的范畴中。对其余模块中函数的援用,最罕用的办法是蕴含这些函数申明的头文件。应用extern和蕴含头文件来援用函数有什么区别呢?extern的援用形式比蕴含头文件要简洁得多!extern的应用办法是直接了当的,想援用哪个函数就用extern申明哪个函数。这大略是KISS准则的一种体现吧!这样做的一个显著的益处是,会减速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差别是非常明显的。此外,extern修饰符可用于批示C或者C++函数的调用标准。比方在C++中调用C库函数,就须要在C++程序中用extern “C”申明要援用的函数。这是给链接器用的,通知链接器在链接的时候用C函数标准来链接。次要起因是C++和C程序编译实现后在指标代码中命名规定不同。

January 28, 2021 · 1 min · jiezi

关于c:C语言Windows编程入门简介

学习C语言很久了,是不是始终在跟黑乎乎的屏幕打交道,像QQ、360、VC6.0这样的软件都是带界面的,怎么做到的呢?后面咱们讲的”黑屏“叫控制台应用程序(Win32 Console Application),也称DOS程序(或MS-DOS程序)。DOS是晚期的命令式操作系统,很难做出丑陋的界面,除了开发人员,”黑屏“对普通用户很不敌对。带界面的程序叫Windows应用程序(Win32 Application)。Windows是一款古代操作系统,带有丰盛的交互界面,应用简略,无需记忆繁冗的命令。应用C语言能够开发出Windows应用程序,也就是带界面的程序,只是绝大部分C语言教程没有讲,它们只讲了根本语法,让很多初学者认为学C语言没用,什么都做不进去。其实不是这样的,C语言只是一种工具,须要与Windows零碎联合,借助Windows提供的函数能力开发出丑陋的程序。Windows API 编程、Windows编程、Windows SDK 编程是一个概念。 什么是windos编程: 在C语言中,应用fopen()函数能够关上一个文件,感觉非常简单。文件保留在硬盘上,要通过简单的解决能力显示,这些细节对咱们来说是通明的,由操作系统实现。也就是说,咱们调用fopen()函数来告诉操作系统,让操作系统关上一个文件。那么,咱们如何通知操作系统关上文件呢?看似简略的操作到底层都非常复杂,关上文件首先要扫描硬盘,找到文件的地位,而后从文件中读取一部分数据,将数据放进I/O缓冲区,放进内存;这些数据都是0、1序列,还要对照ASCII表或Unicode表”翻译“成字符,再在显示器上显示进去。这个过程如果要让程序员来实现,那几乎是噩梦!怎么办呢?Windows想了一个很好的方法,它事后把这些简单的操作写在一个函数外面,编译成动态链接库(DLL),随Windows一起公布,程序员只须要简略地调用这些函数就能够实现简单的工作,让编程变得简略乏味。这些封装好的函数,叫做 API(Application Programming Interface),即应用程序编程接口。API 函数以C语言的模式向外裸露,能够通过C语言间接调用。除了函数,Windows 还事后定义了很多数据类型(应用C语言的 typedef 关键字定义)。狭义上来说,这些数据类型也是 API 的一部分。API 屏蔽了很多细节,大大简化了程序员的工作,这就是操作系统的威力,岂但让普通用户使用方便,也让程序员如释重负。在Windows上运行的程序(包含MS-DOS程序),实质上都是通过调用Windows API来实现性能的,包含QQ、360、VC6.0等,别看这些团队牛,也不可能从底层做起,那几乎不可设想。C语言也一样,也是调用Windows API,fopen() 函数就是通过调用 CreateFile() 函数实现的。CreateFile() 是Windows API中的一个函数,能够用来关上或创立文件。通常所说的 SDK 编程就是间接调用API 函数进行编程。SDK 是 Software Development Kit 的缩写,即软件开发工具包。Windows API 函数成千上万,具体理解每一个函数的用法是不可能的,也是齐全没有必要的。只需晓得哪些性能由哪些API 函数提供就行了,等应用它们时再去查阅帮助文件。带界面的程序的业余称说是GUI程序。GUI 是 Graphical User 域名交易Interface 的简写,即图形用户界面。本教程将教你应用 Windows API 来编写GUI程序,编程语言为C语言。 C语言学来干什么? 你或者学C语言很久了,没什么感觉,可能学python都能够抓取网站的数据了,C语言还是默默无声。 不要放弃,明天咱们介绍了windows编程,windows编程就是使用C语言,咱们的底层零碎C语言的卓越作品。 那些特地牛的软件能够没有C语言吗?请记住:C语言永不过期!

January 26, 2021 · 1 min · jiezi

关于c:有意思的C语言运算符

在C语言中,运算符用于执行程序代码运算,会针对两个或者两个以上操作数进行运算。比方:5 - 2,它的操作数是 5 和 2,而运算符则是 “-”。常见的运算符可大抵分为 4 种类型:算术运算符、关系运算符、赋值运算符和逻辑运算符;它优先级从低到高的程序为: 赋值运算符 < 逻辑运算符 < 关系运算符 < 算术运算符;上面对这4种类型一一解说。 1、赋值运算符 赋值运算符可分为简略赋值、复合算术赋值和复合位运算赋值。 1、1 简略运算符只有一个 “=”,它的用法可用如下例子示意: int n = 2;1、2 复合算术赋值运算符有 5 个,别离为 “+=”, “-=”, “*=”, “/=”, “%=”,它们的用法可用如下例子示意: int j = 1;j += 2; //等同于 j = j + 2,示意j的值加 2 后再赋值给jj -= 2; //等同于 j = j - 2,示意j的值减 2 后再赋值给jj *= 2; //等同于 j = j * 2,示意j的值乘以 2 后再赋值给jj /= 2; //等同于 j = j / 2,示意j的值除以 2 后再赋值给jj %= 2; //等同于 j = j % 2,示意j的值除以 2 后再赋值给j1、3 复合位运算赋值运算符有 5 个,参加运算的量,按二进制位进行运算,别离是 “&=”, “|=”, “^=”, “>>=”, “<<=”,它们的用法可用如下例子示意: ...

January 26, 2021 · 2 min · jiezi

关于c:struct和typedef-struct区别

1.构造体的定义: 容许用户本人建设由不同类型数据组成的组合型的数据结构,它称为构造体(实际上应称为 构造体类型)。 2.上面以一个构造体实例来阐明一下struct的用法: struct os_tcb{ OS_STK    *OSTCBStkPtr; OS_STK    *OSTCBStkBottom; INT32U      OSTCBStkSize; INT16U      OSTCBOpt; INT16U      OSTCBId; };Sturct 是申明构造体类型时所必须应用的关键字,不能省略。os_tcb 是构造体名花括号内 是该构造体所包含的子项,称为构造体成员。后面只是建设了一个构造体类型,它相当于一个模型,并没有定义变量,其中并无具体数据,系统对之也不调配存储单元。为了能在程序中应用构造体类型的数据,该当定义构造体类型的变量,并在其中寄存具体的数据。 能够采取以下3中形式定义构造体类型变量。 (1)先申明构造体类型,再定义该类型的变量定义下面的构造体类型后struct os_tcb    OS_TCB; // 在定义了构造体变量后,零碎会为之分配内存单元(2)在申明类型的同时定义变量struct os_tcb{ OS_STK    *OSTCBStkPtr; OS_STK    *OSTCBStkBottom; INT32U      OSTCBStkSize; INT16U      OSTCBOpt; INT16U      OSTCBId; } OS_TCB;     // OS_TCB 是一个构造体变量(3)不指定类型名而间接定义构造体类型变量struct { OS_STK    *OSTCBStkPtr; OS_STK    *OSTCBStkBottom; INT32U      OSTCBStkSize; INT16U      OSTCBOpt; INT16U      OSTCBId; } OS_TCB;     // OS_TCB 是一个构造体变量指定了一个无构造体名的构造体类型,显然不能再以此构造体类型去定义其余变量。 注:构造体类型与构造体变量是不同的概念,不要混同。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取后运算。在编译时,对类型不调配空间,只对变量调配空间。 3.上面再以此构造体实例来阐明一下typedef struct的用法: (次要参考技术博客:http://dzdesigned80.blog.163....) 阅读程序发现两种用 typedef struct 定义构造体的办法 第一种:typedef struct os_tcb{ OS_STK    *OSTCBStkPtr; OS_STK    *OSTCBStkBottom; INT32U      OSTCBStkSize; INT16U      OSTCBOpt; INT16U      OSTCBId; }OS_TCB;第二种:typedef struct { OS_STK    *OSTCBStkPtr; OS_STK    *OSTCBStkBottom; INT32U      OSTCBStkSize; INT16U      OSTCBOpt; INT16U      OSTCBId; }OS_TCB;能够看出,下面的区别在于 typedef struct 前面一个跟了标识符,另外一个没有跟标识符,这两个有什么区别呢?这里的os_tcb代表什么?OS_TCB的意义又是什么? 要搞清楚下面的问题,要从两方面动手。 第一 typedef的用法是什么? typedef是在根本类型的根底上定义类型的同义字。留神typedef并不产生新的类型。例如 typedef int exam_score;这里的exam_score 就是一种根本类型,它的意义等同于 int,那么即能够用它来定义整型变量,例如:exam_score  LIMING。 ...

January 26, 2021 · 1 min · jiezi

关于c:C语言-一个简单的C程序-c语言学习计划第2天

一个简略的C程序所蕴含的内容1.头文件艰深的了解来说,相似于将stdio.h的文件中所有的内容复制粘贴到以后文件中 #include <stdio.h> //蕴含另一个文件2.正文//不同的正文格调/* 不同的正文格调 */3.main()函数c程序的根本模块,c程序必定会从main()函数开始执行 int main(void) //一个简略的C程序//int是返回“整数”的数据类型,返回给操作系统//void示意不须要传入函数任何信息4.花括号记录了函数体的开始和完结的地位 //函数开始{5.申明int num; //申明变量num//无效的标识符只能包含小写字母,大写字母,数字和下划线,数字不能作为结尾int num,feet//多条申明6.赋值num = 1;//须要先做申明才可能赋值,等于号为赋值号并不是比照左右是否相等,这里的等于号代表了将左边的数据赋值到右边的变量中。7.输入函数c语言中会有很多的输入函数,printf是最常见的一个,此函数在括号内接管到数据,从而将其输入到屏幕中 printf("I am a simple"); printf("computer.\n"); printf("My favorite number is %d because it is first.\n",num); printf("press Enter to quit the game..."); getchar();8.return函数在程序的最初返回出0 return 0;}//函数完结9.函数申明#include<stdio.h>void secondFunc(void)int main(void){ printf("引入另一个函数,他在这里\n"); secondFunc();}void secondFunc(void){ printf("我在这里,我叫secondFunc");}总结:一个C程序的构造#include <stdio.h> int main(void)//函数头//函数体{ //申明 //语句 return 0;}//大部分语句以分号结尾顺便记录一下g++避免中文呈现乱码的状况间接编译的办法, g++ -fexec-charset=GBK anotherFunc.c -o test

January 25, 2021 · 1 min · jiezi

关于c:Linux学习系统数据文件和信息时间和日期函数口令文件

简介:这节咱们将简略介绍一下Linux零碎中一些数据据。(在这里、咱们只简略介绍几个例子) 1:passwd数据文件和组文件。 a:首先咱们在这里介绍passwd数据文件,此数据文件寄存在/etc/passwd文件目录中。该文件中的数据可能有如下各项的内容。 注:因为不同的零碎,该密钥文件中显示的条目可能不一样。在这里我介绍一个非凡的例子:就是咱们如何组织用于登录,这里我列举几种办法。1>:将登录的shell换成/bin/false。2>:将登录的shell换成/bin/nologin。(能够容许该用户应用ftp等服务)3>:应用命令usermod -s /sbin/nologin 。接下来、咱们介绍一些无关密钥文件的一些函数。struct passwd *getpwuid(uid_t uid);struct passwd* getpwnam(const char* name);这两个函数别离依据用户的ID和名字获取无关用户的一些信息,具体应用办法请参考man手册、这里就不一一赘述了。接下来同样,咱们介绍几个函数对于查看整个口令文件。struct passwd* getpwent();void setpwent();void endpwent();第一个函数是用来获取口令文件的下一项,它返回一个填充好的passwd构造的指针,第二个函数将文件地位从新置为文件开始的地位。第三个函数是开释文件所是哟个的资源。2:零碎标识,获取零碎的一些信息。 函数原型:int uname(struct utsname *name);此函数获取零碎的一些信息。在终端上咱们能够应用uname命令获取一些信息。3:接下来、咱们解说工夫相干的函数。 这里次要介绍一下工夫和日期相干的函数。time_t time(time_t *calptr);//返回与工夫和日期,如果参数不为空的环,将工夫和日期保留到参数指向的内存空间中。int gettimeofday(struct timeval* tp, void* tzp);//返回更加准确的工夫,他返回的是程序执行的工夫。同时、咱们在取得这种以秒数无关的工夫、咱们也须要将这种工夫转换成年月日等各种模式的工夫。同时struct tm构造体以年月日时分秒示意工夫,咱们也能够用这个工夫转换成其它的工夫。如下图所示,各种工夫格局的转换。 在这里,咱们将所有的函数原型列出来。

January 24, 2021 · 1 min · jiezi

关于c:C语言-部署开发环境-c语言学习计划第一天

window10用MingW搭建C语言开发环境部署环境:Window10工具:mingw下载地址:https://mingw.osdn.io/ 1.装置MingW下载实现后失去mingw-get-setup.exe文件 双击关上进入装置界面 装置配置 装置实现后进入界面 Basic setup-> 右键抉择 -> 将所有的都勾选为Mark for installation点击左上角的Installation -> 点击apply changes -> 点击apply 2.配置环境变量将mingW的bin文件目录增加到环境变量中命令行验证环境是否胜利,能够尝试上面的命令: gcc --versiong++ --versiongdb --version装置胜利!!!

January 23, 2021 · 1 min · jiezi

关于c:14最长公共前缀LeetCodeC语言

办法一、横向遍历法// 横向遍历法#include <stdio.h>#include <string.h>#include <stdlib.h>int getCommonPrefix(char *prev, char *next) { int len = strlen(prev) < strlen(next) ? strlen(prev) : strlen(next); int k = 0; while (k < len) { if (prev[k] != next[k]) { return k; } k++; } return k;}char *longestCommonPrefix(char **strs, int strsSize) { if (strsSize <= 0) { return ""; } int commonPrefixLen = strlen(strs[0]); char *commonPrefix = (char *)malloc((commonPrefixLen + 1) * sizeof(char)); strncpy(commonPrefix, *strs, commonPrefixLen); // 给字符串结尾加上空字符,否则会数组拜访出错,导致堆溢出 // 这个问题找了良久,最初发现strncpy只是复制字符,并不会主动在字符串结尾增加空字符 *(commonPrefix + commonPrefixLen) = '\0'; for (int i = 1; i < strsSize; i++) { int len = getCommonPrefix(commonPrefix, *(strs + i)); // 将字符串初始化为全空,尔后复制字符不必放心结尾增加空字符的问题 memset(commonPrefix, '\0', commonPrefixLen + 1); if (len <= 0) { return commonPrefix; } strncpy(commonPrefix, *(strs + i), len); } return commonPrefix;}int main(void) { char *strs[] = {"flower", "flow", "flight"}; printf("%s\n", longestCommonPrefix(strs, 3));} ...

January 22, 2021 · 2 min · jiezi

关于c:c-Task

1、Task的劣势ThreadPool相比Thread来说具备了很多劣势,然而ThreadPool却又存在一些应用上的不不便。比方: ◆ ThreadPool不反对线程的勾销、实现、失败告诉等交互性操作; ◆ ThreadPool不反对线程执行的先后秩序; 以往,如果开发者要实现上述性能,须要实现很多额定的工作,当初,FCL中提供了一个性能更弱小的概念:Task。Task在线程池的根底上进行了优化,并提供了更多的API。在FCL4.0中,如果咱们要编写多线程程序,Task显然曾经优于传统的形式。 以下是一个简略的工作示例: using System;using System.Threading;using System.Threading.Tasks;namespace ConsoleApp1{ class Program { static void Main(string[] args) { Task t = new Task(() => { Console.WriteLine("工作开始工作……"); //模仿工作过程 Thread.Sleep(5000); }); t.Start(); t.ContinueWith((task) => { Console.WriteLine("工作实现,实现时候的状态为:"); Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); }); Console.ReadKey(); } }}2、Task的用法2.1、创立工作(一)无返回值的形式 形式1: var t1 = new Task(() => TaskMethod("Task 1")); t1.Start(); Task.WaitAll(t1);//期待所有工作完结 注:工作的状态: Start之前为:Created Start之后为:WaitingToRun 形式2: Task.Run(() => TaskMethod("Task 2"));形式3: Task.Factory.StartNew(() => TaskMethod("Task 3")); 间接异步的办法 //或者 var t3=Task.Factory.StartNew(() => TaskMethod("Task 3")); Task.WaitAll(t3);//期待所有工作完结 //工作的状态: Start之前为:Running Start之后为:Runningusing System;using System.Threading;using System.Threading.Tasks;namespace ConsoleApp1{ class Program { static void Main(string[] args) { var t1 = new Task(() => TaskMethod("Task 1")); var t2 = new Task(() => TaskMethod("Task 2")); t2.Start(); t1.Start(); Task.WaitAll(t1, t2); Task.Run(() => TaskMethod("Task 3")); Task.Factory.StartNew(() => TaskMethod("Task 4")); //标记为长时间运行工作,则工作不会应用线程池,而在独自的线程中运行。 Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning); #region 惯例的应用形式 Console.WriteLine("主线程执行业务解决."); //创立工作 Task task = new Task(() => { Console.WriteLine("应用System.Threading.Tasks.Task执行异步操作."); for (int i = 0; i < 10; i++) { Console.WriteLine(i); } }); //启动工作,并安顿到当前任务队列线程中执行工作(System.Threading.Tasks.TaskScheduler) task.Start(); Console.WriteLine("主线程执行其余解决"); task.Wait(); #endregion Thread.Sleep(TimeSpan.FromSeconds(1)); Console.ReadLine(); } static void TaskMethod(string name) { Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); } }}async/await的实现形式: ...

January 21, 2021 · 9 min · jiezi

关于SegmentFault:13-罗马数字转整数leetcodeC语言

思路: 遍历字符串,比拟以后罗马字符与后一个罗马字符对应的十进制数字的大小。如果大于,则将该数字累加到num,如果小于,则求出他们的差值,并将其累加到num。遍历完结,将num返回即可。 第一版、没有应用哈希表,应用数组模仿 #include <stdio.h>#include <stdlib.h>#include <string.h>int getIndex(char *str, char c) { int k = 0; while (str[k] != '\0') { if (str[k] == c) { return k; } k++; } return -1;}int romanToInt(char *str){ int numArr[] = {1000, 500, 100, 50, 10, 5, 1}; char roman[] = {'M', 'D', 'C', 'L', 'X', 'V', 'I'}; int k = 0; int num = 0; while (str[k] != '\0') { int indexCurt = getIndex(roman, str[k]); // 判断下一个字符是否是空字符,避免字符串越界 // 如果是空字符,就将其下标与前一个字符的下标置为一样 int indexNext = str[k + 1] != '\0' ? getIndex(roman, str[k + 1]) : indexCurt; if (numArr[indexCurt] < numArr[indexNext]) { num += numArr[indexNext] - numArr[indexCurt]; k += 2; } else { num += numArr[indexCurt]; k++; } } return num;}int main(void) { char *str = "MCMXCIV"; printf("%d\n", romanToInt(str));} ...

January 20, 2021 · 1 min · jiezi

关于c:Linux学习bash的配置终端颜色显示

1:bash是咱们在登录零碎是执行的shell程序,咱们适当配置属于本人的bash能够不便咱们和Linux零碎的交互。是我本人更改的bash配置。 通常的状况下、在咱们的零碎会保留备份bash配置,在/etc/skel/.bashrc这个地位。我便借助这个地位保留的备份更改了本人的终端色彩显示。如下图所示,是我很喜爱的显示。如果各位须要本人的显示内容,齐全能够本人配置属于本人的bash。上面我将我的配置文件粘贴到这里。 在这里我重点说一下终端色彩显示要批改linux终端命令行色彩,咱们须要用到PS1,PS1是Linux终端用户的一个环境变量,用来阐明命令行提示符的设置。在终端输出命令:#set,即可在输入中找到对于PS1的定义如下: PS1的定义中个罕用的参数的含意如下: \d :#代表日期,格局为weekday month date,例如:"Mon Aug 1" \H :#残缺的主机名称 \h :#仅取主机的第一个名字 \t :#显示工夫为24小时格局,如:HH:MM:SS \T :#显示工夫为12小时格局 \A :#显示工夫为24小时格局:HH:MM \u :#以后用户的账号名称 \v :#BASH的版本信息 \w :#残缺的工作目录名称 \W :#利用basename获得工作目录名称,所以只会列出最初一个目录 \# :#下达的第几个命令 \$ :#提醒字符,如果是root时,提示符为:# ,普通用户则为:$ 由此,咱们可知linux默认的命令行提示信息为:[以后用户的账号名称@主机的第一个名字 工作目录的最初一项]# 2.色彩的设置 F B 30 40 彩色 31 41 红色 32 42 绿色 33 43 黄色 34 44 蓝色 35 45 紫红色 36 46 青蓝色 37 47 红色 依据色彩表,套用入字符色彩设置格局中,就能够对linux终端命令行色彩进行个性化设置了。 3.批改.bashrc文件 通过下面的设置只能扭转以后终端的命令行格局,敞开这个终端,在从新关上的一个终端中命令行格局又会复原到默认的模式。想要永久性的扭转终端命令行格局,须要批改.bashrc文件。 在.bashrc文件中对PS1的内容进行更改就能够做到终端显示了 PS1='${debian_chroot:+($debian_chroot)}\[\033[36;40m\]\u \[\033[1;31;40m\]\W \[\033[1;35;40m\]\$ > \[\033[1;0;0m\]:这是我的配置,心愿你可能喜爱。 同时,这个文件默认在~/.bashrc,关上进行复制粘贴就能够。最初将一张残缺的终端图像贴到这里。心愿你可能喜爱我这份bash配置。 ...

January 18, 2021 · 3 min · jiezi

关于c:11-盛最多水的容器LeetCode-C语言

双指针法:int maxArea(int* height, int heightSize){ int max = 0; for (int i = 0, j = heightSize - 1; i < j; ) { int min = height[i] <= height[j] ? i : j; if (height[min] * (j - i) > max) { max = height[min] * (j - i); } if (min == i) { i++; } else if (min == j) { j--; } } return max;}

January 18, 2021 · 1 min · jiezi

关于c:flutter使用C代码库android篇

1) 和原生Android开发的NDK技术一样,编译出.so动静库(下文称之为libgalaxy.so,蕴含简略的native_add函数int32_t native_add(int32_t x, int32_t y) {return x + y;})。并把动静库拷贝到flutter我的项目的android/app/src/main/jniLibs子目录下: 要留神因为flutter应用的是比拟新的android技术,所以NDK编译套件也是须要比拟新的,因为libflutter.so提供的平台版本都是比拟新的,如果NDK编译进去的libgalaxy.so动静库只蕴含较老的平台版本,会导致libflutter.so反对的平台libgalaxy.so不反对,导致运行时找不到定义的C函数。 2) 编辑android/app目录下的build.gradle,减少以下内容: release版本要管制包的大小,所以,限定只须要提供armeabi-v7a和arm64-v8a,如果不做此限定,打包时,会把libgalaxy.so的x86和x86_64也打进release版本!这就没有必要了。当前甚至能够只打包arm64-v8a版本。(如果上一步jniLibs目录下只提供了arm64-v8a版本,这一个就不必做了) debug局部,我认为就没有必要做abiFilters限定了,由flutter编译器本人决定吧,多打几个版本进去也无所谓了。 3) main.dart里的内容: main函数下面的局部,能够独自拎进去放在一个dart文件中。 这样就OK了。

January 18, 2021 · 1 min · jiezi

关于c:栈的解读

前沿栈广泛应用在各种软件系统中,所以这块的知识点咱们也要好好把握起来。 定义栈(stack)是限定仅在表尾进行插入或删除操作的线性表。 简略的来说就是一种能够实现"先进后出" 的存储构造 栈相似于箱子 分类栈个别分为两类 动态栈 (相似于用数组实现)动静栈 (相似于用链表实现)算法这边咱们来看看 栈的出栈 和入栈的伪算法 先来看看入栈 //伪代码void push(struct Stack *pS, int val){ struct Node * pNew = (struct Node *)malloc(sizeof(struct Node)); pNew->data = val; pNew->pNext = pS->pTop; pS->pTop = pNew; return;}首先初始化的时候 pTop 和 pBottom 都指向空而后咱们创立一个节点 pNew, 让他指向下一个节点的指针域。这里咱们要留神,这里应该指向的是 pTop所指向的指针域最初把 pNew 赋值给 pTop, 实现pTop 指向新的节点。再来看看出栈 写法 //伪代码bool pop(struct Stack *pS, int * pVal){ struct Node * q = pS->pTop; *pVal = q->data; pS->pTop = q->pNext; free(q); q = NULL; return true; }出栈算法看过来时简略的,但有点要特地留神,就是要记得开释内存,防止野指针所以咱们定义一个 指针变量 q 来做长期存储应用。最初咱们在free()开释内存致谢感激你看完这篇文章,有什么不对的中央欢送指出,谢谢???? ...

January 17, 2021 · 1 min · jiezi

关于c:Linux学习文件IO不带缓冲区原子操作概念

在后面的文章咱们介绍过不带缓冲区的IO,这节咱们次要介绍不带缓冲区的IO相干内容。原子操作对于文件共享是非常重要的,因而咱们将介绍一些原子操作相干概念。 1:文件描述符 对于内核而言,所有关上的文件都通过文件描述符援用。当咱们关上或者创立一个文件的时候,都会返回一个非负的整数,咱们关上和创立文件都用这个非负的整数。同时,通常在咱们执行一个过程的时候,都会默认关上三个文件描述符,他们别离是规范输出(0:STDIN_FILENO)、输入(1:STDOUT_FILENO)、谬误(2:STDERR_FILENO)相关联的。他们的宏定义在unistd.h文件中。文件描述符下限是OPEN_MAX。2:open函数介绍 函数原型:int open(const char* pathname, int flag); int open(const char* pathname, int flag, mode_t mode);pathname:是关上文件的路径名。flag:是关上文件的一些选项。mode:只有咱们须要创立文件的时候,才会将指定mode的模式。O_RDONLY:只以读的形式关上文件。O_WRONLY:只一写的形式关上文件。O_EXCL:测试文件受否存在,如果不存在则创立文件。(注:如果同时指定O_CREAT并且文件曾经存在则出错)O_APPEND:每次写入都追加到文件开端。O_CREAT:如果文件不存在,则创立文件。应用这个选项的时候,须要第三个参数。 mode:权限位,也就是咱们应用ls -l显示的各种读写和执行权限的位。 S_IRWXU:用户有读写执行权限。 S_IRUSR:用户有读权限。 S_IWUSR:用户有写权限。 S_IXUSR:用户有执行权限。 S_IRWXG:同组用户有读写执行权限。 S_IRGRP:同组用户有读权限。 S_IWGRP:同组用户有写权限。 S_IXGRP:同组用户有执行权限。 S_IRWXO:其余用户有读写执行权限。 S_IROTH:其余用户有读权限。 S_IWOTH:其余用户有写权限。 S_IXOTH:其余用户有执行权限。O_TRUNC:如果文件存在的话,并且咱们只为读写关上文件的话,那么文件长度将会截断为0。也就是文件长度会变为0,相当于咱们将文件内容全副删去再从新写入。O_NOBLOCK:指定文件为非阻塞IO,失常状况下在咱们写入文件的时候,咱们都期待写入实现之后再返回。如果指定此flag,那么将间接返回,而不论是否写入实现。O_SYNC:每一次写操作实现之前都会更新文件的属性。O_RSYNC:期待任何对文件同一部分未决写操作实现。(这个标记和上面一个标记不是很了解,如果了解的话,请在评论区探讨。最好是能有一个理论的例子)。O_DSYNC:每次写期待物理操作实现。仅当文件属性根棍以反映文件数据变动时,此标记才会影响文件属性。open函数返回的文件描述符肯定是以后可用的最小文件描述符。文件名和路径名的截断:这个概念我简略介绍一下,就是咱们输出关上文件的路径名的时候,因为咱们输出的太长了,咱们无奈辨认那么长的路径名,就会保留其中的一部分。这样就无奈辨认出具体的关上的文件了(咱们保留这部分路径名可能与其余文件的路径名重名)。咱们这里能够应用pathconf获取一些路径名长度,我发现我的机器长度限度是4096。creat:创立文件函数原型:create(const char *pahtname, mode_t mode);此函数和open函数是有雷同的局部,不做理解。close:敞开关上的文件描述符(就是咱们关上一个文件肯定会用到一些资源去治理这个关上的文件,那么这里敞开就是开释这些被栈用的资源)函数原型:int close(int filedes);lseek函数:更改以后文件的偏移量,也就是咱们从文件哪里开始读写文件。 当咱们关上一个文件或者创立一个新的文件,文件偏移量默认为0。也就是说从文件开始地位读写文件。同时咱们也能够用lseek函数将以后文件偏移量定位到文件指定的置为。 函数原型:lseek(int filedes, off_t offset, int whence); lseek_64(int filedes, off64_t offset, int whence); filedes:是文件描述符。 offset:是偏移的大小。(能够为正<减少文件偏移>,也能够为负<缩小文件偏移>) 对于off_t是否够用的问题,我在这里简略解说一下。 然而咱们看函数原型的时候,有32位和64位两种文件偏移量。如果是32位那么文件偏移量最大时2的32次方减一,64位也是如此。 32位的文件偏移量最大时2TB(2^31-1字节),64位文件最大偏移量2^33TB,应该足够应用了。如果是不同的平台会用不同的大小,这里只列举了32位和64位。具体实现还是靠硬件, 这里就不逐个列举了。 whence:是指定地位开始进行便宜。 SEEK_SET:即从文件结尾为只进行便宜。 SEEK_CUR:从以后文件地位进行便宜。 SEEK_END:从文件开端地位开始便宜。同时lseek也能够测试文件是否设置偏移量,如果文件描述符援用的是一个管道、套接字,则lseek返回-1.read函数:从文件中读取指定字节数目标数据。函数原型:ssize_t read(int filedes, void *buf, size_t nbytes);filedes:文件描述符。buf:读取到字节寄存的缓冲区。nbytes:读取多少个字节。返回值:理论读取到的字节数。理论读取到字节数可能小于咱们要求读取的字节数。(eg,以后文件地位到文件开端还有三十个可读的字节,然而咱们要求读取200个字节,这就返回30个字节。然而在下次读取的时候,返回30个字节。还有从网络中读取数据,有些时候可能因为网络提早读取小于咱们读取到的字节数)。write函数:向文件中写入指定字节数的数据。函数原型:ssize_t write(int filedes, const void* buf, size_t nbytes);同上,buf是写入字节存储地位。nbytes是写入的字节数。返回值是理论写入的字节数。同时,如果咱们指定了O_APPEND,每次咱们写入之前都将文件偏移量设置为文件开端的地位。文件共享:在介绍文件共享之前,咱们先介绍一下内核是应用什么构造示意关上的文件的。内核应用三种数据结构示意关上的文件。(1)每个过程都有一个记录项,记录项抱哈一张关上的文件描述符表,每个文件描述符占用一项。咱们在过程中应用一个整型记录这个文件项的地位。 每个文件描述符相关联的有两局部:文件标记,指向文件表的指针。(2)对于关上的文件内核维持一张文件表。 文件表中有文件状态标记、以后文件的偏移量和v节点指针。(3)每个关上的文件都有一个v节点。 v点蕴含了文件类型和对该文件各种操作的函数指针。同时还有i节点信息,以后文件的长度。对于这些内容理解即可。如下图所示,能够很好的了解这三者之间的关系。 ...

January 17, 2021 · 1 min · jiezi

关于c:Linux学习unix的标准化的实现Linux中各种限制数据类型各种标准化头文件介绍

作为Linux的前身,unix标准化是非常重要的。我在这里挑几个重要的点阐明。 1:Linux中各种限度。 Linux中限度有编译时限度和运行时限度,另外有一些限度是因为咱们的实现不同而不同,因而咱们须要调用对应的函数获取对应的值不同。(eg:编译时限度:整形最大值是什么。运行时限度:文件名能够有多少个字符)对应的咱们能够调用对应的内容获取其限度值: (1)编译时限度 --->头文件。 (2)不是与文件或目录相干的运行时限度--->sysconf函数 (3)与文件或目录相干的运行时限度---->pathconf或fpathconf 在ISO C中定义的限度都是编译时限度,在Linux limits.h文件中定义了C规范限度。在float.h中定义了对于浮点数的各种限度。如下图时ISO C在limint.h头文件中的各种限度值。 POSIX定义了许多零碎实现的限度,这些限度被分成了5类。 (1)不变的最小值,下图中的19个常量。 (2)不变值:SSIZE_MAX。 (3)运行时能够减少的值: (4)运行时不变的值。 (5)路径名可变值。 等等,这些我就不做过多介绍了,因为介绍这么多恐怕咱们也记不住。在日后shi理论应用过程中逐个介绍。 实例:上面获取两个限度的值,#include <unistd.h>#include <limit.h>#include <stdio.h>int main(){ printf("%lu\n", sysconf(_SC_LINE_MAX)); printf("%lu\n", pathconf(".", _PC_LINK_MAX));}来个小总结:对于限度获取这块,咱们就讲这么多。咱们次要是应用这些限度的值,获取办法有两种,头文件、库函数。2:这部分咱们次要将定义的各种数据类型,次要介绍根本的数据类型。如下图所示这些数据类型是我在一本书上截取下来的,日后如果应用到这种数据类型的时候会逐个的具体介绍。 还有一些其余的数据类型:int double long float char short 等,这里就不在这里逐个介绍了。3:我介绍一下ISO C的各种头文件,至于其余头文件就不逐个列出了。 assert.h:验证程序某些判断是否正确。如下是一个试验。输入后果 limit.h各种限度。time.h获取工夫的各种函数。string.h:字符串操作函数集。stdlib.h:实用程序函数。signal.h:信号相干内容。wchar.h:宽字符相干的内容。至于其余的我就间接列出即可,作为理解 本文由博客群发一文多发等经营工具平台 OpenWrite 公布

January 16, 2021 · 1 min · jiezi

关于c:Linux学习Linux基础简介

这一节,咱们将简略介绍一下Linux。1:Linux同windows是一样的,都是操作系统的一种,都给咱们提供一个平台去开发或者执行某些程序。能够说Linux零碎也是软件的一种,不要将他想的太过于神秘。(注:通常咱们所说的什么零碎都是内核+软件形成的) 2:接下来咱们简略介绍一下Linux的倒退历史 Linux是一套自在加凋谢源代码的类Unix操作系统,诞生于1991年10月5日(第一次正式向外颁布),由芬兰学生Linus Torvalds和起初陆续退出的泛滥爱好者共同开发实现。 Linux是一个基于POSIX和Unix的多用户、多任务、反对多线程和多CPU的操作系统。它能运行次要的Unix工具软件、应用程序和网络协议,可反对32位和64位硬件。Linux继承了Unix以网络为外围的设计思维,是一个性能稳固的多用户网络操作系统。 Linux存在着许多不同的版本,但它们都应用了Linux内核。Linux可装置在各种计算机硬件设施中,比方:手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。 如果想理解具体的信息请本人查找一下3:咱们在这里说一下Linux的系统结构,以便于从大方向理解Linux的零碎。 a:之前说了,Linux零碎能够作为一种软件,提供程序运行的化境,咱们将之成为内核。学过单片机的人都是到,咱们最底层是有很简略的各种小程序形成,例如咱们用汇编语言写一 些小的程序去管制硬件的某些行为(也就是高下点平,这里不做过多介绍)。而咱们的Linux零碎就是管制咱们硬件所有的资源,对这些资源进行批准的调配性能,咱们的软件是可能 通过一些调用去应用这些资源。 b:在Linux中,咱们写一些函数须要调用内核来实现,这里咱们将调用内核实现这中操作叫做零碎调用。 三种形式进行零碎调用:shell、 库函数(全副建设在零碎给的接口上)、 间接调用。(如下图所示)。 4:在这里咱们介绍一下对于登录Linux进行的操作。 用户在登录的时候须要先后输出登录名和登录的用户明码,而这个用户名和用户明码通常是存在/etc/passwd文件下,这个文件每一行都是一个用户,其中登录项由7个冒号分隔开来。 他们别离是登录名、加密口令、用户数值ID、用户数值组ID、正文字段、其实目录和用户应用的SHELL程序。后面咱们说shell能够用来和Linux进行交互,因而咱们能够配置本人的shell 程序,这样更改不便咱们的应用。通常默认的shell程序是bash,我喜爱用zsh程序,无关zsh的配置请参考如下连贯https://blog.csdn.net/towedjfiowaj/article/details/112691257 5:接下来咱们简略介绍一下文件和目录 在Linux中或者unix中能够成为所有皆文件,因而理解Linux中的文件和目录是非常有必要的。 在windows中,咱们能够将磁盘划分为c盘、d盘等。然而在Linux中、咱们只有一个磁盘,而这个磁盘是从/(根)目录开始的,其余的都是以各种形式进行挂载。 咱们这里说介绍文件和目录,其实每个目录能够了解为蕴含多个目录项的文件,如果不信的话能够应用vim dirname去关上一个目录,你就发下外面存储了许多目录项/文件。 在Linux中文件中还蕴含文件的属性信息,文件属性有文件大小、文件类型、文件所有者、文件权限、文件的批改工夫等。这些属性咱们stat或者fstat进行获取。 在这里我阐明一下文件类型(在执行ls -l时候如下图1就能够辨别文件各种信息了,其中文件类型前面九个字符别离是所有者:所属组:其他人对该文件的读写和执行权限): 文件类型:一般文件(-)、目录(d)、管道(p)、链接文件(l)字符设施文件(c)、 块设施文件(b)、 套接字(s)。 文件名:在Linux呈现文件名的字符除了/和null,前者用于宰割目录、后者用于完结一个目录。当然在失常的状况下,尽量只应用印刷字符集,免得引起不必要的麻烦。 同时咱们在创立目录的时候,会主动创立两个.和..目录,这两个目录别离代表当前目录和上一级目录。 路径名:门路分为绝对路径和相对路径。绝对路径是从根目录开始的,例子:/etc/passwd。绝对目录是从当前目录开始的目录./../passwd。 在这里介绍一下如何失去路径名:如上面一个小程序就是读取指定目录下的所有内容。 #include <dirent.h> #inlcude <stdio.h> int main(int argc, char* argv[]) { DIR *dp; struct dirent *dirp; if(argc != 2) { return 0; } if((dp = opendir(argv[1])) == NULL) { return 0; } while((dirp = readdir(dp)) != NULL) { printf("%s\n", dirp->d_name); } } 对于这个程序只有两点须要了解两个其中两个函数就能够。 工作目录:每一个执行的过程都会一个工作目录。所说的相对路径都是绝对于当前工作目录而定的。 起始目录:也就是当咱们登录到零碎是坐在的目录。 5:接下来介绍一下Linux中无关的输出和输入。 ...

January 16, 2021 · 1 min · jiezi

关于c:Linux好用的vim超级配置zsh配置完整版

1:介绍vim vim是用于Linux下编译代码的工具,具备肯定的补全性能,然而与咱们配置的相比就要差商许多了。在这里、我首先介绍如何配置vimplus(是一款十分好用的vim配置)。之后我再介绍zsh的配置,zsh是一种shell脚本,是咱们和Linux交互的工具。 vimplus的装置只须要执行几个命令就能够,非常简单。 cd ~。 sudo git clone https://github.com/chxuan/vimplus.git ~/.vimplus. cd ~/.vimplus ./install.sh 这样,弱小的vim配置就装置实现了。在这里须要留神的是,如果你没有git这个工具,能够应用sudo apt install git 命令进行装置。如果你的apt源配置谬误,也就是在保障你能够失常连贯网络的状况下不可能下载这个工具,请关上这个链接[https://developer.aliyun.com/mirror/ubuntu?spm=a2c6h.13651102.0.0.3e221b11vU4mtw]() 这是一个阿里源的地址,点击就能够下载。好了对于vim的配置我这里就介绍这么多。 2:介绍zsh的相干配置。 在这里介绍我的zsh配置,之前说过了,zsh是咱们和Linux交互的命令工具。 首先、第一点须要确定的是你有没有zsh这个工具,如果你没有zsh这个工具请应用sudo apt install zsh进行装置。 第二点就是咱们的zsh配置了,咱们能够在本人的家目录下进行配置,首先咱们创立一个.zshrc这个文件,此文件是咱们这个用户登陆的时候zsh读取的配置文件。 最初一点,我将zsh配置连贯放到这里,间接应用下述命令装置即可。 git clone https://github.com/spicycode/ze-best-zsh-config.git 目录 如果下载下来的是压缩包,请解压,而后将其中的暗藏文件拷贝到家目录下,也就是~目录下。 最初我将我改过的.zshrc文件贴到这里,我改的文件是依据这个下载内容进行更改整合的,因为下载的货色不可能齐全应用。 之后在家目录创立.zshrc文件,将下述内容增加到.zshrc文件中,之后应用命令sourc .zshrc就能够失效了,如果还没有失效的话,请退出Linux从新登录。 if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi export PATH=bin:script:.bin:$PATH precmd() { if [[ -n "$TMUX" ]]; then tmux setenv "$(tmux display -p 'TMUX_PWD_#D')" "$PWD" fi } # 别名局部,这部分依据本人的爱好进行甚至 alias la='ls -a' alias lc='ls *.c' alias sl='ls' alias ll='ls -l' alias grepn='grep -nrE ./' alias findn='find ./ -name' alias rm='rm -i' alias cpr='cp -r' alias rmd='rm -r' alias v.z='vim ~/.zshrc' alias s.z='source ~/.zshrc' # color 局部内容 autoload colors; colors # The variables are wrapped in %{%}. This should be the case for every # variable that does not contain space. for COLOR in RED GREEN YELLOW BLUE MAGENTA CYAN BLACK WHITE; do eval PR_$COLOR='%{$fg_no_bold[${(L)COLOR}]%}' eval PR_BOLD_$COLOR='%{$fg_bold[${(L)COLOR}]%}' done eval RESET='$reset_color' export PR_RED PR_GREEN PR_YELLOW PR_BLUE PR_WHITE PR_BLACK export PR_BOLD_RED PR_BOLD_GREEN PR_BOLD_YELLOW PR_BOLD_BLUE export PR_BOLD_WHITE PR_BOLD_BLACK # Clear LSCOLORS unset LSCOLORS export CLICOLOR=1 export LS_COLORS=exfxcxdxbxegedabagacad # 绑定某些按键 # To see the key combo you want to use just do: # cat > /dev/null # And press it bindkey "^K" kill-whole-line # ctrl-k bindkey "^R" history-incremental-search-backward # ctrl-r bindkey "^A" beginning-of-line # ctrl-a bindkey "^E" end-of-line # ctrl-e bindkey "[B" history-search-forward # down arrow bindkey "[A" history-search-backward # up arrow bindkey "^D" delete-char # ctrl-d bindkey "^F" forward-char # ctrl-f bindkey "^B" backward-char # ctrl-b bindkey -e # Default to standard emacs bindings, regardless of editor string # 定义某些全局变量 # Currently this path is appendend to dynamically when picking a ruby version export PATH=node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/local/share/npm/bin:~/.cabal/bin:~/.local/bin:$PATH export PATH=$PATH:/opt/boxen/homebrew/opt/go/libexec/bin # Setup terminal, and turn on colors export TERM=xterm-256color export CLICOLOR=1 export LSCOLORS=Gxfxcxdxbxegedabagacad # Enable color in grep export GREP_OPTIONS='--color=auto' export GREP_COLOR='3;33' # This resolves issues install the mysql, postgres, and other gems with native non universal binary extensions export ARCHFLAGS='-arch x86_64' export LESS='--ignore-case --raw-control-chars' export PAGER='most' export EDITOR='vim' export PYTHONPATH=/usr/local/lib/python2.6/site-packages # CTAGS Sorting in VIM/Emacs is better behaved with this in place export LC_COLLATE=C # GitHub token with no scope, used to get around API limits #xport HOMEBREW_GITHUB_API_TOKEN=$(cat ~/.gh_api_token) # setopt 某些选项设置 # ===== Basics # If you type foo, and it isn't a command, and it is a directory in your cdpath, go there setopt AUTO_CD # Allow comments even in interactive shells (especially for Muness) # setopt INTERACTIVE_COMMENTS # ===== History # Allow multiple terminal sessions to all append to one zsh command history setopt APPEND_HISTORY # Add comamnds as they are typed, don't wait until shell exit setopt INC_APPEND_HISTORY # Do not write events to history that are duplicates of previous events setopt HIST_IGNORE_DUPS # When searching history don't display results already cycled through twice setopt HIST_FIND_NO_DUPS # Remove extra blanks from each command line being added to history setopt HIST_REDUCE_BLANKS # Include more information about when the command was executed, etc setopt EXTENDED_HISTORY # ===== Completion # Allow completion from within a word/phrase setopt COMPLETE_IN_WORD # When completing from the middle of a word, move the cursor to the end of the word setopt ALWAYS_TO_END # ===== Prompt # Enable parameter expansion, command substitution, and arithmetic expansion in the prompt setopt PROMPT_SUBST unsetopt MENU_COMPLETE setopt AUTO_MENU # 历史设置 HISTSIZE=10000 SAVEHIST=10000 HISTFILE=~/.zsh_history bindkey '^R' zaw-history function git_prompt_info { local ref=$(=git symbolic-ref HEAD 2> /dev/null) local gitst="$(=git status 2> /dev/null)" if [[ -f .git/MERGE_HEAD ]]; then if [[ ${gitst} =~ "unmerged" ]]; then gitstatus=" %{$fg[red]%}unmerged%{$reset_color%}" else gitstatus=" %{$fg[green]%}merged%{$reset_color%}" fi elif [[ ${gitst} =~ "Changes to be committed" ]]; then gitstatus=" %{$fg[blue]%}!%{$reset_color%}" elif [[ ${gitst} =~ "use \"git add" ]]; then gitstatus=" %{$fg[red]%}!%{$reset_color%}" elif [[ -n `git checkout HEAD 2> /dev/null | grep ahead` ]]; then gitstatus=" %{$fg[yellow]%}*%{$reset_color%}" else gitstatus='' fi if [[ -n $ref ]]; then echo "%{$fg_bold[green]%}/${ref#refs/heads/}%{$reset_color%}$gitstatus" fi } PROMPT='%~%<< $(git_prompt_info)${PR_BOLD_WHITE}>%{${reset_color}%} ' precmd() { if [[ -n "$TMUX" ]]; then tmux setenv "$(tmux display -p 'TMUX_PWD_#D')" "$PWD" fi }本文由博客群发一文多发等经营工具平台 OpenWrite 公布

January 16, 2021 · 4 min · jiezi

关于c:dhcp10源码分析讲解dhcpd的源码流程

1:首先阐明dhcpd代码下载方式,请参考我这篇文章dhcp源码下载2:接下来咱们解析dhcp源码。(这里只是大体介绍dhcp源码) a:首先咱们来剖析各个目录中的文件。 如图1所示有如下的各种文件。 alloc.c文件时管制内存的操作,咱们在其余文件中应用申请和开释内存都是通过这个文件中的函数来实现。 confpars文件用于解析配置文件内容的文件。也就是说,当咱们读取配置文件时,咱们调用这个文件中的函数 来接写配置文件中的内容。 dhcpd.c文件时DHCP服务程序的主流程管制,咱们次要剖析这个文件中的源码。 options.c文件用于解析咱们的option的,能够通过man手册查看dhcpd的option。(eg:man dhcpd) socket.c是对套接字的封装,用于传递包的。 convert.c用于对一些类型的转换。 dhcpxlt.c同样用于对一些地址进行转换。 inet.c用于对网络地址进行转换。 packet.c用于对包的解决。 tables.c是一些表的治理(eg:dhcp硬件类型、option。咱们解析一些内容的时候回去这些表中进行查问)。 bpf.c:对于套解析一些ioctl设置(eg:设置关上的套接字传输速率)。 db.c:是对DHCP数据库的治理,也就是说将一些数据存储到文件中进行治理。 dispatch.c:这个文件设置。 memory.c:对dhcp用到的内存进行治理,例如咱们动静租任的IP print.c:打印一些信息。 tree.c conflex.c errwarn.c:谬误和正告信息 raw.c:包的发送 upf.c:同样也是包的解决。 图1 b:在这里咱们先剖析一下DHCP包的内容,如图所示DHCP包的内容。如图1所示。option字段如图2所示OP:报文的操作类型。分为申请报文和响应报文。1:申请报文,2:应答报文。即client送给server的封包,设为1,反之为2。申请报文: DHCP Discover、DHCP Request、DHCP Release、DHCP Inform和DHCP Decline。应答报文: DHCP Offer、DHCP ACK和DHCP NAK。Htype: DHCP客户端的MAC地址类型。 MAC地址类型其实是指明网络类型 ,Htype值为1时示意为最常见的以太网 MAC地址类型。Hlen: DHCP客户端的MAC地址 长度。以太网MAC地址长度为6个字节,即以太网时Hlen值为6。Hops:DHCP报文通过的DHCP中继的数目,默认为0。DHCP申请报文每通过一个DHCP中继,该字段就会减少1。没有通过DHCP中继时值为0。( 若数据包需通过router传送,每站加1,若在同一网内,为0。 )Xid:客户端通过DHCP Discover报文发动一次IP地址申请时抉择的随机数,相当于申请标识。用来标识一次IP地址申请过程。在一次申请中所有报文的Xid都是一样的。Secs:DHCP客户端从获取到IP地址或者续约过程开始到当初所耗费的工夫,以秒为单位。在没有取得IP地址前该字段始终为0。( DHCP客户端开始DHCP申请后所通过的工夫。目前尚未应用,固定为0。)Flags:标记位,只应用第0比特位,是播送应答标识位,用来标识DHCP服务器应答报文是采纳单播还是播送发送,0示意采纳单播发送形式,1示意采纳播送发送形式。其余位 尚未应用 。(即 从0-15bits,最左1bit为1时示意server将以播送形式传送封包给client。 )【留神】在客户端正式调配了IP地址之前的第一次IP地址申请过程中,所有DHCP报文都是以播送形式发送的,包含客户端发送的DHCP Discover和DHCP Request报文,以及DHCP服务器发送的DHCP Offer、DHCP ACK和DHCP NAK报文。当然,如果是由DHCP中继器转的报文,则都是以单播形式发送的。另外,IP地址续约、IP地址开释的相干报文都是采纳单播形式进行发送的。Ciaddr:DHCP客户端的IP地址。仅在DHCP服务器发送的ACK报文中显示,在其余报文中均显示0,因为在失去DHCP服务器确认前,DHCP客户端是还没有调配到IP地址的。只有客户端是Bound、Renew、Rebinding状态,并且能响应ARP申请时,能力被填充。Yiaddr:DHCP服务器调配给客户端的IP地址。仅在DHCP服务器发送的Offer和ACK报文中显示,其余报文中显示为0。Siaddr:下一个为DHCP客户端调配IP地址等信息的DHCP服务器IP地址。仅在DHCP Offer、DHCP ACK报文中显示,其余报文中显示为0。( 用于bootstrap过程中的IP地址)Giaddr:DHCP客户端发出请求报文后通过的第一个DHCP中继的IP地址。如果没有通过DHCP中继, 则显示为0。( 转发代理(网关)IP地址 )Chaddr:DHCP客户端的MAC地址。在每个报文中都会显示对应DHCP客户端的MAC地址。Sname:为DHCP客户端调配IP地址的DHCP服务器名称(DNS域名格局)。在Offer和ACK报文中显示发送报文的DHCP服务器名称,其余报文显示为0。File:DHCP服务器为DHCP客户端指定的启动配置文件名称及门路信息。仅在DHCP Offer报文中显示,其余报文中显示为空。Options: 可选项字段,长度可变,格局为"代码+长度+数据"。 c:接下来我这里介绍一下DHCP次要流程调用,如下图1所示 这里次要是指文件之间的调用,不做具体介绍。在接下来的局部会做具体介绍。(对于存储客户数据局部同样不做具体介绍,只是对主题流程做一些具体的介绍)。我当初这里列出这个文件中的函数: dhcpd.c#ifndef lintstatic char ocopyright[] ="$Id: dhcpd.c,v 1.36.2.1 1997/03/29 08:11:03 mellon Exp $ Copyright 1995, 1996 The Internet Software Consortium.";#endif//这部分能够不必管,因为这部分是对版权和一些音讯的定义。用到时权当字符串应用就能够了。static char copyright[] ="Copyright 1995, 1996 The Internet Software Consortium.";static char arr [] = "All rights reserved.";static char message [] = "Internet Software Consortium DHCPD $Name: V1-0-0 $";/***这里我阐明这个dhcpd.h头文件这个头文件定义了所有对于dhcp用到的函数,和一些公共的全局变量等***/#include "dhcpd.h"//这个函数次要是在解析参数的时候显示一些应用的配置信息static void usage PROTO ((void));//如函数名字所示,次要是获取以后的工夫信息。TIME cur_time;//用于保留一些公共成员,如最大租任的工夫等,具体的看头文件中的变量名字即可。struct group root_group;//服务端的以太网地址。struct iaddr server_identifier;int server_identifier_matched; #ifdef USE_FALLBACK//定义接口的信息,在应用fallback时候。具体的我也不是很分明,这里只是剖析代码。//如果想分明具体性能的时候,请参考rfcDHCP文档。struct interface_info fallback_interface;#endif//服务端的端口u_int16_t server_port;//定义log的优先级,不同的优先级打印显示音讯是不同的。{eg,在有缓冲区输入的状况下,优先级高的可能不期待缓冲区满就间接输入,例如stderror就是间接输入。而优先级低的可能须要期待缓冲区满再输入}int log_priority;//在debug的状况下,定义log_perror,这可可能影响一些输入。#ifdef DEBUGint log_perror = -1;#elseint log_perror = 1;#endif//这里是一些配置文件的门路。如下别离是配置文件、数据存储文件、过程id文件。char *path_dhcpd_conf = _PATH_DHCPD_CONF;char *path_dhcpd_db = _PATH_DHCPD_DB;char *path_dhcpd_pid = _PATH_DHCPD_PID;//main函数,程序的入口,这个main函数中次要负责读取配置文件,初始化一些参数,之后调用外围dispatch函数用于ip的散发。int main (argc, argv, envp) int argc; char **argv, **envp;{ int i, status; struct servent *ent; char *s;#ifndef DEBUG int pidfilewritten = 0; int pid; char pbuf [20]; int daemon = 1;#endif //这一部分次要是显示一些音讯,对于剖析DHCP主流程无关紧要。然而如果想要剖析DHCP的log工作原理,就非常重要了。 /* Initially, log errors to stderr as well as to syslogd. */#ifdef SYSLOG_4_2 openlog ("dhcpd", LOG_NDELAY); log_priority = DHCPD_LOG_FACILITY;#else openlog ("dhcpd", LOG_NDELAY, DHCPD_LOG_FACILITY);#endif#ifndef DEBUG#ifndef SYSLOG_4_2 setlogmask (LOG_UPTO (LOG_INFO));#endif#endif note (message); note (copyright); note (arr); //在这里次要是对命令行参数进行解析,依据不同的参数进行不同的初始化操作。具体的能够参考man dhcp进行比拟看。 //在这里我举一个例子,-d选项用于debug,如果咱们指定-d选项那么咱们将显示一些debug信息。 for (i = 1; i < argc; i++) { if (!strcmp (argv [i], "-p")) { if (++i == argc) usage (); for (s = argv [i]; *s; s++) if (!isdigit (*s)) error ("%s: not a valid UDP port", argv [i]); status = atoi (argv [i]); if (status < 1 || status > 65535) error ("%s: not a valid UDP port", argv [i]); server_port = htons (status); debug ("binding to user-specified port %d", ntohs (server_port)); } else if (!strcmp (argv [i], "-f")) {#ifndef DEBUG daemon = 0;#endif } else if (!strcmp (argv [i], "-d")) {#ifndef DEBUG daemon = 0;#endif log_perror = -1; } else if (!strcmp (argv [i], "-cf")) { if (++i == argc) usage (); path_dhcpd_conf = argv [i]; } else if (!strcmp (argv [i], "-lf")) { if (++i == argc) usage (); path_dhcpd_db = argv [i]; } else if (argv [i][0] == '-') { usage (); } else { struct interface_info *tmp = ((struct interface_info *) dmalloc (sizeof *tmp, "get_interface_list")); if (!tmp) error ("Insufficient memory to %s %s", "record interface", argv [i]); memset (tmp, 0, sizeof *tmp); strcpy (tmp -> name, argv [i]); tmp -> next = interfaces; tmp -> flags = INTERFACE_REQUESTED; interfaces = tmp; } }#ifndef DEBUG if (daemon) { /* First part of becoming a daemon... */ if ((pid = fork ()) < 0) error ("Can't fork daemon: %m"); else if (pid) exit (0); } /* Read previous pid file. */ if ((i = open (path_dhcpd_pid, O_RDONLY)) >= 0) { status = read (i, pbuf, (sizeof pbuf) - 1); close (i); pbuf [status] = 0; pid = atoi (pbuf); /* If the previous server process is not still running, write a new pid file immediately. */ if (pid && kill (pid, 0) < 0) { unlink (path_dhcpd_pid); if ((i = open (path_dhcpd_pid, O_WRONLY | O_CREAT, 0640)) >= 0) { sprintf (pbuf, "%d\n", (int)getpid ()); write (i, pbuf, strlen (pbuf)); close (i); pidfilewritten = 1; } } }#endif /* !DEBUG */ /* Default to the DHCP/BOOTP port. */ //在这里如果没有指定端口号的环,咱们将通过函数获取端口好的一些信息,当然默认是67。 if (!server_port) { ent = getservbyname ("dhcp", "udp"); if (!ent) server_port = htons (67); else server_port = ent -> s_port; endservent (); } //很容易了解,获取以后的工夫 /* Get the current time... */ GET_TIME (&cur_time); /* 读取配置文件,在之后的会剖析这个函数 */ if (!readconf ()) error ("Configuration file errors encountered -- exiting"); /* 对数据库做一些初始化操作 */ db_startup (); /* discover所有网络,并且初始化他们 */ discover_interfaces (1);#ifndef DEBUG /* If we were requested to log to stdout on the command line, keep doing so; otherwise, stop. */ if (log_perror == -1) log_perror = 1; else log_perror = 0; if (daemon) { /* Become session leader and get pid... */ close (0); close (1); close (2); pid = setsid (); } /* If we didn't write the pid file earlier because we found a process running the logged pid, but we made it to here, meaning nothing is listening on the bootp port, then write the pid file out - what's in it now is bogus anyway. */ if (!pidfilewritten) { unlink (path_dhcpd_pid); if ((i = open (path_dhcpd_pid, O_WRONLY | O_CREAT, 0640)) >= 0) { sprintf (pbuf, "%d\n", (int)getpid ()); write (i, pbuf, strlen (pbuf)); close (i); pidfilewritten = 1; } }#endif /* !DEBUG */ /* 这个函数将进行接管包,并且散发ip地址*/ dispatch (); /* Not reached */ return 0;}/* 此函数用于打印一些应用的信息,在参数中能够指定显示实用信息,程序就会显示一些应用信息 */static void usage (){ error ("Usage: dhcpd [-p <UDP port #>] [-d] [-f] [-cf config-file]%s", "\n [-lf lease-file] [if0 [...ifN]]");}//此函数临时没有用到void cleanup (){}图1 ...

January 16, 2021 · 9 min · jiezi

关于c:9回文数leetcode-C语言

题目如下:判断一个整数是否是回文数。回文数是斧正序(从左向右)和倒序(从右向左)读都是一样的整数。 思路一、先将数字转换为一个字符串,而后判断字符串是否是回文字符串。代码如下: /*** 判断一个整数是不是回文数***/#include <stdio.h>#include <string.h>#include <stdbool.h>#include <stdlib.h>// 将整数转换为数字字符串void myItoA(int num, char *str){ int i = 0; while (num > 0) { str[i++] = num % 10 + 48; num /= 10; } str[i] = '\0';}// 判断字符串是否是回文字符串// 双指针法,从首位开始遍历bool isPalindrome(int x){ if (x < 0) { return false; } if (x == 0) { return true; } // 获取数字长度 int len = 0; int temp = x; while (temp > 0) { len++; temp /= 10; } printf("len is %d\n", len); // 动态分配存储空间 char *str = (char *)malloc((len + 1) * sizeof(char)); myItoA(x, str); int k = 0; while (str[k] != '\0') { printf("%c", str[k++]); } for (int i = 0, j = len - 1; i < j; i++, j--) { if (str[i] != str[j]) { return false; } } return true;}int main (void) { int num = -12321; printf("\n%d\n", isPalindrome(num));} ...

January 13, 2021 · 2 min · jiezi

关于c:计算机如何表示整数

[TOC] 在计算机中,任何的数据都是用二进制: 0 和 1 来示意。整数也不例外。生存中的 10,在 8 个字节的整数中示意为 00001010。然而这样子只能示意负数和零。怎么示意正数呢?于是有了符号位的概念。在 8 个字节的整数中,最高位为符号位,0 代表负数,1 代表正数。所以 -10 就能够用 10001010 来示意。然而,间接采纳符号位会带来一系列问题: 00000000 和 10000000 示意为 0 和 -0 ,那么用那个来示意 0,还是都是零?加法问题。对于加法问题,试着运算 1 - 1 的值: 将 1 - 1 转换成 1 + (-1)转换成二进制:00000001 + 10000001运算后果为 10000010 转换成十进制为 -2很显著,这个后果不对。为了解决这个问题,在计算机中引入补码(2's complement)来解决。要解释为什么要应用补码,还得从无符号整型开始说起。 为了便于了解和不便,我用 3 个字节的整数来解说。但因为 C 语言最小都是 8 个字节,在代码验证方面应用 8 个字节。无符号整型(unsigned integer)3 个字节的无符号整型中,能够示意 2^3^ = 8 个数: 000 = 2^2 * 0 + 2^1 * 0 + 2^0 * 0 = 0+0+0 = 0001 = 2^2 * 0 + 2^1 * 0 + 2^0 * 1 = 0+0+1 = 1010 = 2^2 * 0 + 2^1 * 1 + 2^0 * 0 = 0+2+0 = 2011 = 2^2 * 0 + 2^1 * 1 + 2^0 * 1 = 0+2+1 = 3100 = 2^2 * 1 + 2^1 * 0 + 2^0 * 0 = 4+0+0 = 4101 = 2^2 * 1 + 2^1 * 0 + 2^0 * 1 = 4+0+1 = 5110 = 2^2 * 1 + 2^1 * 1 + 2^0 * 0 = 4+2+0 = 6111 = 2^2 * 1 + 2^1 * 1 + 2^0 * 1 = 4+2+1 = 7既然是整数,免不了要加减乘除。 ...

January 13, 2021 · 6 min · jiezi

关于c:Golang源码分析Golang如何实现自举-dist介绍二

前言  依据《Golang如何实现自举(一)》的相干疏导,晓得了go1.3的go编译是须要go_bootstrap、然而生成go_bootstrap,须要dist工具进行生成。那么本期次要关注dist工具。 1.dist工具介绍  其实dist工具是属于go的一个疏导工具,它负责构建C程序(如Go编译器)和go工具的初始疏导正本。它也能够作为一个无所不包用shell脚本替换以前实现的零工。通过“go tool dist”命令能够操作该工具。该工具不同零碎下对应在pkg/tool/下的目录中。<center>图1-1-1 dist工具介绍</center>  那么来看一下dist工作都有哪些操作,如图1-1-1。能够看出dist工作有6个操作,别离为打印装置信息,编译go_boostrap,清理编译文件,查看go env,装置拷贝go工具,查看go版本, 这几个操作。   通过对《【Golang源码剖析】Golang如何实现自举(一)》的理解,晓得dist是C源码所写。linux下是通过make.bash中gcc编译进去的,命令如下: #gcc -O2 -Wall -Werror -ggdb -o cmd/dist/dist -Icmd/dist '-DGOROOT_FINAL="/mnt"' cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildruntime.c cmd/dist/goc2c.c cmd/dist/main.c cmd/dist/unix.c cmd/dist/windows.c2.dist文件介绍  所有学习的本源都是先看看官网文档怎么说,而后学习能力强的能够在看看源码,加深对学习对了解。看dist目录前,先在看看它对应的文档:https://github.com/golang/go/...   文档中说:Dist自身是用非常简单的C编写的。所有与C库的交互,甚至规范的C库也被限度在单个零碎特定的文件中(plan9.c,unix.c,windows.c),以进步可移植性。须要的性能其余文件应通过可移植性层公开。职能在可移植层中以x前缀结尾,否则应用与现有性能雷同的名称,或与现有性能混同。例如,xprintf是可移植的printf。   到目前为止,dist中最常见的数据类型是字符串和字符串。然而,dist应用了两个命名为而不是应用char和char *数据结构Buf和Vec,它们领有它们指向的所有数据。Buf操作是以b结尾的函数;Vec操作是以v结尾的函数。任何函数申明的根本模式堆栈上的Buf或Vecs应该是 void myfunc(void){ Buf b1, b2; Vec v1; binit(&b1); binit(&b2); vinit(&v1); ... main code ... bprintf(&b1, "hello, world"); vadd(&v1, bstr(&b1)); // v1 takes a copy of its argument bprintf(&b2, "another string"); vadd(&v1, bstr(&b2)); // v1 now has two strings bfree(&b1); bfree(&b2); vfree(&v1);}binit / vinit调用筹备要应用的缓冲区或向量,从而初始化 数据结构以及bfree / vfree调用开释它们仍在的任何内存保持。应用这个习惯用法能够给咱们提供词法范畴的调配。 ...

January 11, 2021 · 2 min · jiezi

关于c:signal-信号处理测试

最近再看unix编程的书籍,理解下操作系统。对于sinal信号的毛病的形容有一些似懂非懂的中央,就依照例子做一个试验,看看零碎如何解决。代码如下: #include <stdio.h>#include <signal.h>#include <unistd.h>#include <string.h>#define INPUTLEN 100void inthandler(int s){ printf("received a signal %d\n",s); sleep(2); printf("leaving inthandler\n");}void quithandler(int s){ printf("received a signal %d\n",s); sleep(3); printf("leaving quithandler\n");}int main(int ac,char *av[]){ char input[INPUTLEN]; int nchars; signal(SIGINT,inthandler); signal(SIGQUIT,quithandler); do{ printf("\n type a message\n"); nchars=read(0,input,(INPUTLEN-1)); if(nchars==-1) perror("read return an error"); else{ input[nchars]='\0'; printf("you typed ,%s",input); } }while(strncmp(input,"quit",4)!=0);}测试场景(在centos7 零碎下测试): 间断屡次中断信号 通过上图的演示,在收回第一个中断信号后,捕捉信号处理。在屡次收回终端信号后,信号被阻塞,信号处理程序处理实现后,梗塞的雷同的信号被捕捉一次。2.距离发送中断信号和QUIT信号 发送一个中断信号后,零碎捕捉信号处理。在收回一个QUIT信号后,从中断信号处理的函数跳转导QUIT信号处理函数解决。3、被中断的零碎调用 当输出“hello” 后产生了中断,中断实现后输出“world n",最初只打印出 "world".看来在centos,并不是在read 时产生中断返回-1.

January 11, 2021 · 1 min · jiezi

关于c:8字符串转换整数LeetCodeC语言

/*** 将字符串转换为数字——LeetCode第8题** author: aliao*/#include <stdio.h>#include <limits.h>int myAtoi(char *str){ int i = 0; int start = 0; int len = 0; // 遍历字符串中起始的空格字符 while (str[i] == 32) { i++; } // 如果非空首字符不是-+或者0-9这三类字符,则间接返回0 // 如果是空字符串或者字符串只蕴含空白子字符 if (str[i] != '-' && str[i] != '+' && (str[i] < 48 || str[i] > 57)) { return 0; } else { start = i; len += 1; i++; } while (str[i] != '\0') { if (str[i] >= 48 && str[i] <= 57) { len += 1; i++; } else { break; } } int reverse = 0; int k = 10; int isNeg = 0; for (int j = 0; j < len; j++) { if (j == 0) { if (str[start + j] == '-') { isNeg = 1; continue; } else if (str[start + j] == '+') { continue; } } if (isNeg) { if (reverse < INT_MIN / 10 || reverse == INT_MIN / 10 && str[start + j] - 48 > 8) { return INT_MIN; } else { reverse = reverse * k - (str[start + j] - 48); } } else { if (reverse > INT_MAX / 10 || reverse == INT_MAX / 10 && str[start + j] - 48 > 7) { return INT_MAX; } else { reverse = reverse * k + (str[start + j] - 48); } } } return reverse;}int main (void) { char *str = "-2147483648"; printf("final value: %d", myAtoi(str));}执行用时:0 ms, 在所有 C 提交中击败了100.00%的用户内存耗费:5.8 MB, 在所有 C 提交中击败了6.09%的用户工夫复杂度O(n),空间复杂度O(1)

January 11, 2021 · 2 min · jiezi

关于c:8字符串转换整数LeetCodeC语言

/*** 将字符串转换为数字——LeetCode第8题** author: aliao*/#include <stdio.h>#include <limits.h>int myAtoi(char *str){ int i = 0; int start = 0; int len = 0; // 遍历字符串中起始的空格字符 while (str[i] == 32) { i++; } // 如果非空首字符不是-+或者0-9这三类字符,则间接返回0 // 如果是空字符串或者字符串只蕴含空白子字符 if (str[i] != '-' && str[i] != '+' && (str[i] < 48 || str[i] > 57)) { return 0; } else { start = i; len += 1; i++; } while (str[i] != '\0') { if (str[i] >= 48 && str[i] <= 57) { len += 1; i++; } else { break; } } int reverse = 0; int k = 10; int isNeg = 0; for (int j = 0; j < len; j++) { if (j == 0) { if (str[start + j] == '-') { isNeg = 1; continue; } else if (str[start + j] == '+') { continue; } } if (isNeg) { if (reverse < INT_MIN / 10 || reverse == INT_MIN / 10 && str[start + j] - 48 > 8) { return INT_MIN; } else { reverse = reverse * k - (str[start + j] - 48); } } else { if (reverse > INT_MAX / 10 || reverse == INT_MAX / 10 && str[start + j] - 48 > 7) { return INT_MAX; } else { reverse = reverse * k + (str[start + j] - 48); } } } return reverse;}int main (void) { char *str = "-2147483648"; printf("final value: %d", myAtoi(str));}执行用时:0 ms, 在所有 C 提交中击败了100.00%的用户内存耗费:5.8 MB, 在所有 C 提交中击败了6.09%的用户工夫复杂度O(n),空间复杂度O(1)

January 11, 2021 · 2 min · jiezi

关于c:链表和数组的比较

前沿咱们如何把事实中大量简单的数据保留到主存储器(内存)中呢?为了解决这个问题,咱们出了数据结构的学科,专门钻研----个体的存储+个体关系的存储。所以当咱们要解决问题时,首先要先解决的是如何把这些问题转换成数据,先保留到咱们的主存中。 线性构造什么是线性构造呢? 就是把所有的结点用一根直线穿起来 线性构造次要分为2种 间断存储【数组】 什么叫数组?元素类型雷同,大小相等(数组传参,只有传进去首地址和长度就行) 数组的优缺点 长处存取速度快 毛病当时必须晓得数组的长度 插入删除元素很慢 空间通常是有限度的 须要大块间断的存储块 插入删除元素的效率很低 离散存储【链表】 定义: n个节点离散调配彼此通过指针相连每个节点只有一个前驱节点,每个节点只有一个后续节点首节点没有前驱节点,尾节点没有后续节点数组和链表的排序咱们来看个数组和链表排序的伪代码 //数组排序void sort_arr(struct Arr * pArr){ int i, j, t; int len = length_list(pArr); for (i=0; i<len-1; ++i) { for (j=i+1; j<len; ++j) { if (pArr->pBase[i] > pArr->pBase[j]) { t = pArr->pBase[i]; pArr->pBase[i] = pArr->pBase[j]; pArr->pBase[j] = t; } } }}//链表排序void sort_list(struct Arr * pHead){ int i, j, t; int len = length_list(pHead); struct Arr * p, q; for (i=0,p=pHead->pNext; i<len-1; ++i,p=p->pNext) { for (j=i+1,q=p->pNext; j<len; ++j,q=q->pNext) { if (p->data > q->data) //等价于 a[i] > a[j] { t = p->data;//等价于: t = a[i]; p->data = q->data; //等价于: a[i] = a[j]; q->data = t; //等价于: a[j] = t; } } } return;}在咱们看来数组和链表的存储形式是不同的 ...

January 3, 2021 · 1 min · jiezi

关于c:深入理解计算机系统CSAPP读书笔记-第六章-存储器层次结构

在计算机系统模型中,CPU执行指令,而存储器零碎为CPU寄存指令和数据。实际上,存储器零碎是一个具备不同容量、老本和拜访工夫的存储设备的层次结构。 如果你的程序须要的数据是存储在CPU寄存器中,那么在指令的执行期间,在0个周期内就能拜访到它们。如果存储在高速缓存中,须要4~75个周期。如果存储在主存中,须要上百个周期。而如果存储在磁盘上,须要大概几千万个周期! 计算机程序的一个根本属性称为局部性。具备良好局部性的程序偏向于一次又一次地拜访雷同的数据项汇合,或是偏向于拜访邻近的数据项汇合。具备良好局部性的程序比局部性差的程序更多地偏向于从存储器层次结构中较高层次处拜访数据项,因而运行得更快。[TOC] 存储技术随机拜访存储器 随机拜访存储器( Random-Access Memory,RAM)分为两类:动态的和动静的。动态RAM(SRAM)比动静RAM(DRAM)更快,但也贵得多。SRAM用来作为高速缓存存储器。DRAM用来作为主存以及图形系统的帧缓冲区。 动态RAM SRAM将每个位存储在一个双稳态的( bistable)存储器单元里。每个单元是用一个六晶体管电路来实现的。这个电路有这样一个属性,它能够无限期地放弃在两个不同的电压配置( configuration)或状态( state)之一。其余任何状态都是不稳固的,在不稳固状态时,电路会迅速转移到两个稳固状态的一个。 因为SRAM存储器单元的双稳态个性,只有有电,它就会永远地放弃它的值。即便有烦扰(例如电子乐音)来扰乱电压,当烦扰打消时,电路就会复原到稳固值。 动静RAM DRAM将每个位存储为对一个电容的充电。DRAM存储器能够制作得十分密集。每个单元由一个电容和一个拜访晶体管组成。然而,与SRAM不同,DRAM存储器单元对烦扰十分敏感。当电容的电压被扰乱之后,它就永远不会复原了。裸露在光线下会导致电容电压扭转。 下表总结了SRAM和DRAM存储器的个性。只有有供电,SRAM就会放弃不变。与DRAM不同,它不须要刷新。SRAM的存取比DRAM快。SRAM对诸如光和电噪声这样的烦扰不敏感。代价是SRAM单元比DRAM单元应用更多的晶体管,因此密集度低,而且更贵,功耗更大。 每位晶体管数绝对拜访工夫继续的敏感的绝对破费利用SRAM61X是否1000X高速缓存存储器DRAM110X否是1X主存,帧缓冲区传统的DRAM DRAM芯片中的单元(位)被分成d个超单元( supercell),每个超单元都由w个DRAM单元组成。一个$d \times w$的DRAM总共存储了$dw$位信息。超单元被组织成一个r行c列的长方形阵列,这里rc=d。每个超单元无形如(i,j)的地址,这里i示意行,而j示意列。 例如,如下图所示是一个16×8的DRAM芯片的组织,有d=16个超单元,每个超单元有w=8位,r=4行,c=4列。带暗影的方框示意地址(2,1)处的超单元。信息通过称为引脚(pin)的内部连接器流入和流出芯片。每个引脚携带一个1位的信号。下图给出了两组引脚:8个data引脚,它们能传送一个字节到芯片或从芯片传出一个字节,以及2个addr引脚,它们携带2位的行和列超单元地址。其余携带管制信息的引脚没有显示进去。 每个DRAM芯片被连贯到某个称为内存控制器( memory controller)的电路,这个电路能够一次传送w位到每个DRAM芯片或一次从每个DRAM芯片传出w位。为了读出超单元(i,j)的内容,内存控制器将行地址i发送到DRAM,而后是列地址j。DRAM把超单元(i,j)的内容发回给控制器作为响应。行地址i称为RAS( Row Access strobe,行拜访选通脉冲)申请。列地址j称为CAS( Column Access strobe,列拜访选通脉冲)申请。留神,RAS和CAS申请共享雷同的DRAM地址引脚。 例如,要从图6-3中16×8的DRAM中读出超单元(2,1),内存控制器发送行地址2,如下图a所示。DRAM的响应是将行2的整个内容都复制到一个外部行缓冲区。接下来,内存控制器发送列地址1,如下图b所示。DRAM的响应是从行缓冲区复制出超单元(2,1)中的8位,并把它们发送到内存控制器。 电路设计者将DRAM组织成二维阵列而不是线性数组的一个起因是升高芯片上地址引脚的数量。例如,如果示例的128位DRAM被组织成一个16个超单元的线性数组,地址为0~15,那么芯片会须要4个地址引脚而不是2个。二维阵列组织的毛病是必须分两步发送地址,这减少了拜访工夫。 加强的DRAM 能够通过以下形式进步拜访根本DRAM的速度。 快页模式DRAM( Fast Page Mode dram, FPM DRAM)。传统的DRAM将超单元的一整行复制到它的外部行缓冲区中,应用一个,而后抛弃残余的。FPM DRAM容许对同一行间断地拜访能够间接从行缓冲区失去服务。 如果要读取第4行的3个超单元,传统DRAM须要收回3次RAS,CAS。而FPM DRAM只须要收回一次RAS,CAS,前面跟2个CAS即可。 &emsp;扩大数据输入DRAM( Extended Data Out Dram, EDO DRAM)。 FPM DRAM的个加强的模式,它容许各个CAS信号在工夫上靠得更严密一点。 ...

January 1, 2021 · 2 min · jiezi

关于c:24张图7000字详解计算机中的高速缓存

[toc] 1. 什么是缓存 缓存又叫高速缓存,是计算机存储器中的一种,实质上和硬盘是一样的,都是用来<font color=#0000FF size=3>存储数据和指令的 </font>。它们最大的区别在于<font color=#0000FF size=3>读取速度的不同。</font>程序个别是放在内存中的,当CPU执行程序的时候,执行完一条指令须要从内存中读取下一条指令,读取内存中的指令要花费100000个时钟周期(缓存读取速度为200个时钟周期,相差500倍),如果每次都从内存中取指令,CPU运行时将破费大量的工夫在读取指令上。这显然是一种资源节约。 如何解决这个问题呢?有人必定会问,<font color=#0000FF size=3>间接把程序存储在缓存中不行吗? </font> 答案是能够的。然而,缓存的造价太贵了。具体如下图所示。以2015年的售价为例,1GB SRAM的价格大概为327680美元,而1GB 一般硬盘的价格仅仅为0.03美元。用缓存来存储程序老本太高了,得失相当。 于是,有人就提出了这样一种办法,<font color=#0000FF size=3>在CPU和内存之间增加一个高速内存, </font>这个高速内存容量小,只用来存储CPU执行时罕用的指令。既保证了硬件老本,又进步了CPU的访问速度。这个高速内存就是缓存(高速缓存)。 2. 缓存的定义 高速缓存是一个小而疾速的<font color=#0000FF size=3>存储设备 </font>,它作为存储在更大更慢的设 备中的数据对象的缓冲区域。<font color=#0000FF size=3>应用高速缓存的过程称为缓存 </font>。 具体如下图所示,主存能够作为一个存储设备,L3是主存的缓冲区域,从L3存取数据的过程就叫做缓存。 3. 计算机中的高速缓存3.1 高速缓存相干名词 如下图所示,数据总是以<font color=#0000FF size=3>块为单位 </font>在高速缓存和主存之间来回复制。 如果咱们的程序申请一个数据字,这个数据字存储在编号为10的块中。将分以下几种状况思考: 1. 高速缓存行中为空,这叫做<font color=#0000FF size=3>冷不命中 </font>。 2.高速缓存中有数据块,但没有数据块10,这叫做<font color=#0000FF size=3>缓存不命中 </font>。接下来缓存申请主存将该块复制到高速缓存,高速缓存接管到之后将替换一个现有的数据块,从而存储新的数据块在高速缓存中。最初,高速缓存将数据块10返回给CPU。 3. 高速缓存中有数据,将内存中的数据块搁置到高速缓存中时,产生了抵触,这叫做<font color=#0000FF size=3>抵触不命中 </font>。 搁置策略中最罕用的是:第k+1层的块i必须放在第k层的块(i mod 4)中。比方,第k+1层的0,4,8,12会映射到第k层的块0。块1,5,9,13会映射到块1。 4. 缓存中有数据块10,则间接返回给CPU。这叫做<font color=#0000FF size=3>缓存命中 </font>。 ...

January 1, 2021 · 2 min · jiezi

关于c:深入理解计算机系统CSAPP实验四-Attack-Lab

这是CSAPP的第四个试验,这个试验比拟有意思,也比拟难。通过这个试验咱们能够更加相熟GDB的应用和机器代码的栈和参数传递机制。@[toc] 试验目标 本试验要求在两个有着不同安全漏洞的程序上实现五种攻打。通过实现本试验达到: 深刻了解当程序没有对缓冲区溢出做足够防备时,攻击者可能会如何利用这些安全漏洞。深刻了解x86-64机器代码的栈和参数传递机制。深刻了解x86-64指令的编码方式。纯熟应用gdb和objdump等调试工具。更好地了解写出平安的程序的重要性,理解到一些编译器和操作系统提供的帮忙改善程序安全性的个性。 做本次试验之前,倡议好好浏览下本篇博文 面试官不讲武德,竟然让我讲讲蠕虫和金丝雀!,了解缓冲区溢出时函数的返回值是如何被批改和精准定位的。筹备工作 在官网下载失去实验所需文件解压后会失去五个不同的文件。对六个文件简要阐明如下所示。 README.txt:形容文件夹目录 ctarget:一个容易蒙受code injection攻打的可执行程序。 rtarget:一个容易蒙受return-oriented programming攻打的可执行程序。 cookie.txt一个8位的十六进制码,用于验证身份的惟一标识符。 farm.c:指标“gadget farm”的源代码,用于产生return-oriented programming攻打。 hex2raw:一个生成攻打字符串的工具。 HEX2RAW冀望由一个或多个空格分隔的两位十六进制值。所以如果你想创立一个十六进制值为0的字节,须要将其写为00。要创立单词0xdeadbeef应将“ ef be ad de”传递给HEX2RAW(请留神,小字节序须要反转)。 编译环境:Ubuntu 16.04,gcc 5.4.0。 留神:因为咱们应用的是外网编译,所以在运行程序时加上-q参数。 内容简介CTARGET和RTARGET从规范输出中读取字符串,应用的getbuf函数如下所示。 unsigned getbuf(){ char buf[BUFFER_SIZE]; Gets(buf); return 1;} 函数Gets()相似于规范库函数gets(),从规范输出读入一个字符串,将字符串(带null结束符)存储在指定的目标地址。二者都只会简略地拷贝字节序列,无奈确定指标缓冲区是否足够大以存储下读入的字符串,因而可能会超出指标地址处调配的存储空间。字符串不能蕴含字节值0x0a,这是换行符 \n 的ASCII码,Gets()遇到这个字节时会认为意在完结该字符串。 如果用户输出并由getbuf读取的字符串足够短,则很显著getbuf将返回1,如以下执行示例所示: 当输出一个很长的字符串时,将会呈现段谬误,具体如下图所示: 如上图所示,呈现了缓冲区溢出谬误。咱们能够利用缓冲区溢出来批改程序的返回值,使它指向咱们要求的地址来实现攻打。 CTARGET和RTARGET都采纳几个不同的命令行参数:-h:打印可能的命令行参数列表 -q:本地测评,不要将后果发送到评分服务器 -i FILE:提供来自文件的输出,而不是来自规范输出的输出 代码注入攻打Level 1 对于第1个例程,将不会注入新代码,而是缓冲区溢出破绽利用字符串将重定向程序来执行现有程序。在CTARGET文件中中调用了函数getbuf。当getbuf执行完return语句后,程序通常会接着向下执行第5行的内容。 void test() { int val; val = getbuf(); printf("NO explit. Getbuf returned 0x%x\n", val);} 如果咱们想扭转这种行为。在文件ctarget中,咱们要把getbuf函数的返回值指向函数touch1,touch1代码如下所示: ...

December 31, 2020 · 6 min · jiezi

关于c:深入理解计算机系统CSAPP读书笔记-第五章-优化程序性能

写程序最次要的指标就是使它在所有可能的状况下都正确工作。一个运行得很快然而给出谬误后果的程序没有任何用途。程序员必须写出清晰简洁的代码,这样做不仅是为了本人可能看懂代码,也是为了在检査代码和今后须要批改代码时,其他人可能读懂和了解代码。另一方面,在很多状况下,让程序运行得快也是一个重要的思考因素。本章次要介绍了循环展开,减小过程调用,打消不必要的内存援用等优化代码的办法,有助于咱们写出高效的代码,进步代码的性能。@[toc] 编写高效程序须要做到以下几点: 1.适合的算法和数据结构 2.编写出编译器可能无效优化以转换成高效可执行代码的源代码(例如,在C语言中,指针运算和强制类型转换使得编译器很难对它进行优化)。 3.针对解决运算量特地大的计算,将一个工作分成多局部,即利用并行性。 优化编译器的能力和局限性GCC优化指令 -Og:默认配置,不优化。 -O1:编译器试图优化代码大小和执行工夫,它次要对代码的分支,常量以及表达式等进行优化,但不执行任何会占用大量编译工夫的优化。 -O2:GCC执行简直所有不蕴含工夫和空间衡量的优化(比方,尝试更多的寄存器级的优化以及指令级的优化)。与-O相比,此选项减少了编译工夫,但进步了代码的效率。 -O3:比-O2更优化,对于-O3编译选项,在-O2的根底上,关上了更多的优化项(比方,应用伪寄存器网络,一般函数的内联,以及针对循环的更多优化)。不过可能导致编译进去的二级制程序不能debug。 -Os:次要是对代码大小的优化,咱们根本不必做更多的关怀。 通常各种优化都会打乱程序的构造,让调试工作变得无从着手。并且会打乱执行程序,依赖内存操作程序的程序须要做相干解决能力确保程序的正确性。 内存别名应用 两个指针可能指向同一个内存地位的状况成为内存别名应用。 void twiddle1 (long *xp,long*yp){ *xp+ = *yp; *xp+ = *yp;}void twiddle2(long *xp,long*yp){ *xp+ = 2 * *yp;}twiddle1它们都是将存储在由指针yp批示的地位处的值两次加到指针xp批示的地位处的值。twiddle2须要3次内存援用(读 xp,读yp,写 xp)。 twiddle1须要6次(2次读 xp,2次读yp,2次写 xp),个别,咱们认为twiddle2要优于twiddle2。那么将twiddle1优化是不是就能产生相似twiddle2的代码了? 答案显然是不能够的。当 xp == yp 时,twiddle1执行后的后果是:xp的值减少4倍。而twiddle2的后果是xp的值减少3倍。因而,两个程序是有实质差异的。 以上这个例子就介绍了内存别名应用,编译器在优化时,并不知道xp 和 yp是否相等,只能假如他们不相等,即xp和yp指针不会指向同一地位。 示意程序性能 程序性能度量规范:每元素的周期数(Cycles Per Element,CPE)。 处理器流动的程序是由时钟管制的,时钟提供了某个频率的法则信号,通常用千兆赫兹(GHz),即十亿周期每秒来示意。例如,当表明一个零碎有“4GHz”处理器,这示意处理器时钟运行频率为每秒$4 \times {10^9}$个周期。每个时钟周期的工夫是时钟频率的倒数。通常是以纳秒( nanosecond,1纳秒等于${10^{ - 8}}$秒)或皮秒( picosecond,1皮秒等于${10^{ - 12}}$秒)为单位的。例如,一个4GHz的时钟其周期为0.25纳秒,或者250皮秒。从程序员的角度来看,用时钟周期来示意度量规范要比用纳秒或皮秒来示意有帮忙得多。用时钟周期来示意,度量值示意的是执行了多少条指令,而不是时钟运行得有多快。 程序示例打消循环的低效率(Code Motion) 举个例子如下所示: ...

December 31, 2020 · 5 min · jiezi

关于c:深入理解计算机系统实验五-Perfom-Lab

本次试验是CSAPP的第5个试验,这次试验次要是让咱们相熟如何优化程序,如何写出更具备效率的代码。通过这次试验,咱们能够更好的了解计算机的工作原理,在当前编写代码时,具备能联合软硬件思考的能力。@[toc] 试验简介 本次试验次要解决优化内存密集型代码。图像处理提供了许多能够从优化中受害的性能示例。在本试验中,咱们将思考两种图像处理操作:旋转,可将图像逆时针旋转90o,平滑,能够“平滑”或“含糊”图片。 在本试验中,咱们将思考将图像示意为二维矩阵M,其中${M_{i,j}}$示意M的第(i,j)个像素的值,像素值是红色,绿色和蓝色(RGB)值的三倍。咱们只会思考方形图像。令N示意图像的行(或列)数。行和列以C款式编号,从0到N − 1。 给定这种示意模式,旋转操作能够非常简单地实现为以下两个矩阵运算: 转置:对于每对(i,j),${M_{i,j}}$和${M_{j,i}}$是调换的 替换行:第i行与第N-1 − i行替换。 具体如下图所示 <img src="https://gitee.com/dongxingbo/Picture/raw/master//Blog/2020/%E5%8D%81%E4%BA%8C%E6%9C%88//PerformanceLab_%E5%9B%BE%E7%89%87%E6%97%8B%E8%BD%AC90%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="image-20201203203611824" style="zoom:80%;" /> 通过用四周所有像素的平均值替换每个像素值(在以该像素为核心的最大3×3窗口)中替换每个像素值来实现平滑操作。如下图所示。像素的值$M2[1][1]$ 和$M2[N - 1][N - 1]$如下所示: $M2[1][1] = \frac{{\sum\nolimits_{i = 0}^2 {\sum\nolimits_{j = 0}^2 {M1[i][j]} } }}{9}$ $M2[N - 1][N - 1] = \frac{{\sum\nolimits_{i = N - 2}^{N - 1} {\sum\nolimits_{j = N - 2}^{N - 1} {M1[i][j]} } }}{4}$ <img src="https://gitee.com/dongxingbo/Picture/raw/master//Blog/2020/%E5%8D%81%E4%BA%8C%E6%9C%88//PerformanceLab_%E5%B9%B3%E6%BB%91%E5%9B%BE%E5%83%8F%E5%9B%BE%E7%A4%BA.png" alt="image-20201203204903889" style="zoom:80%;" /> 本次试验中,咱们须要批改惟一文件是kernels.c。driver.c程序是一个驱动程序,可让对咱们批改的程序进行评分。应用命令make driver生成驱动程序代码并应用./driver命令运行它。 ...

December 31, 2020 · 5 min · jiezi

关于c:S3C2440移植uboot之支持NANDFLASH操作

上一节咱们移植了uboot,S3C2440移植uboot之反对NORFLASH。这节咱们持续移植,反对NANDFLASH。@[TOC] 编译报错 之前因为nand局部报错,间接正文了 u-boot-2012.04.01\include\configs\smdk2440.h 中的#define CONFIG_CMD_NAND。当初咱们去掉正文,从新编译。报错如下 咱们没有定义CONFIG_S3C2410导致的 能够看到上面有2440的NAND构造体 拷贝s3c2410_nand.c,批改宏定义反对SC32440 所以咱们能够拷贝一份s3c2410_nand.c给2440应用2410的NandFlash位于drivers/mtd/nand/s3c2410_nand.c,首先复制s3c2410_nand.c,改为s3c2440_nand.c,改Makefile,如下图所示: 在上一章剖析过CONFIG_NAND_S3C2410宏,位于include/configs/smdk2440.h: 如上图所示,其中CONFIG_CMD_NAND宏:示意uboot是否反对nand,在上章里,咱们把它屏蔽了,接下来便勾销屏蔽CONFIG_CMD_NAND宏。持续增加对CONFIG_NAND_S3C2440宏的反对,将: #ifdef CONFIG_CMD_NAND#define CONFIG_NAND_S3C2410#define CONFIG_SYS_S3C2410_NAND_HWECC#define CONFIG_SYS_MAX_NAND_DEVICE 1#define CONFIG_SYS_NAND_BASE 0x4E000000#endif改为 #ifdef CONFIG_CMD_NAND #ifdef CONFIG_S3C2410 #define CONFIG_NAND_S3C2410#define CONFIG_SYS_S3C2410_NAND_HWECC#else // CONFIG_S3C2440 #define CONFIG_NAND_S3C2440 #define CONFIG_SYS_S3C2440_NAND_HWECC#endif#define CONFIG_SYS_MAX_NAND_DEVICE 1#define CONFIG_SYS_NAND_BASE 0x4E000000#endif 因为smdk2410.h中定义的是CONFIG_S3C2410,而smdk2440.h中定义的是CONFIG_S3C2440,所以便会依据下面的#ifdef来动静定义宏 批改s3c2440_nand.c 中的NFCONF,NFCONT,反对S3C2440 往下看代码发现原来的NFCONF设置并不能匹配咱们的2440 2440的NFCONF的15位是保留的 所以正文掉这部分代码 2410 NFCONF的其余位设置也不匹配咱们的2440 2440NFCONF 时序参数设置 s3c2440_hwcontrol中使能选中 对照2440手册批改为反对2440的 批改为 /*2440的NAND时序设置*/ cfg = ((tacls-1)<<12)|((twrph0-1)<<8)|((twrph1-1)<<4); nand_reg->nfcont=(1<<1)|(1<<0); // bit1:敞开片选(), bit0:开启nand flash 控制器 nand_reg->nfconf = (tacls<<12) | (twrph0<<8) | (twrph1<<4); //设置时序 writel(cfg, &nand_reg->nfconf); /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ writel((1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont); /* initialize nand_chip data structure */ nand->IO_ADDR_R= (void *)&nand_reg->nfdata; nand->IO_ADDR_W = (void *)&nand_reg->nfdata; nand->select_chip = s3c2440_select_chip; //设置CE ;批改s3c2440_hwcontrol辨别命令和地址/*ctrl:示意做什么,选中芯片/勾销选中,发命令还是发地址* cmd :命令值或者地址值*/static void s3c2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl){ struct nand_chip *chip = mtd->priv; struct s3c2440_nand *nand = s3c2440_get_base_nand(); //获取nand寄存器地址 if (ctrl & NAND_CLE) // 传输的是命令 writeb(dat,&nand->nfcmd); else if (ctrl & NAND_ALE) // 传输的是地址 writeb(dat,&nand->nfaddr); } 批改实现 ...

December 30, 2020 · 1 min · jiezi

关于c:Linux内核中断顶半部和底半部的理解

@[toc] 中断上半部、下半部的概念 设施的中断会打断内核过程中的失常调度和运行,系统对更高吞吐率的谋求势必要求中断服务程序尽量短小精悍。然而,这个良好的欲望往往与事实并不吻合。在大多数实在的零碎中,当中断到来时,要实现的工作往往并不会是短小的,它可能要进行较大量的耗时解决。 下图形容了Linux内核的中断解决机制。为了在中断执行工夫尽量短和中断解决需实现的工作尽量大之间找到一个平衡点,Linux将中断处理程序合成为两个半部:顶半部和底半部。 顶半部用于实现尽量少的比拟紧急的性能,它往往只是简略地读取寄存器中的中断状态,并在革除中断标记后就进行“注销中断”的工作。“注销中断”意味着将底半部处理程序挂到该设施的底半部执行队列中去。这样,顶半部执行的速度就会很快,从而能够服务更多的中断请求。 当初,中断解决工作的重心就落在了底半部的头上,需用它来实现中断事件的绝大多数工作。底半部简直做了中断处理程序所有的事件,而且能够被新的中断打断,这也是底半部和顶半部的最大不同,因为顶半部往往被设计成不可中断。底半部相对来说并不是十分紧急的,而且绝对比拟耗时,不在硬件中断服务程序中执行。 只管顶半部、底半部的联合可能善零碎的响应能力,然而,僵化地认为Linux设施驱动中的中断解决肯定要分两个半部则是不对的。如果中断要解决的工作自身很少,则齐全能够间接在顶半部全副实现。 其余操作系统中对中断的解决也采纳了相似于 Linux的办法,真正的硬件中断服务程序都斥尽量短。因而,许多操作系统都提供了中断上下文和非中断上下文相结合的机制,将中断的耗时工作保留到非中断上下文去执行。 实现中断下半部的三种办法软中断 软中断( Softirq)也是一种传统的底半部解决机制,它的执行机会通常是顶半部返回的时候, tasklet是基于软中断实现的,因而也运行于软中断上下文。 在Linux内核中,用 softing_action构造体表征一个软中断,这个构造体蕴含软中断解决函数指针和传递给该函数的参数。应用 open_softirq()函数能够注册软中断对应的处理函数,而 raise_softirq()函数能够触发一个软中断。 软中断和 tasklet运行于软中断上下文,依然属于原子上下文的一种,而工作队列则运行于过程上下文。因而,在软中断和 tasklet处理函数中不容许睡眠,而在工作队列处理函数中容许睡眠。 local_bh_disable()和 llocal_bh_enable()是内核中用于禁止和使能软中断及 tasklet底半部机制的函数 软中断模版asmlinkage void do_softirq(void){ __u32 pending; unsigned long flags; /* 判断是否在中断解决中,如果正在中断解决,就间接返回 */ if (in_interrupt()) return; /* 保留以后寄存器的值 */ local_irq_save(flags); /* 获得以后已注册软中断的位图 */ pending = local_softirq_pending(); /* 循环解决所有已注册的软中断 */ if (pending) __do_softirq(); /* 复原寄存器的值到中断解决前 */ local_irq_restore(flags);}tasklet tasklet的应用较简略,它的执行上下文是软中断,执行机会通常是顶半部返回的时候。咱们只须要定义 tasklet及其处理函数,并将两者关联则可,例如 ...

December 30, 2020 · 2 min · jiezi

关于c:面试官不讲武德居然让我讲讲蠕虫和金丝雀

蠕虫病毒是一种常见的利用Unix零碎中的毛病来进行攻打的病毒。缓冲区溢出一个常见的结果是:黑客利用函数调用过程中程序的返回地址,将寄存这块地址的指针精准指向计算机中寄存攻打代码的地位,造成程序异样停止。为了避免产生重大的结果,计算机会采纳栈随机化,利用金丝雀值查看毁坏栈,限度代码可执行区域等办法来尽量避免被攻打。尽管,古代计算机曾经能够“智能”查错了,然而咱们还是要养成良好的编程习惯,尽量避免写出有破绽的代码,以节俭贵重的工夫!蠕虫病毒简介 蠕虫是一种能够自我复制的代码,并且通过网络流传,通常无需人为干涉就能流传。蠕虫病毒入侵并齐全管制一台计算机之后,就会把这台机器作为宿主,进而扫描并感化其余计算机。当这些新的被蠕虫入侵的计算机被管制之后,蠕虫会以这些计算机为宿主持续扫描并感化其余计算机,这种行为会始终延续下去。蠕虫应用这种递归的办法进行流传,依照指数增长的法则散布本人,进而及时管制越来越多的计算机。 (起源百度百科) 缓冲区溢出 缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区自身的容量,溢出的数据笼罩在非法数据上。现实的状况是:程序会检查数据长度,而且并不容许输出超过缓冲区长度的字符。然而绝大多数程序都会假如数据长度总是与所调配的贮存空间相匹配,这就为缓冲区溢出埋下隐患。操作系统所应用的缓冲区,又被称为“堆栈”,在各个操作过程之间,指令会被长期贮存在“堆栈”当中,“堆栈”也会呈现缓冲区溢出。 (起源百度百科) 缓冲区溢出举例void echo(){ char buf[4]; /*成心设置很小*/ gets(buf); puts(buf);}void call_echo(){ echo();} 反汇编如下: /*echo*/000000000040069c <echo>: 40069c:48 83 ec 18 sub $0x18,%rsp /*0X18 == 24,调配了24字节内存。计算机会多调配一些给缓冲区*/4006a0:48 89 e7 mov %rsp,%rdi 4006a3:e8 a5 ff ff ff callq 40064d <gets>4006a8::48 89 e7 mov %rsp,%rdi4006ab:e8 50 fe ff ff callq callq 400500 <puts@plt>4006b0:48 83 c4 18 add $0x18,%rsp 4006b4:c3 retq /*call_echo*/4006b5:48 83 ec 08 sub $0x8,%rsp 4006b9:b8 00 00 00 00 mov $0x0,%eax4006be:e8 d9 ff ff ff callq 40069c <echo>4006c3:48 83 c4 08 add $0x8,%rsp 4006c7:c3 retq 在这个例子中,咱们成心把buf设置的很小。运行该程序,咱们在命令行中输出012345678901234567890123,程序立马就会报错:Segmentation fault。 ...

December 30, 2020 · 2 min · jiezi

关于c:C语言哈希表uthash的使用方法详解附下载链接

uthash简介 因为C语言自身不存在哈希,然而当须要应用哈希表的时候本人构建哈希会异样简单。因而,咱们能够调用开源的第三方头文件,这只是一个头文件:uthash.h。咱们须要做的就是将头文件复制到您的我的项目中,而后:#include "uthash.h"。因为uthash仅是头文件,因而没有可链接的库代码。 应用uthash增加,查找和删除通常是常数工夫的操作,此哈希的指标是简洁高效。它大概有1000行C。它会主动内联,因为它是作为宏实现的。 uthash还包含三个额定的头文件,次要提供链表,动静数组和字符串。utlist.h为C构造提供了链接列表宏。utarray.h应用宏实现动静数组。utstring.h实现根本的动静字符串。 github下载链接:https://github.com/troydhanso... uthash的应用定义构造体 这里咱们将id作为一个索引值,也就是键值,将name作为value。 #include "uthash.h"struct my_struct { int id; /* key */ char name[10]; UT_hash_handle hh; /* makes this structure hashable */};/*申明哈希为NULL指针*/struct my_struct *users = NULL; /* important! initialize to NULL */ 留神:肯定要蕴含UT_hash_handle hh;hh不须要初始化。它能够命名为任何名称,然而咱们个别都命名为hh。 增加 HASH_ADD_INT示意增加的键值为int类型 HASH_ADD_STR示意增加的键值为字符串类型 HASH_ADD_PTR示意增加的键值为指针类型 HASH_ADD示意增加的键值能够是任意类型 void add_user(int user_id, char *name) { struct my_struct *s; /*重复性查看,当把两个雷同key值的构造体增加到哈希表中时会报错*/ HASH_FIND_INT(users, &user_id, s); /* id already in the hash? */ /*只有在哈希中不存在ID的状况下,咱们才创立该我的项目并将其增加。否则,咱们只批改曾经存在的构造。*/ if (s==NULL) { s = (struct my_struct *)malloc(sizeof *s); s->id = user_id; HASH_ADD_INT( users, id, s ); /* id: name of key field */ } strcpy(s->name, name);} HASH_ADD_INT函数中,第一个参数users是哈希表,第二个参数id是键字段的名称。最初一个参数s是指向要增加的构造的指针。 ...

December 30, 2020 · 6 min · jiezi

关于c:qsort函数使用方法总结详细全面代码

@[toc] qsort函数原型void qsort( void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *) ); 头文件:<stdlib.h> 函数性能:qsort()函数的性能是对数组进行排序,数组有nmemb个元素,每个元素大小为size。 参数base - base指向数组的起始地址,通常该地位传入的是一个数组名 参数nmemb - nmemb示意该数组的元素个数 参数size - size示意该数组中每个元素的大小(字节数) 参数(*compar)(const void *, const void *) - 此为指向比拟函数的函数指针,决定了排序的程序。 函数返回值:无 留神:如果两个元素的值是雷同的,那么它们的前后程序是不确定的。也就是说qsort()是一个不稳固的排序算法。 compar参数 compar参数是qsort函数排序的核心内容,它指向一个比拟两个元素的函数,留神两个形参必须是const void *型,同时在调用compar 函数(compar本质为函数指针,这里称它所指向的函数也为compar)时,传入的实参也必须转换成const void *型。在compar函数外部会将const void *型转换成理论类型,见下文。 int compar(const void *p1, const void *p2); 如果compar返回值小于0(< 0),那么p1所指向元素会被排在p2所指向元素的后面 如果compar返回值等于0(= 0),那么p1所指向元素与p2所指向元素的程序不确定 如果compar返回值大于0(> 0),那么p1所指向元素会被排在p2所指向元素的前面 因而,如果想让qsort()进行从小到大(升序)排序,那么一个通用的compar函数能够写成这样: int compare (const void * a, const void * b) { if ( *(MyType*)a < *(MyType*)b ) return -1; if ( *(MyType*)a == *(MyType*)b ) return 0; if ( *(MyType*)a > *(MyType*)b ) return 1; } 留神:你要将MyType换成理论数组元素的类型。 或者 ...

December 30, 2020 · 4 min · jiezi

关于c:带你一文看懂二叉树的先中后序遍历以及层次遍历图解递归非递归代码实现

@[TOC] 先序遍历先序遍历规定 先序遍历的核心思想:1.拜访根节点;2.拜访以后节点的左子树;3.若以后节点无左子树,则拜访以后节点的右子树;即考查到一个节点后,即刻输入该节点的值,并持续遍历其左右子树。(根左右) 先序遍历举例 如图所示,采纳先序遍历拜访这颗二叉树的具体过程为: 1.拜访该二叉树的根节点,找到 1; 2.拜访节点 1 的左子树,找到节点 2; 3.拜访节点 2 的左子树,找到节点 4; 4.因为拜访节点 4 左子树失败,且也没有右子树,因而以节点 4 为根节点的子树遍历实现。但节点 2 还没有遍历其右子树,因而当初开始遍历,即拜访节点 5; 5.因为节点 5 无左右子树,因而节点 5 遍历实现,并且由此以节点 2 为根节点的子树也遍历实现。当初回到节点 1 ,并开始遍历该节点的右子树,即拜访节点 3; 6.拜访节点 3 左子树,找到节点 6; 7.因为节点 6 无左右子树,因而节点 6 遍历实现,回到节点 3 并遍历其右子树,找到节点 7; 8.节点 7 无左右子树,因而以节点 3 为根节点的子树遍历实现,同时回归节点 1。因为节点 1 的左右子树全副遍历实现,因而整个二叉树遍历实现; 因而,图 中二叉树采纳先序遍历失去的序列为:1 2 4 5 3 6 7 先序遍历代码(递归)/* * @Description: * @Version: * @Autor: Carlos * @Date: 2020-05-29 16:55:41 * @LastEditors: Carlos * @LastEditTime: 2020-05-30 17:03:23 */ #include <stdio.h>#include <string.h>#include <stdlib.h>#define TElemType int//结构结点的构造体typedef struct BiTNode{ TElemType data;//数据域 struct BiTNode *lchild,*rchild;//左右孩子指针}BiTNode,*BiTree;/** * @Description: 初始化树的函数 * @Param: BiTree *T 构造体指针的指针 * @Return: 无 * @Author: Carlos */void CreateBiTree(BiTree *T){ *T=(BiTree)malloc(sizeof(BiTNode)); //根节点 (*T)->data=1; (*T)->lchild=(BiTree)malloc(sizeof(BiTNode)); (*T)->rchild=(BiTree)malloc(sizeof(BiTNode)); //1节点的左孩子2 (*T)->lchild->data=2; (*T)->lchild->lchild=(BiTree)malloc(sizeof(BiTNode)); (*T)->lchild->rchild=(BiTree)malloc(sizeof(BiTNode)); //2节点的右孩子5 (*T)->lchild->rchild->data=5; (*T)->lchild->rchild->lchild=NULL; (*T)->lchild->rchild->rchild=NULL; //1节点的右孩子3 (*T)->rchild->data=3; (*T)->rchild->lchild=(BiTree)malloc(sizeof(BiTNode)); //3节点的左孩子6 (*T)->rchild->lchild->data=6; (*T)->rchild->lchild->lchild=NULL; (*T)->rchild->lchild->rchild=NULL; (*T)->rchild->rchild=(BiTree)malloc(sizeof(BiTNode)); //3节点的右孩子7 (*T)->rchild->rchild->data=7; (*T)->rchild->rchild->lchild=NULL; (*T)->rchild->rchild->rchild=NULL; //2节点的左孩子4 (*T)->lchild->lchild->data=4; (*T)->lchild->lchild->lchild=NULL; (*T)->lchild->lchild->rchild=NULL;}/** * @Description: 模仿操作结点元素的函数,输入结点自身的数值 * @Param:BiTree elem 就构造体指针 * @Return: 无 * @Author: Carlos */void PrintBiT(BiTree elem){ printf("%d ",elem->data);}/** * @Description: 先序遍历 * @Param: BiTree T 构造体指针 * @Return: 无 * @Author: Carlos */void PreOrderTraverse(BiTree T){ if (T) { PrintBiT(T);//调用操作结点数据的函数办法 PreOrderTraverse(T->lchild);//拜访该结点的左孩子 PreOrderTraverse(T->rchild);//拜访该结点的右孩子 } //如果结点为空,返回上一层 return;}int main() { BiTree Tree; CreateBiTree(&Tree); printf("先序遍历: \n"); PreOrderTraverse(Tree);}先序遍历代码(非递归) 因为要在遍历完某个树的根节点的左子树后接着遍历节点的右子树,为了能找到该树的根节点,须要应用栈来进行暂存。中序和后序也都波及到回溯,所以都须要用到栈。 ...

December 29, 2020 · 7 min · jiezi

关于c:二叉树的基本概念介绍与代码实现多图代码

@[TOC] 树的基本概念 图1 树的结点 结点:应用树结构存储的每一个数据元素都被称为“结点”。例如,上图1中,数据元素 1 就是一个结点; 父结点(双亲结点)、子结点和兄弟结点:对于上图1中的结点 1,2,3,4 来说,1 是 2,3,4 结点的父结点(也称为“双亲结点”),而 2,3,4 都是 1 结点的子结点(也称“孩子结点”)。对于 2,3,4 来说,它们都有雷同的父结点,所以它们互为兄弟结点。 树根结点(简称“根结点”):每一个非空树都有且只有一个被称为根的结点。上图1中,结点1就是整棵树的根结点。 叶子结点:如果结点没有任何子结点,那么此结点称为叶子结点(叶结点)。例如上图1中,结点 11,12,6,7,13,9,10都是这棵树的叶子结点。 子树和空树 子树:上图1中,整棵树的根结点为结点 1,而如果单看结点2,5,6,11,12 组成的局部来说,也是棵树,而且节点 2 为这棵树的根结点。所以称 2,5,6,11,12这几个结点组成的树为整棵树的子树;同样,结点 5,11,12 形成的也是一棵子树,根结点为5。 留神:单个结点也是一棵树,只不过根结点就是它自身。上图1中,结点 11,12,6 等都是树,且都是整棵树的子树。 晓得了子树的概念后,树也能够这样定义:树是由根结点和若干棵子树形成的。 空树:如果汇合自身为空,那么形成的树就被称为空树。空树中没有结点。 补充:在树结构中,对于具备同一个根结点的各个子树,相互之间不能有交加。例如,上图1中,除了根结点1,其余元素又各自形成了三个子树,根结点别离为 2,3,4,这三个子树相互之间没有雷同的结点。如果有,就毁坏了树的构造,不能算做是一棵树。 结点的度和档次 对于一个结点,领有的子树数(结点有多少分支)称为结点的度(Degree)。例如,上图1中,根结点1下分出了 3 个子树,所以,结点 1 的度为 3。 一棵树的度是树内各结点的度的最大值。上图1示意的树中,各个结点的度的最大值为 3,所以,整棵树的度的值是 3。 结点的档次:从一棵树的树根开始,树根所在层为第一层,根的孩子结点所在的层为第二层,顺次类推。对于上图1来说,1 结点在第一层,2,3,4 为第二层,5,6,7,8,9,10在第三层,11,12,13在第四层。 一棵树的深度(高度) 是树中结点所在的最大的档次。上图1树的深度为 4。 如果两个结点的父结点虽不雷同,然而它们的父结点处在同一档次上,那么这两个结点互为堂兄弟。例如,上图1中,结点5,6,7,8,9,10 的父结点都在第二层,所以之间为堂兄弟的关系。 有序树和无序树 如果树中结点的子树从左到右看,谁在右边,谁在左边,是有规定的,这棵树称为有序树;反之称为无序树。 在有序树中,一个结点最右边的子树称为"第一个孩子",最左边的称为"最初一个孩子"。拿上图1来说,如果是其自身是一棵有序树,则以结点 2 为根结点的子树为整棵树的第一个孩子,以结点 4 为根结点的子树为整棵树的最初一个孩子。 森林 由 m(m >= 0)个互不相交的树组成的汇合被称为森林。上图1中,别离以2,3,4为根结点的三棵子树就能够称为森林。 后面讲到,树能够了解为是由根结点和若干子树形成的,而这若干子树自身是一个森林,所以,树还能够了解为是由根结点和森林组成的。用一个式子示意为:Tree =(root,F) 其中,root 示意树的根结点,F 示意由 m(m >= 0)棵树组成的森林。 ...

December 29, 2020 · 2 min · jiezi

关于c:矩阵的三种存储方式三元组法-行逻辑链接法-十字链表法

在介绍矩阵的压缩存储前,咱们须要明确一个概念:对于非凡矩阵,比方对称矩阵,稠密矩阵,上(下)三角矩阵,在数据结构中雷同的数据元素只存储一个。@[TOC] 三元组程序表 稠密矩阵因为其本身的稠密个性,通过压缩能够大大节俭稠密矩阵的内存代价。具体操作是:将非零元素所在的行、列以及它的值形成一个三元组(i,j,v),而后再按某种法则存储这些三元组,这种办法能够节约存储空间 。 如下图所示为一个稠密矩阵,咱们应该怎么样存储呢? 若对其进行压缩存储,咱们能够将一个非零数组元素的三元看成一个单位存入一维数组,具体如下所示。比方(1,1,1)代表第一行第一列的元素值为1。留神,这里咱们只存储非零值。 具体代码如下: #include<stdio.h>#include <stdlib.h>#include <time.h>#define NUMBER 3//三元组构造体typedef struct { //行标r,列标c int r,c; //元素值 int data;}triple;//矩阵的构造示意typedef struct { //存储该矩阵中所有非0元素的三元组 triple data[NUMBER]; //row和column别离记录矩阵的行数和列数,num记录矩阵中所有的非0元素的个数 int row,column,num;}TSMatrix;//输入存储的稠密矩阵void print(TSMatrix M);int main() { int i; srand((unsigned)time(NULL)); TSMatrix M; M.row=3; M.column=3; M.num=3; //初始化矩阵 for(i=0;i<M.num;i++){ //随机数范畴[1,3] M.data[i].r=rand()%M.num+1; M.data[i].c=rand()%M.num+1; M.data[i].data=rand()%10; } print(M); return 0;}void print(TSMatrix M){ for(int i=1;i<=M.row;i++){ for(int j=1;j<=M.column;j++){ int value =0; for(int k=0;k<M.num;k++){ //遍历时的r,c行列值和理论存储row,column行列值的比拟,雷同的话就阐明有非零元素,打印存储的非0值 if(i == M.data[k].r && j == M.data[k].c){ printf("%d ",M.data[k].data); value =1; break; } } if(value == 0) printf("0 "); } printf("\n"); }}行逻辑链接的程序表 应用三元组程序表存储稠密矩阵,咱们每次拜访其中一个元素都要遍历整个矩阵,效率比拟低。咱们能够应用一个一维数组来存储每行第一个非零元素在一维数组中的地位,这样就能够晋升拜访效率。这样的表就叫做行逻辑链接的程序表。 下图为一个稠密矩阵,当应用行逻辑链接的程序表对其进行压缩存储时,须要做以下两个工作: 1.将矩阵中的非 0 元素采纳三元组的模式存储到一维数组 data 中: 2.应用数组 rpos 记录矩阵中每行第一个非 0 元素在一维数组中的存储地位。 通过以上两步操作,即实现了应用行逻辑链接的程序表存储稠密矩阵。 此时,如果想从行逻辑链接的程序表中提取元素,则能够借助 rpos 数组进步遍历数组的效率。 例如,提取图 1 稠密矩阵中的元素 2 的过程如下: 由 rpos 数组可知,第一行首个非 0 元素位于data[1],因而在遍历此行时,能够间接从第 data[1] 的地位开始,始终遍历到下一行首个非 0 元素所在的地位(data[3])之前; 同样遍历第二行时,由 rpos 数组可知,此行首个非 0 元素位于 data[3],因而能够间接从第 data[3] 开始,始终遍历到下一行首个非 0 元素所在的地位(data[4])之前;遍历第三行时,由 rpos 数组可知,此行首个非 0 元素位于 data[4],因为这是矩阵的最初一行,因而始终遍历到 rpos 数组完结即可(也就是 data[tu],tu 指的是矩阵非 0 元素的总个数)。具体代码如下 ...

December 29, 2020 · 4 min · jiezi

关于c:字符串的三种存储方式

@[TOC] 在数据结构中,字符串要独自用一种存储构造来存储,称为串存储构造。这里的串指的就是字符串。无论学习哪种编程语言,操作最多的总是字符串。咱们平时应用最多的存储构造无疑是利用定长数组存储。然而这种存储构造须要提前调配空间,当咱们不晓得字符串长度的时候,过大的分配内存无疑是一种节约。因而,正当的抉择字符串的存储形式显得分外重要。上面将顺次介绍三种存储形式。 定长顺序存储 字符串的定长顺序存储构造,能够了解为采纳 "固定长度的顺序存储构造" 来存储字符串,因而限定了其底层实现只能应用动态数组。 应用定长顺序存储构造存储字符串时,需联合指标字符串的长度,事后申请足够大的内存空间。 例如,采纳定长顺序存储构造存储 "feizhufeifei",通过目测得悉此字符串长度为12(不蕴含结束符 '\0'),因而咱们申请的数组空间长度至多为 12,用 C 语言示意为: char str[18] = "feizhufeifei"; 上面是具体的C语言实现 #include<stdio.h>int main(){ char str[15]="feizhufeifei"; printf("%s\r\n",str); return 0;} 这种存储形式根本是初学者都应该把握的。上面介绍第二种存储形式。 动静数组存储 首先咱们应该明确两个概念:堆和栈。 堆是由咱们程序员本人治理的,当过程调用malloc等函数分配内存时,新调配的内存就被动态分配到堆上,当利用free等函数开释内存时,被开释的内存从堆中被剔除。 栈又称堆栈,是用户存放程序长期创立的变量,也就是咱们函数{}中定义的变量,但不包含static申明的变量,static意味着在数据段中寄存变量。除此之外,在函数被调用时,其参数也会被压入发动调用的过程栈中,并且待到调用完结后,函数的返回值也会被寄存回栈中,因为栈的先进后出特点,所以栈特地不便用来保留、复原调用现场。从这个意义上讲,咱们能够把堆栈看成一个存放,替换长期数据的内存区。 当咱们调用malloc时,就会在堆上划分一块空间给咱们应用,具体代码如下: //创立了一个动静数组str,通过应用 malloc 申请了 10个 char 类型大小的堆存储空间。char * str = (char*)malloc(10*sizeof(char)); 动静数组的劣势是长度可变,依据须要动静进行调配。当我不想申请新的变量,然而又想要扩充str的空间怎么办呢?这个时候realloc函数就起作用了。 //通过应用这行代码,之前具备10 个 char 型存储空间的动静数组,其容量扩充为可存储 20 个 char 型数据。str = (char*)realloc(str, 20*sizeof(char)); 上面通过一个合并两个字符串的例子来更好地了解下动态分配过程。 /* * @Description: 字符串的堆动静堆分配内存 * @Version: V1.0 * @Autor: Carlos * @Date: 2020-05-25 * @LastEditors: Carlos * @LastEditTime: 2020-05-25 */ #include <stdio.h>#include <stdlib.h>#include <string.h>//打印测试语句#define DEBUG 0#if DEBUG#define DBG_PRINTF(fmt, args...) \do\{\ printf("<<File:%s Line:%d Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\ printf(fmt, ##args);\}while(0)# else# define DBG_PRINTF(fmt, args...)#endifint main(){ char *s1 = NULL; char *s2 = NULL; s1 = (char *)malloc(5*sizeof(char *)); strcpy(s1,"test"); DBG_PRINTF("s1:%s\r\n",s1); s2 = (char *)malloc(7*sizeof(char *)); strcpy(s2,"string"); DBG_PRINTF("s2:%s\r\n",s2); int length1 = strlen(s1); int length2 = strlen(s2); //尝试将合并的串存储在 s1 中,如果 s1 空间不够,则用realloc动静申请 if(length1<length1+length2) s1 =(char*) realloc(s1,(length1 + length2+1) * sizeof(char)); //合并两个串到 s1 中 for(int i = length1; i < length1 + length2;i++) s1[i] = s2[i - length1]; //串的开端要增加 \0,防止出错 s1[length1 + length2] = '\0'; printf("s1+s2:%s", s1); //用完动静数组要立刻开释 free(s1); free(s2); return 0;}块链存储 块链存储就是利用链表来存储字符串。本文应用的是无头结点的链表构造(即链表的第一个头结点也存储数据)。咱们晓得,单链表中的 "单" 强调的仅仅是链表各个节点只能有一个指针,并没有限度数据域中存储数据的具体个数。因而在设计链表节点的构造时,能够令各节点存储多个数据。 例如,咱们要用链表存储feizhu字符串,链表构造如下所示: 咱们也能够每个链表存储四个字符,那么最初一个节点必定不会占满。这时,咱们能够应用#或者其余符号将其填满。 怎么确定链表中每个节点存储数据的个数呢? 链表各节点存储数据个数的多少可参考以下几个因素: 串的长度和存储空间的大小:若串蕴含数据量很大,且链表申请的存储空间无限,此时应尽可能的让各节点存储更多的数据,进步空间的利用率(每多一个节点,就要多申请一个指针域的空间);反之,如果串不是特地长,或者存储空间足够,就须要再联合其余因素综合思考; 程序实现的性能:如果理论场景中须要对存储的串做大量的插入或删除操作,则应尽可能减少各节点存储数据的数量;反之,就须要再联合其余因素。 上面是具体的代码实现。 ...

December 29, 2020 · 2 min · jiezi

关于c:2020中兴捧月傅里叶派记录

前段时间看到了同学转发的中兴通讯的较量链接,之前也没有加入过算法类的较量,这次打算报着试一试的态度加入下,减少下教训。在初步看了几个门派的题目简介后,发现只有傅里叶派比拟适宜本人,所以最终抉择了傅里叶派。@[TOC] 题目形容 在某片边远的大陆上,寓居着两个世代敌对的部落,别离是部落A和部落B。他们一起耕耘劳作,互相帮助,亲如一家。长此以往,部落里的每个人都在对方部落里找到了志趣相投,相互观赏的好敌人。有的人性情激情开朗,好敌人很多;有的人性情沉稳内敛,好敌人绝对少一些。 每到秋天丰登的节令,这两个部落的人民都会汇集在一起举办隆重的“丰登祭”,来祷告下一年的风调雨顺。往年的丰登祭马上又要举办了。为了进一步增进两个部落的友情,也为了明年能有一个好收成,这两个部落的祭司们磋商后决定:在往年的丰登祭前举办一场特地的“击鼓传花”游戏。只不过游戏中并非有人真的击鼓,并且所传递的“花”也不是真的花,而是期待在丰登祭上献上的祭品。 游戏规则如下: 1. 两个部落的所有人都能够当时筹备本人的祭品,且每个人的祭品款式都不同,每一个祭品都别离盛放在一个绝对应的木托盘里;筹备此祭品的人相熟本人的祭品; 2. 每个人能够筹备的祭品数量不限;祭品的最小不可分割单位是1份; 3. 游戏开始后,在整个游戏过程中,每个人都能且只能将祭品(包含木托盘)传递给本人在对方部落里的好敌人们,每个好友能够接管的祭品数量不限; 4. 收到祭品的人必须在盛放此祭品的木托盘上刻上本人的名字(代表留下本人美妙的祝福),随后按依照上一条规定,持续传递; 5. 如果祭品回到最后筹备此祭品的人手中,此人也在木托盘上刻上本人的名字之后,终止传递; 6. 木托盘上不容许呈现反复的人名,如果无奈满足此条件,则不再持续传递该祭品; 7. 当所有的祭品都不再传递后,游戏完结; 游戏发展得十分顺利。游戏完结后,祭司们将收集同时满足如下三个条件的祭品用于接下来的丰登祭流动: 1. 此祭品回到了最后筹备它的人手中; 2. 盛放此祭品的木托盘上至多有4个名字,至少有14个名字; 3. 如果有多个木托盘上的名字齐全一样(不辨别名字的排列程序),则从其中随机抉择一个木托盘所对应的祭品。 已知这两个部落里的所有人都不重名,并且部落A的人和部落B的人之间的好敌人关系以附件的csv数据表格文件给出,其中行索引代表部落A中的人,列索引代表部落B中的人,表格中的数字“1”代表他们两人是好敌人,“0”代表他们两人不是好敌人。请问: 如果以木托盘上的名字的数量对用于丰登祭的祭品分类,每一类别离最多有多少个祭品? 木托盘上有4个名字的祭品最多有()个;木托盘上有6个名字的祭品最多有()个;木托盘上有8个名字的祭品最多有()个;木托盘上有10个名字的祭品最多有()个;木托盘上有12个名字的祭品最多有()个;木托盘上有14个名字的祭品最多有()个; 设计思路 根据数据来看,A部落有256集体,B部落有640集体。A部落和B部落的人只能给对方部落的人,以人为节点建图,能够发现这是一个二分图。excel数据中给的是邻接矩阵,咱们转换成邻接表,而后题目的问题转化成:每个节点登程能够有很多条门路,问其中环的数量有几个,并且是门路大于等于4小于等于14的环的数量,最初要去重。咱们以每个节点作为终点,进行DFS,并进行计数,统计每种长度的门路条数,最初再去重。(此处感激群里的大佬提供的思路!) 算法形容 1.将B部落的人用编号0-639示意,将A部落的人用编号700-955示意,将excel中的邻接矩阵转换为邻接表。 2. 第一列示意i号节点,第二列数字示意有k个好敌人,再前面k列的数字就示意好敌人别离是谁。 3.对每个节点开始进行深度优先搜寻,对i个节点,开始先赋给vis数组初值,所有节点都没拜访过,拜访i,而后顺次拜访与他相邻的点,始终递归上来,直到找到拜访过的节点。若该节点被拜访过,则判断该节点是不是根节点,如果是则满足题意,记录该门路长度,返回。否则,就此节点就不再递归,间接返回。因为题目只有门路为4-14的环,所以设置递归深度小于等于14。 4.因为用DFS算进去的后果是会呈现反复: 比方说1->2->3->4->1 用星标记另外一个部落 因为是无向图: 他会呈现1->4->3->2->1 也会呈现3->4->1->2->3 也会呈现3->2->1->4->3 …… 如果循环节是i就会由i 2种可能会被DFS到,所以要去重,最初门路长度为i的数量的要除以i 2,最初失去去重后果。 5.剪枝优化 在第二阶段的数据集变成了1344 1344。进行暴力DFS会做很多无用功。所以要进行剪枝优化。打算把在上述的算法中,门路1->2->3->4->1在别的节点的DFS中还会呈现3->4->1->2*->3,这实际上是一个圈,咱们须要在DFS中防止这种反复的计算。思考到在第i个节点统计完圈了当前,在第i+1个节点中,如果拜访到第i个节点,那么咱们间接跳过这个节点,就能够防止在前面的dfs中反复呈现后面统计过的点。那么咱们只须要创立一个数组vv[],用来统计哪些节点之前统计过,而后给他附上DFS过的标记,再前面的节点的统计的时候能够间接跳过这个点,就能够不反复算某个圈,这样就能够在前面的DFS达到剪枝的成果。 总结 期间被其余事件耽搁了很久,想进去剪枝优化的办法曾经是5.8,惋惜最初程序没有调试进去。二阶段只有五分的得分(一阶段后果完全正确)。总得来说还是本人能力有余。这次较量裸露了本人的有余,对于数据结构和算法理解比拟少。非常感谢群里各位大佬的探讨,参考了各位同学的思路后,本人才晓得如何去把问题形象进去。在有了思路后,在百度,google,github搜寻相干问题和代码,参考了一些文章,本人也学习了对于图论的一些常识。接下来有工夫的话要重点相熟下根本的算法,刷力扣题目,一直晋升本人的编程能力。 赛后看大家在群里探讨题目的各种解法,老师官网的回复是,给的csv数据集有肯定法则能够利用。当然,如果有很强的编程能力,DFS+剪枝也是能够很快给出后果的(C++最快1min之内能够出后果)。对于法则的利用,能够往QC-LDPC码这个方向思考,这是5G数据信道的编码方式。如果有趣味能够钻研下。 如遇到排版错乱的问题,能够通过以下链接拜访我的CSDN。 **CSDN:[CSDN搜寻“嵌入式与Linux那些事”]

December 29, 2020 · 1 min · jiezi

关于c:S3C2440移植linux342内核之内核框架介绍及简单修改

@[TOC] uboot启动内核剖析 进入cmd_bootm.c,找到对应的bootm命令对应的do_bootm(): int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){boot_os_fn *boot_fn; //boot_fn是个数组函数 ... ..boot_fn(0, argc, argv, &images); //调用数组函数 ... ...} boot_os_fn是个typedef型,如下图所示: 因为定义了宏CONFIG_BOOTM_LINUX,最终会跳转到do_bootm ->do_bootm_linux() 代码如下所示: int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images){ /* No need for those on ARM */ if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE) return -1; if (flag & BOOTM_STATE_OS_PREP) { boot_prep_linux(images); return 0; } if (flag & BOOTM_STATE_OS_GO) { boot_jump_linux(images); return 0; } boot_prep_linux(images); //该函数会将各个tag参数保留在指定地位,比方:内存tag、bootargs环境变量tag、串口tag等 boot_jump_linux(images); //该函数会跳转到内核起始地址 return 0;} 最终跳转到do_bootm ->do_bootm_linux-> boot_jump_linux() ...

December 29, 2020 · 2 min · jiezi

关于c:S3C2440移植linux342内核之修改分区以及制作根文件系统

上一节S3C2440移植linux3.4.2内核之内核框架介绍及简略批改咱们简略配置了内核,这节来依据持续批改内核。 启动内核 内核启动的打印信息如下图所示 能够看到内核有8个分区,而咱们的uboot只有4个分区。 0x00000000-0x00040000 : "bootloader" //寄存uboot0x00040000-0x00060000 : "params" //寄存环境变量0x00060000-0x00260000 : "kernel" //寄存内核0x00260000-0x10000000 : "rootfs" //寄存文件系统 所以接下来咱们来批改内核分区 批改内核分区 在si里搜寻上图呈现的”S3C2410 flash partition”字段,找到位于common-smdk.c中,外面有个数组smdk_default_nand_part[],内容如下所示: 批改smdk_default_nand_part[]数组(位于arch/arm/mach-s3c24xx/common-smdk.c)批改为: static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "bootloader", //0x00000000-0x00040000 .size = SZ_256K, .offset = 0, }, [1] = { .name = "params", //0x00040000-0x00060000 .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [2] = { .name = "kernel", //0x00060000-0x00260000 .offset = MTDPART_OFS_APPEND, .size = SZ_2M, }, [3] = { .name = "rootfs", //0x00260000-0x10000000 .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, }}; 下面局部宏的定义,如下所示: MTDPART_OFS_RETAIN: 填在offset里,示意先后保留多少size空间大小 MTDPART_OFS_NXTBLK: 填在offset里,示意从下一个块开始 MTDPART_OFS_APPEND: 填在offset里,示意该分区地位附加在上个分区完结的地址上 MTDPART_SIZ_FULL: 填在size里,示意剩下的内存size都归于该分区若须要mini2440的机器ID,则还须要批改mini2440单板对应的mach-mini2440.c 因为该单板的mtd分区也不对,将外面的mini2440_default_nand_part[]内容改为和下面一样,拷贝文件到ubuntu从新编译下载内核 ...

December 29, 2020 · 2 min · jiezi

关于c:S3C2440移植linux342内核之支持YAFFS文件系统

上一节S3C2440移植linux3.4.2内核之批改分区以及制作根文件系统咱们构建了根文件系统,这节咱们批改内核反对yaffs2文件系统@[TOC] 移植Linux3.4.2内核其余文章链接: S3C2440移植linux3.4.2内核之内核框架介绍及简略批改 S3C2440移植linux3.4.2内核之批改分区以及制作根文件系统 S3C2440移植linux3.4.2内核之反对YAFFS文件系统 S3C2440移植linux3.4.2内核之内核裁剪 获取yaffs2源码并给内核打补丁 首先获取yaffs2源码(参考git命令应用详解) cd /work/nfs_rootgit clone git@github.com:lifeyx/yaffs2.git//若下载呈现error:403,能够试试vi /etc/resolv.conf,将nameserver地址改为: 114.114.114.114 将yaffs2源码来配置到内核里(使内核反对yaffs2) vi /work/nfs_root/yaffs2/README-linux 参考上图: /*给内核打补丁*/cd /work/nfs_root/yaffs2/./patch-ker.sh c m /work/system/linux-3.4.2//c:将yffs2文件夹copy到linux-3.4.2/fs里, m:指定多版本/*通过menuconfig,来使内核反对yaffs2*/cd /work/system/linux-3.4.2/make menuconfig 搜寻yaffs,找到位于: -> File systems -> Miscellaneous filesystems (MISC_FILESYSTEMS [=y]) <*> yaffs2 file system support //按Y键,使内核反对yaffs2编译内核make uImage 如下图所示,呈现很多谬误: 如上图,问题都是出在fs/yaffs2/下,很多error都讲述:调用的成员名,在struct mtd_info构造体里没有定义. 批改yaffs2源码 接下来咱们便将fs/yaffs2文件夹增加到内核3.4的si工程中. 而后进入yaffs2/yaffs_vfs.c,第1958行:(linux下显示的是yaffs_vfs.c,vi能够关上yaffs_vfs.c。然而在si工程中没有找到yaffs_vfs.c,所以从ubuntu中拷贝一份yaffs_vfs.c增加到si中。) 然而发现struct mtd_info *mtd的构造体mtd_info定义的是_sync成员,如下图所示: 所以接下来只有遇到与struct mtd_info无关的error,都在成员前加上: _ 比方: 将yaffs_vfs.c文件2514~2515行的mtd->sync 改为: mtd->_sync 将yaffs_vfs.c文件2702行的mtd->erase改为: mtd->_erase 将yaffs_vfs.c文件2703行的mtd->read改为: mtd->_read ... ... 并将yaffs_vfs.c的第2967行的 root = d_alloc_root(inode);改为: root = d_make_root(inode); 全副批改后,将yaffs_vfs.c上传到ubuntu,再次make uImage, 报错如下 ...

December 29, 2020 · 1 min · jiezi

关于c:S3C2440移植linux342内核之内核裁剪

上一节S3C2440移植linux3.4.2内核之反对YAFFS文件系统咱们批改了内核反对了yaffs2文件系统,这节咱们裁剪内核。@[TOC]移植Linux3.4.2内核其余文章链接: S3C2440移植linux3.4.2内核之内核框架介绍及简略批改 S3C2440移植linux3.4.2内核之批改分区以及制作根文件系统 S3C2440移植linux3.4.2内核之反对YAFFS文件系统 S3C2440移植linux3.4.2内核之内核裁剪 为什么要裁剪内核? 因为mtd的kernel分区只有2M大,而理论内核有2.37MB,所以须要裁剪到小于2M(或者批改mtd分区值) 首先裁剪内核里无关的CPU/单板文件 通过vi .config,而后搜寻2440,如下图所示: 而后参考上图,make menuconfig 进入System Type ---> SAMSUNG S3C24XX SoCs Support: 如上图所示,CPU下只抉择2440,单板文件下只抉择SMDK2440以及MINI2440相干 裁剪无关的文件系统ext2、ext3、ext4 从新make menuconfig,进入File systems,去掉: < > Second extended fs support //ext2< > Ext3 journalling file system support < > The Extended 4 (ext4) filesystem裁剪光盘文件系统CD-ROM进入File systems---> CD-ROM/DVD Filesystems,去掉: < > ISO 9660 CDROM file system support 裁剪不罕用的杂项文件系统进入File systems---> Miscellaneous filesystems,去掉: < > Compressed ROM file system support (cramfs) // cramfs压缩文件零碎[ ] Include support for ZLIB compressed file systems //zlib压缩文件零碎< > ROM file system support // Romfs通过内核启动信息来裁剪 如下图所示,内核启动时,看到初始化了PS2鼠标驱动 因为,咱们2440板子只有USB接口,所以须要裁剪掉PS2鼠标驱动: ...

December 29, 2020 · 1 min · jiezi

关于c:S3C2440移植uboot之编译烧写uboot

@[TOC] 移植环境主 机:VMWare--ubuntu16.04开发板:S3C2440编译器:arm-linux-gcc-4.3.2.tgzu-boot:u-boot-2012.04.01.tar.bz2 获取uboot 进入https://www.denx.de/wiki/U-Bo... 始终往下拉抉择如下 将下载好的压缩包解压到任意文件夹,并创立source insight工程。将下载好的u-boot-2012.04.01.tar_2.bz2放到ubuntu服务器/work/system中。执行以下命令解压uboot: cd /work/systemtar xjf u-boot-2012.04.01.tar_2.bz2进入文件夹配置uboot cd u-boot-2012.04.01make smdk2410_configmake 编译报错如下 产生了段谬误,arm-linux-ld 工具链版本不反对uboot中新的属性。咱们的穿插编译链版本为3.4.5,因而,须要更换新的穿插编译链反对uboot。 更新穿插编译工具 将arm-linux-gcc-4.3.2.tar.bz2上传到ubuntu服务器/work/tools。将arm-linux-gcc-4.3.2.tar.bz2解压到/work/tools/tmp文件夹。 cd /work/toolsmkdir tmptar xjf arm-linux-gcc-4.3.2.tar.bz2 -C tmp/ 解压实现,进入tmp目录查看usr/local/arm/4.3.2/bin文件夹,有各种工具链。 cd tmp/usr/local/arm/4.3.2/binls 配置环境变量 将新的工具链解压到根目录并配置环境变量,新的穿插编译工具链就失效了。 sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C / 为什么要设置环境变量? 因为咱们执行ls 命令时,零碎会去寻找所执行的命令,去哪里找呢?就在环境变量中。在ubuntu查看环境变量如下 设置新的环境变量 export PATH=/usr/local/arm/4.3.2/bin:.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games 执行 arm-linux-gcc -v 永恒批改环境变量须要批改/etc/environment sudo vi /etc/environment 批改前 PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/work/tools/gcc-3.4.5-glibc-2.3.6/bin" 批改后 PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.3.2/bin" 重启后即可失效。 再次编译新的uboot cd /work/sysytem/u-boot-2012.04.01make distcleanmake smdk2410_configmake 编译没有报错,将新的uboot.bin烧写到开发板发现,程序不能运行。所以须要剖析并批改uboot下一节S3C2440移植uboot之启动过程概述咱们将剖析UBOOT的整个启动流程。 如遇到排版错乱的问题,能够通过以下链接拜访我的CSDN。 **CSDN:[CSDN搜寻“嵌入式与Linux那些事”]

December 29, 2020 · 1 min · jiezi

关于c:S3C2440移植uboot之新建单板时钟SDRAM串口

上一节S3C2440移植uboot之启动过程概述咱们咱们剖析了uboot启动流程,这节将开始新建一块单板反对S3C2440。@[TOC] 1.新建单板1.1 将2410的单板文件夹拷贝成2440:cd /work/system/u-boot-2012.04.01/board/samsungcp smdk2410 smdk2440 -rf 1.2 将2410的头文件拷贝成2440cd ../../include/configs/cp smdk2410.h smdk2440.h批改2440文件夹下的smdk2410.c和Makefile文件 2.批改boards.cfg,使uboot反对2440单板:仿照smdk2410 arm arm920t - samsung s3c24x0增加:smdk2440 arm arm920t - samsung s3c24x0 应用make smdk2440_config命令(命令便会调用include/configs/smdk2440.h和board/samsung/smdk2440里的文件来配置uboot) 同样的,在windows下把u-boot-2012.04.01.tar_2\u-boot-2012.04.01\board\samsung下的smdk2410拷贝成smdk2440,把u-boot-2012.04.01.tar_2\u-boot-2012.04.01\include\configs smdk2410.h复制为smdk2440.h。在source insight中增加2440相干的文件,去掉2410局部。 3.批改uboot零碎时钟 从start.s开始剖析启动过程,浏览代码发现有余:UBOOT里先以60MHZ的时钟计算参数来设置内存控制器,然而MPLL还未设置 关上u-boot-2012.04.01.tar_2\u-boot-2012.04.01\board\samsung\smdk2440\smdk2440.c 正文掉以下局部,把MPLL的设置放到start.S里,勾销board_early_init_f里对MPLL的设置 # 4.烧写批改后的uboot 烧写测试时,因为新的uboot较大,能够应用nor上的旧uboot,通过DNW烧写新的uboot到nor下面. 因为咱们的分区不够大,所以应用命令烧写 usb 1 30000000 //应用usb下载到SDRAM上,1示意始终下载,直到实现 //而后关上DNW,传输新的uboot.bin给usbprotect off all //敞开nor的写爱护erase 0 +7FFFF //擦除nor上的 0~7FFFF地址内容, (512k*1024-1)=+7FFF=擦除长度=512kb,要大于新的uboot.bin才行cp.b 30000000 0 80000 //将SDRAM上的新的uboot.bin,拷贝到nor上(烧写512K) 烧写实现后,重启,通过JTAG调试的读地址命令, 判断是否与新uboot文件统一应用JTAG调试时,发现向0x30000000地址上写值出错 拷贝之前写bootloader的初始化时钟的代码 #define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))/* 2. 设置时钟 */ ldr r0, =0x4c000014 // mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1 mov r1, #0x05; // FCLK:HCLK:PCLK=1:4:8 str r1, [r0] /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */ mrc p15, 0, r1, c1, c0, 0 /* 读出管制寄存器 */ orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */ mcr p15, 0, r1, c1, c0, 0 /* 写入管制寄存器 */ /* MPLLCON = S3C2440_MPLL_200MHZ */ ldr r0, =0x4c000004 ldr r1, =S3C2440_MPLL_400MHZ str r1, [r0] /* 启动ICACHE */ mrc p15, 0, r0, c1, c0, 0 @ read control reg orr r0, r0, #(1<<12) mcr p15, 0, r0, c1, c0, 0 @ write it back 批改start.s如下 批改cpu初始化代码 批改前 ...

December 27, 2020 · 2 min · jiezi

关于c:S3C2440移植uboot之支持NAND启动

上一节S3C2440移植uboot之新建单板_时钟_SDRAM_串口移植uboot初始化了时钟,配置了反对串口,这一节咱们持续批改uboot反对NAND启动。@[TOC] 1.去掉 "-pie"选项 参考之前uboot应用的start.S, init.c来批改uboot代码新的uboot链接地址位于0,且在arm-linux-ld时加了"-pie"选项, 使得u-boot.bin里多了"*(.rel*)", "*(.dynsym)",从而程序十分大,不利于从NAND启动(重定位之前的启动代码应该少于4K). 所以接下来批改代码,并勾销"-pie"选项. 应用grep "-pie" * -nR找到: arch/arm/config.mk:75:LDFLAGS_u-boot += -pie // LDFLAGS: arm-linux-ld的参数 所以屏蔽arch/arm/config.mk文件的"LDFLAGS_u-boot += -pie"这行即可 2.批改之前的init.c 将以前写uboot里的init.c放入board/samsung/smdk2440目录, 并查看是否有同名函数名,若函数只在同文件应用,则增加static.并批改Makefile 减少对init.c的反对 vi board/samsung/smdk2440/Makefile 批改include/configs/smdk2440.h文件,将CONFIG_SYS_TEXT_BASE宏改为0x33f80000,也就是uboot重定位后的地位, 这里留了512K空间供应uboot重定位批改完的代码如下所示 /* NAND FLASH控制器 */#define NFCONF (*((volatile unsigned long *)0x4E000000))#define NFCONT (*((volatile unsigned long *)0x4E000004))#define NFCMMD (*((volatile unsigned char *)0x4E000008))#define NFADDR (*((volatile unsigned char *)0x4E00000C))#define NFDATA (*((volatile unsigned char *)0x4E000010))#define NFSTAT (*((volatile unsigned char *)0x4E000020))/* GPIO */#define GPHCON (*(volatile unsigned long *)0x56000070)#define GPHUP (*(volatile unsigned long *)0x56000078)/* UART registers*/#define ULCON0 (*(volatile unsigned long *)0x50000000)#define UCON0 (*(volatile unsigned long *)0x50000004)#define UFCON0 (*(volatile unsigned long *)0x50000008)#define UMCON0 (*(volatile unsigned long *)0x5000000c)#define UTRSTAT0 (*(volatile unsigned long *)0x50000010)#define UTXH0 (*(volatile unsigned char *)0x50000020)#define URXH0 (*(volatile unsigned char *)0x50000024)#define UBRDIV0 (*(volatile unsigned long *)0x50000028)#define TXD0READY (1<<2)void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len);static int isBootFromNorFlash(void){ volatile int *p = (volatile int *)0; int val; val = *p; *p = 0x12345678; if (*p == 0x12345678) { /* 写胜利, 是nand启动 */ *p = val; return 0; } else { /* NOR不能像内存一样写 */ return 1; }}void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len){ int i = 0; /* 如果是NOR启动 */ if (isBootFromNorFlash()) { while (i < len) { dest[i] = src[i]; i++; } } else { //nand_init(); nand_read_ll((unsigned int)src, dest, len); }}void clear_bss(void){ extern int __bss_start, __bss_end__; int *p = &__bss_start; for (; p < &__bss_end__; p++) *p = 0;}void nand_init_ll(void){#define TACLS 0#define TWRPH0 1#define TWRPH1 0 /* 设置时序 */ NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ NFCONT = (1<<4)|(1<<1)|(1<<0); }static void nand_select(void){ NFCONT &= ~(1<<1); }static void nand_deselect(void){ NFCONT |= (1<<1); }static void nand_cmd(unsigned char cmd){ volatile int i; NFCMMD = cmd; for (i = 0; i < 10; i++);}static void nand_addr(unsigned int addr){ unsigned int col = addr % 2048; unsigned int page = addr / 2048; volatile int i; NFADDR = col & 0xff; for (i = 0; i < 10; i++); NFADDR = (col >> 8) & 0xff; for (i = 0; i < 10; i++); NFADDR = page & 0xff; for (i = 0; i < 10; i++); NFADDR = (page >> 8) & 0xff; for (i = 0; i < 10; i++); NFADDR = (page >> 16) & 0xff; for (i = 0; i < 10; i++); }static void nand_wait_ready(void){ while (!(NFSTAT & 1));}static unsigned char nand_data(void){ return NFDATA;}void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len){ int col = addr % 2048; int i = 0; /* 1. 选中 */ nand_select(); while (i < len) { /* 2. 收回读命令00h */ nand_cmd(0x00); /* 3. 收回地址(分5步收回) */ nand_addr(addr); /* 4. 收回读命令30h */ nand_cmd(0x30); /* 5. 判断状态 */ nand_wait_ready(); /* 6. 读数据 */ for (; (col < 2048) && (i < len); col++) { buf[i] = nand_data(); i++; addr++; } col = 0; } /* 7. 勾销选中 */ nand_deselect();}3.批改start.s重定位局部 批改arch/arm/cpu/arm920t/start.S,更改重定位代码。因为nand启动时,2440未初始化之前只有前4K可读写,所以将重定位代码放在start.S的cpu_init_crit(初始化SDRAM)段前面。批改后代码如下 ...

December 27, 2020 · 6 min · jiezi

关于c:S3C2440移植uboot之启动过程概述

上节烧写了uboot到开发板,不能运行。这节咱们剖析uboot从新编译uboot,由最初一条链接命令开始剖析uboot@[TOC] 下图为编译uboot后显示的最初一条链接命令。 1.剖析start.S 关上uboot.lds,发现链接地址为0,所以新的uboot只能在nor flash运行。运行开始文件为start.o。 上面咱们剖析arch/arm/cpu/arm920t/start.S 从start_code开始运行 .globl _start //申明_start全局符号,这个符号会被lds链接脚本用到_start: b start_code //跳转到start_code符号处,0x00 ldr pc, _undefined_instruction //0x04 ldr pc, _software_interrupt //0x08 ldr pc, _prefetch_abort //0x0c ldr pc, _data_abort //0x10 ldr pc, _not_used //0x14 ldr pc, _irq //0x18 ldr pc, _fiq //0x20_undefined_instruction: .word undefined_instruction //定义_undefined_instruction指向undefined_instruction(32位地址)_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word fiq .balignl 16,0xdeadbeef //balignl应用,参考http://www.cnblogs.com/lifexy/p/7171507.html2._start会跳转到start_code处start_code: /*设置CPSR寄存器,让CPU进入管理模式*/ mrs r0, cpsr //读出cpsr的值 bic r0, r0, #0x1f //清位 orr r0, r0, #0xd3 //位或 msr cpsr, r0 //写入cpsr#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) /* * relocate exception table */ ldr r0, =_start ldr r1, =0x0 //r1等于异样向量基地址 mov r2, #16copyex: subs r2, r2, #1 //减16次,s示意每次减都要更新条件标记位 ldr r3, [r0], #4 str r3, [r1], #4 //将_start标号后的16个符号存到异样向量基地址0x0~0x3c处 bne copyex //直到r2减为0#endif#ifdef CONFIG_S3C24X0 /* 关看门狗*/# define pWTCON 0x53000000# define INTMSK 0x4A000008 /* Interrupt-Controller base addresses */# define INTSUBMSK 0x4A00001C# define CLKDIVN 0x4C000014 /* clock divisor register */ ldr r0, =pWTCON mov r1, #0x0 str r1, [r0] //关看门狗,使WTCON寄存器=0 /*关中断*/ mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0] //敞开所有中断# if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0] //敞开次级所有中断# endif /* 设置时钟频率, FCLK:HCLK:PCLK = 1:2:4 ,而FCLK默认为120Mhz*/ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0] #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit //敞开mmu,并初始化各个bank#endifcall_board_init_f: ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) //CONFIG_SYS_INIT_SP_ADDR=0x30000f80 bic sp, sp, #7 //sp=0x30000f80 ldr r0,=0x00000000 bl board_init_f 下面的CONFIG_SYS_INIT_SP_ADDR =0x30000f80,是通过arm-linux-objdump -D u-boot>u-boot.dis生成反汇编,而后从u-boot.dis失去的。 ...

December 27, 2020 · 5 min · jiezi

关于c:S3C2440移植uboot之支持NANDFLASH操作

上一节咱们移植了uboot,S3C2440移植uboot之反对NORFLASH。这节咱们持续移植,反对NANDFLASH。@[TOC] 编译报错 之前因为nand局部报错,间接正文了 u-boot-2012.04.01\include\configs\smdk2440.h 中的#define CONFIG_CMD_NAND。当初咱们去掉正文,从新编译。报错如下 咱们没有定义CONFIG_S3C2410导致的 能够看到上面有2440的NAND构造体 拷贝s3c2410_nand.c,批改宏定义反对SC32440 所以咱们能够拷贝一份s3c2410_nand.c给2440应用2410的NandFlash位于drivers/mtd/nand/s3c2410_nand.c,首先复制s3c2410_nand.c,改为s3c2440_nand.c,改Makefile,如下图所示: 在上一章剖析过CONFIG_NAND_S3C2410宏,位于include/configs/smdk2440.h: 如上图所示,其中CONFIG_CMD_NAND宏:示意uboot是否反对nand,在上章里,咱们把它屏蔽了,接下来便勾销屏蔽CONFIG_CMD_NAND宏。持续增加对CONFIG_NAND_S3C2440宏的反对,将: #ifdef CONFIG_CMD_NAND#define CONFIG_NAND_S3C2410#define CONFIG_SYS_S3C2410_NAND_HWECC#define CONFIG_SYS_MAX_NAND_DEVICE 1#define CONFIG_SYS_NAND_BASE 0x4E000000#endif改为 #ifdef CONFIG_CMD_NAND #ifdef CONFIG_S3C2410 #define CONFIG_NAND_S3C2410#define CONFIG_SYS_S3C2410_NAND_HWECC#else // CONFIG_S3C2440 #define CONFIG_NAND_S3C2440 #define CONFIG_SYS_S3C2440_NAND_HWECC#endif#define CONFIG_SYS_MAX_NAND_DEVICE 1#define CONFIG_SYS_NAND_BASE 0x4E000000#endif 因为smdk2410.h中定义的是CONFIG_S3C2410,而smdk2440.h中定义的是CONFIG_S3C2440,所以便会依据下面的#ifdef来动静定义宏 批改s3c2440_nand.c 中的NFCONF,NFCONT,反对S3C2440 往下看代码发现原来的NFCONF设置并不能匹配咱们的2440 2440的NFCONF的15位是保留的 所以正文掉这部分代码 2410 NFCONF的其余位设置也不匹配咱们的2440 2440NFCONF 时序参数设置 s3c2440_hwcontrol中使能选中 对照2440手册批改为反对2440的 批改为 /*2440的NAND时序设置*/ cfg = ((tacls-1)<<12)|((twrph0-1)<<8)|((twrph1-1)<<4); nand_reg->nfcont=(1<<1)|(1<<0); // bit1:敞开片选(), bit0:开启nand flash 控制器 nand_reg->nfconf = (tacls<<12) | (twrph0<<8) | (twrph1<<4); //设置时序 writel(cfg, &nand_reg->nfconf); /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ writel((1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont); /* initialize nand_chip data structure */ nand->IO_ADDR_R= (void *)&nand_reg->nfdata; nand->IO_ADDR_W = (void *)&nand_reg->nfdata; nand->select_chip = s3c2440_select_chip; //设置CE ;批改s3c2440_hwcontrol辨别命令和地址/*ctrl:示意做什么,选中芯片/勾销选中,发命令还是发地址* cmd :命令值或者地址值*/static void s3c2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl){ struct nand_chip *chip = mtd->priv; struct s3c2440_nand *nand = s3c2440_get_base_nand(); //获取nand寄存器地址 if (ctrl & NAND_CLE) // 传输的是命令 writeb(dat,&nand->nfcmd); else if (ctrl & NAND_ALE) // 传输的是地址 writeb(dat,&nand->nfaddr); } 批改实现 ...

December 27, 2020 · 1 min · jiezi

关于c:ubuntu1604挂载根文件系统报错errnoNetwork-is-unreachable

环境:ubuntu16.04开发板:s3c2440挂载根文件系统时,报错mount:RPC:Unable to send;errno=Network is unreachable。本机ip:192.168.2.100开发板ip:192.168.2.101虚拟机ip:192.168.2.105三者能够ping通在ubuntu中本人挂接本人没问题s3c2440进入linux后,手动挂载不上。挂载命令如下:mount -t nfs -o nolock 192.168.2.105:/work/nfs_root/first_fs/mnt。在linux中ping 192.168.2.105 发现ping不通。查看s3c2440 linux ip批改ubuntu 文件系统中 work/nfs_root_first_fs/etc/init.d/rcS减少权限从新设置参数后主动挂载挂载胜利s3c2440的linux ip和咱们写入文件的雷同 如遇到排版错乱的问题,能够通过以下链接拜访我的CSDN。 **CSDN:[CSDN搜寻“嵌入式与Linux那些事”]

December 27, 2020 · 1 min · jiezi

关于c:编译busybox错误汇总

提醒谬误:arm-linux-gcc:Command not found PATH里有/usr/oca/arm/bin,然而make的时候,就是找不到 arm-linux-gcc 起因: export PATH=$PATH:/usr/local/arm/bin是设置以后用户的PATH,而sudo执行make的时候,应用的是超级用户权限,那也就应用了超级用户的PATH(然而这个PATH里,并没有/usr/ local/arm/bin) 解决办法:先关上一个超级用户权限的 sudo -s 在以后模式下,设置环境变量export PATH=$PATH /usr/local/arm/bin,再进入到busybox目录, make CONFIG_PREFIX=/work/nfs_root/first_fs install,就胜利装置busybox了。 提醒谬误:/home/book/work/busybox-1.7.0/scripts/gcc-version.sh: line 11: arm-linux-gcc: command not found/home/book/work/busybox-1.7.0/scripts/gcc-version.sh: line 11: arm-linux-gcc: command not found CC applets/applets.o/bin/sh: 1: arm-linux-gcc: not foundscripts/Makefile.build:192: recipe for target 'applets/applets.o' failedmake[1]: * [applets/applets.o] Error 127Makefile:701: recipe for target 'applets' failedmake: * [applets] Error 2 起因:穿插编译器的绝对路径与相对路径问题 解决办法:在busybox的顶层Makefile 176行 CROSS COMPILE ?= 中增加/work/tools/gcc-3.4.5-glibc-2.3.6/bin/arm-linux-的绝对路径就胜利了。(为什么以前用arm-linux-就能够本人找到?) 提醒谬误: CC applets/applets.oIn file included from include/busybox.h:10, ...

December 27, 2020 · 1 min · jiezi

关于c:C语言程序设计快三走势图分析81512073

c程序设计加一Q一带一你✅10319281✅邀一情一玛✅33339333✅进【0 9 1 9 1x . c o m 】✅已助上千人胜利翻盘,欢送减少,沟通交流! 第一章 1.什么是程序?什么是程序设计?程序:就是一组能辨认和执行的指令,每一条指令使计算机执行特定的操作程序设计:是指从确定工作到失去后果、写出文档的全过程2.为什么须要计算机语言?高级语言有哪些特点?为什么须要计算机语言:计算机语言解决了人和计算机交换是的语言问题,使得计算机和人都能辨认高级语言有哪些特点:2.1 高级语言的数据结构要比汇编和机器语言丰盛;2.2 高级语言与具体机器构造的关联没有汇编以及机器语言亲密;2.3 高级语言更靠近自然语言更容易把握;2.4 高级语言编写的程序要通过编译或解释计算机能力执行;3.正确理解以下名词及其含意:(1)源程序,目标程序,可执行程序。源程序:指未编译的依照肯定的程序设计语言标准书写的文本文件,是一系列人类可读的计算机语言指令目标程序:为源程序经编译可间接被计算机运行的机器码汇合,在计算机文件上以.obj作扩展名可执行程序:将所有编译后失去的指标模块连贯装配起来,在与函数库相连接成为一个整体,生成一个可供计算机执行的目标程序,成为可执行程序(2)程序编辑,程序编译,程序连贯。程序编辑:上机输出或者编辑源程序。 程序编译: 先用C提供的“预处理器”,对程序中的预处理指令进行编译预处理对源程序进行语法查看, 判断是否有语法错误,直到没有语法错误未知编译程序主动把源程序转换为二进制模式的目标程序程序连贯:将所有编译后失去的指标模块连贯装配起来,在与函数库相连接成为一个整体的过程称之为程序连贯 (3)程序,程序模块,程序文件。程序:一组计算机能辨认和执行的指令,运行于电子计算机上,满足人们某种需要的信息化工具程序模块:可由汇编程序、编译程序、装入程序或翻译程序作为一个整体来解决的一级独立的、可辨认的程序指令程序文件:程序的文件称为程序文件,程序文件存储的是程序,包含源程序和可执行程序(4)函数,主函数,被调用函数,库函数。函数:将一段常常须要应用的代码封装起来,在须要应用时能够间接调用,来实现肯定性能主函数:又称main函数,是程序执行的终点被调用函数:由一个函数调用另一个函数,则称第二个函数为被调用函数库函数:个别是指编译器提供的可在c源程序中调用的函数。可分为两类,一类是c语言标准规定的库函数,一类是 编译器特定的库函数 (5)程序调试,程序测试。程序调试:是将编制的程序投入理论运行前,用手工或编译程序等办法进行测试,修改语法错误和逻辑谬误的过程程序测试:是指对一个实现了全副或局部性能、模块的计算机程序在正式应用前的检测,以确保该程序能按预约的形式正确地运行4.编写一个C程序,运行时输入Hello World!这个程序是一些国外C教材中作为第一个程序例子介绍的,个别称为Hello程序。 代码示例:`#include <stdio.h>int main(){ printf("%sn", "Hello World!"); return 0;}` * 1* 2* 3* 4* 5* 6* 7运行截图: 5.编写一个C程序,运行时输入以下图形: ** ** 代码示例:`#include <stdio.h>int main(){ for (int i = 0; i < 4; i++) { for (int j = 0; j < i; j++) { printf("%s", " "); } printf("%sn", "*****"); } return 0;}` * 1* 2* 3* 4* 5* 6* 7* 8* 9* 10* 11* 12* 13* 14运行截图: ...

December 26, 2020 · 1 min · jiezi

关于c:面试官让你讲讲Linux内核的竞争与并发你该如何回答

@[TOC] 内核中的并发和竞争简介 在晚期的 Linux内核中,并发的起源绝对较少。晚期内核不反对对称多解决( symmetric multi processing,SMP),因而,导致并发执行的惟一起因是对硬件中断的服务。这种状况解决起来较为简单,但并不适用于为取得更好的性能而应用更多处理器且强调疾速响应事件的零碎。 为了响应古代硬件和应用程序的需要, Linux内核曾经倒退到同时解决更多事件的时代。Linux零碎是个多任务操作系统,会存在多个工作同时拜访同一片内存区域的状况,这些工作可能会互相笼罩这段内存中的数据,造成内存数据凌乱。针对这个问题必须要做解决,重大的话可能会导致系统解体。当初的 Linux零碎并发产生的起因很简单,总结一下有上面几个次要起因: 多线程并发拜访, Linux是多任务(线程)的零碎,所以多线程拜访是最根本的起因。抢占式并发拜访,内核代码是可抢占的,因而,咱们的驱动程序代码可能在任何时候失落对处理器的独占中断程序并发拜访,设施中断是异步事件,也会导致代码的并发执行。SMP(多核)核间并发拜访,当初ARM架构的多核SOC很常见,多核CPU存在核间并发拜访。正在运行的多个用户空间过程可能以一种令人诧异的组合形式拜访咱们的代码,SMP零碎甚至可在不同的处理器上同时执行咱们的代码。 只有咱们的程序在运行当中,就有可能产生并发和竞争。比方,当两个执行线程须要拜访雷同的数据结构(或硬件资源)时,混合的可能性就永远存在。因而在设计本人的驱动程序时,就应该防止资源的共享。如果没有并发的拜访,也就不会有竞态的产生。因而,认真编写的内核代码应该具备起码的共享。这种思维的最显著利用就是防止应用全局变量。如果咱们将资源放在多个执行线程都会找到的中央(临界区),则必须有足够的理由。 如何避免咱们的数据被并发拜访呢?这个时候就要建设一种爱护机制,上面介绍几种内核提供的几种并发和竞争的解决办法。 原子操作原子操作简介 原子,在早接触到是在化学概念中。原子指化学反应不可再分的根本微粒。同样的,在内核中所说的原子操作示意这一个拜访是一个步骤,必须一次性执行完,不能被打断,不能再进行拆分。 例如,在多线程拜访中,咱们的线程一对a进行赋值操作,a=1,线程二也对a进行赋值操作a=2,咱们现实的执行程序是线程一先执行,线程二再执行。然而很有可能在线程一执行的时候被其余操作打断,使得线程一最初的执行后果变为a=2。要解决这个问题,必须保障咱们的线程一在对数据拜访的过程中不能被其余的操作打断,一次性执行实现。 整型原子操作函数函数形容ATOMIC_INIT(int i)定义原子变量的时候对其初始化。int atomic_read(atomic_t*v)读取 v的值,并且返回void atomic_set(atomic_t *v, int i)向 v写入 i值。void atomic_add(int i, atomic_t *v)给 v加上 i值。void atomic_sub(int i, atomic_t *v)从 v减去 i值。void atomic_inc(atomic_t *v)给 v加 1,也就是自增。void atomic_dec(atomic_t *v)从 v减 1,也就是自减 。int atomic_dec_return(atomic_t *v)从 v减 1,并且返回v的值 。int atomic_inc_return(atomic_t *v)给 v加 1,并且返回 v的值。int atomic_sub_and_test(int i, atomic_t *v)从 v减 i,如果后果为0就返回真,否则就返回假int atomic_dec_and_test(atomic_t *v)从 v减 1,如果后果为0就返回真,否则就返回假int atomic_inc_and_test(atomic_t *v)给 v加 1,如果后果为0就返回真,否则就返回假int atomic_add_negative(int i, atomic_t *v)给 v加 i,如果后果为负就返回真,否则返回假注:64位的整型原子操作只是将“atomic_”前缀换成“atomic64_”,将int换成long long。 ...

December 26, 2020 · 3 min · jiezi

关于c:你真的懂Linux内核中的阻塞和异步通知机制吗

@[TOC] 阻塞/非阻塞简介 阻塞操作是指在执行设施操作时,若不能取得资源,则挂起过程直到满足可操作的条件后再进行操作。被挂起的过程进入睡眠状态,被从调度器的运行队列移走,直到期待的条件被满足。而非阻塞操作的过程在不能进行设施操作时,并不挂起,它要么放弃,要么不停地查问,直至能够进行操作为止。 阻塞/非阻塞例程 阻塞形式 int fd;int data = 0;fd = open("/dev/xxx_dev", O_RDWR); /* 阻塞形式关上 */ret = read(fd, &data, sizeof(data)); /* 读取数据 */ 非阻塞形式 int fd;int data = 0; fd = open("/dev/xxx_dev", O_RDWR | O_NONBLOCK); /* 非阻塞形式关上 */ ret = read(fd, &data, sizeof(data)); /* 读取数据 */期待队列简介 期待队列是内核中一个重要的数据结构。阻塞形式拜访设施时,如果设施不可操作,那么过程就会进入休眠状态。期待队列就是来实现过程休眠操作的一种数据结构。 期待队列相干函数定义期待队列wait_queue_head_t my_queue; wait_queue_head_t是__wait_queue_head构造体的一个typedef。 初始化期待队列头void init_waitqueue_head(wait_queue_head_t *q) 参数q就是要初始化的期待队列头,也能够应用宏 DECLARE_WAIT_QUEUE_HEAD (name)来一次性实现期待队列头的定义的初始化。 定义并初始化一个期待队列项DECLARE_WAITQUEUE(name, tsk) name就是期待队列项的名字,tsk示意这个期待队列项属于哪个工作过程,个别设置为current,在 Linux内核中 current相当于一个全局变量,示意以后过程。因而宏DECLARE_WAITQUEUE就是给以后正在运行的过程创立并初始化了一个期待队列项。 将队列项增加到期待队列头void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) q:期待队列项要退出的期待队列头 wait:要退出的期待队列项 返回值:无 ...

December 26, 2020 · 5 min · jiezi

关于c:S3C2440移植uboot之支持烧写yaffs映像及制作补丁

上一节S3C2440移植uboot之裁剪和批改默认参数裁剪了uboot,批改了默认的参数,这一节开始制作yaffs映像以及补丁文件@[TOC] 烧写文件系统 尝试应用如下命令烧写JFFS2文件系统 tftp 30000000 fs_mini_mdev.jffs2nand erase.part rootfsnand write.jffs2 30000000 0x00260000 5b89a8 批改启动参数 set bootargs console=ttySAC0 root=/dev/mtdblock3 rootfstype=jffs2 烧写JFFS2文件系统没问题 尝试应用如下命令烧写YAFFS文件系统 tftp 30000000 fs_mini_mdev.yaffs2nand erase.part rootfsnand write.yaffs 30000000 260000 889bc0 报错 搜寻.yaffs,发现位于Cmd_nand.c文件中。 短少了相干宏定义 在smdk2440.h中增加如下代码 #define CONFIG_CMD_NAND_YAFFS 应用如下命令从新编译烧写 tftp 30000000 u-boot_new.bin; protect off all; erase 0 3ffff; cp.b 30000000 0 40000tftp 30000000 fs_mini_mdev.yaffs2nand erase.part rootfsnand write.yaffs 30000000 260000 889bc0烧写一瞬间实现,不太失常。而且最初启动文件系统报错如下通过比照烧写的fs_mini_mdev_yaffs2文件内容和nand dump 260000显示的内容,发现OOB区的内容不同。 剖析源码 首先,每个命令都会对应一个文件,比方nand命令对应的common/cmd_nand.c 而咱们应用nand命令时,便会进入do_nand()函数,位于common/cmd_nand.c (1)do_nand()函数代码如下所示: int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]){ ... ... if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0){ ... ...#ifdef CONFIG_CMD_NAND_YAFFS //是否反对YAFFS烧写else if (!strcmp(s, ".yaffs")) { //若是nand write.yaffs ... ... ,则进入该判断 if (read) { printf("Unknown nand command suffix '%s'.\n", s); return 1; } ret = nand_write_skip_bad(nand, off, &rwsize, (u_char *)addr, WITH_YAFFS_OOB); //进入nand_write_skip_bad,烧写#endif ... ...} 所以须要在smdk2440.h里,增加CONFIG_CMD_NAND_YAFFS宏定义. (2)而后进入nand_write_skip_bad(),位于drivers/mtd/nand/nand_util.c ...

December 26, 2020 · 2 min · jiezi

关于c:南大计算机系统基础实验-ics2020pa0

理解到这个南大的这个试验不错,记录一下本人的进度,也给后来者一些参考。 1.相干材料PA我的项目官网地址:https://nju-projectn.github.io/ics-pa-gitbook/ics2010/tmux应用办法:http://www.ruanyifeng.com/blog/2019/10/tmux.htmlvim学习游戏:http://vim-adventures.com/Linux教程:https://nju-projectn.github.io/ics-pa-gitbook/ics2019/linux.html#%E6%8E%A2%E7%B4%A2%E5%91%BD%E4%BB%A4%E8%A1%8CLinux26个命令:https://linux.cn/article-6160-1.htmlman入门教程:https://nju-projectn.github.io/ics-pa-gitbook/ics2019/man.htmlGDB入门教程:https://www.cprogramming.com/gdb.html 作为一个对linux简直没有接触的人来说,这一部分实现须要破费的工夫比拟多,对于官网给出的手册尽量一步一步实现,不要跳过,不然前面还要掉过头来学。2.遇到的问题解决办法(1)环境问题:官网举荐在真机上安装debain,作为一个linux菜鸟,没有勇气尝试,我抉择了VMware Workstation和Ubuntu20.04。倡议能够抉择版本略微低一些的Ubuntu版本,如Ubuntu18.04。因为20.04这个版本比拟大,对性能要求高一些,开始调配了2GB的内存和20GB的虚构硬盘,有些卡顿。(2)官网文档在Getting Source Code for PAs这一步中,提到了ubuntu18.04可能会遇到的kvm编译谬误并给出了解决方案。 在我的Ubuntu20.04零碎中也遇到了这一问题,在尝试了官网给出的解决办法后,问题依然存在。 谬误提醒如下: Building x86-nemu-interpreterMakefile:51: *** invalid DIFF. Supported: qemu kvm nemu. Stop. 依据给出的谬误提示信息,查看Makefile文件51的内容发现,发现是因为一个else条件中抛出了一个谬误。 37 ifeq ($(DIFF),qemu) 38 DIFF_REF_PATH = $(NEMU_HOME)/tools/qemu-diff 39 DIFF_REF_SO = $(DIFF_REF_PATH)/build/$(ISA)-qemu-so 40 CFLAGS += -D__DIFF_REF_QEMU__ 41 else ifeq ($(DIFF),kvm) 42 DIFF_REF_PATH = $(NEMU_HOME)/tools/kvm-diff 43 DIFF_REF_SO = $(DIFF_REF_PATH)/build/$(ISA)-kvm-so 44 CFLAGS += -D__DIFF_REF_KVM__ 45 else ifeq ($(DIFF),nemu) 46 DIFF_REF_PATH = $(NEMU_HOME) 47 DIFF_REF_SO = $(DIFF_REF_PATH)/build/$(ISA)-nemu-interpreter-so 48 CFLAGS += -D__DIFF_REF_NEMU__ 49 MKFLAGS = ISA=$(ISA) SHARE=1 ENGINE=interpreter 50 else 51 $(error invalid DIFF. Supported: qemu kvm nemu) 52 endif 再通过查看之前的代码,发现ifeq($(DIFF),qemu)中的条件总会成立,因而前面的else if和else应该没有必要,所以就简略粗犷,将前面的else if 和else都正文掉。。批改如下: ...

December 22, 2020 · 1 min · jiezi

关于c:C语言学习你要的都在这里

对于C语言怎么学习这个话题,很多读者始终在问,因为网上相似的文章很多了,这里就不简明扼要,可能你也看过不少,到底孰是孰非须要你本人去思考、判断和实际。说切实的,集体认为在你入门或者根底学习阶段,教材、软件(写代码)和教学视频这三样就够了。本文次要是给大家提供一些材料,各位按需抉择。 对于书籍学习任何一门课程你首先得有书吧,有人说C语言书这么多怎么办?没事,咱们找了36本C语言的书,你本人缓缓看,缓缓选,挑本人喜爱的的,适宜你的才是最好的!这里咱们不评估哪本好、哪本坏,所有货色都是优缺点并存,不过对于初学者咱们举荐《C Primer Plus》。 关于软件学编程语言只有书不行,你必须得敲代码吧,那编译软件呢?咱们找来了6款编译软件,它们别离是:VS,DevC++,VC++,CodeBlocks,C-Free,Turbo C。对于它们的特点网上介绍很多了,咱们就不赘述了。讲真,入门和根底阶段你用哪个无所谓,还是选本人喜爱的,不过对于初学者咱们举荐Dev C++。 对于视频看了书,敲了代码,那遇到不会不懂的中央怎么办,得有人给你讲吧,这时候就须要教学视频了。咱们给大家找来三种不同格调C语言学习视频,各位还是依照本人的需要抉择,三个总有一个适宜你的,当然你要说都不喜爱,那也能够本人去找其余的,适宜本人就行。 对于源码学习一段时间,略微有点根底了,不能每天还是printf加Hello world,你得做个略微像样点的小我的项目吧,比方很多学校都会让做的“学生信息管理系统”。这里给大家提供了一些小我的项目的源码,不多但足够了,大家能够参考一下。如果这些你都可能很顺利地实现,那么祝贺你,你的C语言能够算入门了,但也仅仅如此,因为对于编程这条路来说,这只是冰山一角。 写在最初 不便但大家下载打包到了百度网盘了,百度网盘链接:链接:https://pan.baidu.com/s/14VrZ5yCg9EC2YEBA1GKUjw 提取码:1234 啰嗦这么多,材料也不少了,再多你也看不过去。总而言之,对于如何学好C语言或者如何学好编程语言这个话题,这句话说的挺好:编程之路漫漫,要说存在捷径,那惟一可能的就是致力!

December 20, 2020 · 1 min · jiezi

关于c:2020年秋招联发科小米等20家公司面经总结

秋招投递公司23家,简历被刷1家。口试/测评挂掉3家。至今无音讯的8家。取得Offer的公司有小米,兆易翻新,全志科技,浙江大华,海格通信,京信通信,景嘉微电子,广州朗国电子,北京华大电子,中国长科技团体。已签约浙江大华。@[toc] 情谊提醒:公司名字前面的日期代表投递日期,面试批次前面的工夫代表面试时长和面试日期。 有面试联发科北京(7.16) 20200805接到告诉,0806早上九点半面试。邮件中写的是用Webex Meet,之前都没听过的一个软件,网上找了半天才找到,而且软件没有简体,只好调成繁体了。邮件中写的是期待告诉后再连入,大略9.40的时候接到了电话,要我退出会议中。面试官是个女的,首先让我自我介绍下,而后开始看我的简历。介绍完了间接问我的项目。 一面(35min,8.5)你本人做了那局部?是不是在师兄师姐根底上做的? 不是,师兄师姐之前次要做的是实践钻研。我自己所做的是硬件的设计和软件代码的移植。 移植的开源代码,做了那些批改?如何批改的? 次要批改的是硬件的管脚,时钟的配置,SPI总线的调试,芯片通信过程的调试。 上位机局部你说用了卡尔曼滤波,有没有调研过其余的滤波形式? 没有思考,过后求教了也做这个方向的一些人,他们给的倡议就是用卡尔曼就能够。没有思考其余形式。(其实最次要的是解决问题,能解决问题就能够) 复盘:过后想到的第一个就是卡尔曼,因为卡尔曼在理论工程中利用比拟宽泛且成熟,成果也不错。过后就拿来试了下,定位精度失去了很好的晋升。(定位漂移和抖动40cm左右。漂移和抖动的次要起因就是每次接管到的不止是多个信号叠加的后果,卡尔曼滤波次要是滤除除了首径信号以外的其余信号) 做的货色成果怎么样?和其他人做的对标了吗? 定位成果还能够,每秒钟能够定位64个标签。 复盘:业界的评判规范次要有几个方面。 整个工程文件有多少行代码? 具体多少行不分明,最初编译的hex文件为112k 代码移植过程中遇到什么问题,如何解决的? 巴拉巴拉,通信过程有点简单,预计面试官没听懂,就没往下问了(其实应该边画图边讲的)。 复盘:解决的整个过程应该形容的再具体一些,重点突出要害局部,这个问题的三个局部都要讲清楚! 我的项目中理论写的代码量有多少 没多少,次要是硬件的设计和调试,软件的移植,解决问题,批改。 复盘:显然面试官厌弃代码写少了,这个时候能够说微信小程序的代码本人写了很多,70%以上。从0到1. 我的项目代码中多线程,多过程是如何运行的 没有用到多线程,多过程。 汇编,C++把握怎么样? 汇编自学过,能够看懂。C++根本没用过。 复盘:汇编是自学的,C++和C的语法差不多,都能够看懂。 重写strcpy函数? 写完了给面试官解释了下。写对了 将一个寄存器的第三位的值从0改成1 写完了给解释下。写对了 你有什么想问我的? 如果我有幸能进入贵公司,驱动次要负责那局部? 次要还是看你分到那个部门,camera,音视频,IO驱动都有在做的。 什么时候能给到面试后果的回答? 不确定,要先把面试过程的记录交给HR。总结 1.我我的项目上做的是软件+硬件的一个实现,面试官全程在问软件,硬件一点没问。 2.在简历中写了本人在写博客,放了一个链接,不晓得面试官看没看。 3.面试的岗位是Linux驱动开发,全程没有问一点像bootloader,Linux内核的输出子系统,总线设施驱动模型等偏底层的货色。 4.女面试官可能都不太懂硬件?全程都是软件,而且本人的我的项目中写的代码不是太多,次要是批改。面试官还是偏重理论的写代码能力吧。感觉凉了。 5.全程35分钟吧。 6.总结下,我的项目思考再深入下,如何解说? 广州朗国电子科技(8.24)一面(60min,9.14) 无领导小组讨论。没有标准答案,上网搜寻下无领导小组讨论的注意事项,想好本人要表演什么角色。然而肯定不要不谈话,要有逻辑的表白本人的观点。 二面(25min,9.16) HR面,次要问了家庭状况,有没有女朋友,工作地点的问题,能不能承受加班,HR也很坦率的说,公司处在上升期。咱们是规范的996。 三面(40min,9.18)我的项目 次要针对简历上写的内容来问,我的项目画原理图,流程图讲清楚,并进行公式推导。 什么是内核空间?什么是用户空间内核空间和用户空间通信形式 为什么须要uboot?不必行不行? 用uboot的目标是疏导内核启动。 我了解的,实践是能够的。把uboot中所做的一些工作写进内核里,板子也能启动。然而很少有人这么做,毕竟内核很宏大,大面积批改难度比拟大。 volatile关键字总结 9.25号发来邮件,要先签两方协定。这家公司做Smart TV之类的显示设施的,安卓驱动和Linux驱动都有,也有嵌入式应用层的。零食甜点下午茶,10点当前打车报销,每个季度有奖金(0.5-1个月月薪),年初还有年终奖(据说能够拿到18薪),就是加班太多(据说996是标配,忙的时候9107),怕受不了。最初还是回绝了。 ...

December 20, 2020 · 3 min · jiezi

关于c:一个普通硕士生的2020秋招总结文末送福利

秋招是每个在校学生都要经验的一个阶段。本篇文章记录了本人的秋招历程。秋招投递公司23家,简历被刷1家。口试/测评挂掉3家。至今无音讯的8家。取得Offer的公司有小米,兆易翻新,全志科技,浙江大华,海格通信,京信通信,景嘉微电子,广州朗国电子,北京华大电子,中国长城科技团体。已签约浙江大华。最初播种了一个称心的Offer。前事不忘,后事之师。心愿本人总结的这些内容能对前面筹备秋招的同学有所帮忙!1. 自我介绍 本硕双非,本科电子信息工程,硕士电子与通信工程。导师申请的我的项目中有一部分须要用Stm32实现,所以本人在硕士期间接触Stm32比拟多。过后也思考到,如果只会Stm32,找工作可能会比拟吃力。而本人对嵌入式底层的内容也比拟感兴趣。所以,在研二的时候每天花一点工夫来学习下驱动开发,当前找工作打算从事底层驱动开发相干的内容。 2. 秋招筹备2.1 Linux驱动 在2019年12月的时候,根本就把韦东山老师的第二期课程学习了一遍了,尽管在学习过程中有很多不明确的,但也保持看了一遍。把有疑难的中央记录了下来,打算前面再缓缓的去深入研究。 韦东山老师讲的课程的确很好,然而对于根底不太好的可能会比拟吃力,很容易劝退。过后思考了下,本人为什么听不懂呢,哪里有欠缺?咱们对本人应该有一个清晰的意识,我从Stm32转驱动开发,劣势就是我对于根本的硬件原理都比拟相熟,欠缺的是对于Arm架构的深刻理解,操作系统和计算机组成原理的基本知识。所以,这个时候发现自己听不懂的状况下,有些问题搞不明确,先不要深究,前面能够缓缓补。 往年疫情在家,在3月份的时候,对照本人之前的学习笔记和遗留的疑难,把之前学过的内容又看了下,当看第二遍的时候,对于很多问题也能够想分明了。 思考到驱动这块没有做过具体的我的项目,我就对照着韦东山老师的移植Uboot的视频,本人移植了一遍。(S3C2440移植uboot之编译烧写uboot)本人对于Uboot的启动流程也就很相熟了。超详细分析Bootloader(Uboot)到内核的启动流程(万字长文!)既然打算把这个写在简历上,就要把这个启动流程搞得特地明确,有些根本的源码也是要晓得的(比方,如何初始化NAND FLASH,时钟如何配置的)。而且,写在简历上的面试官肯定会问的。 2.2 Arm体系与架构 对于Arm的体系架构这部分是肯定要看的。举荐一本书,杜春雷老师写的《ARM体系结构与编程》,这本书其实就是ARMV7开发手册的中文版,很多内容都是手册外面的。书的话不肯定都要看,学习过程中,哪里不理解再去查书,成果可能会更好。 2.3 数据结构与算法剖析 数据结构与算法的内容时秋招中的重中之重,口试必考,面试必考。所以这部分要好好筹备。 数据结构的话本科也没学过。4月份的时候开始在家,从根本的链表,二叉树,堆,队列,字符串,排序算法,查找算法等根底内容学起。过后参考的材料是在网上找的一本PDF。外面蕴含了根本数据结构的算法的实现。对着PDF材料边看边了解,本人入手去实现了一遍。刚开始学习数据结构的内容,肯定要多画图,像链表的内容,不画图有时候可能不太好了解指针是如何指向的。总之要找到适宜本人的学习形式。 图论的内容比拟难,如果不打较量,能够间接跳过(只针对嵌入式开发的同学)。还有字符串的KMP算法也比拟难了解(其实刷题Leetcode题目之后就会发现,字符串匹配的题目解法很多,不肯定要KMP)。也能够先不看。 对于我写的一些数据结构的内容,能够在数据结构与算法剖析专栏中看到。大家能够参考下。 此外我还整顿了下数据结构中比拟重要的内容,在面试中肯定要达到能够在白纸上写的程度。具体如下图所示。 在学习完根本的数据结构与算法的内容之后,接下来就是刷题了。我本人是在Leetcode上刷的题目。因为工夫无限,我刷题一开始的策略是依照 题目类型 去刷题(参考了知乎各位大佬的刷题策略),次要内容集中在了链表,二叉树,字符串,数组这四个局部。把这部几个局部呈现频率高的题目都总结了进去。各个局部频率总结链接: 【leetcode】高频题目整顿_所有题目汇总篇( High Frequency Problems, All Problems ) 面试leetcode题型总结 刷题过程中会遇到一些特定算法的题目,比方滑动窗口,双指针,动静布局等。遇到这种题目,能解决的先解决掉,不能解决的前面能够依照算法类型对立解决。 最初,在口试的时候,很多公司的大题的口试零碎其实并不是像Leetcode一样只写个子函数就行了,而是和ACM比赛的类型一样,须要本人解决输入输出。这部分肯定要提前练习。如果不相熟零碎,口试的时候尽管有思路,然而也写不进去。倡议提前相熟下输入输出。 OJ在线编程常见输入输出练习场 2.4 C语言 C语言这部分的话,能够上网搜寻一些面试中常常问到的内容。这里也举荐一本书,何昊老师写的《程序员面试口试宝典第三版》(不要找错了)。这本书是针对C/C++程序员的,次要介绍了面试过程中常常问到的问题,整体的内容偏差底层,问题解释的也比较清楚。然而有些中央有谬误,看的时候要留神。能够加书前面的QQ群,向作者反馈谬误。 C++语言,平时用得少,我这部分就没筹备,给不了大家意见。在面试中好几次问到我C++的指针援用之类的基础知识,只能和面试官说C++内容用得少,不相熟。不过,这并不会成为面试官最初是否要你的决定性因素(只针对嵌入式底层来说)。 2.5 操作系统&计组 这块是我的软肋,好多概念不太懂。不过在学习驱动的过程中,我把这些概念也都整顿了一些。具体文章能够看上面的内容。 你真的懂Linux内核中的阻塞和异步告诉机制吗?(花了五天整顿,墙裂举荐!) 面试官让你讲讲Linux内核的竞争与并发,你该如何答复? S3C2410 MMU(存储器治理单元)详述 Linux内核中断顶半部和底半部的了解 谈谈过程上下文、中断上下文及原子上下文的一些概念 对于操作系统的一些常识,同样是整顿了网上常见的一些面试题目。这份嵌入式软件开发知识点总结一共有13W字,涵盖了Linux,C语言,Arm体系与架构,操作系统,计算机组成原理等方方面面的常识。我在秋招过程中问到的问题,在外面根本都能够找到。这份材料给了我很大帮忙。材料放在了公众号【嵌入式与Linux那些事】中,大家能够关注后回复“秋招大礼包”收费自取。 ...

December 18, 2020 · 1 min · jiezi

关于c:Linux内核中containerof宏的详细解释

上一节回绝造轮子!如何移植并应用Linux内核的通用链表(附残缺代码实现)咱们在剖析Linux内核链表的时候留神到内核在求解构造体偏移的时候奇妙的应用了container_of宏定义,明天咱们来具体分析下内核到底是如何求解构造体成员变量的地址的。@[TOC] 构造体在内存中是如何存储的int main(){ Student stu; stu.id = 123456; strcpy(stu.name,"feizhufeifei"); stu.math = 90; stu.PE = 80; printf("Student:%p\r\n",&stu); printf("stu.ID:%p\r\n",&stu.ID); printf("stu.name:%p\r\n",&stu.name); printf("stu.math:%p\r\n",&stu.math); return 0;} 打印后果如下: //构造体的地址Student:0xffffcbb0//构造体第一个成员的地址stu.ID:0xffffcbb0 //偏移地址 +0stu.name:0xffffcbb4//偏移地址 +4stu.math:0xffffcbd4//偏移地址 +24 咱们能够看到,构造体的地址和构造体第一个成员的地址是雷同的。这也就是咱们之前在回绝造轮子!如何移植并应用Linux内核的通用链表(附残缺代码实现)中提到的为什么在构造体中要把 struct list_head放在首位。 不太了解的再看下这两个例子struct A { int a; char b; int c; char d; };a 偏移为 0 , b 偏移为 4 , c 偏移为 8 (大于 4 + 1 的 4 的最小整数倍), d 偏移为 12 。 A 对齐为 4 ,大小为 16 。struct B { int a; char b; char c; long d; };a 偏移为 0 , b 偏移为 4 , c 偏移为 5 , d 偏移为 8 。 B 对齐为 8 , 大小为 16 。 ...

December 18, 2020 · 2 min · jiezi

关于c:拒绝造轮子如何移植并使用Linux内核的通用链表附完整代码实现

在理论的工作中,咱们可能会常常应用链表构造来存储数据,特地是嵌入式开发,常常会应用linux内核最经典的双向链表 list_head。本篇文章具体介绍了Linux内核的通用链表是如何实现的,对于常常应用的函数都给出了具体的阐明和测试用例,并且移植了Linux内核的链表构造,在任意平台都能够不便的调用内核曾经写好的函数。倡议珍藏,以备不时之需!@[TOC] 链表简介 链表是一种罕用的组织有序数据的数据结构,它通过指针将一系列数据节点连接成一条数据链,是线性表的一种重要实现形式。绝对于数组,链表具备更好的动态性,建设链表时无需事后晓得数据总量,能够随机调配空间,能够高效地在链表中的任意地位实时插入或删除数据。 通常链表数据结构至多应蕴含两个域:数据域和指针域,数据域用于存储数据,指针域用于建设与下一个节点的分割。依照指针域的组织以及各个节点之间的分割模式,链表又能够分为单链表、双链表、循环链表等多种类型,上面别离给出这几类常见链表类型的示意图: 单链表 单链表是最简略的一类链表,它的特点是仅有一个指针域指向后继节点(next),因而,对单链表的遍历只能从头至尾(通常是NULL空指针)程序进行。 双链表 通过设计前驱和后继两个指针域,双链表能够从两个方向遍历,这是它区别于单链表的中央。如果打乱前驱、后继的依赖关系,就能够形成"二叉树";如果再让首节点的前驱指向链表尾节点、尾节点的后继指向首节点,就形成了循环链表;如果设计更多的指针域,就能够形成各种简单的树状数据结构。 循环链表 循环链表的特点是尾节点的后继指向首节点。后面曾经给出了双链表的示意图,它的特点是从任意一个节点登程,沿两个方向的任何一个,都能找到链表中的任意一个数据。如果去掉前驱指针,就是单循环链表。 对于链表的更具体的内容能够看这两篇博客史上最全单链表的增删改查反转等操作汇总以及5种排序算法详解双向链表的基本操作. Linux内核中的链表 下面介绍了一般链表的实现形式,能够看到数据域都是包裹在节点指针中的,通过节点指针拜访下一组数据。然而 Linux内核的链表实现能够说比拟非凡,只有前驱和后继指针,而没有数据域。链表的头文件是在include/list.h(Linux2.6内核)下。在理论工作中,也能够将内核中的链表拷贝进去供咱们应用,就需不要造轮子了。 链表的定义 内核链表只有前驱和后继指针,并不蕴含数据域,这个链表具备通用性,应用十分不便。因而能够很容易的将内核链表构造体蕴含在任意数据的构造体中,非常容易扩大。咱们只须要将链表构造体包含在数据结构体中就能够。上面看具体的代码。 内核链表的构造 //链表构造struct list_head{ struct list_head *prev; struct list_head *next;}; 当须要用内核的链表构造时,只须要在数据结构体中定义一个struct list_head{}类型的构造体成员对象就能够。这样,咱们就能够很不便地应用内核提供给咱们的一组标准接口来对链表进行各种操作。咱们定义一个学生构造体,外面蕴含学号和数学问题。构造体如下: struct student{ struct list_head list;//暂且将链表放在构造体的第一位 int ID; int math; };链表的初始化内核实现#define LIST_HEAD_INIT(name) { &(name), &(name) }#define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list){ list->next = list; list->prev = list;}阐明 INIT_LIST_HEAD 和LIST_HEAD都能够初始化链表,二者的区别如下: LIST_HEAD(stu_list) 初始化链表时会顺便创立链表对象。 ...

December 18, 2020 · 15 min · jiezi

关于c:详解汇编语言B和LDR指令与相对跳转和绝对跳转的关系

@[TOC] 为什么要有绝对跳转和相对跳转?程序执行:指令一条一条依照程序往下执行,比方变量的定义和赋值都是依照程序执行的。跳转执行:当指令执行到以后地位后跳转到其余地位执行。比方,在主函数中调用其余函数就是典型的跳转执行。其中跳转又分为相对跳转和绝对跳转。相对跳转:间接跳转到一个固定的,实实在在的地址。绝对跳转:绝对于以后pc值的一个跳转,跳转到pc+offset的地址。 咱们分明了下面几个概念,就晓得了为什么要有绝对跳转和相对跳转。各种指令相互配合能力使得cpu有更高的解决效率。正是因为有了程序和跳转指令,咱们的cpu才能够解决各种简单的计算。 在程序中只有绝对跳转/相对跳转是否能够? 答案必定是不能够的。咱们以一个例子具体分析。 指令编号指令性能指令1程序执行指令2程序执行指令3绝对跳转到指令5指令4程序执行指令5程序执行指令6相对跳转到指令8指令7程序执行指令8程序执行 假如程序被放在0x00000000地位开始执行,编译链接后的后果为: 指令地址指令编号指令性能下条指令地址0x00000000程序执行程序执行以后地址+40x00000004程序执行程序执行以后地址+40x00000008跳转到指令5跳转到指令5以后地址+80x0000000C程序执行程序执行以后地址+40x00000010程序执行程序执行以后地址+40x00000014跳转到指令8跳转到指令80xC000001C0x00000018程序执行程序执行以后地址+40x0000001C程序执行程序执行以后地址+4 当这段程序被放在0xC000000空间时,开始执行指令1,而后采纳绝对寻址的办法就能够运行到指令6,在指令6执行时也能够应用相对寻址的办法从0xC0000014正确跳转到指令8所在的0xC00001C地位,这段代码运行失常。 当这段代码被放在0x00000000空间时,开始执行指令1,而后采纳绝对寻址的办法就能够运行到指令6,但在指令6执行时应用相对寻址的办法从0x0000014跳转到了0xC000001C,但0xC000001C空间没有代码,这样程序就跑飞了。 因而,当编译地址(加载地址)和运行地址雷同时,相对跳转和绝对跳转都能够正确执行。比方,程序在NORFLASH存储时。然而,当编译地址(加载地址)和运行地址不雷同时,绝对跳转都就会呈现问题。比方,代码存储在NANDFLASH,因为NANDFLASH并不能运行代码,所以须要重定位代码到外部的SRAM。对于NANDFLASH和NORFLASH能够看这篇文章S3C2440从NAND Flash启动和NOR FLASH启动的问题。 B(BL)和LDR指令具体怎么执行的? 咱们以下图中的这句跳转代码剖析下指令具体的执行过程。 #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit#endif 上述代码对应的反汇编代码如下: 33f000ac: eb000017 bl 33f00110 <cpu_init_crit>33f00110 <cpu_init_crit>:33f00110: e3a00000 mov r0, #0 ; 0x033f00114: ee070f17 mcr 15, 0, r0, cr7, cr7, {0} 当指令执行到33f000ac时,对应的机器码为eb000017(1110 1011 0000 0000 0000 0000 0001 0111‬),其中[31,28]高四位为条件码,1110示意无条件执行。[25,27]位保留区域,24位示意是否带有返回值,1示意带有返回值,也就是BL指令。[23,0]为指令的操作数,0000 0000 0000 0000 0001 0111。依照如下计算形式: 1、将指令中24位带符号的补码立刻数扩大为32位(扩大其符号位)原数变成 0000 0000 0000 0000 0000 0000 0001 0111。 ...

December 17, 2020 · 1 min · jiezi