关于c:带头结点链表的基本操作

#include <stdio.h>#include <stdlib.h>#define INIT_LEN 6typedef struct node{ int x; struct node * next;}link;link * initLink(link * head){ link * pr = head, *p; int i, num; printf("输出数组%d个元素的值。\n", INIT_LEN); for(i = 0;i < INIT_LEN;i++){ //create node p = (link*)malloc(sizeof(link)); if(p == NULL) exit(EXIT_FAILURE); scanf("%d", &num); p->x = num; p->next = NULL; //link node pr->next = p; pr = p; } return head;}link * deleteLink(link * head, int num){ link * p = head->next, *pr = head; while(p != NULL){ //带头结点的删除 if(p->x == num) break; pr = p; p = p->next; } if(p == NULL){ printf("NOT FOUND!\n"); }else{ pr->next = p->next; p->next = NULL; free(p); } return head;}link * insertLink(link * head, int num){// 带头结点的程序插入 //create node link * pt = (link*)malloc(sizeof(link)); if(pt == NULL) exit(EXIT_FAILURE); pt->x = num; pt->next = NULL; if(head == NULL) head = pt; link * p = head; while(p->next != NULL && p->next->x < num) p = p->next; //when p->next->x >= num pt->next = p->next; p->next = pt; return head;}void printLink(link * head){ link * p = head->next; //头结点内容不输入 while(p != NULL){ printf("%d ",p->x); p = p->next; } printf("\n");}void destoryLink(link * head){ link * p = head; while(p != NULL){ head = p->next; free(p); p = head; }}int main() { int cnt, del, add; link * head = (link*)malloc(sizeof(link)); // 带头结点// head = initLink(head); printf("此链表各个结点的数据域为:"); printLink(head); printf("输出要插入的数据个数:"); scanf("%d",&cnt); printf("输出要插入的数据x: "); for(int i = 0;i < cnt;i++){ scanf("%d", &add); insertLink(head, add); } printf("以后链表各个结点的数据域为:"); printLink(head); printf("输出要删除的数据x: "); scanf("%d", &del); deleteLink(head, del); printf("删除后链表各个结点的数据域为:"); printLink(head); destoryLink(head); return 0; }

September 17, 2021 · 2 min · jiezi

关于c:Clion-Debug模式使用实践

一、背景最近为了考研,在学习C语言与数据结构,最开始应用Visual Studio 2019作为编辑器,然而总感觉不习惯; 之前始终应用jetbrains公司的编辑器,正好发现C语言能够用CLion,然而发现不会应用他的调试性能,有些时候为了调试代码,还须要将代码复制到 Visual Studio 2019编辑器中;起初感觉太麻烦了,摸索了一段时间终于找到了CLion的调试办法,将办法记录下来给须要的同学吧。 二、开启调试关上CLion,新建我的项目;接在以此在菜单中选择如下菜单file->settings->Build、Execution、Deployment->Debugger->Data Views->C/C++ 找到Enable NatVis renderes for LLDB Diagnostics 抉择 Verbose,如下图所示 抉择之后,点击下方的OK按钮进行确认。 三、编译代码当初须要在代码中轻易编辑一些代码,代码中须要有变量赋值操作,用于察看debug模式,参考代码如下所示 #include <stdio.h>int main() { int i = 0; while (i < 5) { i++; } return 0;}编写完代码之后,将须要察看的变量标注一下,标注办法是用鼠标点击行号右侧旁边,会呈现小红点,如下图所示。 接下来就能够应用debug模式察看变量的数据了,在编辑器的右侧上方有一个绿色虫子的图标,点击此图标就进入了debug模式。 四、 调试代码在debug模式下,能够看到变量以后在内存中的值,如下图所示 当须要让程序继续执行时,须要点击两头的红色框,这样程序会往下一步执行,同时能够看到变量的值也在发生变化,在最下方能够看到整个程序的变量列表,以及对应值是多少。 作者:汤青松日期:2021-09-14微信:songboy8888

September 16, 2021 · 1 min · jiezi

关于c:c-打印函数堆栈

代码 #include <execinfo .h>#include <stdio .h>#include <stdlib .h> void fun1();void fun2();void fun3(); void print_stacktrace(); int main(){ fun3();} void fun1(){ printf("stackstrace begin:\n"); print_stacktrace();} void fun2(){ fun1();} void fun3(){ fun2();} void print_stacktrace(){ int size = 16; void * array[16]; int stack_num = backtrace(array, size); char ** stacktrace = backtrace_symbols(array, stack_num); for (int i = 0; i < stack_num; ++i) { printf("%s\n", stacktrace[i]); } free(stacktrace);}编译 gcc test.cc -rdynamic -g -o test3

September 16, 2021 · 1 min · jiezi

关于c:HelixQAC软件代码静态测试工具

原PRQA动态测试软件产品线(包含QA-C、 QA-C++、QA-Verify等),对立更名为“Helix QAC”,PRQA的代码动态剖析工具可能帮忙企业开发团队进步代码的品质和平安,缩短软件开发所需的工夫,HelixQAC是作为其主打产品目前已广泛应用于汽车、航空航天、电子商务、医疗器械、生产和通信等畛域。 HelixQAC HelixQAC是动态代码剖析工具,根据C和C++编码规定主动扫描代码对规定的违反。开发团队在开发过程的晚期就能够用它来检测缺点,因为此时批改代码是最不便也最经济的。Helix QAC因而自动化强制施行代码编程规范,比方MISRA,保障代码的合规性。 性能个性 ◆ 遵循代码规范 遵循编码和工业规范。Helix QAC主动审查代码,确保它们合乎用户抉择的编码标准。合规性报告可视化地揭示用户哪些代码须要多加注意。Helix QAC反对多种C和C++编码标准,提供相应的合规性模块,也反对规范的客户化定制。 ◆   查看更多缺点 在开发晚期查看编译器没有发现的要害缺点。Helix QAC为用户的软件建设了准确的行为模型,跟踪代码中的变量值,如同运行时一样。因而这种剖析最大化地笼罩了代码,使误报和漏报最低。它甚至能辨认极其简单的代码引起的问题。 ◆   进步代码品质 提供任何应用程序的整体品质和平安。Helix QAC辨认必须批改的缺点,提供具体的领导帮忙开发人员批改问题。这是不须要运行程序的。开发人员既然取得了即时的上下文反馈,他们将因而从谬误中取得学习,下一次编写新的代码(或者评审代码)时,能力将失去晋升。 ◆ 协同代码审查 Helix QAC的仪表盘提供了协同代码审查的能力,用户可能在Helix QAC查看出的诊断上增加注解,为其余用户调配须要他们采取的动作。 ◆ 适应数百万行代码 让动态剖析适应你的环境。Helix QAC有能力解决数百万行代码,保障你的产品无论代码由如许简单它都是平安的。 ◆ 重用代码 重用品质信得过的代码。Helix QAC检测代码移植性问题,所以你能重用让你释怀的代码,帮忙你的疾速开发。 ◆ 减速开发过程 升高瓶颈减速开发。Helix QAC能集成在构建零碎和继续集成环境中,尽早且频繁地发现缺点,从而防止了在开发前期往往须要破费甚巨的谬误。它也减速了以后代码的评审,你甚至能够只让它查看新的代码变动,疾速提供反馈。 ◆ 监督整体代码品质 应用Helix QAC的仪表盘监督代码品质。你可能用它监督代码品质度量,取得品质趋势。仪表盘还能帮你为利益相干方创立属于他们的报告。 编程规范合规性 ● MISRA ① MISRA编码标准查看平安要害零碎的潜在问题。MSIRA C和MISRA C++合规性模块指出违反这些规定的代码段。 ② MISRA C模块强制施行MISRA C:1998、MISRA C:2004和MISRA C:2012。 ③ MISRA C++模块强制施行MISRA C++:2008。 ④ 在MISRA规定查看方面,Helix QAC的准确性远高于其余工具。它对规定的违反划分出重大度的优先级,你能够据此批改最重要的问题。 MISRA C MISRA C++ ...

September 13, 2021 · 2 min · jiezi

关于c:C进阶13接续符和转义符

Summary1)编译器会将反斜杠'\'剔除,跟在反斜杠前面的字符主动接续到前一行 2)在接续单词时,反斜杠'\'之后不能有空格,反斜杠'\'的下一行之前也不能有空格 3)接续符'\'适宜用于定义宏代码块,进步可读性 4)C语言中的本义符'\'次要用于示意无回显字符(不会在屏幕上显示的),也能够用于示意惯例字符。 5)当反斜杠'\'作为本义符应用时,必须呈现在单引号或者双引号之间 \n回车换行\t横向跳到下一制表地位\反斜杠符'\'\'单引号符\ddd1~3位八进制数所代表的字符\xdd1~2位十六进制数所代表的字符\a响铃\v竖向跳格\b退格\r回车\f走纸换页接续符和本义符1、接续符'\'上面代码正确么? #incl\ud\e <s\tdio\.h>in\t main(\){ printf(\ "Hello World.\n" )\ ; ret\urn 0;}编译器会将反斜杠'\'剔除,跟在反斜杠前面的字符主动接续到前一行 // 将反斜杠'\'剔除,前面的字符主动接续到前一行的代码如下#include <stdio.h>int main(){ printf( "Hello World.\n" ) ; return 0;}在接续单词时,反斜杠'\'之后不能有空格,反斜杠的下一行之前也不能有空格 // 如果格局像上面这样,urn后面有4个空格,这时候编译就不过了,因为失去的语句是// ret urn 0; ret\ urn 0;接续符'\n'适宜在定义宏代码块时应用 // 用宏代码块实现的替换变量语句#define SWAP(a, b) \{ \ int temp = a; \ a = b; \ b = temp; \}2、本义符'\'C语言中的本义符'\'次要用于示意无回显字符(不会在屏幕上显示的),也能够用于示意惯例字符。\n回车换行\t横向跳到下一制表地位\反斜杠符'\'\'单引号符\ddd1~3位八进制数所代表的字符\xdd1~2位十六进制数所代表的字符\a响铃\v竖向跳格\b退格\r回车f走纸换页当反斜杠'\'作为本义符应用时,必须呈现在单引号或者双引号之间 char* p = "\141 \t \'\a \x62 "; // \141: 八进制141对应十进制97,Ascii中示意字符a // \t: 制表符 // \': 单引号' // \a: 响铃一次 // \x62: 十六进制62对应于十进制98,Ascii中示意字符bprintf("%s\n", p); 本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。 ...

September 11, 2021 · 1 min · jiezi

关于c:C进阶12注释符号

Summary1)编译器在编译过程中应用空格替换整个正文 2)反斜杠'\'是接续符,编译器会将反斜杠剔除,跟在反斜杠前面的字符主动接续到前一行 3)字符串里的//和/**/都会被看做斜杠字符,不会作为正文 4)/*..*/正文不能嵌套应用 5)留神 正文用来形容程序的起因和用意,而不是逐渐形容语句正文要防止二义性,防止缩写正文要精简,防止臃肿正文1. 以下正文代码是否正确? 编译器在编译过程中应用空格代替整个正文字符串字面量里的//和/*..*/不代表正文符号/*..*/正文不能嵌套依据以上规定: Line10正确;Line13的输入为“abcd // efgh”;Line15正确,‘\’是一个接续符,把下一行的内容接到上一行去;Line18谬误,正文替换成立空格,变成了in t i;Line20谬误,/*..*/里不能嵌套2. 正文用来论述起因和用意,而不是形容程序的运行过程反例 int r = n/2; // r是n的一半r++; // 变量r自增13. 正文必须无二义性,起到对代码的提醒作用,防止缩写反例 sad = 0x723; // R.I.P.L.V.B这一行代码的正文,让人看不明确sad是什么,这个值又是什么意思。 4. 正文是对代码的提醒,防止过于臃肿反例 /* * b.s. 09/11/2021 * 这段代码这么写很不优雅 * 代码简单,且工夫、空间复杂度都较高 * 但我当前会去批改这段代码的 * 当初这么做因为交付压力较大 * 肯定 */ if(xx) { } else { if(xx) {} else {} }本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。

September 11, 2021 · 1 min · jiezi

关于c:C语言中动态内存分配的本质是什么

摘要:C语言中比拟重要的就是指针,它能够用来链表操作,谈到链表,很多时候为此分配内存采纳动态分配而不是动态调配。本文分享自华为云社区《【云驻共创】C语言中动态内存调配的实质》,作者: G-washington。 C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发。只管C语言提供了许多低级解决的性能,但依然放弃着跨平台的个性,因为C语言具备可移植性,可拓展性,可重用性等个性,促使C语言依然在编程语言排行榜上占据肯定无利位置。而C语言中比拟重要的就是指针,它能够用来链表操作,谈到链表,很多时候为此分配内存采纳动态分配而不是动态调配。 内存调配的概念通常定义变量(或对象),编译器在编译时都能够依据该变量(或对象)的类型晓得所需内存空间的大小,从而零碎在适当的时候为他们调配确定的存储空间。这种内存调配称为动态存储调配;有些操作对象只在程序运行时能力确定,这样编译时就无奈为他们预约存储空间,只能在程序运行时,零碎依据运行时的要求进行内存调配,这种办法称为动态存储分配。所有动态存储分配都在堆区中进行。 内存不是取之不尽用之不竭,4g、8g、16g是常见的电脑内存大小,关上工作管理器,能看到不同的利用占据的内存状况。如果一个应用程序占了大部分内存,预计别的利用就资源缓和了,那这个利用可能会被卸载,找个节俭内存的。 内存治理是计算机靠近物理实质的操作,那些程序语言之下的动作,最终都要调动内存来实现。零碎的资源不是有限的,零碎上运行的程序也不是只有这一个,疏忽内存,就会设计出危险的、冗余的代码产品,或者没法更好的交互。 动态内存调配的特点动态内存是绝对动态内存而言的。所谓动静和动态就是指内存的调配形式。动态内存是指在堆上调配的内存,而动态内存是指在栈上调配的内存。动态内存调配的实质就是,什么时候须要一块内存的时候,再调配这块内存;当不再须要某一块内存的时候,就能够把这块内存开释掉。这种灵便的内存调配形式,正好适宜链表这种数据结构。 传统数组的毛病数组与动态内存调配相比有以下毛病: 数组的长度必须当时指定,而且只能是常量,不能是变量。因为数组长度只能是常量,所以它的长度不能在函数运行的过程当中动静地裁减和放大。对于数组所占内存空间程序员无奈手动编程开释,只能在函数运行完结后由零碎主动开释,所以在一个函数中定义的数组只能在该函数运行期间被其余函数应用。而“传统数组”的问题,实际上就是动态内存的问题。然而动态内存就不存在这个问题,因为动态内存是由程序员手动编程释的,所以想什么时候开释就什么时候开释。只有程序员不手动编程开释,就算函数运行完结,动态分配的内存空间也不会被开释,其余函数仍可持续应用它。除非是整个程序运行完结,这时零碎为该程序调配的所有内存空间都会被开释。 动态内存的申请与开释动态内存的申请与开释次要依附两个函数malloc和free。malloc 是一个零碎函数,它是 memory allocate 的缩写。其中memory是“内存”的意思,allocate是“调配”的意思。顾名思义 malloc 函数的性能就是“分配内存”,要调用它必须要蕴含头文件<stdlib.h>。 malloc()函数会向堆中申请一片间断的可用内存空间;若申请胜利 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以咱们在用malloc()函数开拓动态内存之后, 肯定要判断函数返回值是否为NULL;返回值的类型为void型, malloc()函数并不知道间断开拓的size个字节是存储什么类型数据的 ,所以须要咱们自行决定 ,办法是在malloc()前增强制转 ,转化成咱们所需类型 ,如: (int)malloc(sizeof(int)*n). 上面应用 malloc 函数写一个程序,程序的性能是:调用被调函数,将主调函数中动态分配的内存中的数据放大 10 倍。 输入后果是:*p = 100 free是开释函数,在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会主动开释内存 , 如果咱们不手动开释, 直到程序运行完结才会开释, 这样就可能会造成内存透露, 即堆中这片内存中的数据曾经不再应用, 但它始终占着这片空间, 所以当咱们申请的动态内存不再应用时 ,肯定要及时开释 .不过须要留神的是,开释并不是指清空内存空间,而是指将该内存空间标记为“可用”状态,使操作系统在分配内存时能够将它重新分配给其余变量应用。 那么,当指针变量被开释后,它所指向的内存空间中的数据会怎么呢?free 的规范行为只是示意这块内存能够被再调配,至于它外面的数据是否被清空并没有强制要求。不同的编译器解决的形式可能不一样。咱们就看一下 VC++6.0 这个编译器是怎么解决的: 可见在 VC++6.0 中,当指针变量被开释后,尽管它依然是指向那个内存空间的,但那个内存空间中的值将会被从新置一个十分小的正数。动态创建的内存如果不必了必须要开释。留神,一个动态内存只能开释一次。如果开释屡次程序就会解体,因为曾经开释了,不能再开释第二次。 综上所述,malloc 和 free 肯定要成对存在,一一对应。有 malloc 就肯定要有 free,有几个 malloc 就要有几个 free,与此同时,每开释一个指向动态内存的指针变量后要立即把它指向 NULL。 注意事项1)开释一块内存的一部分是不容许的。动态分配的内存必须整块一起开释。然而,realloc函数能够放大一块动态分配的内存,无效地开释它尾部的局部内存。 2)不要拜访曾经被free函数开释了的内存。假设对一个指向动态分配的内存的指针进行了复制,而且这个指针的几份拷贝扩散于程序各处。你无奈保障当你应用其中一个指针时它所指向的内存是不是已被另一个指针开释。还要确保程序中所有应用这块内存的中央在这块内存开释之前进行对它的应用。 3)当动态分配的内存不再须要应用时,应该被开释,这样能够被重新分配应用。分配内存但在应用结束后不开释将引起内存透露(memory leak)。 ...

September 10, 2021 · 1 min · jiezi

关于c:11enumtypedefsizeof

Summary1)enum是C语言中的一种自定义类型; enum类型的变量只能取定义时的离散值。 2)enum在C语言中能够定义真正意义上的常量,工程中罕用枚举来定义常量(无名枚举)。 3)sizeof是编译器的内置批示符,sizeof的值在编译期就确定。 4)sizeof是C语言的内置关键字而不是函数 在编译过程中所有的sizeof将被具体的数值替换,括号里的函数或者语句都不会执行程序的执行过程与sizeof没有任何关系5)typedef用于给已有的类型重命名,实质上不能产生新类型,新的类型名不能被unsigned或者signed润饰,原类型名能够在typedef后定义。 enum、typedef、siezof剖析1、enumenum是C语言中的一种自定义类型enum的值是能够依据须要自定义的整形值第一个定义enum值默认为0默认状况下的enum值是在前一个定义值的根底上加1enum类型的变量只能取定义时的离散值 enum Color{ RED , YELLOW = 2, GREEN};int main(){ enum Color cc = GREEN; printf("cc = %d\n", cc); // 3 return 0;}enum中定义的值是C语言中真正意义上的常量在工程中,enum多用于定义整形常量 enum // 无名枚举{ ARRAY_SIZE = 5 // 用来定义数组大小,常量};int arr[ARRAY_SIZE] = {0};2、sizeofsizeof是编译器的内置批示符sizeof的值在编译期就曾经确定sizeof用于计算类型或变量所占的内存大小 sizeof用于类型:sizeof(type)sizeof用于变量:sizeof(var)或者sizeof var int var = 0;printf("%d\n", sizeof(int));printf("%d\n", sizeof(var));printf("%d\n", sizeof var);sizeof是C语言的内置关键字而不是函数 在编译过程中所有的sizeof将被具体的数值替换程序的执行过程与sizeof没有任何关系 // 上面程序的输入是?int var = 0;int size = sizeof(var++);printf("var = %d, size = %d\n", var, size); // 0, 4程序输入var = 0, size = 4,因为sizeof在编译期时候就曾经确定值,即sizeof(var++)被替换成了4,var++也不会再执行。 ...

September 5, 2021 · 1 min · jiezi

关于c:C进阶10struct和union分析

Summary1)空构造体的大小在不同的编译器下行为不同:gcc编译器下空构造体的大小为0;bcc和vc编译器认为空构造体的语法错误,编译谬误 2)柔型数组的要点: struct SoftArray{ int len; int array[];};sizeof(struct SoftArray) = 4 struct SoftArray* sa = NULL; sa = (struct SoftArray*)malloc(sizeof(struct SoftArray) + 5*sizeof(int)); sa->len = 5;柔性数组的最初一个数组是一个标识符,不占用空间,sizeof(struct SoftArray)失去的是数组长度占用的空间。柔性数组须要从堆空间中申请,能够自在调配想要大小数组,malloc完了之后指定数组的大小。益处在于,不须要在定义的时候指定数组大小,能够在运行时指定大小;带了长度信息,作为函数参数的时候不须要额定指定数组的大小。3)union中的所有成员共享一段内存,大小为成员中最大的一个所占内存。 4)小端系统:低地址存储低位数据;大端系统:低地址存储高位数据。应用union能够判断零碎的大小端。不管大小端系统,在取uu.c的值是,都是从低地址处按字节来取。 struct和union剖析1、struct1.1 空构造体占用多大的内存?struct TS{};sizeof(struct TS) = ?以上的代码,在不同的编译器下,后果不同: gcc编译器下,空构造体的大小为0bcc编译器下,编译谬误vc编译器下,编译谬误这对应了两种解释思路: bcc、vc认为,既然构造体是存储变量的汇合,如果一个构造体里什么都不放,天然是不对的,编译谬误gcc认为,空的构造体里什么变量都没有,天然大小为0;同时也能用空构造体类型来定义变量,大小也为0。1.2 柔性数组柔性数组是数组大小待定的数组C语言中能够由构造体产生柔性数组C语言中构造体的最初一个元素能够是大小未知的数组 struct SoftArray{ int len; int array[];};sizeof(struct SoftArray) = 4柔性数组的用法: struct SoftArray{ int len; int array[];};int main(){ printf("sizeof(struct SoftArr) = %d\n", sizeof(struct SoftArray)); struct SoftArray* sa = NULL; sa = (struct SoftArray*)malloc(sizeof(struct SoftArray) + 5*sizeof(int)); sa->len = 5; for(int i=0; i<sa->len; ++i) { sa->array[i] = i+1; printf("sa->array[%d] = %d\n", i, sa->array[i]); } return 0;}柔性数组的最初一个数组是一个标识符,不占用空间,sizeof(struct SoftArray)失去的是数组长度占用的空间。柔性数组须要从堆空间中申请,能够自在调配想要大小数组,malloc完了之后指定数组的大小。益处在于,不须要在定义的时候指定数组大小,能够在运行时指定大小;带了长度信息,作为函数参数的时候不须要额定指定数组的大小。2、union2.1 union占用的内存C语言中的union在语法上与struc类似union只调配最大成员的空间,所有成员共享这个空间。 ...

September 5, 2021 · 1 min · jiezi

关于c:一些优质CC-IDE

我这里只举荐3款自认为比拟好的ide 1.CLionCLion是JetBrains的,代码补全很爽,写起货色来很棘手,PyCharm,WebStrom和GoLand都是神作。然而不自带编译器官网 教程 如何激活?点我! 2.Visual studio很好用,代码补全,智能性能剖析....就是有点大,占空间。然而,一个业余的IDE性能齐备,很好的 Visual Studio有不同版本,社区版收费。不仅反对C/C++,还有C/C++游戏开发等等反对。就是代码补全很蛋疼... 官网 3.Dev C++很不便,能够间接上,然而没有代码补全,且很老官网曾经停运了。。。 当初如果有小伙伴还想用,举荐小熊猫Dev-C++ 总结老手不怕麻烦能够选VS,喜爱丝滑的感觉果决CLion,不爱折腾...DevC++

September 4, 2021 · 1 min · jiezi

关于c:C读取XML文件

C#读取XML文件try{ XmlDocument xmlDoc = new XmlDocument();xmlDoc.Load(@"types.xml");XmlElement xmlRoot = xmlDoc.DocumentElement;foreach (XmlNode node in xmlRoot.ChildNodes){ Type type = new Type(); string num = node["num"].InnerText; string desc = node["desc"].InnerText; string content = node["content"].InnerXml;}}catch { }

September 3, 2021 · 1 min · jiezi

关于c:五零基础上手HAL库之按键外部中断

【五】零根底上手HAL库之—按键内部中断 5.1 前言咱们曾经大抵的理解了Cubemx软件中GPIO的基本操作了,接下来咱们开始进入内部中断的操作,这是一个触目惊心的时刻,为什么呢?在应用Hal库的同时你会感觉到其劣势性,开发效率大大提高带来的快感。 后期筹备 STM32各类型的板子(自己应用F103Rc和F407ZE)。CubeMx软件,Keil\_IDE。5.2 Key按键中断同样的咱们先来看看按键模块的原理图局部: 当按键没有按下时,按键局部相当于断路,PC13的电压相当于电容两端电压为3.3V。 当按键按下时,按键局部相当于短路(即一根导线),PC13的电压和GND地相连为0V。 所以按键从未按下到按下相当于是一个高电平到低电平的跳变,简称为降落沿。所以说咱们在按键中断时抉择的模式就是降落沿触发。 1、新建工程 搜寻或者筛选芯片后,点击Start Project胜利创立一个我的项目。 2、时钟配置 点击RCC进入时钟配置,配置高速时钟为内部晶振,软件主动配置了两个晶振的引脚如图PD0和PD1 3、Debug配置为DAP 4、GPIO配置为中断 ① GPIO模块配置模块② 各引脚配置: PC12/PC13(内部中断模式中断线12和13),共用一个中断向量。PC14/PC15输入模式为LED的两个引脚③ 引脚性能具体配置: GPIO mode :回升沿/降落沿/上降落沿 中断和事件模式。GPIO Pull-up/Pull-down:因为我的板子硬件上有上拉电阻即平时电平为高电平,所以配置为弱上拉模式。User Lable:这里没有应用到用户标签为了不便在图中右半局部看到引脚所配置的性能。④ 输入模式配置:PC14/PC15配置为输入模式,管制LED灯亮灭。⑤ 内部中断线13:PC13为降落沿触发的内部中断模式,KEY1。⑥ 内部中断线12:PC12为降落沿触发的内部中断模式,KEY2。 5、项目管理配置以及代码生成配置这里咱们还是一样配置为最高的时钟频率72M,在对应的框中输出72,按下回车即可见到如下两图的变动。按下回车前如图: 按下回车后如图: 6、项目管理配置以及代码生成配置同样的咱们填写好工程名KEY,抉择好文件门路,以及所用的IDE以及版本号即可 代码生成抉择好这两个后点击右上角按钮,产生代码: 7、业务逻辑代码7.1 API咱们再来看看GPIO模块对应的API函数: /* 初始化和删除初始化函数, HAL_GPIO_Init:cubemx生成代码后主动调用初始化函数 HAL_GPIO_DeInit:解除初始化,不想应用时能够被动应用*/void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);/* 操作GPIO的函数,包含读取,写入,翻转,加锁,中断服务函数以及回调函数 HAL_GPIO_ReadPin:读取引脚电平 返回值为(GPIO_PIN_RESET/GPIO_PIN_SET) HAL_GPIO_WritePin:写入引脚电平 HAL_GPIO_TogglePin:翻转引脚电平 HAL_GPIO_LockPin:所以引脚以后电平,将无奈扭转 HAL_GPIO_EXTI_IRQHandler:引脚的中断服务函数 HAL_GPIO_EXTI_Callback:引脚的中断回调函数 */GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);7.2 编写回调函数void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ if(GPIO_Pin == GPIO_PIN_12) { HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_14); } if(GPIO_Pin == GPIO_PIN_13) { HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_15); }}咱们来看看回调函数是怎么工作的: ...

September 2, 2021 · 1 min · jiezi

关于c:四零基础上手HAL库之GPIO按键

【四】零根底上手HAL库之—GPIO按键3.1前言咱们在上一节曾经晓得了点灯操作,相当于是学会了GPIO输入的配置,接下来是GPIO输出实现按键扫描的操作。 后期筹备 STM32各类型的板子(自己应用F103Rc和F407ZE)。CubeMx软件,Keil\_IDE。 3.2 KEY按键同样的咱们先来看看按键模块的原理图局部: 当按键没有按下时,按键局部相当于断路,PC13的电压相当于电容两端电压为3.3V。 当按键按下时,按键局部相当于短路(即一根导线),PC13的电压和GND地相连为0V。 所以按键从未按下到按下相当于是一个高电平到低电平的跳变,简称为降落沿。 1、新建工程 搜寻或者筛选芯片后,点击Start Project胜利创立一个我的项目。 2、时钟配置 点击RCC进入时钟配置,配置高速时钟为内部晶振,软件主动配置了两个晶振的引脚如图PD0和PD1 3、Debug配置为DAP 4、输出模式配置 ① 抉择如原理图所示的引脚,这里PC14/PC15为LED引脚,PC12/PC13为KEY引脚,别离设置为输入和输出模式② 选中GPIO模块,对引脚模式进行具体配置③ 设置PC12的具体模式, GPIO mode:输出模式。GPIO Pull:由原理图可知,当没有按键按下时为高电平,所以抉择上拉模式。User Label:用户标签,用于标识对应引脚的端口和Pin,不便调用API,这里按键为KEY1/KEY2,灯为LED1/LED2。 5、时钟配置这里咱们还是一样配置为最高的时钟频率72M,在对应的框中输出72,按下回车即可见到如下两图的变动。按下回车前如图: 按下回车后如图: 6、项目管理配置以及代码生成配置同样的咱们填写好工程名KEY,抉择好文件门路,以及所用的IDE以及版本号即可 代码生成抉择好这两个后点击右上角按钮,产生代码: 7、业务逻辑代码7.1 API咱们再来看看GPIO模块对应的API函数: /* 初始化和删除初始化函数, HAL_GPIO_Init:cubemx生成代码后主动调用初始化函数 HAL_GPIO_DeInit:解除初始化,不想应用时能够被动应用*/void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);/* 操作GPIO的函数,包含读取,写入,翻转,加锁,中断服务函数以及回调函数 HAL_GPIO_ReadPin:读取引脚电平 返回值为(GPIO_PIN_RESET/GPIO_PIN_SET) HAL_GPIO_WritePin:写入引脚电平 HAL_GPIO_TogglePin:翻转引脚电平 HAL_GPIO_LockPin:所以引脚以后电平,将无奈扭转 HAL_GPIO_EXTI_IRQHandler:引脚的中断服务函数 HAL_GPIO_EXTI_Callback:引脚的中断回调函数 */GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);main( )函数咱们能够在main.h中看到之前定义的用户标签,如下: ...

September 1, 2021 · 2 min · jiezi

关于c:三零基础上手HAL库之GPIO点灯

【三】零根底上手HAL库之—GPIO点灯3.1前言咱们曾经大抵的理解了Cubemx软件界面的个别操作,懂得如何新建工程和应用步骤了,接下来咱们正式进入应用Hal库开发的旅程,这是一个触目惊心的时刻,为什么呢?在应用Hal库的同时你会感觉到其劣势性,开发效率大大提高带来的快感,来吧咱们先点灯。 后期筹备 STM32各类型的板子(自己应用F103Rc和F407ZE)。CubeMx软件,Keil\_IDE。 3.2 LED流水灯咱们先来看看板子对应流水灯的原理图: 采纳共阴极连贯的LED灯,只有当引脚输入高电平时,发光二极管正向导通发光。咱们晓得了板子硬件的电路结构,那么咱们来看看软件上怎么配置。 1、新建工程: 搜寻或者筛选芯片后,点击Start Project胜利创立一个我的项目 2、时钟配置 点击RCC进入时钟配置,配置高速时钟为内部晶振,软件主动配置了两个晶振的引脚如图PD0和PD1 留神引脚色彩: 绿色 :曾经配置了性能且抉择了模式黄色:配置了性能,但未配置模式灰色:暂未配置米红色:不可配置引脚 3、GPIO引脚配置 ① 找到流水灯对应的引脚PC14(PC15),设置为输入。② 找到GPIO模块设置对应的模式③ 以后我的项目配置的引脚的总览页面④ GPIO模式的具体配置(包含初始化输入电平,输入模式,高低拉模式,引脚速度,引脚性能用户标签) 4、GPIO模式具体配置 ① 以后配置的引脚,这里是PC15② 初始化输入电平,因为咱们的LED灯是共阴极接法,所以输入为高电平时灯会亮③ 输入的模式(推挽、开漏输入),这里应用默认的推挽输入即可。④ 是否高低拉引脚(上拉,下拉),这里应用默认。⑤ 引脚速度(低,中,高速),流水灯对引脚速度没有特殊要求,低速即可。⑥ 用户标签,在初始化实现后,对应的引脚和端口会有对应的宏定义在main.h中生成。 5、下载模式配置(肯定要配置) DAP-LinkJTAG依照本人所应用的下载(调式)形式进行对应的配置,我是用的是DAP所以配置为Serial Wire,如果不进行配置进行一次下载后,再一次下程序可能就下不进了,留神留神肯定要配置。 6、时钟配置这里咱们采纳最简略,最不便,最快捷的配置形式,见下图: 点击Clock Configuration后对应②中填入72(F407最高频率为168,不同芯片能够配置的最高频率不同)按下回车。 而后失去如下模式,示意时钟曾经配置胜利: 7、项目管理配置以及代码生成在相熟Cubemx软件框架中曾经细细的讲过这部分内容,咱们须要配置的是项目名称,我的项目保留地位,所用IDE的配置 抉择如下两个选项后,点击右上角的GNEERATE CODE按钮,产生代码。 在进行大型项目编写时,经常要求模块化编程,以及对文件大小有所束缚,个别状况下,抉择以上两个选项能够大大的优化编程。在代码量和代码可读性上有所优化。8、编写业务代码关上main.c文件,咱们能够看到如下界面,咱们在应用Cubemx和Keil开发时,所有的用户代码都是写入在 /* USER CODE BEGIN 1 *//* USER CODE END 1 */这样的正文之间,而且经常在不必的区间编写不同的代码。 API咱们再来看看GPIO模块对应的API函数: /* 初始化和删除初始化函数, HAL_GPIO_Init:cubemx生成代码后主动调用初始化函数 HAL_GPIO_DeInit:解除初始化,不想应用时能够被动应用*/void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);/* 操作GPIO的函数,包含读取,写入,翻转,加锁,中断服务函数以及回调函数 HAL_GPIO_ReadPin:读取引脚电平 HAL_GPIO_WritePin:写入引脚电平 HAL_GPIO_TogglePin:翻转引脚电平 HAL_GPIO_LockPin:所以引脚以后电平,将无奈扭转 HAL_GPIO_EXTI_IRQHandler:引脚的中断服务函数 HAL_GPIO_EXTI_Callback:引脚的中断回调函数 */GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);main( )函数咱们能够在main.h中看到之前定义的用户标签,如下: ...

August 31, 2021 · 1 min · jiezi

关于c:二零基础上手HAL库之熟悉Cubemx软件的框架

【二】零根底上手HAL库之—相熟Cubemx软件的框架在实现了Hal库开发环境的搭建后,就是要开始应用此工具了,那么咱们必定有一些必要的筹备工作要进行,本节内容仅仅解说软件各个模块的性能,想要用好软件,第一步当然就是大体上的相熟软件创立一个工程。如果还没搭建好环境能够看上一节内容: 2.1 后期筹备Stm32f103Rct6芯片的板子Keil5软件以及上一节内容装置好的开发环境(Cubemx、JRE、Hal包) 点击关上软件能够失去如下界面: ①创立,关上,导入我的项目等操作。②调整窗口大小,以及输入值的一些操作。③更新软件,以及一些文献资料查找。④通过芯片类型创立一个我的项目⑤通过板子类型创立一个我的项目⑥查找一个例程 点击主界面④框选的局部,开始通过芯片类型创立一个工程 ①搜寻所须要的芯片。②内核类型。③芯片系列。(F1/F4/F7等等)④芯片系列细分。(103/100等等)⑤芯片封装类型。⑥其余(芯片的主频,Rom,Flash等等)⑦外设资源。⑧通过筛选后的芯片。 双击选取芯片界面中⑧框选的局部,一个依据芯片类型的工程创立实现,能够看到如下工程界面,此界面大抵能够分成如下四块用于配置芯片。 ①Categories(品种)依据外设的品种来抉择配置的外设。②A->Z(序号)依据外设首字母序号来抉择配置的外设。③外设模式的配置以及此模式下的具体参数配置。④芯片引脚配置详情,绿色示意曾经应用 点击Clock Configuaration进入时钟树配置界面,学过stm32规范库的都应该很革除这个时钟树,咱们先晓得有这个货色,下一节会讲到这部分内容。 点击Project Manager进入我的项目配置界面, ①项目名称(尽量不要用中文)。 ②我的项目地址(留神留神!!!! 肯定不要有中文名门路)。 ③编译代码所用的IDE开发(我个别应用Keil进行)。 ④堆栈大小的设置(个别应用默认即可)。 ⑤芯片类型(肯定要和所应用的芯片对应上)。 ⑥固件包版本(不同的版本会有些许差别,但最新版准没错)。 点击Code Generator 选项卡,后可见如下界面: ①复制所有的软件包到工程中。②复制所需的软件包到工程中。③不复制文件,从固件包地位援用相干的文件到工程中(见上一步固件包的默认地位)。④依照类型给每个外设设立独立的源文件(.c)和头文件(.h)。⑤从新生成时备份上一次产生的文件(有谬误时,能够还原上一版本)。⑥从新生成时保留用户区的代码。⑦从新生成时删除原有的文件。⑧没应用的引脚设置为模仿状态引脚。 ①驱动库抉择能够是HAL库和LL库,不晓得二者区别能够看之前内容②是否调用初始化函数,也就是你代码是否调用对应初始化函数。 比方:你GPIO项勾选Not Generate Function Call,你main.c函数中就不会调用MX\_GPIO\_Init这个函数。 ③是否申明为static动态内链接函数,也就勾选时在外文件不能调用此函数。

August 30, 2021 · 1 min · jiezi

关于c:C进阶9const和volatile分析

Summary 1)const润饰的变量是只读的,实质还是变量。(不管值是否扭转) 2)调配空间:const润饰的局部变量在栈上调配空间。const润饰的全局变量在全局数据区调配空间。(这两点印证了const润饰的依然是变量,因为在内存中仍然会调配空间) 3)const只在编译期有用,在运行期无用。(示意:const润饰的变量,在编译的时候不能放在赋值符号左侧,然而在运行期,就能够通过指针扭转该变量的值) 4)在古代C语言编译器中,批改const全局变量会导致程序解体。因为古代C语言编译器,会将具备全局生命期的const变量放在只读存储区,改了只读存储区的内容,造成解体。规范C语言编译器依然将全局生命期的const变量放在全局数据区,所以是能够扭转的。 5)const润饰函数参数,示意在函数体内不心愿扭转参数的值(形参不能够呈现在赋值符号左侧)。 6)const润饰函数返回值,示意返回值不可扭转,多用于返回指针的状况(指针指向的值不可扭转)。 7)字符串字面量存储于只读存储区,必须应用const char*来援用字符串字面量(这样在编译期会将批改只读存储区字面量的谬误报进去);如果只是应用了char *,此时编译的过,然而运行时就会遇到段谬误。 8)volatile通知编译器每次都必须去内存中去变量值;volatile多用于多线程环境中(变量可能在其余线程或其余中央被扭转) const和volatile1、const1.1 const润饰变量const int const_global_i = 1; // const全局变量,全局生命期int main(){ const static int const_static_i = 2; // static局部变量,全局生命期 const int const_local_i = 3; // 一般局部变量 // const_local_i = 30; // error,在编译期不容许呈现在赋值符号左侧 int* p = NULL; // 规范C语法须要将要应用的变量先申明。 p = (int*)&const_global_i; *p = 10; printf("const_global_i = %d\n", const_global_i); p = (int*)&const_static_i; *p = 20; printf("const_statici = %d\n", const_static_i); p = (int*)&const_local_i; *p = 30; printf("const_local_i = %d\n", const_local_i); return 0;}在bcc编译器下(规范C语言编译器),这段代码可失常编译,测试的后果为三个变量的值均被扭转,阐明,在规范C编译器下,const定义的所有只读变量的值都能够扭转。在gcc编译器下(扩大C编译器),这段代码能够编译的过,然而上面这两处赋值语句,在运行时都会产生段谬误。阐明,在古代C语言编译器下,const定义的只读变量如果是全局生命期(如全局变量、static变量),则会将该变量放到只读存储区,批改了就会段谬误。 ...

August 29, 2021 · 1 min · jiezi

关于c:二零基础上手HAL库之环境搭建

【二】零根底上手HAL库之—环境搭建JRE :Java运行环境STM32CubeMx:意法半导体官网图形化配置软件HAL库:软件库包2.1 JRE装置STM32Cubemx是基于Java开发,并须要Java环境能力反对失常运行。 官网地址:Java下载官网地址百度云地址:百度云地址提取码见文章尾公众号,上面是一个Java装置的步骤。 关上网站后点击如下选项 关上刚下载实现的软件,见得如下页面 点击装置后,见得如下页面,点击更改抉择本人想要装置的门路 点击下一步后进行装置,吃点货色,坐等进度条拉满 点击敞开,装置胜利 2.2 Cubemx装置Cubemx就是用于图形化配置stm32的,由意法半导体业余团队开发的一个软件。 官网地址 : 官网地址百度云地址 : 百度云地址提取码见文章尾公众号 点击Get software获取软件 下载实现后,关上安装包如下,点击next: 抉择对应的选项,点击next 点击红框中的抉择,点击next 抉择对应的装置门路后,点击next 点击next进行装置 期待装置实现即可 2.3 装置Hal包 关上刚刚下载完的Cubemx后点击上面按钮: 选取对应型号芯片的包,点击install now 即可 期待装置实现即可 最初祝贺你,入门Hal库的第一步就这样实现啦,上面就是"爽"的时候了,曾经急不可待了吧,来吧,咱们先点灯。

August 29, 2021 · 1 min · jiezi

关于c:C进阶8goto和void分析

Summary1) 个别工程开发中须要禁用goto语句,不同的编译器对goto语句的错误处理可能不同。 2)在C语言中,如果函数没写返回值,则默认返回值类型是int;如果函数没有写参数列表,则函数承受任意数量的参数。因而,如果函数没有返回值,必须显式申明返回值类型为void;如果函数没有参数,则必须申明参数列表为void。 3)void是一种根底类型,但不是根底数据类型,所以无奈用来定义变量(C语言没规定void是多大内存空间的别名)。 4)对于规范C语言标准,不容许sizeof(void)这种写法,如bcc编译器;对于扩大C语言,可能容许sizeof(void)这种写法,如gcc编译器,输入为1。 5)void*指针的次要作用是:用于接管任意类型的指针。在C语言中,void* 和 type*能够互相转换;在C++中,void*能够作为左值接管任意类型的指针,作为右值时,必须进行强制类型转换。 goto和void1、gotogoto语句带有很浓重的汇编语言个性,可能跳转到指定的语句,如同汇编语言的跳转指令。在工程实际中,个别都禁用“goto”。goto会毁坏程序的构造,带来意想不到的谬误。 // 代码示例:以下代码输入什么?int main(){goto End; int i = 0;End: printf("i = %d\n", i); return 0;}gcc编译器:编译谬误error,提醒i变量的初始化被跳过了。vs编译器:编译正告warning,提醒i未初始化,然而编出了可执行程序。运行的后果是随机值,意料之外的谬误!2、void2.1 void润饰返回值和参数在C语言中: 如果函数没有返回值,那么应该将其申明为void如果函数没有参数,应该申明其参数为void留神, C语言中: 如果函数没写返回值,默认的返回值类型是int如果函数没有申明参数列表,默认承受任意多参数 f(){}int main(){ int i = f(1, 2, 3); // ok return 0;}2.2 是否存在void类型的变量?论断:不存在void类型的变量 void是一种根底类型,但不是根底数据类型;C语言中没有定义void到底是多大内存的别名,所以也无奈定义对应的变量。 int main(){ void var; // error void arr[5]; // error return 0;}小贴士: ANSI C:规范C语言标准,如bcc编译器扩大C:在ANSI C的根底上进行了扩大,如gcc编译器 // void类型有大小么?int main(){ prinft("%d\n", sizeof(void)); return 0;}gcc编译器,Demo能够编过,输入为1bcc编译器,编译谬误,提醒void是不容许的类型。2.3 void类型的指针在C语言中,对指针类型的查看不那么严格,所以void类型的指针能够和任意数据类型的指针进行互相转换。 // test.cint main(){ int* p = nullptr; void* pv = p; // ok char* pc = pv; // ok return 0;}在C++语言中,对类型进行了加强,共事也兼容了C语言中的写法。void能够作为左值承受任意类型的指针,然而,void作为右值时,就必须进行强制类型转换。 ...

August 29, 2021 · 1 min · jiezi

关于c:一零基础上手HAL库之为什么要选择HAL库

【一】零根底上手HAL库之—为什么要抉择HAL库寄存器:(Snippets)ST规范库:(Standard Peripheral Libraries)LL库:(Low Layer Libraries)HAL库:(Hardware Abstraction Layer Libraries)1.1 寄存器操作寄存器,不同于其余三种库的操作形式,实用于同C51中的开发模式,间接操作低层的寄存器,自身只对寄存器的地址进行了封装映射关系。因为在C51这样的单片机中,只有大量的寄存器须要操作,应用比较简单。但在stm32寄存器极多,须要记忆十分难,每次进行开发都须要看开发手册,开发周期增大,所以个别很少人应用,当然谋求效率另说。但不得不说在寄存器操作中的位带操作是真的挺好用的,但须要留神不同的芯片的IO的映射关系的区别(能够参考晚点原子的sys.h文件)。 实用于谋求高效率的人群,但LL库能够齐全代替寄存器,效率并驾齐驱。 1.2 ST规范库 官网首先推出的一套由低层寄存器对STM32芯片封装而来的一个库,包含规范器件外设的器件驱动文件。基本上用C语言实现,仅仅应用了大量的汇编。库思维是面向过程的,面向过程的了解就是一个函数的执行仅仅实现一件事件。库自身的运作也更靠近于寄存器,仅仅是在寄存器上的一层封装操作。 实用于初学stm32的人群,能够让学者通过库理解局部的低层寄存器。 1.3 LL库LL 库的一大特点就是奇妙的使用 C 语言的动态、内联函数来间接操作寄存器,操作硬件低层的库。LL库是配合CubeMx软件开发时,更靠近与硬件的一个库,能够独自应用LL库开发,也能够配合HAL库进行开发。 实用于谋求效率,同时须要放慢开发周期的人群。 1.4 HAL库 HAL库的一大特点就是面向对象,置信很多人对面向对象曾经再相熟不过了,操作形象层的库。通过几层的封装,HAL库将每个外设封装为一个对象,应用CubeMx软件配置后,只须要操作对象句柄就能够应用对应的外设,操作非常简单。当然谋求效率能够配合LL库进行开发。 实用于不执着与低层代码实现,而在业务逻辑代码上有更高要求的人群,不须要理解太多的硬件,开发简略,但效率有所落后。 1.5 规范库与Cube LL,Cube HAL,寄存器的效率比照图 1.6 总结两个方面,效率和开发周期。须要进行取舍越形象的库开发周期越短,但效率往往越低。但对应当代社会的需要,倒退十分迅速,设施改新换代十分之快,所以往往须要在短周期内进行开发,谋求先机,分得市场红利,这也是官网为什么力推HAL库而停更ST规范库的起因之一,对于我集体学过了规范库上手HAL之后,就一个字 “爽”。至于为什么爽,一起来看看吧~当他人还在配置低层驱动的时候,你曾经能够开始你的业务逻辑代码的编写了,就是这么爽!!!!

August 28, 2021 · 1 min · jiezi

关于c:C语言在嵌入式系统编程时的注意事项

C语言是一门通用计算机编程语言,利用宽泛。C语言的设计指标是提供一种能以繁难的形式编译、解决低级存储器、产生大量的机器码以及不须要任何运行环境反对便能运行的编程语言。 只管C语言提供了许多低级解决的性能,但依然放弃着良好跨平台的个性,以一个规范规格写出的C语言程序可在许多电脑平台上进行编译,甚至蕴含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。 20世纪80年代,为了防止各开发厂商用的C语言语法产生差别,由美国国家标准局为C语言订定了一套残缺的国际标准语法,称为ANSI C,作为C语言最后的规范。 C语言嵌入式零碎编程注意事项不同于个别模式的软件编程,嵌入式零碎编程建设在特定的硬件平台上,势必要求其编程语言具备较强的硬件间接操作能力。无疑,汇编语言具备这样的特质。然而,归因于汇编语言开发过程的复杂性,它并不是嵌入式零碎开发的个别抉择。而与之相比,C语言--一种“高级的低级”语言,则成为嵌入式零碎开发的最佳抉择。笔者在嵌入式零碎我的项目的开发过程中,一次又一次感触到C语言的精妙,沉醉于C语言给嵌入式开发带来的便当。 大多数嵌入式零碎的硬件平台。它包含两局部: (1) 以通用处理器为核心的协定解决模块,用于网络控制协议的解决; (2) 以数字信号处理器(DSP)为核心的信号处理模块,用于调制、解调和数/模信号转换。 本文的探讨次要围绕以通用处理器为核心的协定解决模块进行,因为它更多地牵涉到具体的C语言编程技巧。而DSP编程则重点关注具体的数字信号处理算法,次要波及通信畛域的常识,不是本文的探讨重点。 着眼于探讨广泛的嵌入式零碎C编程技巧,零碎的协定解决模块没有抉择特地的CPU,嵌入式零碎学习加意义气呜呜吧久林就易,而是抉择了家喻户晓的CPU芯片--80186,每一位学习过《微机原理》的读者都应该对此芯片有一个根本的意识,且对其指令集比拟相熟。80186的字长是16位,能够寻址到的内存空间为1MB,只有实地址模式。C语言编译生成的指针为32位(双字),高16位为段地址,低16位为段内编译,一段最多64KB。 协定解决模块中的FLASH和RAM简直是每个嵌入式零碎的必备设施,前者用于存储程序,后者则是程序运行时指令及数据的寄存地位。零碎所抉择的FLASH和RAM的位宽都为16位,与CPU统一。 实时钟芯片能够为零碎定时,给出以后的年、月、日及具体工夫(小时、分、秒及毫秒),能够设定其通过一段时间即向CPU提出中断或设定报警工夫到来时向CPU提出中断(相似闹钟性能)。 NVRAM(非易失去性RAM)具备掉电不失落数据的个性,能够用于保留零碎的设置信息,譬如网络协议参数等。在零碎掉电或重新启动后,依然能够读取先前的设置信息。其位宽为8位,比CPU字长小。文章特意抉择一个与CPU字长不统一的存储芯片,为后文中一节的探讨创造条件。 UART则实现CPU并行数据传输与RS-232串行数据传输的转换,它能够在接管到[1~MAX_BUFFER]字节后向CPU提出中断,MAX_BUFFER为UART芯片存储接管到字节的最大缓冲区。 键盘控制器和显示控制器则实现零碎人机界面的管制。 以上提供的是一个较齐备的嵌入式零碎硬件架构,理论的零碎可能蕴含更少的外设。之所以抉择一个齐备的零碎,是为了后文更全面的探讨嵌入式零碎C语言编程技巧的方方面面,所有设施都会成为后文的剖析指标。 嵌入式零碎须要良好的软件开发环境的反对,因为嵌入式零碎的指标机资源受限,不可能在其上建设宏大、简单的开发环境,因此其开发环境和指标运行环境互相拆散。因而,嵌入式应用软件的开发方式个别是,在宿主机(Host)上建设开发环境,进行应用程序编码和穿插编译,而后宿主机同指标机(Target)建设连贯,将应用程序下载到指标机上进行穿插调试,通过调试和优化,最初将应用程序固化到指标机中理论运行。 CAD-UL是实用于x86处理器的嵌入式应用软件开发环境,它运行在Windows操作系统之上,可生成x86处理器的指标代码并通过PC机的COM口(RS-232串口)或以太网口下载到指标机上运行。其驻留于指标机FLASH存储器中的monitor程序能够监控宿主机Windows调试平台上的用户调试指令,获取CPU寄存器的值及指标机存储空间、I/O空间的内容。 后续章节将从软件架构、内存操作、屏幕操作、键盘操作、性能优化等多方面论述C语言嵌入式零碎的编程技巧。软件架构是一个宏观概念,与具体硬件的分割不大;内存操作次要波及零碎中的FLASH、RAM和NVRAM芯片;屏幕操作则波及显示控制器和实时钟;键盘操作次要波及键盘控制器;性能优化则给出一些具体的减小程序工夫、空间耗费的技巧。 在咱们的修炼旅途中将通过25个关口,这些关口主分为两类,一类是技巧型,有很强的适用性;一类则是常识型,在实践上有些意义。 So, let’s go. C语言嵌入式零碎编程注意事项之软件架构篇模块划分的“划”是布局的意思,意指怎么正当的将一个很大的软件划分为一系列性能独立的局部单干实现零碎的需要。 模块划分 模块划分的“划”是布局的意思,意指怎么正当的将一个很大的软件划分为一系列性能独立的局部单干实现零碎的需要。C语言作为一种结构化的程序设计语言,在模块的划分上次要根据性能(依性能进行划分在面向对象设计中成为一个谬误,牛顿定律遇到了相对论),C语言模块化程序设计需了解如下概念: (1) 模块即是一个.c文件和一个.h文件的联合,头文件(.h)中是对于该模块接口的申明; (2) 某模块提供给其它模块调用的内部函数及数据需在.h中文件中冠以extern关键字申明; (3) 模块内的函数和全局变量需在.c文件结尾冠以staTIc关键字申明; (4) 永远不要在.h文件中定义变量!定义变量和申明变量的区别在于定义会产生内存调配的操作,是汇编阶段的概念;而申明则只是通知蕴含该申明的模块在连贯阶段从其它模块寻找内部函数和变量。如: /module1.h/ int a = 5; / 在模块1的.h文件中定义int a / /module1 .c/ include “module1.h”/ 在模块1中蕴含模块1的.h文件 //module2 .c/ #i nclude “module1.h” / 在模块2中蕴含模块1的.h文件 / /module3 .c/ #i nclude “module1.h” / 在模块3中蕴含模块1的.h文件 / 以上程序的后果是在模块1、2、3中都定义了整型变量a,a在不同的模块中对应不同的地址单元,这个世界上从来不须要这样的程序。正确的做法是: /module1.h/ extern int a; / 在模块1的.h文件中申明int a / ...

August 28, 2021 · 2 min · jiezi

关于c:Qt-自定义组件

源码合集 鸣谢qtcn飞腾青云一去二三里雨田哥问题反馈邮箱:1508539502@qq.com滑动窗口具体阐明 背景切换 背景图放弃 网格窗口具体阐明 动静曲线具体阐明 多彩仪表盘具体阐明 通用仪表盘具体阐明 水波进度条具体阐明 圆弧进度条具体阐明 滚动横幅 Banner具体阐明 音讯弹窗对话框具体阐明 繁难对话框具体阐明 IconHelper 字体图标辅助类具体阐明 公共辅助类具体阐明

August 24, 2021 · 1 min · jiezi

关于c:线程间同步方式详解

线程间同步形式 引言互斥锁 探索底层,实现一个锁 测试并加锁(TAS)比拟并替换(CAS)另一个问题,过多的自旋?回到互斥锁信号量 有名信号量无名信号量总结条件变量 什么是条件变量?相干函数 1. 初始化2. 期待条件3. 告诉条件用法与思考实际——读写者锁文章已收录至我的仓库:Java学习笔记与收费书籍分享 线程间同步形式引言不同线程间对临界区资源的拜访可能会引起常见的并发问题,咱们心愿线程原子式的执行一系列指令,但因为单处理器上的中断,咱们必须想一些其余方法以同步各线程,本文就来介绍一些线程间的同步形式。 互斥锁互斥锁(又名互斥量),强调的是资源的拜访互斥:互斥锁是用在多线程多任务互斥的,当一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其余的线程才开始能够利用这个资源。 int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);留神了解trylock函数,这与一般的lock不一样,一般的lock函数在资源被锁住时会被梗塞,直到锁被开释。 trylock函数是非阻塞调用模式,也就是说如果互斥量没被锁住,trylock函数将把互斥量加锁,并取得对共享资源的拜访权限; 如果互斥量被锁住了,trylock函数将不会阻塞期待而间接返回EBUSY,示意共享资源处于忙状态,这样就能够防止死锁或饿死等一些极其状况产生。 探索底层,实现一个锁实现一个锁必须须要硬件的反对,因为咱们必须要保障锁也是并发平安的,这就须要硬件反对以保障锁外部是原子实现的。 很容易想到保护一个全局变量flag,当该变量为0时,容许线程加锁,并设置flag为1;否则,线程必须挂起期待,直到flag为0. typedef struct lock_t { int flag;}lock_t;void init(lock_t &mutex) { mutex->flag = 0;}void lock(lock_t &mutex) { while (mutex->flag == 1) {;} //自旋期待变量为0才可进入 mutex->flag = 1;}void unlock(lock_t &mutex) { mutex->flag = 0;}这是基于软件的初步实现,初始化变量为0,线程自旋期待变量为0才可进入,这看上去仿佛并没有什么故障,然而认真思考,这是有问题的: 当线程恰好通过while断定时陷入中断,此时并未设置flag为1,另一个线程闯入,此时flag依然为0,通过while断定进入临界区,此时中断,回到原线程,原线程继续执行,也进入临界区,这就造成了同步问题。 在while循环中,仅仅设置mutex->flag == 1是不够的,只管他是一个原语,咱们必须有更多的代码,同时,当咱们引入更多代码时,咱们必须保障这些代码也是原子的,这就意味着咱们须要硬件的反对。 咱们思考下面代码为什么会失败?起因是当退出while循环时,在这一时刻flag依然为0,这就给了其余线程抢入临界区的机会。 解决办法也很直观 —— 在退出while时,借助硬件反对保障flag被设置为1。 测试并加锁(TAS)咱们编写如下函数: int TestAndSet(int *old_ptr, int new) { int old = *old_ptr; *old_ptr = new; return old;}同时从新设置while循环: ...

August 22, 2021 · 2 min · jiezi

关于c:C进阶7循环语句

Summary1)do...while语句先执行后判断,循环体至多执行一次;while语句先判断后执行,循环体可能不执行;for语句先判断后执行,相比while更简洁(因为for的一行里包含了循环变量初始化、条件判断、批改循环变量三个因素)。 2)break和continue的区别: break示意终止循环的执行continue示意终止本次循环,进入下次循环执行循环语句循环语句的根本工作形式: 通过条件表达式判断是否执行循环体条件表达式遵循if语句表达式的准则1、do ... while 2、while 3、for 4、do... while示例应用do while(0);配合break语句,实现对动静申请内存的开释。 int func(int n){ int i = 0; int ret = 0; int* p = (int*)malloc(sizeof(int) * n); do { if(NULL == p) break; if(n < 5) break; if(n > 100) break; for(i = 0; i<n; i++) { p[i] = i; printf("%d\n", p[i]); } ret = 1; } while(0); free(p); return ret;}本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。

August 22, 2021 · 1 min · jiezi

关于c:C宏定义中使用可变参数

宏定义中应用可变参数C99 减少了宏定义中应用可变参数的反对。用法为在定义中通过'...'定义可变参数,前面通过__VA_ARGS__援用。如上面定义DGB宏,在log中主动减少'DEBUG'。 #define DBG(format, ...) printf("DEBUG: " #format "\n", ##__VA_ARGS__)宏应用例 DBG("%d - %s", a,b);问题如下不指定参数应用,则会编译失败 DBG("hahaha");: error: expected expression before ‘)’ token#define DBG(format, ...) printf("DEBUG: " #format "\n", __VA_ARGS__) ^解决办法 #define DBG(format, ...) printf("DEBUG: " #format "\n", ##__VA_ARGS__) ^^参考 https://stackoverflow.com/que...http://gcc.gnu.org/onlinedocs...

August 22, 2021 · 1 min · jiezi

关于c:C进阶6分支语句

Summary1) 和0值、常量值(立刻数)进行比拟时,须要把立刻数放在比拟符号的左侧,如if(0 == i),避免出现if(i = 0)这样的书写谬误。 2)float型变量因为是不准确的表示法,所以在和0值比拟时不能间接用==,必须定义一个精度。如 #define EOSILON 0.00000001float f = 0.0;if( (-EPSILON <= f) && (f <= EPSILON) ) { // ...}else { //...}3)switch分支语句中,case语句中的值只能是整型或字符型(字符型实质也是一种整型);肯定留神每种case完结的break。 4)if次要实用于按片比拟的状况,简单逻辑;switch次要实用于单值、多分支的状况,离散值。 分支语句1、if ... else ...if语句用于依据条件抉择执行语句else不能独立存在并且总是与他最近的if相匹配else语句后能够连贯其余if语句if语句中零值比拟的留神点: bool型的变量应该间接呈现于条件中进行比拟变量和0值比拟时,0值应该呈现在比拟符号右边float型变量不能间接和0进行比拟,应定义精度2、switch ... case ...switch语句对应单个条件,多个分值的情景case语句分支必须要有break,否则会导致分支重叠default语句有必要加上,以解决非凡状况留神: case语句中的值只能是整型或字符型失常状况放在后面,异常情况放在前面default语句只能用于解决真正的默认状况本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。

August 15, 2021 · 1 min · jiezi

关于c:5回溯算法

回溯算法(back tracking)实际上一个相似枚举的搜寻尝试过程,次要是在搜寻尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的门路。 能够了解为深度优先算法。通过枚举各种类型,依据条件限度,失去符合条件的后果。 个别用于要求出所有可能的后果。例如排列组合等等。回溯算法框架: result = []def backtrack(门路, 抉择列表): if 满足完结条件: result.add(门路) return for 抉择 in 抉择列表: 做抉择 backtrack(门路, 抉择列表) 撤销抉择以全排列为例: 给定一个不含反复数字的数组 nums ,返回其 所有可能的全排列 。你能够 按任意程序 返回答案。int **ret = NULL;int ret_num = 0;int *array = NULL;int array_num = 0;int *used = NULL;void backtrack(int *nums, int numsSize, int index){ if(index == numsSize) { int *temp = malloc(sizeof(int) * numsSize); memcpy(temp, array, sizeof(int) * numsSize); ret[ret_num] = temp; ret_num++; } else { for(int i =0; i < numsSize; i++) { if(used[i] == 1) continue; array[array_num] = nums[i]; array_num++; used[i] = 1; backtrack(nums, numsSize, array_num);//array num 其实就是遍历的深度。 used[i] = 0; array_num--; } }}int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){ if(numsSize <= 0) { *returnSize = 0; return ret; } ret_num = array_num = 0; int total_num = 1; for(int i = 1; i <= numsSize; i++) total_num *= i; used = malloc(sizeof(int) * numsSize); memset(used, 0, sizeof(int) * numsSize); ret = malloc(sizeof(int*) * total_num); array = malloc(sizeof(int) * numsSize); backtrack(nums, numsSize, 0); *returnColumnSizes = malloc(sizeof(int) * total_num); for(int i = 0; i < total_num; i++) (*returnColumnSizes)[i] = numsSize; *returnSize = total_num; return ret;}此处应用一个数组来示意数字是否曾经应用。每次抉择只抉择没有应用的数字。重点是每次backtracking完当前,要复原现场,复原到backtracking的上一步,保障对下次抉择没有影响。 ...

August 12, 2021 · 2 min · jiezi

关于c:常见字符串和内存函数的使用和剖析

1.strlen函数,通过字符串的地址计算其长度,遇到/0进行计算. strlen函数的三种实现办法: #include<stdio.h>#include<assert.h>//1.计数器办法int my_strlen1(char* p){ int count = 0; assert(p != NULL); while (*p != '\0') { p++; count++; } return count;}//2递归办法int my_strlen2(char* p){ assert(p!= NULL); if (*p =='\0') { return 0; } else { return 1 + my_strlen2(p + 1); }}//指针相减法int my_strlen3(char* p){ assert(p!= NULL); char* ret = p; while (*p!='\0') { p++; } return p - ret; }int main(){ char arr[] = "abcdefgh"; int ret = my_strlen1(arr); printf("%d\n", ret);}2.strcpy函数 ...

August 10, 2021 · 3 min · jiezi

关于c:C进阶5变量属性

Summary1)auto是C语言中局部变量的默认属性;auto表明将被润饰的变量存储于栈上(局部变量的存储地位) 2)register将申明的局部变量存储于寄存器中;register是一种申请,不肯定胜利;不能用取地址符&去取register变量的地址 3)static关键字次要体现两个方面的意义: 指明变量的动态属性(生命期、存储地位:动态局变存储区从栈上变到全局数据区)限定变量的作用域(动态全局变量、动态函数:作用域只在以后文件中,原本是程序作用域)4)extern关键字次要有两个方面的用处: extern通知编译器,我申明的变量和函数都存在的,在其余中央定义了,你只管释怀用。(定义指的是:给变量分配内存空间,给函数确定的函数体)extern "C" { // Demo here }:命令编译器,用C的形式编译。常见用法:C++须要用到C的库,都是二进制sdk,源码是不可能改的。如果用C++的形式编不过,就用上面形式应用 extern "C"{#include "xx.h" // C库的头文件}C语言中的各种属性关键字C语言中的变量能够有本人的属性在定义变量的时候能够加上属性关键字“属性”关键字指明变量的特有意义 语法:property type var_name;auto char i;register int j;static long k;extern double m;1、autoauto是C语言中局部变量的默认属性;auto表明将被润饰的变量存储于栈上;编译器默认所有的局部变量都是auto的; void f(){ int i; // 局部变量默认属性是auto auto int j; // 显示申明属性为auto}2、registerregister关键字指明将局部变量存储于寄存器中;register只是申请寄存器变量,但不肯定申请胜利;register变量的值必须是CPU寄存器能够承受的值;不能用&运算符获取register变量的地址; register int g_v; // errorint main(){ register char var; printf("%08x", &var); // error}问题1:为什么不能将全局变量存储为寄存器变量?答:因为寄存器的访问速度比内存的速度要快的多,所以在C语言中为了高效,把一些须要拜访高效的变量存储为寄存器变量。全局变量的生命期是程序生命期,如果存储为register变量,就会始终占用该寄存器,这是不容许的。因为CPU的寄存器是无限的,因而register只是一种申请,不肯定胜利。 问题2:为什么不能取寄存器的地址?答:因为取地址符&只能用来取内存的地址。 3、staticstatic关键字指明变量的“动态”属性 static润饰的局部变量存储在程序动态区static关键字同时具备“作用域限定符”的意义 static润饰的全局变量作用域只是申明的文件中static润饰的函数作用域只是申明的文件中int g_v; // 全局变量,程序的任意中央都能够拜访static int g_vs; // 动态全局变量,只能在以后文件中拜访(作用域只是以后文件)int main(){ int var; // 局部变量,在栈上调配空间 static int svar; // 动态局部变量,全局数据区调配空间}4、 externextern用于申明“内部”定义的变量和函数 ...

August 8, 2021 · 1 min · jiezi

关于c:C进阶4类型之间的转换

Summary0)工程中须要防止隐式转换,强转有时无奈防止,但肯定要时刻留神操作的数据的类型,对操作的数据的类型要非常清晰,对转换的后果也要非常清晰。 1)C语言中有强制类型转换和隐式类型转换 2)强制类型转换的语法为:(Type)var; 强制类型转换的后果为: 指标类型可能包容目标值:后果不变指标类型不能包容目标值:后果将产生截断(截断只会保留低地址的字节)(对于整型值,和第2节中总结的整型值溢出运算后果统一)浮点数到整型数的强转会间接舍去小数位不是所有的强转都能胜利,如果无奈胜利强转,编译器会报错3)隐式类型转换指的是:编译器被动进行的转换。规范C编译器的类型查看是比拟宽松的,因而隐式类型转换可能带来意外的谬误(如高类型向低类型转换会产生截断)。 4)隐式类型转换的产生点: 算术运算式中,低类型转换为高类型(char和short进行算术运算时,全都会先转换为int)赋值时,表达式的值转换为右边变量的类型函数调用时,实参转换为形参的类型函数返回值,return表达式转换为返回值类型 C语言中的强制类型转换强制类型转换 long l = 50;int i = (int)l; // 强制类型转换,long --> int隐式类型转换 short s = 800;int i = s; // 隐式类型转换,short --> int // no error, no warning1、强制类型转换强制类型转换的语法: (Type)var_name;(Type)value;强制类型转换的后果: 指标类型可能包容目标值:后果不变指标类型不能包容目标值:后果将产生截断(截断只会保留低地址的字节)留神:并不是所有的强制类型转换都能胜利,如果不能强制类型转换时,编译器会报错 short s = 256; // 256的16进制示意:0x 0 1 0 0char c = (char)s; // 产生截断,只保留低地址的字节, // 即c的值为:0x00, c = 0示例详解: struct TS{ int i; int j;};struct TS ts;int main(){ short s = 0x1122; char c = (char)s; // short类型到char类型的转换,有溢出,产生截断,只保留低地址的字节 int i = (int)s; // short类型到int类型的转换,不会溢出,无风险 int j = (int)3.1415; // 浮点数到整形的转换,间接舍去小数局部 unsigned int p = (unsigned int)&ts; long l = (long)ts; // error,从构造体类型到long类型无奈强转 ts = (struct TS)l; // error printf("s = 0x%x\n", s); // 0x1122 printf("c = 0x%x\n", c); // 0x22 printf("i = 0x%x\n", i); // 0x1122 printf("j = 0x%x\n", j); // 0x3 printf("p = 0x%x\n", p); // 0x804a01c printf("&ts = %x\n", &ts);// 0x804a01c return 0;}2、隐式类型转换隐式类型转换指的是:编译器被动进行的类型转换 ...

August 1, 2021 · 1 min · jiezi

关于c:C进阶3浮点数的秘密

Summary1)浮点数在内存中的存储形式: 类型符号位指数尾数float1位(第31位)8位(第23-30位)23位(第0-22位)double1位(第63位)11位(第52-62位)52位(第0-51位)2)float与double类型的数据在计算机外部的表示法是雷同的,然而因为所占存储空间大小的不同,其别离可能示意的数值范畴和精度不同。 3)浮点数的转换如:浮点数-8.25的二进制转换,float类型:1. 符号位:1(正数)2. 绝对值二进制:1000.01(整数局部的指数顺次为0,1,2...;正数局部的指数一次为-1,-2,-3...)3. 迷信计数法:1.00001 * 23,指数为34. 指数+偏移:3 + 127 = 130 <--> 1000 0010(float的指数占8位)5. 尾数:尾数局部为00001,占23位,有余的前面补0:00001 0000 0000 0000 0000 006. 最终:二进制为:1 1000 0010 00001000000000000000000(符号位 + 指数 + 尾数);用16进制示意为:0xc1040000 留神: 对于float类型,指数的偏移为+127;对于double类型,指数的偏移为+1023。尾数局部,有余的位数前面补0。通过如下指针形式,能够取得一段内存中的二进制位 unsigned int* p = (unsigned int*)&val;printf("%08x", *p); // %08x示意以16进制的模式来打印内存里的值4)float能示意的具体数字的个数与int雷同(都占用4个字节,一共32个bit位,所以最多就232种排列组合形式,即最多能示意232个数) 5)float的表示法是不准确的,所以能示意的范畴比int大。因为float的值不准确,所以对于一个float值,间接打印的数据可能有偏差;同时对于浮点数的运算,不能间接和0比拟。 6)因为float的内存表示法比int简单,所以float的运算速度比int慢。 浮点数的机密1、浮点数在内存中的存储形式类型符号位指数尾数float1位(第31位)8位(第23-30位)23位(第0-22位)double1位(第63位)11位(第52-62位)52位(第0-51位)float与double类型的数据在计算机外部的表示法是雷同的,然而因为所占存储空间大小的不同,其别离可能示意的数值范畴和精度不同。 2、浮点数的转换如:浮点数-8.25的二进制转换,float类型:1. 符号位:1(正数)2. 绝对值二进制:1000.01(整数局部的指数顺次为0,1,2...;正数局部的指数一次为-1,-2,-3...)3. 迷信计数法:1.00001 * 23,指数为34. 指数+偏移:3 + 127 = 130 <--> 1000 0010(float的指数占8位)5. 尾数:尾数局部为00001,占23位,有余的前面补0:00001 0000 0000 0000 0000 006. 最终:二进制为:1 1000 0010 00001000000000000000000(符号位 + 指数 + 尾数);用16进制示意为:0xc1040000 ...

August 1, 2021 · 1 min · jiezi

关于c:C进阶2有符号与无符号

Summary0)在进行数据运算、应用变量的时候,肯定要非常分明变量的具体类型! 1)对于整形数据,数据类型的最高位用于标识数据的符号:最高位为1示意正数,最高位为0示意整数。 2)类型溢出时的运算总结: 溢出的值为正的:理论值为:溢出后的值 - 该类型能示意的个数溢出的值为负的:理论值为:溢出后的值 + 该类型能示意的个数3)十进制正数和补码的互相转换:如何计算正数的补码:如十进制整数-7,8位 符号位:1绝对值:7 <--> 0000 0111取反:1111 1000加1:1111 1001(补码)如何从一个补码推算十进制正数:如已知(1111 1001)示意一个十进制正数上述规定反向计算 减1:1111 1000取反:0000 01111绝对值:计算得7符号位:-7非凡的,如1个字节的整数-128,它没有原码和反码,只有补码为1000 0000,即应用(-0)来示意(-128)。但依然能够应用上述规定进行推算二进制补码。 4)C语言中的变量默认为有符号的类型signed;unsigned关键字将变量申明为无符号类型,留神:只有整数类型才能够申明为unsigned 5)当无符号数和有符号数进行数学运算时,有符号数会被转换为有符号数,运算后果为无符号数。 有符号与无符号数分析1、负数和正数数据类型的最高位用于标识数据的符号 最高位为1,标识这个数为正数最高位为0,标识这个书为负数 int sign = 0;char i = -5;short j = 5;int k = -1;sign = (i & 0x80); // i为正数,最高位为1 ==> sign != 0sign = (j & 0x8000); // j为负数,最高位为0 ==> sign == 0sign = (k & 0x80000000);// k为正数,最高位为1 ==> sign != 02、原码和补码2.1 类型溢出的运算在计算机外部用原码示意无符号数 无符号数默认为负数无符号数没有符号位对于固定长度的无符号数 MAX_VALUE + 1 --> MIN_VALUEMIN_VALUE - 1 --> MAX_VALUE类型溢出时的运算总结: ...

August 1, 2021 · 2 min · jiezi

关于c:C进阶1基本数据类型

Summary1)数据类型的实质是固定内存大小的别名;变量的实质是具体的一段内存的别名。 2)变量隶属于某一种数据类型,变量所在的内存大小取决于其所属的数据类型。 1、什么是数据类型数据类型能够了解为固定内存大小的别名数据类型是创立变量的模子 2、变量的实质变量是一段理论间断存储空间的别名程序中通过变量来申请并命名存储空间通过变量的名字能够应用存储空间 本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。

August 1, 2021 · 1 min · jiezi

关于c:C入门12多文件程序设计

Summary1)头文件(.h)是源文件(.c)的 接口定义文件。接口指的是可被其余文件拜访的函数或者数据。如果a.c中想应用b.c中定义的函数或变量,就能够通过b.h来应用,b.h中则声明了b.c中的函数和变量。 2)include中(<>)和("")的区别:#include <header.h>,指的是在编译系统文件中查找头文件#include "header.h",指的是先查找以后工程文件夹,再查找编译系统文件夹 3)头文件的一些规定: 头文件中只做函数申明和变量申明(不做具体定义)头文件中能够定义数据类型(typedef,struct,union,enum)一个头文件能够对应于多个源文件(少数状况一对一)不要应用#include蕴含源文件4)再论全局变量: 一般全局变量 源文件中定义的全局变量能够在其余任意源文件中进行应用,如果其余源文件中定义了同名的全局元素,则会产生反复定义谬误。(因为即便没有通过extern申明,不可间接可见,但实质各自的作用域依然是全局的)可应用extern关键字在头文件中申明,并在其余文件中应用(如果不申明,则在其余源文件中不可见)留神,应用extern申明时的类型和变量名都必须统一,且不能够给初始值(否则就会报反复定义谬误)。动态全局变量 static润饰的全局变量只能在以后源文件中应用无奈通过extern关键字申明,也就没有方法在其余文件中应用5)函数申明语句默认自带extern关键字润饰;类型定义必须放在头文件里,仅仅是类型申明无奈创立变量。 6)static函数和static全局变量的行为是统一的。static关键字润饰函数使得函数具备文件作用域;动态函数无奈在其余文件中被调用;函数设计时就须要思考是否在其余文件中应用。 多文件程序设计问题:理论工程开发中,所有的代码都是写在同一个文件中吗?答:很显然不是。理论的工程开发中,通常是不同工程师或不同的团队负责不同的模块,而后各自的模块组合起来实现整个工程。 多文件编译链接: 问题:不同文件之间如何互相拜访各自的程序元素?多文件之间的互相拜访: 每个文件能够定义性能接口(可被其余文件拜访的函数或数据) 源文件:代码实现文件,后缀为.c头文件:源文件的接口定义文件,后缀为.h当须要应用其余文件提供的性能时,蕴含对应的头文件 语法: #include <header.h>,指的是在编译系统文件中查找头文件#include "header.h",指的是先查找以后工程文件夹,再查找编译系统文件夹再论全局变量: 一般全局变量 源文件中定义的全局变量能够在其余任意源文件中进行应用可应用extern关键字在头文件中申明,并在其余文件中应用(如果不申明,则在其余源文件中不可见)动态全局变量 static润饰的全局变量只能在以后源文件中应用无奈通过extern关键字申明,也就没有方法在其余文件中应用本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。如有错漏之处,恳请斧正。

July 31, 2021 · 1 min · jiezi

关于c:喝饮料问题饮料价格1元瓶两瓶饮料空瓶可以重新兑换一瓶饮料问20元可以喝多少瓶饮料

办法1: int main()//办法一{ int money = 0;//手上的钱,即用钱买回来的汽水数量 int total = 0;//买的+兑的 int empty = 0;//手里的空瓶 scanf_s("%d", &money); int sum = totalsum(money); printf("sum=%d\n", sum); total = money;//用钱买回来的汽水数量 empty = money;//喝完用钱买的汽水后手里的空瓶数量 while (empty>=2)//只有空瓶不低于两瓶就能够换汽水 { total += empty / 2; empty = empty / 2 + empty % 2;//空瓶的数量为换回来的空瓶+手里余下的空瓶 } printf("total=%d\n", total);}办法2: 办法2,因为两个空瓶换一瓶饮料,一瓶饮料1块钱,相当于1个空瓶等于0.5元,20块钱能够换40个空瓶,相当于喝了40瓶汽水,但因为最初手里留了一个空瓶,所以只能换来39个空瓶,即喝了39瓶汽水int totalsum(int money){ int sum = 0; if (money== 0)//手里没钱就不能喝饮料 { return 0; } else { sum = money * 2 - 1; return sum; } //}

July 29, 2021 · 1 min · jiezi

关于c:c指针面试题相关问题1

1. 只有在sizeof(arr),&arr这两种状况下,arr示意的整个数组,而sizeof()计算传入元素类型的大小2. strlen函数的形参为一个常量指针,即const char*str,这示意传入strlen中的应该是某个地址,且strlen在计算长度时3. char*p=“abcdef”,实际上p寄存的是a的地址,即字符串首元素的地址 注:p+0x1是构造体指针+1,跳过一个构造体,所以地址+20,又因为是16进制,所以,地址为0x00100014** (unsigned long)p+0x1,首先把指针p强转为无符号整型,所以P+0x1就相当于是一个无符号整数+1 (unsigned int*)p+0x1先把p强转成无符号整型指针,所以p+0x1相当于跳过一个整型8.注:零碎采纳小端存储模式,&a+1跳过了整个数组,如图,指针ptr指向数组开端前面的空间,再再将其强转成int的指阵类型,所以ptr[-1]等价于*(ptr-1)即向前跳了一个整型,再解援用(int)a+1将数组首元素地址转化为一个整型,假如数组首元素的地址为0x00 00 00 05,强转为整型后+1相当于5+1=6,再把6强转成地址,即0x00 00 00 06即向前跳过一个字节,再解援用*ptr为00 00 00 02,计算机打印时按高地址到低地址打印,所以%x为0x 2 00 00 00 9.注:逗号表达式,在(x,y)示意的最初一个y 10.注:p的类型为int(*)[4] a的类型为int(*)[5],&p[4][2] 等价于*(*(p+4)+2),即为第19个存储空间,同理*(*(a+4)+2)为第23个存储空间,而地址-地址=元素个数,所以,&p[4][2]-&a[4][2]=-4,当-4以地址的模式打印的时候,间接打印其补码11111111111111111111111111111100转化为16进制为FFFFFFFC11.正文:后果为at,char**pa二级指针,寄存的是一级指针的地址 12. 注:char***cp是一个三级指针,寄存的是数组cp的首元素的地址,cp是一个寄存指针地址的数组,每个数组元素寄存的是指针数组c的每个元素的地址,而数组c又别离寄存了四个字符串的首元素的地址,并将其作为元素存储。存储示意图如上图所示,1.**++cpp,此时指针cpp会指向cp数组的第二个元素,即c+2,再通过两次解援用找到了字符串“POINT”的首地址,以%s的模式打印,便打印整个“POINT”字符串2.*--*++cpp,首先指针cpp自增1,持续向数组cp的下一个元素挪动,即指向了c+1元素,解援用,即找到了c+1寄存的地址,即找到了数组c的第二个指针元素。接着--操作符,使得找到了c数组的第一个指针元素,再解援用,失去了"ENTER"的首元素地址,再+3即第四个字符E打印字符串,所以后果为“ER”。3.*cpp[-2]+3等价于**(cpp-2)+3,此时cpp自身并不挪动,但cpp-2找到了cp数组的c+3元素,解援用,失去c+3指向的元素的地址,即数组c的最初一个元素的地址,再次解援用,失去了c数组最初一个指针寄存的字符串元素的首地址,最初+3,即从S开始打印字符串,后果为“ST”4.cpp[-1][-1]等价于*(*(cpp-1)-1),cpp-1找到了C+2,解援用找到了对应的c数组第三个元素的地址,-1,找向c数组上一个元素,再解援用,失去了“NEW”的首元素地址,+1,向后找到了“E”,从此处开始打印字符串,所以后果为“EW”

July 29, 2021 · 1 min · jiezi

关于c:杨氏矩阵查找问题

杨氏矩阵阐明:即一个n*n的矩阵,从左往右顺次递增,从上往下顺次递增例如: 就是一个简略的杨氏矩阵当初要查找一个数字是否在该矩阵外部,并且工夫复杂度要小于O(N),即不能程序查找首先察看杨氏矩阵的法则,右上角的元素是本列最小,本行最大。因而只有和右上角的元素进行比拟,若被查找元素小于右上角元素,则阐明该元素看到不在右上角元素对应的列,则矩阵能够向左缩减列,若被查找元素大于右上角元素,则阐明该元素不在右上角元素的同一行,则矩阵能够向下缩减一行,依照这个思路迭代的去比拟被查找元素与右上角元素的关系,若在某一次二者相等,则在杨氏矩阵中存在该元素,若找到左下角的元素依然没有找到该元素,则阐明该元素不存在于杨氏矩阵中。同样的思路可用于左下角的元素代码实现:int Find_Num(int arr[3][3], int k, int* row, int* col)//传入一个3*3的矩阵,并传入被查找元素k以及行数的指针,列数的指针{ int x = 0; int y = *col - 1; while (x <= *row - 1 && y >= 0)//循环终止条件,找到了左下角元素 { if (arr[x][y] > k) { y--;//若右上角元素大于被查找元素,则向左列查找 } if (arr[x][y] < k) { x++;//若右上角元素小于被查找元素,则向下行查找 } else { *row = x;//若二者相等,则将该元素的坐标地位返回 *col = y; return 1; } } return 0;}注:传入指针的目标是不便扭转内部实参的值,使得内部实参能够获取被查找元素的坐标值

July 27, 2021 · 1 min · jiezi

关于c:实现将一个数组中的所有奇数放到数组前部分偶数放到数组后部分

如题:利用数组的左右指针,从两端往两头遍历数组,若左指针遇到偶数,右指针遇到奇数则奇偶替换,晓得左右指针不能挪动地位 #include<stdio.h>#include<string.h>void swap(int* left, int* right)//办法一{ while (left < right) { if ((*left) % 2 == 1) { left++; } if ((*right) % 2 == 0) { right--; } else { int temp = 0; temp = *left; *left = *right; *right = temp; } }}void swap1(int* left, int* right)//办法二{ while (left < right) { while ((*left) % 2 == 1) { left++; } while ((*right) % 2 == 0) { right--; } while (left < right) { int temp = 0; temp = *left; *left = *right; *right = temp; } }}void print(int* arr,int len){ int i = 0; for ( i = 0; i < len; i++) { printf("%3d", arr[i]); }}int main(){ int arr[] = { 1,6,5,2,7,8,10,4,9 }; int len = sizeof(arr) / sizeof(arr[0]); //swap(arr, arr + len - 1); swap1(arr, arr + len - 1); print(arr, len);}正文:当数组全为奇数或偶数时,应加上约束条件left<right避免指针越界拜访数组元素

July 27, 2021 · 1 min · jiezi

关于c:C入门11自定义数据类型

Summary1)typedef并不是创立了一个新类型,而是给一个已有类型创立了一个新的名字 2)typedef次要用来简化类型名和对立定义变量的形式(重命名函数和数组) 3)struct用于自定义新类型,可能将不同数据类型的变量组成一个汇合。struct创立新类型是一条非法的语句,因而要以分号;完结,外面的每个定义变量的语句,也都是以分号;完结。 4)stuct构造体类型能够先申明再定义(申明只是通知编译器构造体类型的新名字是什么,所以不定义也能够)。如果只是前置类型申明,然而还没有定义,这时候只能定义指针(因为在定义变量的时候须要分配内存,只有类型无奈确定变量大小;但能够定义指针,因为指针固定大小4字节或8字节)。 5)struct构造体类型能够省略类型名,匿名构造体。两个匿名构造体的类型即便看起来一样,但依然是不同的两个构造体类型。 6)struct构造体变量的实质是变量的汇合,外面的成员占用独立的内存。 7)union联合体也是C语言中用于自定义类型的一个关键字,用法和struct很像。union与struct的不同之处在于: 不论union XX{}; 中有几个变量,所有的成员都共享一段内存,所有成员的起始地址都是雷同的union XX{}; 占用的内存大小取决于成员的最大类型union XX{}; 类型的变量只能以第一个成员类型的有效值进行初始化当取不同联合体里不同变量的值时,要以相应的类型去解读这一段内存8)小端系统:低地址内存存储低位数据;大端系统:低地址处存储高位数据;union联合体能够用于判断零碎的大小端: int isLittleEndian(){ union { int i; char c; } test = {0}; test.i = 1; // 如果是小端系统,低地址就存低位数据,1就会存在低地址处 return (test.c == 1); // c只占一个字节,从低地址处取一个字节,应该失去1}9)enum是C语言中的自定义类型关键字,可能定义整形常量的汇合类型。特点:第一个枚举常量的默认值为0;能够对任意枚举常量指定值(只能指定为整形);后续常量的值默认在前一个值的根底上+1;enum类型的变量的实质依然是一个整形变量。所以enum类型变量的大小为4。C语言中能够用任意一个整形值去初始化枚举变量(在C++中就必须用枚举值进行初始化,强类型) 10)古代程序设计中,内存应用的最小单元是字节;有一些特定场合,能够将比特位作为最小单元应用内存:位域可能指定成员变量占用内存的比特位宽度 11)深刻位域:位域的实质依然是一个构造体类型 位域成员必须是整型,默认状况下成员顺次排列 struct Bits1{ int a : 16; short b : 8; char c : 8; // float f : 16; // error, bit-field ‘f’ has invalid type'位域的成员必须是整型!' float f; // 如果不应用 : 指定位域宽度,那么f就是一个非法的构造体成员};sizeof(Bits1) = 8; // a:2, b:1, c:1, f:4位域成员占用的位数不能超过类型宽度 ...

July 26, 2021 · 3 min · jiezi

关于c:C入门10指针

Summary1)指针的实质是变量,非凡之处在于指针存储的值是内存地址(内存中每个存储单元的编号:计算机中的最小存储单元是1byte ,即每个字节的编号都是一个内存地址) 2)程序中的所有元素都存在于内存中,因而能够通过内存地址拜访程序元素 3)内存地址的实质是一个无符号整数(4字节或8字节);4字节和8字节别离对应于32位和64位,所以咱们常说的32位和64位零碎指的就是可拜访的最大内存地址是多少。因为64位零碎能反对更大的内存寻址,所以64位零碎会比32位零碎能同时运行的程序要多。 4)只有通过内存地址+长度能力确定一个变量中保留的值。内存地址只是一个起始值,只有依据类型信息,晓得占多少内存、解读形式,才能够精确的读出变量里保留的值。 5)函数参数是指针时(传址调用),能够实现在函数外部批改函数内部变量的值;应用指针作为参数,能够作为一个传出参数,使得函数能返回多个值。 6)对于数组int a[5],a和&a的关系: a能够看做一个常量指针,示意数组首元素的地址,值是0xA,类型是int*,即内存长度为4&a是数组的地址,值是0xA,类型是int(*)[5],即内存长度为20。指向数组的指针:int(*pArr)[5] = &a;7)指针与数组的等价用法: int a = {1, 2, 3, 4, 5};int* p = a;a[i] <--> *(a + i) <--> *(p + i) <--> p[i]8)C语言中,字符串常量的类型是char*(c++里则是const char*);int v = *p++;等价于int v = *p; p++;因为(*)的优先级和(++)雷同 9)函数的实质是一段内存中的代码,占用一段间断的内存。函数名就是函数的入口地址(函数体代码的起始地址)。通过函数名调用函数,实质为指定具体地址的跳转执行。因而能够定义指针,保留函数的入口地址:type (*pFunc)(param) = funcName; 10)问:既然通过函数名能够间接调用函数,那么为什么还须要函数指针呢?答:能够定义函数指针参数。应用雷同的代码,实现不同的性能。主调函数只晓得函数原型是这样,但不晓得具体会调用哪个函数。函数指针是回调函数的实现机制:函数作为参数应用时,就形成了callback; 11)堆空间的实质是程序备用的“内存仓库”,以字节为单位预留的可用内存 12)在C语言中,void*指针能够和其余类型的指针type*进行互相赋值,因为C语言对指针的查看并不严格。然而在应用时肯定要留神操作的数据类型,void*仅仅是一个初始地址,不蕴含长度信息。 13)malloc用于向堆空间中以字节为单位申请内存;free用于偿还堆空间的内存,malloc而不free,内存泄露;屡次free,解体。 14)指针是一个变量,那么天然也就会有指向指针的指针,即多级指针。之前变量能够应用指针进行传址调用,那么也能够通过多级指针实现对指针的传址调用,在函数外部扭转内部的指针的值。 15)二维数组的实质是一维数组,所以二维数组名a示意的是数组的首元素,即a[0]示意的是一个一维数组;a的类型是type (*)[size2] int arr[2] = {0};int* pa = arr;int (*pArr)[2] = &arr;int bArr[2][2] = {0};int (*pb)[2] = bArr;typedef int (oneDimension)[2];oneDimension (*pbArr)[2] = &bArr;16)禁止返回局部变量的地址,因为局部变量在函数调用完结后,生命期就完结了。此时返回的是个野指针。 ...

July 25, 2021 · 2 min · jiezi

关于c:C入门9宏定义

Summary1)C语言中,实参和函数的形参之间仅仅是值传递,因而在函数外部无奈扭转实参的值。 2)函数是一种代码复用的伎俩 3)宏是C语言中代码复用的补充形式,是对函数的一种补充;函数和宏的关系,相似于生存中演员和替身的关系,只是用于某些非凡场景。在大部分状况依然优先思考函数。 4)宏没有函数调用的开销,函数调用须要在栈上保护流动记录;宏只是简略的文本替换,因而代码块会收缩,函数只会应用同一段函数体;宏替换不会对类型进行查看,当宏和运算符联合在一起时,会呈现意想不到的谬误,函数会进行类型查看,也不会呈现二义性; 5)编译器组成简介: 预处理模块:解决所有#结尾的语句(复制粘贴替换 --> 两头文件.i)编译模块:将C程序翻译成二进制程序(两头文件.i --> 汇编文件.s --> 二进制文件.o)链接模块:将二进制程序组合成可执行程序(.o --> .out)宏定义问题:以下代码输入什么?为什么? void swap(int a, int b){ int t = a; a = b; b = t;}int main(){ int x = 1; int y = 2; swap(x, y); x = ? y = ?}以上代码输入为x = 1, y = 2;尽管咱们进行了替换,然而x和y的值依然没变,起因在于:实参和形参之间仅仅是值传递,所有函数中无奈间接扭转实参的值。 再次了解函数:函数是一种代码复用的伎俩 把实现某个性能的代码片进行封装(当做一个整体)给这个代码片段一个适合的名字(通过名字来应用代码)定义参数(定义代码片段须要解决的问题)C语言中的的宏,是对函数“缺点”的补充 宏是C语言中代码复用的补充形式宏定义的语法:#define MACRO(param) code_segment宏应用语法:MACRO(num); #define ADD(a, b) a + bint z = ADD(1, 2);宏与函数的不同: 宏不是函数,没有函数调用的过程(不须要在栈上保护函数调用的记录);函数调用会先传递参数值,而后跳转执行函数体,而后返回应用宏只是单纯“代码复制粘贴”,而后替换参数同一个函数,无论调用多少次,都只有一份函数体代码;同一个宏,每次都会“复制粘贴”雷同代码编译器组成简介: 预处理模块:解决所有#结尾的语句(复制粘贴替换 --> 两头文件.i)编译模块:将C程序翻译成二进制程序(两头文件.i --> 汇编文件.s --> 二进制文件.o)链接模块:将二进制程序组合成可执行程序(.o --> .out)再论宏常量:#define NAME value ...

July 25, 2021 · 1 min · jiezi

关于c:C入门8函数

Summary1)C语言中的函数次要有2种类型: Function:数据处理(数据 --> 数据),通过某种规定由输出的数据失去输入数据Procedure:过程定义(数据 --> 性能),(依据数据)执行一系列动作,进而实现某种性能2)应用程序必须依附操作系统能力运行,应用程序承受操作系统的治理。当操作系统运行程序时,首先调用的就是main()函数。)(main()函数个别是第一个被调用的函数,也有其余办法能够让其余函数先于main执行) 3)应用程序执行的过程:(后续补上时序图) 用户双击.exe,或者在命令行中间接运行.exeOS把应用程序加载到内存中:Load()OS找到应用程序的main()函数执行main()函数敞开应用程序时,main()函数执行完结,返回一个值给操作系统(在windows中,能够在命令行,应用echo %errorlevel%命令查看刚刚命令执行的exe的返回值)4) 函数的调用过程: 暂停主调函数跳转到被调函数,执行被调“函数体”被调函数返回,复原执行主调函数5)工具包就是一个函数集,蕴含了一系列定义好的函数。#inlude语句用于申明,要应用工具包中的函数。 6)void是C语言中的一个根本类型,但void不是根底数据类型,不能够用来定义具体变量。 7)C语言中如果想定义一个无参函数,必须应用void申明!如果函数参数列表什么都不写,示意承受任意多的参数。即:void f(void) --> 不承受参数;void f() --> 几个参数都能够; 8)对于void f()无返回值函数,能够间接应用 return; 语句使函数间接返回 9)形参:函数定义时的参数列表;实参:函数调用时指定的具体值;实参用来初始化形参(将实参的值赋值给形参),所以形参相当于一个函数外部的变量(函数参数的实质是变量) 10)C语言中,当数组作为参数时,数组参数会进化为指针,数组大小无奈传递;此时批改数组形参,会同时扭转数组实参的值。 11)排序的要害操作:比拟和替换 12)全局变量不同同名,会产生命名抵触,报重定义的谬误;就近准则,存在多个同名变量时,优先应用最近定义的变量。 13)变量的作用域指的是变量定义后的可拜访范畴;在重叠作用域中,优先应用最近定义的变量。 14)代码块指的是从 { 开始到 } 完结的一段代码;局部变量的作用域从定义开始,到以后代码块完结; 15)对于全局变量,存在两个作用域全局作用域和文件作用域。全局作用域指的是在程序的各个角落都能够拜访到;文件作用域则只能在以后代码文件中拜访并应用; 16)计算机中,物理内存被分为不同区域,不同区域有不同的用处。全局数据区用于寄存全局变量和动态变量;栈区用于寄存局部变量和函数参数;堆空间用于动态创建变量; 17)生命期:变量从创立到销毁的工夫(非法可用的工夫)。全局数据区的变量:生命期从程序开始到程序完结;栈区的变量:生命期从进入作用域开始,到作用域完结; 18)作用域和生命期没有实质分割:作用域是语法层面上对变量是否可拜访的规定(空间上);生命期是二进制层面上变量存在于内存中的工夫(工夫上) 19)static润饰的变量位于全局数据区;static润饰的全局变量只有文件作用域;static局部变量只会初始化一次,作用域依然是所在代码块。 20)变量的生命期由存储地位决定。如果变量未初始化,存储地位决定了变量的初始值是多少:全局数据区的变量如果未初始,值为0(.bss段);栈区的变量如果未初始化,值为随机值;寄存器里的值未初始化为随机值。 -staticauto(默认)register局部变量全局数据区栈空间寄存器(可能)全局变量全局数据区----------------------------------21)C语言中,如果函数不写返回值类型,则默认返回类型是int;对于一个有返回值的函数,如果不写返回值,会返回一个随机值。 22)当参数只是一个字符串,且没提供长度的时候。能够应用while循环来遍历字符串,判断根据是字符串的最初一个字符是0元素'\0'。 23)在写递归函数的时候,首先要先把函数的递归模型画进去,而后去编写递归函数。编写时候要留神:n-1和边界。在了解递归函数的时候,不要想着去执行递归的细节,而是从递归模型来了解。递归模型的个别表示法: 1、函数的概念函数是具备特定性能的程序组件(能够看做黑盒:不须要晓得外面怎么实现的,只晓得怎么用)函数有明确的应用形式(固定输出对应固定输入)函数在程序中能够重复使用(程序中的工具)1.1 函数的类型C语言中的函数次要有2种类型: Function:数据处理(数据 --> 数据),通过某种规定由输出的数据失去输入数据Procedure:过程定义(数据 --> 性能),(依据数据)执行一系列动作,进而实现某种性能1.2 函数的组成部分函数名:函数的惟一标识函数参数:数据输出(数据 --> 数据, 数据 --> 动作)函数返回类型: 数据输入(数据 --> 数据)无返回值(数据 --> 动作)形如: 返回类型 函数名(参数1,参数2){ 程序语句1; ... 程序语句1;}2、函数的调用通过函数名调用曾经定义好的函数函数调用时须要顺次指定函数参数的具体值函数调用的后果(返回值)可保留在同类型的变量中 int r1 = func_name(5);int r2 = func_name(10);2.1 深刻了解main()个别状况下,C语言程序都是从main()开始执行的,那么main()是什么呢? ...

July 24, 2021 · 1 min · jiezi

关于c:调用一个函数之后发生了什么

假如: AMD64 LinuxC/C++首先,咱们不须要讲太多的概念。只须要回顾几个根本的寄存器: %rsp:保留栈顶指针%rbp:保留栈底指针%rbp~%rsp 这一段向下舒展的区域,就是栈帧。%rip:保留下条指令的地址%rdi:保留函数的第一个参数%rsi:保留函数的第二个参数%rax:保留返回值而后,间接看代码吧! 样例程序假如有程序如下: int sum(int x, int y){ return a + b;}int main(int argc, char const *argv[]){ int a = 1, b = 2; int c = sum(a, b); return 0;}应用 gcc -g prog.c -o prog 进行编译。其汇编代码如下: int sum(int x, int y){ 1125: 55 push %rbp 1126: 48 89 e5 mov %rsp,%rbp 1129: 89 7d fc mov %edi,-0x4(%rbp) 112c: 89 75 f8 mov %esi,-0x8(%rbp) return a + b; 112f: 8b 55 fc mov -0x4(%rbp),%edx 1132: 8b 45 f8 mov -0x8(%rbp),%eax 1135: 01 d0 add %edx,%eax} 1137: 5d pop %rbp 1138: c3 retq0000000000001139 <main>:int main(int argc, char const *argv[]){ 1139: 55 push %rbp 113a: 48 89 e5 mov %rsp,%rbp 113d: 48 83 ec 20 sub $0x20,%rsp 1141: 89 7d ec mov %edi,-0x14(%rbp) 1144: 48 89 75 e0 mov %rsi,-0x20(%rbp) int a = 1; 1148: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%rbp) int b = 2; 114f: c7 45 f8 02 00 00 00 movl $0x2,-0x8(%rbp) int c = sum(a, b); 1156: 8b 55 f8 mov -0x8(%rbp),%edx 1159: 8b 45 fc mov -0x4(%rbp),%eax 115c: 89 d6 mov %edx,%esi 115e: 89 c7 mov %eax,%edi 1160: e8 c0 ff ff ff callq 1125 <sum> 1165: 89 45 f4 mov %eax,-0xc(%rbp) return 0; 1168: b8 00 00 00 00 mov $0x0,%eax}执行流程咱们间接从 main 读起。请务必认真关注调用栈的变动。 ...

July 22, 2021 · 4 min · jiezi

关于c:C入门C语言中的数组

Summary1)数组是雷同数据类型变量的有序汇合 2)如果未给定数组元素大小,编译器会依据数组元素的个数主动推断数组的大小;数组中前面未被初始化的元素会被主动初始化为0。 3)数组的实质是一段间断的内存,用于存储数组元素;数组的大小能够通过sizeof获取,单位:字节;sizeof(arrName)。数组的元素个数:sizeof(arrName) / sizeof(arrName[0]) 4)数组的类型由元素类型和元素个数决定,示意为type [N] 5)多维数组的实质依然是一维数组,数组里的每一个元素是数组 6)字符串的实质是一个字符数组 7)C语言中对字符串或者字符数组的操作,肯定要关注0元素('\0'结束符);如果应用字符数组来模仿字符串变量,肯定记得在最初一个元素后加上结束符'\0' 8)字符串字面量的长度strLen:结束符'\0'之前的所有字符;字符串字面量占用的内存大小strSize:包含'\0'的所有字符。即strSize = strLen + 1; 9)char strcpy(char dst, const char *src);留神strcpy的第二个参数必须是一个字符串 1、一维数组1.1 数组的概念数组是雷同数据类型变量的有序汇合。 数组作为整体须要一个非法的命名(数组名)数组中的变量没有独立命名,只有在数组中的编号数组中的变量数量是固定不变的(数组大小固定)1.2 数组的定义语法:type Name[size]; int arr[10]; // 定义名为arr的数组 // 数组中一共有10个元素(变量) // 每个元素的类型为int1.3 数组的拜访通过数组名[下标]的形式拜访数组数组是一个有序的汇合,每个元素都有固定的下标数组元素下标固定从0开始(能够应用变量作为下标)int arr1[3];arr1[-1] = 0; // error,数组下标从0开始arr1[0] = 1; // okarr1[3] = 4; // error,数组中一共有3个元素,最初一个元素下标为2,下标3越界了 留神: 只有整型数能力作为下标来拜访数组元素下标越界是十分重大的谬误下标越界造成的严重后果可能不会立即体现进去1.4 数组的初始化语法:type Name[N] = {v0, v1, ..., vN-1};意义:将数组中的元素别离初始化为v0, v1, ... int main(){ int arr[5] = {2, 4, 6, 8, 10}; // 定义数组arr并初始化 int i = 0; for(i=0; i<5; i++) { printf("arr[%d] = %d\n", i, arr[i]); } return 0;}数组初始化技巧: ...

July 17, 2021 · 4 min · jiezi

关于c:CC如何只保留4位有效数字

文章原文:https://tlanyan.pp.ua/c-cpp-keep-significant-figures/问题敌人要用数值算例验证舍入误差对算法稳定性的影响,实际中遇到的第一个问题是:C/C++如何只保留4位有效数字(significant figures)? 如果接触过Java/JS等语言,保留小数点后几位是非常容易的,调用setScale、toFixed等函数就能够,还能指定舍入模式。然而对于C/C++,如何保留指定有效位数呢? C/C++保留指定有效数字在C/C++语言中,精度的概念只在输入(流)中呈现,因而实现的一个思路是:依照指定精度输入字符串,再转换成数字。 C语言指定有效数字位数先看C语言的解决方案。 C语言的printf系列函数能够指定输入格局,打印成字符串后可用atof转换成数字。于是指定有效数字的C语言实现版本如下: include <stdio.h>include <stdlib.h>double convert1(int precision, double val) { char buffer\[128\];sprintf(buffer, "%.*g", precision, val);return atof(buffer);} 应用代码验证: int main(int argc, char* argv[]) { double f1 = 1235.46698;printf("origin: %.12f, converted: %.12f\\n", f1, convert1(4, f1));double f2 = 1.23546698;printf("origin: %.12f, converted: %.12f\\n", f2, convert1(4, f2));double f3 = 0.00123546698;printf("origin: %.12f, converted: %.12f\\n", f3, convert1(4, f3));double f4 = 0.0000123546698;printf("origin: %.12f, converted: %.12f\\n", f4, convert1(4, f4));return 0;} 输入后果如下: origin: 1235.466980000000, converted: 1235.000000000000origin: 1.235466980000, converted: 1.235000000000origin: 0.001235466980, converted: 0.001235000000origin: 0.000012354670, converted: 0.000012350000 ...

July 15, 2021 · 1 min · jiezi

关于c:memmove-和-memcpy的区别

memcpy和memmove()都是C语言中的库函数,在头文件string.h中,作用是拷贝肯定长度的内存的内容,原型别离如下:void memcpy(void dst, const void *src, size_t count); void memmove(void dst, const void *src, size_t count); 他们的作用是一样的,惟一的区别是,当内存产生部分重叠的时候,memmove保障拷贝的后果是正确的,memcpy不保障拷贝的后果的正确。 第一种状况下,拷贝重叠的区域不会呈现问题,内容均能够正确的被拷贝。第二种状况下,问题呈现在左边的两个字节,这两个字节的原来的内容首先就被笼罩了,而且没有保留。所以接下来拷贝的时候,拷贝的是曾经被笼罩的内容,显然这是有问题的。实际上,memcpy只是memmove的一个子集。 二者的c语言实现很简略,有趣味的敌人能够去看看。在理论状况下,这两个函数都是用汇编实现的。 memmove在copy两个有重叠区域的内存时能够保障copy的正确,而memcopy就不行了,但memcopy比memmove的速度要快一些,如:char s[] = "1234567890";char* p1 = s;char* p2 = s+2;memcpy(p2, p1, 5)与memmove(p2, p1, 5)的后果就可能是不同的,memmove()能够将p1的头5个字符"12345"正确拷贝至p2,而memcpy()的后果就不肯定正确了 变态的命名 咱们在写程序时,个别考究见到变量的命名,就能让他人根本晓得该变量的含意。memcpy内存拷贝,没有问题;memmove,内存挪动?错,如果这样了解的话,那么这篇文章你就必须要好好看看了,memmove还是内存拷贝。那么既然memcpy和memmove二者都是内存拷贝,那二者到底有什么区别呢? 先说memcpy 你有没有好好的加入过一场C++口试。让你写出memcpy的实现,这是如许常见的口试题啊。当初,拿起你的演算纸和笔;是的,是笔和纸,不是让你在你的IDE上写。写不进去?看上面吧: void *memcpy(void *dest, const void *src, size_t count){assert(dest != NULL || src != NULL);char *tmp = (char *)dest;char *p = (char *)src;while (count--){*tmp++ = *p++;}return dest;}memcpy的实现很简略,个别在口试时,呈现写源码的题目,无非就是须要留神以下几点: 1.确定函数原型;2.判断参数合法性;3.逻辑实现(思考各种状况,统称逻辑实现);4.错误处理。 当然了,我的这个没有错误处理,也不须要错误处理。下面,我写出了memcpy的实现源码,实现原理如下图所示: 这样上来,下面的代码会运行的很好,如果呈现上面的状况呢? i、n、k的内存和J、e、l的内存地址重合了,当初再应用下面的代码进行copy时,会呈现什么问题呢?你有没有想过这个问题。如果没有,那就当初想想,不急着浏览上面的内容。 ...

July 13, 2021 · 1 min · jiezi

关于c:那些年写过的bug0

在此记录一下本人写过的那些bug owo。 1)指向数组的指针在malloc前遗记初始化(赋值为NULL) 2) some_struct * ptr = A;array[1] = *A;而后就忘了开释ptr... 3) char * buffer = NULL;size_t sz = 0;while(getline(&buffer, &sz, file) > 0){ ...}而后遗记开释buffer... 4)假如line是一个字符串(比方"apple")。 line[2] = '\0';而后 strchr(line, 'e');就返回NULL了。因为当初的line是"ap"; 5) int* array = malloc(5 * sizeof(*array));for (int i = 0; i < 5; i++){ free(array + i);}反复开释内存了。

July 12, 2021 · 1 min · jiezi

关于c:C入门6C语言中的常量

Summary1)常量是绝对于变量的一个概念;变量的值随时能够扭转,常量的值是不能够扭转的。 2)C语言中的常量类型包含: 字面量:间接示意值含意的符号,如:5, 'a', "Delphi"宏常量:通过#define定义,间接示意值的符号,如:#define FIVE 5枚举常量:通过enum定义,间接示意值的符号,如:First -> 13)C语言中定义常量的形式: 通过#define定义宏常量通过enum定义枚举常量4)C语言中定义常量的语法: 宏常量:#define NAME Value 示例1: #define MyString"Bryson"示例2:#define MyId 1枚举常量:#define NAME Value 示例: enum { ThirdValue = 333, FourthValue = 444, }5)常量的类型: 字面量有默认类型,如“1”的默认类型为int,"Hello world"的类型为const char*#define定义的宏常量能够是任意类型enum定义的枚举常量只能整形6)C语言中的只读变量 C语言中提供了const关键字,用于润饰一个变量被const润饰的变量只能作为右值应用 无奈间接通过赋值操作符扭转const变量的值const润饰的变量并不是真正意义上的常量C语言中,const润饰变量,看起来像常量,用起来像常量,但实质是只读变量。无奈间接扭转(通过"="进行赋值扭转),但能够间接扭转(用指针取到地址进行扭转)。再次留神,实质是只读变量,占用内存的。如下: const int a = 1;a = 2; // error, assignment of read-only variable 'a'int* p = (int*)&a; // 取地址*p = 2; // 间接批改printf("%d\n", a); // 2另: 在C语言中,gcc编译器下,如上代码中a的值被批改为2。阐明const定义了个只读变量。在C++语言中,g++编译器下,如上代码a的值依然为1。因为C++中const定义的是一个常量!a被放到了符号表中。指针p批改的,实际上是内存中的一段空间,把那段空间的值批改为了2,因而打印a的值为1,*p的值为2。(详见后续C++)本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。如有错漏之处,恳请斧正。

July 10, 2021 · 1 min · jiezi

关于c:C入门5程序中的辅助语句

Summary1)C语言中分为单行正文和多行正文:单行正文 以 // 开始的这一行文本,可能不被编译器所反对;多行正文从 /* 到 */之间的所有文本,不反对嵌套; 2)逗号表达式的优先级是最低的;逗号表达式从左向右开始执行语句;逗号表达式的值就是最左边语句的值 3)前置++,先自增,再取值;后置++,先取值,再自增;--运算符同++。++和--运算符的联合方向是从左向右 1、正文C语言中的正文分为“单行正文”和“多行正文”;单行正文:(单行正文可能不被编译器反对)以 **//** 开始的这一行文本多行正文:(多行正文不反对嵌套)从 /* 到 */之间的所有文本 2、逗号表达式逗号(,)是一种非凡的运算符逗号能够将多个语句连接起来变成一条语句语法:语句1,语句2,语句3, ... ,语句n留神: 逗号表达式的优先级是最低的逗号表达式从左向右开始执行语句逗号表达式的值就是最左边语句的值3、自增++与自减--运算符3.1 前置前置++(--):先自增(自减),再取值应用逗号表达式能够示意如下: ++v; <-> (v = v + 1, v)--v; <-> (v = v - 1, v)3.2 后置后置++(--):先取值),再自增(自减)应用逗号表达式能够示意如下: v++; <-> (v = v + 1, v - 1)--v; <-> (v = v - 1, v + 1)本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。如有错漏之处,恳请斧正。

July 10, 2021 · 1 min · jiezi

关于c:C入门4程序中的执行结构

Summary1)程序执行的根本构造包含:程序构造、循环构造、抉择构造 2)if实用于简单逻辑判断,“按片”判断;switch实用于对离散值进行判断,按多个分支判断 3)switch中的var仅能实用于整数值(离散型变量或者值) 4)switch语句中的每个case,个别都要加上break(除非非凡须要),如果不加break,会从以后case始终执行到下一个break,即便var不合乎前面的case条件。 5)循环构造的三要素: 初始化循环变量在循环体中扭转循环变量判断循环条件6)判断质数的办法:[2,x)之间的数没有任何一个整数能够除尽x(x % i == 0),则x为质数 1、抉择构造1.1 if ... else ...if语句用于依据条件抉择执行语句else不能独立存在且总是与离他最近的if相匹配else语句之后能够连贯其余if语句if(condition1){ // statement1}else if(condition2){ // statement2 }else{ // statement3}1.2 switch ... case ...switch是一种更简洁的多分支抉择构造switch的入参var仅能是整数值!须要留神break的应用多个case能够合并在一起,执行雷同的语句 2、循环构造循环构造次要有while循环、for循环、do while循环。在循环中,能够应用break关键字跳出循环;应用continue,终止本次循环,立刻进去下一次循环。 2.1 do ... whiledo是循环的开始,while是循环的完结do...while();能够看做一条语句,所以要以分号完结do...while至多执行一次循环体 2.2 while循环构造的三要素: 初始化循环变量在循环体中扭转循环变量判断循环条件 2.3 forfor循环是一种更简洁的循环构造: int i = 0;int sum = 0;for(i=0; i<=100; i++){ sum += i;}2.4 应用循环判断一个数是否为质数质数x的定义:x只能被1和x整除判断质数的办法:[2,x)之间的数没有任何一个整数能够除尽x(x % i == 0),则x为质数 bool isPrimeNumber(int x){ bool ret = true; int i = 0; for(i=2; i<x; i++) { if(x % i == 0) { ret = false; break; } } return ret;}本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。如有错漏之处,恳请斧正。 ...

July 10, 2021 · 1 min · jiezi

关于c:c语言三子棋游戏每天一个装杯小技巧源码在末尾

三子棋是黑白棋的一种。三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,绝对两边顺次摆上三个单方棋子,只有将本人的三个棋子走成一条线,对方就算输了。然而,有很多时候会呈现和棋的状况。咱们学习了c语言,当初咱们尝试本人用C语言写一个三子棋小游戏玩玩吧! 三子棋棋盘图案:游戏开始抉择:抉择1:开始游戏抉择0:退出游戏当抉择开始游戏时,你与电脑开始对弈 当咱们抉择的坐标为(2,2) 电脑它抉择的坐标为(1,3)对弈的后果只有3种: 玩家赢:电脑赢:平 局:小伙伴们能够本人去尝试一下啦!!! 看看你与你的电脑谁的棋艺更高!!! 难度级别能够本人调: 棋盘格子的多少能够由本人来定 #define ROW 3 #define COL 3 //这里的行和列有本人来设定源码: kt.c (文件名) #include "game.h" void menu(){ printf("***********************\n"); printf("****1.play 0.exit*****\n"); printf("***********************\n");} void game(){ char ret = 0; char board[ROW][COL] = { 0 }; InitBoard(board, ROW, COL); DisplayBoard(board, ROW, COL); while (1) { PlayerMove(board, ROW, COL); DisplayBoard(board, ROW, COL); ret = IsWin(board, ROW, COL); if (ret != 'C'){ break; } ComputerMove(board, ROW, COL); DisplayBoard(board, ROW, COL); ret = IsWin(board, ROW, COL); if (ret != 'C'){ break; } } if (ret == '*'){ printf("玩家赢\n"); } else if (ret == '#'){ printf("电脑赢\n"); } else{ printf("平局\n"); }} void test(){ int input = 0; srand((unsigned int)time(NULL)); do{ menu(); printf("请抉择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏\n"); break; default: printf("抉择谬误,请从新抉择!\n"); break; } } while (input);} int main(){ test(); return 0;}game.c (文件名) ...

July 5, 2021 · 3 min · jiezi

关于c:STM32定时器参数设置TIMPrescalerTIMPeriod

TIM_Prescaler:定时器预分频器设置,时钟源经该预分频器才是定时器时钟,它设定 TIMx_PSC寄存器的值。可设置范畴为 0 至 65535,实现 1 至 65536 分频。 TIM_Period:定时器周期,理论就是设定主动重载寄存器的值,在事件生成时更新到影子寄存器。可设置范畴为 0 至 65535。 依据定时器时钟的频率,比方时钟的频率是72MHZ,能够了解为一秒钟STM32会本人数72M次,预分频系数就是将频率宰割,比方分频系数是72,则该时钟的频率会变成72MHZ/72=1MHZ,然而在设置的时候要留神,数值应该是72-1。 假设分频系数是72-1,那么频率变成1MHZ,也就意味着STM32在一秒钟会数1M次,即1us数一次。 好了,接下来就是确定预装载值,比方须要定时1ms,因为1ms=1us*1000,那么预装载值就是1000-1;如此类推,在预分频系数确定的状况下,定时的时长就由预装载值确定了。至于要把值减一的起因,预计是计数是从0开始,所以要减一。 原文链接:https://blog.csdn.net/ZIIllII... //对于71MHZ的频率,500ms中断一次,这两个参数设置如下:TIM_Prescaler=7199; //预分频值TIM_Period=4999; //下一个更新事件装入流动的主动重装载寄存器周期的值 //分频7200,用72000000/7200=10000Hz //此时的周期就是1/10000=0.0001s //500ms=0.0001s*5000次 //5000-1=4999次 //即通过4999次零碎的运行,就是500ms

July 3, 2021 · 1 min · jiezi

关于c:JIT原理简单介绍

JIT = just in time ,简略来说就是在运行时动静编译。一个程序在它运行的时候创立并且运行了全新的代码,而并非那些最后作为这个程序的一部分保留在硬盘上的固有的代码。其实蕴含两个概念,一个是动静生成代码,再一个是动静运行代码。 咱们都晓得,计算机运行的都是机器码,而汇编语言的全称应该是“机器码注记语言”,每一条汇编都对应一串机器码。而JIT的原理就是在内存中生成和运行一段代码。 生成的过程,是编译器干的,当然手动也是能够的,而在内存中运行一段代码,则是依赖操作系统提供的mmap syscall来实现的。 比方,上面是一个求和的机器码 //求和函数long add(long num) { return num + 2; }//对应机器码0x55,0x48,0x89,0xe5,0x48,0x89,0x7d,0xf8,0x48,0x8b,0x45,0xf8,, 0x83, 0xc0, 0x02,0x5d,0xc3动静地在内存上创立函数之前,咱们须要在内存上调配空间。具体到模仿动态创建函数,其实就是将对应的机器码映射到内存空间中。这里咱们应用c语言,利用 mmap函数来实现这一点。而mmap函数的底层就是对操作系统mmap syscall的一个封装。 头文件:#include <unistd.h> #include <sys/mman.h>定义函数:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);参数阐明:参数 阐明start 指向欲对应的内存起始地址,通常设为NULL,代表让零碎主动选定地址,对应胜利后该地址会返回。length 代表将文件中多大的局部对应到内存。其中,prot 代表映射区域的保护方式,有下列组合 PROT_EXEC 映射区域可被执行; PROT_READ 映射区域可被读取; PROT_WRITE 映射区域可被写入;参数flags:影响映射区域的各种个性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE咱们须要这块代码可读可执行,所以咱们能够这样来创立一块空间 #include <stdio.h> #include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/mman.h>//分配内存void* createSpace(size_t size) { void* ptr = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); return ptr;}咱们能够试试把“可执行”PROT_EXEC权限去掉,看看后果如何。 ...

June 29, 2021 · 2 min · jiezi

关于c:C入门程序中的运算

Summary1)C语言中反对如下4种类型的运算: 运算类型运算符四则运算+,-,*,/,%关系运算<,>,<=,>=,==,!=逻辑运算&&,!,II位运算&,I,^,>>,<<,~2)运算符的优先级:单算移比,按逻三赋。如果不同类型的运算符同时呈现在一个表达式中,尽量用括号()指明运算程序。 3)逻辑运算的短路法令:对于&&运算,第一个为假的操作数之后的其余操作数都不再计算;对于||运算,第一个为真的操作数之后的其余操作数都不再计算。因而对于if表达式的逻辑表达式,肯定要思考到后续的操作数是否须要运算到。 4)十进制正数和二进制的互相转换规则如下: 1)符号位:2)绝对值(二进制):3)按位取反:4)加1:如果一个二进制位示意的是一个正数,那么推算的形式按上述规定逐渐相同。5)在计算机中,不同数据类型的实质在于: a)占用的内存大小不同:如int占4字节,char占1字节b)示意数据的具体形式不同:如正整数用原码示意、负整数用补码示意;整数和浮点数的二进制示意办法不同。6)位运算操作时的关键点: a)操作数的类型(占用的内存大小)b)符号位(0还是1)c)不同数据类型的运算要先对齐,再运算(补符号位进行对齐)7)设置整数integer指定Bit位的值规定如下: 置0:应用按位与(运算数n指定地位为0,其余为1,同integer进行&运算),如果对应地位为0,其余地位能够不变为1,这样运算数n能小一点,直观一些置1:应用按位或(运算数n指定地位为1,其余为0,同interger进行|运算)取反:按位异或(如一次指定多位取反)1、四则运算四则运算(+, -, *, /, %) 就是数学中的加减乘除等运算遵循先乘除后加减的运算优先级能够应用括号扭转运算程序留神:C语言中的除法运算,除数不能为0!/和%都是除法。对于“/”,运算的后果和大类型统一。对于“%”,只能作用于整型数。 int a = 1;int b = 2;int c = 0;c = a / b; // 计算失去除法的商,大类型为int,后果依然是intprinf("c = %d\n", c); // c = 0c = a % b; // 计算失去登程的余数,只能作用于整型数prinf("c = %d\n", c); // c = 1double a = 5;double b = 2;double c = 3;c = a / b; // 两个浮点数的除法,大类型为double,后果依然是浮点数doubleprinf("c = %f\n", c); // c = 2.5c = a % n; // error,取余运算不能够作用于浮点型2、关系运算关系运算(<, >, <=, >=, ==, !=) ...

June 27, 2021 · 2 min · jiezi

关于c:C入门数据类型

Summary0)对于所操作数据的具体类型,肯定要十分明确! 1)C语言对数据的分类:整数类型、浮点数类型、字符类型(一般字符型、无回显字符型)。各个类型的基本区别在于,所占用的内存大小不同。 2)类型所占的字节数是由编译器决定的,32位和64位的次要区别在于:指针所占字节数别离为4和8;long所占字节数别离为4和8。 3)辨别初始化和赋值的关键点:赋值操作是在创立变量时候进行的,还是在创立变量之后进行的。 4) 当遇到以下2种状况时: 以后的值曾经溢出了(超过了以后类型所能示意的范畴里的数)大类型向小类型转换对于溢出当前的值:正值:溢出的值 - 以后类型能示意的个数 = 理论值负值:溢出的值 - 以后类型能示意的个数 = 理论值5)sizeof用于取得类型或者变量所占用的内存字节数,sizeof作用于数组名时,能够失去数组占用的内存。 6)C语言中字面量的默认类型:如2为int,0.2为double,'2'为char 1、数据类型与变量1.1 C语言中的数据类型初学阶段应把握的类型:整形: int:占用4个字节(32位(bit)),示意范畴:-2147483648~2147483647(-231 ~ 231-1)。short:占用2个字节(16位(bit)),示意范畴:-32678 ~ 32767。浮点型: float:占用4个字节(32位(bit)),示意范畴:-3.4 x 1038 ~ 3.4 x 1038。double:占用8个字节(64位(bit)),示意范畴:-1.7 x 10-308 ~ 3.4 x 10308。字符型: char:占用1个字节内存(8位(bit)),示意范畴:-128~127。字符数据应用单引号括起来。包含一般字符(英文字符类型,如'D','t');无回显字符类型(无回显字符必须以反斜杠'\'结尾,是一种打印后在屏幕上看不到的字符,如换行符'\n',制表符'\t',这是一个字符);字符类型理论也是种整型。各个数据类型的基本区别在于,所示意的内存大小不同。 1.2 C语言中变量的命名规定字母(a-z, A-Z)、数字(0-9)、下划线(_)形成的字符序列第一个字符必须为字符或者下划线(不能为数字)大小写敏感(如name和Name是两个不同的变量名字)// 小测试:以下哪些命名是非法的?()A.WORD B.-abc C.2c D._1E.m_test F.a@b G.c3 H._// A、D、E、G、H非法变量命名的标准:1)见名知义:变量命名肯定要一眼能看出这个变量的用处2)便于浏览:能够遵循一些标准,如“驼峰标准”,不便本人和别人浏览、保护代码。 1.3 C语言中变量的定义C语言中创立变量的语法:type name; 如下:int n1;double n2;short n3;初始化:在创立变量的同时,给变量一个值。赋值:在创立变量之后,扭转变量的值。 int n1; // 定义变量,名为n1,类型为intdouble hello; // 定义变量,名为hello,类型为doubleint n2 = 2; // 定义变量,名为n2,类型为int,并初始化为2n1 = 1; // 赋值操作,扭转n1的值,使其示意12、深刻数据类型与变量2.1 程序中数值的类型C语言是类型严格的语言,字面量也有类型,应用字面量时也须要思考字面量的类型。字面量的类型包含默认类型和指定类型,如: ...

June 26, 2021 · 2 min · jiezi

关于c:STM32最小系统下载程序方法

我应用的STM32最小零碎板是stm32f103c8t6 采纳用ISP形式下载: 须要的器件: CH340E USB转TTL模块转串口mcuisp.exe软件stm32f103c8t6USB转TTL模块与stm32的连贯: STM32USB-TTL3.3V3.3VGNDGNDA9RxA10TxSTM32芯片的启动形式: 在应用JLINK烧写程序的时候,BOOT0和BOOT1都接在0的地位,而用mcuisp.exe串口烧写程序,须要将BOOT0接到1的地位. 留神:当程序下载实现后,STM32f103c8t6最小板即开始运行程序了。不过如果将最小板断电之后从新上电,程序无奈执行,须要断电后,将BOOT0接到0上,再从新上电,程序就能失常运行了

June 26, 2021 · 1 min · jiezi

关于c:C入门一Hello-World

Summary1、前言1.1 什么是软件软件是一种计算机部件,是计算机的组成部分;软件是指挥硬件实现具体性能的“意识形态”;硬件是软件的“躯干”,接管并执行软件的命令;当代计算机软硬件架构:最底层的是计算机硬件,包含CPU、内存、显卡、网卡、键盘、鼠标等。而后是运行于计算机硬件之上的各类操作系统,如Linux、Windows、MacOs等。(操作系统也是一种软件)再就是基于操作系统的各类软件,如设计软件、办公软件、聊天软件等。 1.1 什么是程序设计语言应用一组固定规定和符号表达思想的形式;人类应用这组规定和符号形容须要计算机实现的性能;计算机可能读懂由这组规定和符号形成的描述语言,并严格执行;即:程序设计语言是程序员和计算机进行交换沟通的语言,是计算机可能读懂并执行的语言。同咱们日常所用汉语一样,也有本人的一系列的规定。咱们只须要严格遵守规则,就能写出计算机能够执行的程序。 2、C程序中的数据输入2.1 C语言是怎么执行的个别状况下,C语言程序从main()开始执行,从左花括号“{”开始,到右花括号“}”完结,默认状况下,C语言程序是以自上而下的程序来执行,执行的根本单位是语句,每条语句应用分号“;”隔开。(正如中文中对一段文章的浏览,个别也是自上而下,按句子来读,每个句子的完结符号是句号“。”) 上图中,C程序:单纯的文本文件,无奈间接执行编译软件的工作:1)检测C程序的语法是否合规;2)将C程序翻译成二进制可执行程序:如Windows零碎下的.exe文件,Linux零碎下的.out文件 2.2 应用C语言如何打印“Hello World!”C语言中内置了很多实用的“工具包”工具包都有一个固定的名字,通过名字应用(#include <name>)每个工具包中提供了很多“工具”,如stdio.h工具包中就提供了一个用于“打印”输入的工具:printf,printf通过设置参数后能在屏幕上打印出文本。// 在屏幕上打印Hello World。// 代码留神点以正文模式给出#include <stdio.h> // #include用于申明须要应用的工具包 // stdio.h:程序中须要用到的stdio.h工具包int main(){ printf("%s\n", "Hello World!"); // printf是用来打印输出的工具 // %s是对应于字符串的“格式化字符”,第一个参数中除了格式化字符外的,都是一些说明性字符 // 第二个参数"Hello World!"是要打印到屏幕上的数据 return 0;} printf应用数据对参数中的格式化字符进行替换(%s等)不同类型的数据对应于不同的格式化字符:如%s对应字符串,%d对应整数,%f对应浮点数等最终打印的数据是一个字符串。3、C程序中的数据输出3.1 如何从键盘输入数据stdio.h工具包中提供了一个数据输出工具scanfscanf通过正确设置后,可能获取键盘输入的数据scanf将键盘获取的数据“填入”变量 // scanf工具应用示例#include <stdio.h> // scanf工具在stdio.h工具包中int main(){ int i = 0; scanf("%d", &i); // 1)scanf是从键盘输入的工具 // 2)接管键盘输入的变量名前要加上“&” // 3)输出数据的类型必须和变量i的类型统一 // 4)scanf的第一个参数中,只能有格式化字符,不能蕴含任何与类型无关的字符(如\n) return 0;}3.2 使如何间断地从键盘中输出数据#include <stdio.h>int main(){ int i = 0; int j = 0; // 1)一次性输出 scanf("%d%d", &i, &j); // 输出时应用空格或回车对不同数据进行分隔 // 2)离开输出 scanf("%d", &i); scanf("%d", &j); return 0;}本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。如有错漏之处,恳请斧正。 ...

June 20, 2021 · 1 min · jiezi

关于c:解析Large-Coding-Question

Part 3: Large Coding Question[P3 Q1] Quack from afar[P3Q2] Quack Family - Siblings[P3 Q3] Chucky's Chicken App1) Read in Menu From File2) Order Items3) Calculate Prices4) Charge the UserScaffold[P3 Q1] Quack from afarThe ProblemUnfortunately, the duck population has seen an outbreak of QUACK19, and the Duck governmenthas placed social distancing restrictions on all pond activities. However, the Duck government isconstantly changing their distancing requirements, so it is up to you to determine which ducks areallowed to swim in the pond.You are provided with a file which contains a list of coordinates of where ducks will land in apond. Ducks will land in the pond one after the other. The order that coordinates appear in thefile corresponds to the order they will land in the pond.When a duck attempts to land in the pond, it must first determine if the position it wants to landis far enough away from any of the current ducks in the pond. Using the distance that the DuckGovernment has given you, we can calculate whether the new duck will encroach on existingducks in the pond. If their landing spot is too close to other ducks, they will keep flying and findanother pond somewhere (we do not care about the other pond).After all the ducks in violation of social distancing have moved on, you should write to a file thecoordinates of all the ducks which are currently in the pond. The order these ducks appear in thefile should preserve the order in which they arrived in the pond.Your program should the write to a file with the same name as the filename parameter,although prefixed with filtered- .For example, if my filename is ducks.txt , I would write the filtered duck coordinates tofiltered-ducks.txt . Another example, if my filename was pond.ducks , I would write thefiltered duck coordinates to filtered-pond.ducks .The TaskWrite a function distance_ducks(filename, distance) . The function will read a file from thegiven filename which contains the coordinates of duck landing zones in a pond. Thefunction will then filter the file so that only ducks adhering to social distancing rules can stay inthe pond. The function will then write to a file containing the coordinates of ducks which are notin violation of social distancing.If the file cannot be opened because it does not exist, you must catch the FileNotFoundErrorobject and throw a new exception FileNotFoundError with the contents of the exception beingthe filename. Any other exception for failure to open a file is not caught.If the parameter filename is not a string type, then a TypeError exception is thrown with thecontents of the exception being a string "parameter filename is not a string" .If the parameter distance is not a float or int type, then a TypeError exception is thrown withthe contents of the exception being a string "parameter distance is not a numeric type" .The fileEach line in the file will consist of two floating point values separated by a comma. Each new lineof the file represents the position that a new duck will attempt to land in the pond.An example of the file contents is shown below:You can find sample input and output files in the testing folder in your workspace.The directory names in outputs specify the distance value used, e.g. 1-5 indicates 1.5m ofdistance between ducks.Note:You do not have to write your own tests / test driver for this task (although you can if you'd like).The contents of the testing directory are sample outputs for you to check if your program isworking. This is optional.The directory structure for the testing folder is to let you know the distance value that was usedwhen the sample outputs were generated, so that you can test your function with the samearguments (to hopefully get the same output). Check the README file, You can use the diffcommand to check if your output file matches the sample.Your program does not have to mimic this directory structure. You can assume that python willoutput the file in the same directory as the input file (I recommend you put your input files in thecurrent working directory).Disclaimer: you are not guaranteed to be provided sample tests in the examCalculating DistanceYou can use Euclidean distance to calculate the distance between any two ducks:Consider two ducks and with associated coordinates and .1.45,10.5-3.51,0.3151.925,-9.338-9.283,3.190-0.261,-10.5283.877,-13.0030.740,-12.0949.066,10.7071.099,11.568-5.154,-6.7301.134,-5.480-13.035,-0.588The distance between the ducks can be found with the following formula:Due to floating points having some precision issues, you are allowed to correct for errors lessthan 0.0001.That means, 1.500000005135 would be considered as 1.5 in regards to the distance calculation.[P3Q2] Quack Family - SiblingsThe ProblemA duck can usually raise 12 ducklings, which is quite a lot. In the Kingdom of Mallard, there is aculture of recording their own family tree. Some family trees can trace back hundreds of years oflineage, while others have records of only the latest generation.You have found some family trees online. While you were learning about their history, you had anidea to present the family tree interactively. Specifically, you would like to interactively count andidentify the siblings and step-siblings of a given duck of interest.The provided data files present the details of each duck in a separate row in the following format:<name>, <gender>, <year of birth>, <father>, <mother>Each data field is separated by commas. An example of the file contents is shown below:The TaskWrite a function count_siblings(filename, name) . The function will read a file from the givenfilename which contains the family tree of the duck of interest. It will then determine the siblings(same father and mother) and step-siblings (same father, different mother or same mother,different father) of the duck. The function will return a string in the following format:<name> has <number> siblings: <list of siblings>, <list of step-siblings>Siblings will be listed first, followed by step-siblings. Step-siblings will also be identified with thestring (step) following their names. Regular English punctuation will need to be applied. Forexample, from the data of the sample file shown above:If the parameter filename is not a string type, then a TypeError exception is thrown with thecontents of the exception being a string "parameter filename is not a string" .If the parameter name is not a string type, then a TypeError exception is thrown with thecontents of the exception being a string "parameter name is not a string" .Theo, m, 1958, ,Ray, m, 1952, ,Stephen, m, 1979, Theo, MaryDaffy, m, 1976, Ray, MaryAnnabelle, F, 1975, John, OliviaRyan, m, 1981, Ray, PenelopeChris, m, 1981, Theo, MaryDeborah, F, 1996, Stephen, AnnabelleDominic, m, 1997, Ryan, MayBrian, M, 2007, Ray, PenelopeMelvin, m, 1981, Ray, Penelopeprint(count_siblings("family.dcf", "Brian")) ...

June 20, 2021 · 9 min · jiezi

关于c:C语言初阶三子棋

棋盘的实现家喻户晓,三子棋棋盘其实是一个就九方格,所以咱们首先要定义一个二维数组来实现寄存棋子。每一个方格咱们规定为占三个小格例如 ’ X ',且必须有分隔符来离开棋子。 棋盘的初始化棋局开始时,棋盘必须是空的,所以咱们首先的初始化数组,将每一个元素都置为’ ',而后再来实现棋盘的性能,棋盘的初始化代码如下; void InitBoard(char board[ROW][COL], int row, int col){ for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { board[i][j] = ' '; } }}棋盘棋盘是一个二维数组,且每一个元素都要用分隔符来分隔它们,横行用‘ | ’,竖列用‘—’。一行有三个元素所以用两个‘ | ’,有三列所以用两个‘—’。而且在‘—’所占的一行也要加‘ | ’。棋盘的实现代码如下: void ShowBoard(char board[ROW][COL], int row, int col){ printf("=====================\n"); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { //三个空格 printf(" %c ", board[i][j]); //两列竖线 if (j < col - 1) { printf("|"); } } //换行 printf("\n"); //横线只有2行 if (i < row - 1) { for (int j = 0; j < col; j++) { printf("---"); // if (j < col - 1) { printf("|"); } } printf("\n"); } } printf("=====================\n");}这两步实现后,棋盘的实现就实现了 ...

June 18, 2021 · 3 min · jiezi

关于c:利用core-dump进行C项目socket编程的异常定位errno为110-Connection-timed-out

最近在一个碰到一个socket相干的异样。 环境:linux我的项目:C++,波及大量网络IO察看对象:一个常驻过程,既是客户端,也是服务端景象:特定操作后,隔132分钟后,过程便会主动退出。能通过exception异样类捕捉到errno为110, 对应的message为"Connection timed out"。剖析过程: 假如1:从该Connection time out这音讯提醒,联合该我的项目波及大量的socket通信,很天然地联想到是该过程在调用connect(),与服务端建设连贯的时候出了问题?颠覆假如1:connect()是linux的C实现的库函数,不会抛异样(异样是C++中的机制)。 假如2:回到一开始,正是因为代码里有异样捕捉,才得悉errno,的确抛了异样。那么就有可能是人为地依据errno,被动throw进去的。搜寻得悉,我的项目中有大量的throw system_error(errno, system_category()),合乎假如2。 在这种有大量可疑点的状况下,被动从代码的角度去一个个刻意点排查是很吃力的。既然定时可能复现,且有抛异样的机制。那来一个刻舟求剑,不捕获异样解决,让程序间接core dump,便可依据core文件的堆栈信息,定位到起因。 最初无效地找到问题所在:程序作为服务端,其客户端在运行过程中退出了,服务端的连贯没有进行敞开,代码层面没有close与客户端的连贯socket,且该连贯的socket设置了SO_KEEPALIVE属性。这就呈现了,默认7200s,服务端就会往一个半关上的连贯发送一个心跳报文,对方是不会回复该报文的,引发连贯超时的谬误。 解决方案:服务端在检测到客户端发动敞开连贯后,及时进行敞开连贯。惯例来说,客户端close --> 服务端接管完接收缓冲区的数据,close --> 连贯敞开。

June 18, 2021 · 1 min · jiezi

关于c:C语言之拒绝scanf从我做起

大家好,我来了,我是萌杰尔前段时间,我想实现一个命令行工具(相似cmd.exe那种的),于是捡起了万年不必的C语言,写起了输入输出。 问题就出在这里了,我用的scanf函数不承受空字符,只有我不输出货色,按回车还是无奈完结,这我能忍??? 作为一个在计算机这块混了八年的混子,我必定有我的方法,可能不肯定好,然而能够解决当下的这个问题。 我想,很多敌人曾经晓得怎么办了,然而还是有些萌新遇到这种问题不晓得如何解决,那我明天就来讲一讲吧。 解题思路我的思路是应用一个循环,通过getchar函数继续一直的接管字符,而后把字符存储到字符串中。 说干就干,昨天晚上我花了一点工夫实现这个性能,上面展现一下我的代码。 #include <stdio.h>#include <string.h>int main(int argc, char const *argv[]){ //外层循环,让程序每次执行完一个命令就返回到初始中央从新输出命令 while (1) { //命令字符串 char command[128]; //累加器,示意以后输出的字符对应命令字符串中的下标 int scannerIndex = 0; //清空命令字符串(防止缓冲区问题) memset(command, 0, sizeof(command)); //打印Command printf("Command > "); //里层循环,接管字符 while (1) { //接管字符临时存储地位 char buff = getchar(); //判断该字符是否为回车,如果是,就让累加器归零并且退出循环 if (buff == '\n') { scannerIndex = 0; break; } //如果不是,就将输出的字符保留到命令字符串中 command[scannerIndex] = buff; //累加器的值减少 scannerIndex++; } //判断命令是否为空,如果为空,让循环进行从新执行 if (strlen(command) == 0) continue; //这上面能够开始写命令操作的内容了 //Code Here } return 0;}执行后果如下能够看到当我留空时会间接从新执行,解决了scanf的问题。 ...

June 15, 2021 · 2 min · jiezi

关于c:Centos-7安装Pyqt5Qt-Designer以及配置Vscode教程

最近筹备学习Python GUI,除了要写代码的实现外,目前认为最快捷的形式预计是PyQt了,在配置环境的过程中,网上的教程都是基于Windows的,也有基于Ubuntu的,但始终没有找到CentOS下的教程,走了不少弯路,通过摸索,终于搞定。Ubuntu下的装置形式: sudo apt-get install qttools5-dev-toolsbut:......在centos下出错。。。 一、Python装置这一部分没什么好说的,网上有大量的教程,按图索骥就行。 二、装置Pyqt5PyQt5 有两种装置形式,一种是从官网下载源码装置,另外一种是应用 pip 装置。 这里我举荐大家应用pip 装置。因为它会主动依据你的Python 版本来抉择适合的 PyQt5 版本,如果是手动下载源码装置,难免会抉择出错。倡议应用比拟稳当的装置形式。 pip3 install PyQt5个别这种形式在国内的环境会比较慢,有可能会提醒装置失败,这里倡议应用国内装置源(豆瓣)的形式解决: pip install PyQt5 -i https://pypi.douban.com/simple三、装置 PyQt5-tools同样应用国内镜像装置 pip install PyQt5-tools -i https://pypi.douban.com/simple四、配置Vscode1.装置pyqt integration扩大 2.配置pyqt integration这里有个Qt designer门路的设置问题,因为咱们没有独自装置Qt designer,装置PyQt5-tools的时候曾经装置好Qt designer了,每台电脑的门路又不尽相同,所以刚开始寻找起来比拟麻烦,这里举荐应用Linux find命令来定位相干文件的地位。 find / -name designer找到QT designer门路后,复制并配置在pyqt integration里 在文件治理,空白处右键抉择PYQT:New Form建设表单 关上QT Designer,并创立一个表单保留ui文件,返回Vscode 右键选中方才创立的ui文件,抉择PYQT:Compile Form,生成同名的python文件 生成的代码: 尝试运行刚刚生成的“Ui_mainus.py”是没用的,因为生成的文件并没有程序入口。因而咱们在同一个目录下另外创立一个程序叫做“main.py”,并输出如下内容,将Ui_untitled替换为你生成.py文件名。 import sysfrom PyQt5.QtWidgets import QApplication, QMainWindowimport Ui_mainusif __name__ == '__main__': app = QApplication(sys.argv) MainWindow = QMainWindow() ui = Ui_mainus.Ui_Dialog() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_()) ...

June 12, 2021 · 1 min · jiezi

关于c:西电C语言程序设计实验之图书馆管理系统

简略文件数据库-模仿图书馆管理系统波及知识点:文件读写、内存治理、构造体定义、根本数据结构、高级格式化输入输出 要求:编写一个程序模仿图书管理系统。 用户分为管理员和读者两类,别离显示不同文本格式菜单,通过菜单项对应数字进行抉择。 读者菜单包含借书、还书、查问等性能。管理员菜单包含图书和读者信息录入、批改和删除。 图书信息至多应包含:编号、书名、数量。读者信息至多应包含:编号、姓名、所借图书。 可依据图书名称或编号进行图书信息查问,可查问某本书当初被哪些读者借走。 命令行参数如下:Libsim –a(-u) xxxx 第一个参数为可执行程序名称;第二个参数为用户身份,-a示意管理员,-u示意读者;第三个参数为用户名 几个函数阐明: 展现读者和图书信息 void ShowBook(char* bname);void ShowReader(char* rname);图书借阅和偿还 void BookLend(char *rname, char *bname);void BookReturn(char *rname, char* bname);图书和读者信息增删 void AddBook(char* bname);void DelBook(char* bname);void AddReader(char* rname, char* reroot); void DelReader(char* rname);读取读者,管理员和图书信息 void ReadBooksInfo();void ReadReadersInfo();void ReadAdministratorsInfo();更新读者,管理员和图书信息 void UpdateBorrowerList();void UpdateBorrowedBookList();void UpdateBookleft();void UpdateBooks();void UpdateReaders();void UpdateAdministrators();void UpdateReroots();void UpdateAdroots();图书馆信息:信息编写规定: 每个文件以end作为结尾而null示意该书或读者曾经被删除books.txt:每行一本书,第一行的书编号为1readers.txt:每行一个读者姓名,第一行的读者编号为1administrators.txt:每行一个管理员姓名,第管理员一行的读者编号为1books'borrowers.txt:每行若干个人名,第一行对应编号为1的书的借阅人borrowers'books.txt:每行若干个书名,第一行对应编号为1的人的在借书reroots.txt:每行一个明码,第一行对应编号为1的读者的明码adroots.txt每行一个明码,第一行对应编号为1的管理员的明码源码(不蕴含函数局部) #include<stdio.h>#include<string.h>typedef struct Book{ char name[20];//名称 int num; //编号 int left; //库存 }Book;typedef struct Reader{ char name[20];//姓名 int num; //编号 char root[20];//明码 }Reader;typedef struct Administrator{ char name[20];//姓名 int num; //编号 char root[20];//明码 }Administrator; Book books[110]; //图书信息 Reader readers[110]; //读者信息 Administrator administrators[10]; //管理员信息 char borrowerList[110][100]; //图书在借人 char borrowedBookList[110][100]; //读者在借书 void ShowBook(char* bname);void ShowReader(char* rname);void BookLend(char *rname, char *bname);void BookReturn(char *rname, char* bname);void ReadBooksInfo();void ReadReadersInfo();void ReadAdministratorsInfo();void UpdateBorrowerList();void UpdateBorrowedBookList();void UpdateBookleft();void UpdateBooks();void UpdateReaders();void UpdateAdministrators();void UpdateReroots();void UpdateAdroots();void AddBook(char* bname);void DelBook(char* bname);void AddReader(char* rname, char* reroot); void DelReader(char* rname);//Libsim –a(-u) xxxx//第一个参数为可执行程序名称;第二个参数为用户身份,-a示意管理员,-u示意读者;第三个参数为用户名int main(int argc, char *argv[]) { ReadBooksInfo(); ReadReadersInfo(); ReadAdministratorsInfo(); char *command = argv[1], *name = argv[2]; printf("Hello, %s! Welcome to XDULibrary!\n", name); if(!strcmp(command, "-a")){ char root[10]; printf("Please enter your root:\n"); scanf("%s", root); int i, flag = 0, choice; for(i = 0; i < 10; i++) if(!strcmp(administrators[i].root, root)) flag = 1; if(!flag){ printf("wrong root\n"); return -1; } printf("0.exit\n1.show books\n2.show readers\n3.delete readers\n4.delete books\n5.add readers\n6.add books\n"); while (1) { printf("please choose what to do:\n"); scanf("%d", &choice); if (choice == 0){ printf("exit successfully"); return 0; } switch(choice){ case 1:{ char bname[20]; printf("please enter book's name:\n"); scanf("%s", bname); ShowBook(bname); break; } case 2:{ char rname[20]; printf("please enter reader's name:\n"); scanf("%s", rname); ShowReader(rname); break; } case 3:{ char rname[20]; printf("please enter reader's name:\n"); scanf("%s", rname); DelReader(rname); break; } case 4:{ char bname[20]; printf("please enter book's name:\n"); scanf("%s", bname); DelBook(bname); break; } case 5:{ char rname[20], reroot[20]; printf("please enter reader's name:\n"); scanf("%s", rname); printf("please enter reader's root:\n"); scanf("%s", reroot); AddReader(rname, reroot); break; } case 6:{ char bname[20]; printf("please enter book's name:\n"); scanf("%s", bname); AddBook(bname); break; } default:{ printf("illegal input\n"); break; } } } } else if (!strcmp(command, "-u")){ char root[10]; printf("Please enter your root:\n"); scanf("%s", root); int i, flag = 0, choice; for(i = 0; i < 10; i++){ if(!strcmp(readers[i].root, root)) flag = 1; } if(!flag){ printf("wrong root\n"); return -1; } printf("0.exit\n1.show books\n2.show readers\n3.lend books\n4.return books\n"); while (1) { printf("please choose what to do:\n"); scanf("%d", &choice); if (choice == 0){ printf("exit successfully"); return 0; } switch(choice){ case 1:{ char bname[20]; printf("please enter book's name:\n"); scanf("%s", bname); ShowBook(bname); break; } case 2:{ char rname[20]; printf("please enter reader's name:\n"); scanf("%s", rname); ShowReader(rname); break; } case 3:{ char bname[20]; printf("please enter book's name:\n"); scanf("%s", bname); BookLend(name, bname); break; } case 4:{ char bname[20]; printf("please enter book's name:\n"); scanf("%s", bname); BookReturn(name, bname); break; } default:{ printf("illegal input\n"); break; } } } } else{ printf("illegal input\n"); return -1; } return 0;}//编写函数的地位:……………………………………………………………………………………须要残缺源码可移步评论区留言,一键三连是对创作者最大的激励~ ...

June 8, 2021 · 3 min · jiezi

关于c:单链表的基本操作c语言章节实验作业

单链表的基本操作(c语言)——章节试验作业#include<stdio.h>#include<stdlib.h>#include<string.h>#include<windows.h>#include<stdbool.h>typedef char ElemType;typedef struct node{ ElemType data; struct node* next;}Node, *LinkList;void Show_meau() //菜单{ printf("**********************************\n"); printf(" WELCOME!\n"); printf("\n"); printf("\t单链表的基本操作\n\n"); printf(" \t 1.查问\t 2.插入\n\n"); printf(" \t 3.删除\t 4.打印\n\n"); printf(" \t 5.计数\t 6.销毁\n\n"); printf(" \t 7.退出\t 8.建表\n\n"); printf("\n"); printf(" 输出相干序号实现相干性能!\n"); printf("**********************************\n");}void InitList(LinkList *L){ *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL;}void Create_List(LinkList L) //尾插法{ ElemType c; Node* r, * s; bool flag=true; r = L; printf("提醒:输出$示意建表完结!\n"); while (flag==true) { c = getchar(); if (c != '$') { s = (LinkList*)malloc(sizeof(Node)); s->data = c; r->next = s; r = s; } else { flag = false; r->next = NULL; } } printf("建表实现!\a"); Sleep(2000);}void Search(LinkList L){ int flag=1; Node* p; ElemType e; getchar(); //排汇换行符 p = L->next; printf("请输出要查找的值:"); e = getchar(); while (p != NULL) { if (p->data != e) p = p->next; else { printf("该地址为:%p\n", p); printf("输出0退出!\n"); scanf("%d", &flag); break; } } if (flag == 0) return; printf("查无此值!\a"); Sleep(2000);}void DispList(LinkList L){ int n; LinkList p = L->next; if (p == NULL) { printf("表为空!\a"); Sleep(2000); return; } printf("打印值为:\n"); while (p != NULL) { printf("%c", p->data); p = p->next; } printf("\n\n"); while (1) { printf("输出0退出打印!\n"); scanf("%d", &n); if (n == 0) return; else printf("输出有效,请从新输出!\n"); }}int ListLength(LinkList *L){ int n = 0; LinkList p = L; if (p->next == NULL) { printf("表为空!\a\n"); Sleep(2000); return; } while (p->next != NULL) { n++; p=p->next; } n--; return n;}void Print_Length(LinkList L){ int n = 1; while (1) { if (n == 1) { printf("表长为:%d\n", ListLength(L)); printf("输出0退出打印!\n"); scanf("%d", &n); } if (n == 0) return; }}void ListInsert(LinkList L){ int i,j=0; ElemType e; LinkList p = L, s; if (p->next == NULL) { printf("表为空!\a\n"); Sleep(2000); return; } printf("以后表长为:%d\n", ListLength(L)); //留神指针L while (1) { printf("请输出插入的地位:"); scanf("%d", &i); if (i <= 0 || i > ListLength(L)) { printf("输出谬误!请从新输出!\n"); } else break; } getchar(); printf("请输出插入的值:"); scanf("%c", &e); while (j < i && p != NULL) { j++; p = p->next; } if (p == NULL) { printf("查无此值!\n"); Sleep(2000); return; } else { s = (LinkList*)malloc(sizeof(Node)); s->data = e; s->next = p->next; p->next = s; printf("插入胜利!\a"); Sleep(2000); }}void ListDelete(LinkList L){ int i, j = 0; ElemType e; LinkList p = L, q; if (p->next == NULL) { printf("表为空!\a\n"); Sleep(2000); return; } printf("以后表长为:%d\n", ListLength(L)); //留神指针L while (1) { printf("请输出删除值的地位:"); scanf("%d", &i); if (i <= 0 || i > ListLength(L)) { printf("输出谬误!请从新输出!\n"); } else break; } getchar(); while (j < i && p != NULL) { j++; p = p->next; } if (p == NULL) { printf("查无此值!\n"); Sleep(2000); return; } else { q = p->next; if (p == NULL) { printf("查无此值!\n"); Sleep(2000); return; } e = q->data; p->next = q->next; free(q); printf("删除胜利!\a"); Sleep(2000); }}void DestroyList(LinkList L){ LinkList pre = L, p = L->next; while (p != NULL) //逐个开释 { free(pre); pre = p; p = pre->next; } free(pre); printf("销毁胜利!\a"); Sleep(2000);}int main(){ int n; LinkList L; //头结点应为指针,因为调用DestroyList(L)时由手动开释(free()) InitList(&L); //留神栈区(主动开释)和堆区(手动开释)的销毁区别 while (1) { system("cls"); //清屏函数 Show_meau(); printf("请输出1-8的数:"); scanf("%d", &n); switch (n) { case 1: Search(L); break; case 2: ListInsert(L); break; case 3: ListDelete(L); break; case 4: DispList(L); break; case 5: Print_Length(L); break; case 6: DestroyList(L); //留神栈和堆的销毁的区别 break; case 7: printf("........正在退出中........"); Sleep(3000); system("cls"); printf("\a退出胜利!"), exit(0); break; case 8: Create_List(L); break; default: printf("\a输出谬误!请从新输出(1—8)!"), Sleep(2000); break; } } return 0;}

May 30, 2021 · 3 min · jiezi

关于c:给定两个均不超过9的正整数a和n要求编写函数求aaaaaa⋯aa⋯an个a之和

要求:用两个函数//返回要求的和sumA(int a, int n);//返回n个a组成的数字int fn( int a, int n); 输入输出: main函数实现,如下: int main(){ int a, n; scanf("%d %d",&a,&n); printf("fn(%d , %d) = %d\n", a, n, fn(a, n)); printf("s = %d\n", SumA(a, n)); return 0;} fn函数实现,如下: int fn( int a, int n){ int sum1 = 0; for(int i = 1; i<=n; i++) { sum1 += a; a *= 10; } return sum1;} sumA函数实现,如下: int SumA( int a, int n){ int sum2 = 0; int sign = 0; for(int i = 1; i<=n; i++) { sum2 += a; a *= 10; sign += sum2; } return sign;}

May 25, 2021 · 1 min · jiezi

关于c:字符串的输入输出

字符串常识补充:字符串的输出://间接输出字符串且只能输出字符串;空格也被认为是字符串的一部分,只有遇到回车键才会认为字符串输出完。 gets(字符串名称);//通过格局控制符%s输出字符串;遇到空格时会认为字符串到此就完结,空格前面输出的字符都不会被读取到。 scanf("%s",字符串名称);字符串的输入://puts();会主动换行,只能输入字符串 puts(字符串名称);//通过格局控制符%s输入字符串,不能主动换行。 scanf("%s", 字符串名称);

May 20, 2021 · 1 min · jiezi

关于c:函数求水仙花数

要求:1、输出两个数2、用到两个函数://判断number是否是水仙花数,是返回1,否返回0int narcissistic (int number);//打印区间m,n之间的所有水仙花数void PrintN(int m, int n); 输入输出模式: //函数申明 int narcissistic (int number);void PrintN(int m, int n);int main(){ int m, n; scanf("%d %d", &m, &n); if( narcissistic (m)) printf("%d is a narcissistic number\n",m); PrintN(m, n); if( narcissistic(n)) printf("%d is a narcissistic number\n",n); return 0;} //判断number是否是水仙花数,是返回1,否返回0 int narcissistic (int number){ int t = number;//计数 int count = 1;//统计number是几位数 while(t>9){ count++; t/=10; }//设置一个返回值,是水仙花则反judge=1;否则judge=0; int judge = 0; int b = number; int sum = 0; while(b>0){ int a = b%10; b/=10; int p = a;//这一行不会不懂吧->_-> 各个位的位数次方 int j = 1; while(j<count){ p *= a; j++; } sum += p; } if(sum == number){ judge = 1; } return judge;}//打印区间m,n之间所有的水仙花数 void PrintN(int m, int n){ int i; for(i = m+1; i<n; i++){//函数嵌套调用函数 if(narcissistic(i)){ printf("%d\n",i); } } }我犯过谬误的点:(1)遗记申明函数接口了,低级谬误,千万不要忘。不然当前侍奉女友时做事忘东忘西可就有你好受了。。。(2)在统计number是几位数时,判断条件不对。解决:因为我定义的count计数是从1开始,所以只有满足t>9或者t>=10时即可满足判断位数的条件。具体见代码。 ...

May 19, 2021 · 1 min · jiezi

关于c:数字特征值

题目内容:对数字求特征值是罕用的编码算法,奇偶特色是一种简略的特征值。对于一个整数,从个位开始对每一位数字编号,个位是1号,十位是2号,以此类推。这个整数在第n位上的数字记作x,如果x和n的奇偶性雷同,则记下一个1,否则记下一个0。依照整数的程序把对应位的示意奇偶性的0和1都记录下来,就造成了一个二进制数字。比方,对于342315,这个二进制数字就是001101。 这里的计算能够用上面的表格来示意: 你的程序要读入一个非负整数,整数的范畴是[0,1000000],而后依照上述算法计算出示意奇偶性的那个二进制数字,输入它对应的十进制值。 提醒:将整数从右向左合成,数位每次加1,而二进制值每次乘2。 输出格局:一个非负整数,整数的范畴是[0,1000000]。 输入格局:一个整数,示意计算结果。 输出样例:342315 输入样例:13 include<stdio.h>int main() { int num,x,n,t;int i=1;int result=0;scanf("%d",&num);if(num>=0&&num<=1000000){ for(n=1;n<7;n++){ x=num%10; num=num/10; if(x%2==0&&n%2==0){ t=1; }else if(x%2!=0&&n%2!=0){ t=1; }else{ t=0; } t=t*i; i=i*2; result=result+t; }}printf("%d",result);return 0;}

May 14, 2021 · 1 min · jiezi

关于c:c语言中strstr函数的一个注意点

明天说一个之前工作上遇到过的一个问题,也是之前没留神过的一个点。 先看一段代码: #include <stdio.h>#include <string.h>int main(){ char sz1[16] = {0}; char sz2[10] = {0}; char sz3[3] = {0}; strncpy(sz1, "bbAAcc", sizeof(sz1)-1); strcpy(sz3, "AA"); if ( strstr(sz1, sz2) != NULL ) { printf("sz2 is in sz1\n"); } else { printf("sz2 is not in sz1\n"); } if ( strstr(sz1, sz3) != NULL ) { printf("sz3 is in sz1\n"); } else { printf("sz3 is not in sz1\n"); } return 0;}大家猜一猜这段代码会输入什么呢?第二个strstr答案很明确,然而对于第一个strstr的调用,可能会有一点纳闷。 实际上在遇到这个问题之前,我始终很执著的认为,这段代码应该输入如下答案: sz2 is not in sz1sz3 is in sz1那让咱们编译当前执行一下看看: ...

May 11, 2021 · 1 min · jiezi

关于c:C程序打印带有星形图案的-锐角三角形

解题思路绿色部份打印右星形直角三角形红色部份打印右倒置星形直角三角形 /* ============================================================================ Name : LearnC.c Author : SpringSongs Version : 0.0.1 Copyright : SpringSongs Description : C程序打印带有星形图案的锐角三角形, Ansi-style ============================================================================ 打印如下: * ** *** ********* **** *** ** * */#include <stdio.h>int main(){ int i; int j; for (i = 1; i <= 5; i++) { for (j = 0; j < 5 - i; j++) { printf(" "); } for (j = 1; j <= i; j++) { printf("*"); } printf("\n"); } for (i = 1; i <= 5; i++) { for (j = 0 ; j < i; j++) { printf(" "); } for (j = 1; j <= 5-i; j++) { printf("*"); } printf("\n"); }}

May 7, 2021 · 1 min · jiezi

关于c:1C语言学习从源文件如何到可执行文件的

源程序就是一个.txt 的一般文本文件,是经验了哪些过程,变为可执行性文件的呢? 大体上分为四个步骤: 预处理 -> 编译 -> 汇编 -> 链接 四个过程。 对于集成开发环境(IDE,Integrated Development Environment )而言,缩小了环 境配置,合并了流程,使其便于疾速开发。就 Qt 开发环境而言,只需一步即可: unix /linux 环境下,通常没有界面,所以少有集成开发环境。所有的开发均是在命 令行模试下,开发的。以 vim 为编辑器,以 gcc 为编译器为例,演示。

May 6, 2021 · 1 min · jiezi

关于c:C51单片机学习日志

2021/05/01踩坑一开始装置驱动那里就出问题,驱动明明曾经装置,然而辨认不了单片机的接口,问客服,感觉客服也很迷,最初还是解决了。目测要么是USB线接触不良,要么是装置的WIN10驱动没起作用,因为前面还装置了一个XP驱动。 单片机相干概念单片机是单芯片微型计算机的简称,罕用英文缩写_MCU_(Microcontroller Unit)代指单片机。 我所应用的单片机为DIP封装,尽管体积较大,然而不便装配,从而损坏后更换芯片更便捷。 其余两种封装(PLCC,LQFP),尽管体积小,然而更换比拟麻烦,。厂商为_STC_的,这家厂商单片机烧录程序便捷些。 厂商尽管不同,然而内核都是应用的Intel公司的80C51系列内核,_STC_的单片机命名规定如下: 一些零散知识点(C语言)因为C语言曾经学过不少了,就选择性地记了些笔记: 罕用存储单位关系: 中文名称英文名称换算关系比特(字位)bit1B = 8bit字节byte1B = 8bit千字节kibibyte1KB = 1024B兆字节Mebibyte1MB = 1024KB吉字节Gigabyte1GB = 1024MB常说的100M宽带,并非是100MB而是100Mbit,所以换算成下载速度须要除以8,换算成字节单位,也就是12.5MB。 数据类型所占位数范畴bit10~1unsigned char80~255char8-128~127unsigned int160~65535int16-32768~32767unsigned long320~429467295float323.4e-38~3.4e38double641.7e-308~1.7e308其中,有符号位的最高位是符号位,0示意负数,1示意正数。 一些零散知识点(电子电路根底)单片机是一种数字集成芯片,数字电路中只有两种电平:高电平和低电平。 高电平为5V,低电平为0V。 TTL电平信号被利用的较多,因为数字电路中通常数据表示采纳二进制,5V等价于逻辑1,0V等价于逻辑0。TTL电平规定高电平输入电压>2.4V,低电平输入电压<0.4V,这是因为理论发送信号时,可能并不一定达到高电平和低电平的准确值,所以只有在这个稳定范畴内都是会被认定为高电平和低电平的。 而计算机串口应用的是RS232电平。 高电平为-12V,低电平为+12V,单片机与计算机串口通信时须要应用电平转换芯片,把RS232电平转换为TTL电平后,单片机能力辨认。然而电路USB接口的供电口是5V,充电宝也是,所以能够用充电宝和电脑USB接口来给单片机供电。 I/O口是根本输出Input/输入Output接口,单片机对外围设备的管制都是通过I/O口来进行的(输入高低电平)。接管内部管制也是通过I/O口来读取内部电压信号。 选电容时,个别选耐压值比利用零碎高2-3倍的电容。 有极性的电容,长脚为正,短脚为负。 电容上色彩面积小的局部所对应的为负极,反之为正极(对于直插和贴片电解电容)。而对于钽电容则相同。 单片机须要运行起来最根本的条件为: 电源单片机芯片晶振电路复位电路 单片机工作的根本时序: 振荡周期:也称时钟周期,是指为单片机提供时钟脉冲信号的振荡源的周期。 机器周期:一个机器蕴含12个时钟周期。在一个机器周期内,CPU能够实现一个独立的操作。 电路原理图中: 电阻上的471字样代表电阻阻值为47*10^1 电容上的105字样代表电容大小为10*10^5 pF 网络标号雷同的两点示意这两点理论电气相连(有导线连贯)。点亮LED灯LED,即发光二极管。其长处是,功耗低,高亮度,色彩艳丽,寿命长。 同样的,LED灯也是长正短负。它的封装形式和电容电阻相似,开发板中用的就是最小的0603封装。贴片式的LED,有小绿点的一端为负极,另一端为正极。 一般发光二极管工作压降为1.6V~2.1V。工作电流为1~20mA。一般发光二极管导通压降通常为0.7V。因为其工作电流较小,通常会再串一个限流电阻。 原理 由原理图可知,要想点亮LED灯,二极管须要处于导通状态,因为电源输出到阳极的信号恒为高电平,那么阴极为低电平能力导通,那么给阴极输出0即可。 实际 踩坑:在建我的项目编译文件时,记得在output里勾选输入hex文件。 暴力点灯 间接别离对须要亮灯的地位赋值0。 #include <reg52.h>sbit LED2 = P1^1;sbit LED3 = P1^2;sbit LED5 = P1^4;void main(){ LED2 = 0; LED3 = 0; LED5 = 0;}多口操作点灯 对P1的8个口一起操作,因为操作时是按位操作,所以赋值的数是二进制数,二进制数位上的0和1就别离代表了低电平和高电平,然而写程序时个别会赋值16进制的数。 二进制高位到低位就对应了P1的8个口从大到小的序号 ...

May 3, 2021 · 3 min · jiezi

关于c:嵌入式软件工程师笔试面试指南ARM体系与架构

哈喽,大家好。我终于回来了!19号刚提交完大论文,就被抓去出差了,折腾了整整一周,26号早晨,才回到学校。鸽了良久都没更新干货了。明天更新一篇对于Arm的口试面试题目,文章内容已同步更新在github。[TOC] ARM体系与架构 硬件根底NAND FLASH 和NOR FLASH异同?不同点 类别NORNAND读快 像拜访SRAM一样,能够随机拜访任意地址的数据;如:unsighed short pwAddr = (unsighed short )0x02;unisignded short wVal;wVal = *pwAddr快,有严格的时序要求,须要通过一个函数能力读取数据,先发送读命令->发送地址->判断nandflash是否就绪->读取一页数据读命令、发送地址、判断状态、读数据都是通过操作寄存器实现的,如数据寄存器NFDATA写慢,写之前须要擦除,因为写只能是1->0,擦除能够使0->1快,写之前须要擦除,因为写只能是1->0,擦除能够使0->1擦除十分慢(5S)快(3ms)XIP代码能够间接在NOR FLASH上运行NO可靠性比拟高,位反转的比例小于NAND FLASH的10%比拟低,位反转比拟常见,必须有校验措施接口与RAM接口雷同,地址和数据总线离开I/O接口可擦除次数10000~100000100000~1000000容量小,1MB~32MB大,16MB~512MB主要用途罕用于保留代码和要害数据用于保留数据价格高低留神:nandflash和norflash的0地址是不抵触的,norflash占用BANK地址,而nandflash不占用BANK地址,它的0地址是外部的。 相同点 1写之前都要先擦除,因为写操作只能使1->0,而擦除动作是为了把所有位都变12擦除单元都以块为单位CPU,MPU,MCU,SOC,SOPC分割与差异?1.CPU(Central Processing Unit),是一台计算机的运算外围和管制外围。CPU由运算器、控制器和寄存器及实现它们之间分割的数据、管制及状态的总线形成。差不多所有的CPU的运作原理可分为四个阶段:提取(Fetch)、解码(Decode)、执行(Execute)和写回(Writeback)。 CPU从存储器或高速缓冲存储器中取出指令,放入指令寄存器,并对指令译码,并执行指令。所谓的计算机的可编程性次要是指对CPU的编程。 2.MPU (Micro Processor Unit),叫微处理器(不是微控制器),通常代表一个功能强大的CPU(暂且了解为增强版的CPU吧),但不是为任何已有的特定计算目标而设计的芯片。这种芯片往往是集体计算机和高端工作站的外围CPU。最常见的微处理器是Motorola的68K系列和Intel的X86系列。 3.MCU(Micro Control Unit),叫微控制器,是指随着大规模集成电路的呈现及其倒退,将计算机的CPU、RAM、ROM、定时计数器和多种I/O接口集成在一片芯片上,造成芯片级的芯片,比方51,avr这些芯片,外部除了CPU外还有RAM,ROM,能够间接加简略的外围器件(电阻,电容)就能够运行代码了,而MPU如x86,arm这些就不能间接放代码了,它只不过是增强版的CPU,所以得增加RAM,ROM。 MCU MPU 最次要的区别就睡是否间接运行代码。MCU有外部的RAM ROM,而MPU是增强版的CPU,须要增加内部RAM ROM才能够运行代码。 4.SOC(System on Chip),指的是片上零碎,MCU只是芯片级的芯片,而SOC是零碎级的芯片,它既MCU(51,avr)那样有内置RAM,ROM同时又像MPU(arm)那样弱小的,不单单是放简略的代码,能够放零碎级的代码,也就是说能够运行操作系统(将就认为是MCU集成化与MPU强解决力各长处二合一)。 5.SOPC(System On a Programmable Chip)可编程片上零碎(FPGA就是其中一种),下面4点的硬件配置是固化的,就是说51单片机就是51单片机,不能变为avr,而avr就是avr不是51单片机,他们的硬件是一次性掩膜成型的,能改的就是软件配置,说白点就是改代码,原本是跑流水灯的,改下代码,变成数码管,而SOPC则是硬件配置,软件配置都能够批改,软件配置跟下面一样,没什么好说的,至于硬件,是能够本人构建的也就是说这个芯片是本人结构进去的,这颗芯片咱们叫“白片”,什么芯片都不是,把硬件配置信息下载进去了,他就是相应的芯片了,能够让他变成51,也能够是avr,甚至arm,同时SOPC是在SOC根底上来的,所以他也是零碎级的芯片,所以记得当把他变成arm时还得加外围ROM,RAM之类的,不然就是MPU了。 什么是穿插编译?在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,咱们就称这种编译器反对穿插编译。这个编译过程就叫穿插编译。简略地说,就是在一个平台上生成另一个平台上的可执行代码。 这里须要留神的是所谓平台,实际上蕴含两个概念:体系结构(Architecture)、操作系统(OperatingSystem)。同一个体系结构能够运行不同的操作系统;同样,同一个操作系统也能够在不同的体系结构上运行。举例来说,咱们常说的x86 Linux平台实际上是Intel x86体系结构和Linux for x86操作系统的统称;而x86 WinNT平台实际上是Intel x86体系结构和Windows NT for x86操作系统的简称。 为什么须要穿插编译?有时是因为目标平台上不容许或不可能装置咱们所须要的编译器,而咱们又须要这个编译器的某些特色;有时是因为目标平台上的资源贫乏,无奈运行咱们所须要编译器;有时又是因为目标平台还没有建设,连操作系统都没有,基本谈不上运行什么编译器。 形容一下嵌入式基于ROM的运行形式和基于RAM的运行形式有什么区别?基于RAM 须要把硬盘和其余介质的代码先加载到ram中,加载过程中个别有重定位的操作。速度比基于ROM的快,可用RAM比基于ROM的少,因为所有的代码,数据都必须寄存在RAM中。基于ROM 速度较基于RAM的慢,因为会有一个把变量,局部代码等从存储器(硬盘,flash)搬移到RAM的过程。可用RAM资源比基于RAM的多;ARM处理器什么是哈佛构造和冯诺依曼结构?定义 冯诺依曼结构釆用指令和数据对立编址,应用同条总线传输,CPU读取指令和数据的操作无奈重叠。 哈佛构造釆用指令和数据独立编址,应用两条独立的总线传输,CPU读取指令和数据的操作能够重叠。 利弊 冯诺依曼结构次要用于通用计算机领域,须要对存储器中的代码和数据频繁的进行批改,对立编址有利于节约资源。 哈佛构造次要用于嵌入式计算机,程序固化在硬件中,有较高的可靠性、运算速度和较大的吞吐。 什么是ARM流水线技术?流水线技术通 过多个性能部件并行工作来缩短程序执行工夫,进步处理器核的效率和吞吐率,从而成为微处理器设计中最为重要的技术之一。ARM7处理器核应用了典型三级流水线的冯·诺伊曼构造,ARM9系列则采纳了基于五级流水线的哈佛构造。通过减少流水线级数简化了流水线各级的逻辑,进一步提高了处理器的性能。 PC代表程序计数器,流水线应用三个阶段,因而指令分为三个阶段执行:1.取指(从存储器装载一条指令);2.译码(辨认将要被执行的指令);3.执行(解决指令并将后果写回寄存器)。而R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的指令或正在“译码”的指令。一般来说,人们习惯性约定将“正在执行的指令作为参考点”,称之为以后第一条指令,因而PC总是指向第三条指令。当ARM状态时,每条指令为4字节长,所以PC始终指向该指令地址加8字节的地址,即:PC值=以后程序执行地位+8; ARM指令是三级流水线,取指,译指,执行,同时执行的,当初PC指向的是正在取指的地址(下一条指令),那么cpu正在译指的指令地址是PC-4(假如在ARM状态下,一个指令占4个字节),cpu正在执行的指令地址是PC-8,也就是说PC所指向的地址和当初所执行的指令地址相差8。 ...

April 28, 2021 · 3 min · jiezi

关于c:C-代码是如何跑起来的

上一篇「CPU 提供了什么」中,咱们理解了物理的层面的 CPU,为咱们提供了什么。 本篇,咱们介绍下高级语言「C 语言」是如何在物理 CPU 下面跑起来的。 C 语言提供了什么C 语言作为高级语言,为程序员提供了更敌对的表达方式。在我看来,次要是提供了以下形象能力: 变量,以及延长进去的简单构造体咱们能够基于变量来形容简单的状态。函数咱们能够基于函数,把简单的行为逻辑,拆分到不同的函数里,以简化简单的逻辑以。以及,咱们能够复用雷同目标的函数,事实世界里大量的根底库,简化了程序员的编码工作。示例代码构建一个良好的示例代码,能够很好帮忙咱们去了解。上面的示例里,咱们能够看到 变量 和 函数 都用上了。 #include "stdio.h"int add (int a, int b) { return a + b;}int main () { int a = 1; int b = 2; int c = add(a, b); printf("a + b = %d\n", c); return 0;}编译执行毫无意外,咱们失去了冀望的 3。 $ gcc -O0 -g3 -Wall -o simple simple.c$ ./simplea + b = 3汇编代码咱们还是用 objdump 来看看,编译器生成了什么代码: 变量局部变量,包含函数参数,全副被压入了 栈 里。函数函数自身,被独自编译为了一段机器指令函数调用,被编译为了 call 指令,参数则是函数对应那一段机器指令的第一个指令地址。$ objdump -M intel -j .text -d simple# 截取其中最重要的局部000000000040052d <add>: 40052d: 55 push rbp 40052e: 48 89 e5 mov rbp,rsp 400531: 89 7d fc mov DWORD PTR [rbp-0x4],edi 400534: 89 75 f8 mov DWORD PTR [rbp-0x8],esi 400537: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8] 40053a: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] 40053d: 01 d0 add eax,edx 40053f: 5d pop rbp 400540: c3 ret0000000000400541 <main>: 400541: 55 push rbp 400542: 48 89 e5 mov rbp,rsp 400545: 48 83 ec 10 sub rsp,0x10 400549: c7 45 fc 01 00 00 00 mov DWORD PTR [rbp-0x4],0x1 400550: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2 400557: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8] 40055a: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 40055d: 89 d6 mov esi,edx 40055f: 89 c7 mov edi,eax 400561: e8 c7 ff ff ff call 40052d <add> 400566: 89 45 f4 mov DWORD PTR [rbp-0xc],eax 400569: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc] 40056c: 89 c6 mov esi,eax 40056e: bf 20 06 40 00 mov edi,0x400620 400573: b8 00 00 00 00 mov eax,0x0 400578: e8 93 fe ff ff call 400410 <printf@plt> 40057d: b8 00 00 00 00 mov eax,0x0 400582: c9 leave 400583: c3 ret 400584: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 40058b: 00 00 00 40058e: 66 90 xchg ax,ax函数内的局部变量,为什么会放入栈空间呢?这个刚好和局部变量的作用域关联起来了: ...

April 24, 2021 · 3 min · jiezi

关于c:辗转相除法求整数的最大公约数和最小公倍数

求整数的最大公约数和最小公倍数 include<stdio.h>int main(){ int a,b;int t;int m,n;scanf("%d %d",&a,&b);m=a;n=b;/*a=12,b=18a b t12 18 1218 12 612 6 06 0 */while(b!=0){ t=a%b; a=b; b=t;}printf("最大公约数为%d",a);printf("最小公倍数为%d",m*n/a);return 0;}

April 23, 2021 · 1 min · jiezi

关于c:整数分解

题目:输出一个整数,正序输入它的每一个数字。如 输出:12345 输入:1 2 3 4 5 输出:700 输入:7 0 0 留神最初一个数字后无空格。代码如下: #include<stdio.h>int main(){ //输出 int input; scanf("%d",&input); int mask=1; //从新创立一个变量用来存input值 int testValue = input; //循环:当testValue大于9或大于等于10,满足循环要求 while(testValue>9){ //如果整数有5位,那么mask最终就会为10000,用来做最开始的除数 testValue /= 10; mask *= 10; } // printf("mask = %d,testValue = %d",mask,testValue); //能够用来测试以后代码mask值 do{ //用来将最高位分来到:如1434/1000=1; 434/100=4; 34/10=3;4/1=4; int receive = input/mask; //使最初一个数字后没有空格 if(mask>9){ printf(" "); } //用来去除最高位1434%1000=434;。。。 input = input%mask; //放大mask,求整数对应位数的数字 mask /= 10; } while(mask>0); return 0;}

April 23, 2021 · 1 min · jiezi

关于c:c语言结构体字节对齐详解

1.什么是字节对齐在c语言的构造体外面个别会依照某种规定去进行字节对齐。 咱们先看一段代码: struct st1{ char name; double age; char sex;};//32位下 sizeof(struct st1) = 16//64位下 sizeof(struct st1) = 24struct st2{ char a; char b; char c;};//32位和64位下, sizeof(struct st2)都是3个字节从以上后果能够看出,构造体st1在32位下是依照4个字节来对齐的,在64位下则是依照8个字节来对齐的,构造体st2则不论32位还是64位则都是依照1个字节对齐的。 那么咱们能够总结出对齐规定如下: 在所有构造体成员的字节长度都没有超出操作系统根本字节单位(32位操作系统是4,64位操作系统是8)的状况下,依照构造体中字节最大的变量长度来对齐;若构造体中某个变量字节超出操作系统根本字节单位,那么就依照零碎字节单位来对齐。留神:并不是32位就间接依照4个字节对齐,64位依照8个字节对齐。2.为什么要有字节对齐首先遍及一点小常识,cpu一次能读取多少内存要看数据总线是多少位,如果是16位,则一次只能读取2个字节,如果是32位,则能够读取4个字节,并且cpu不能跨内存区间拜访。 假如有这样一个构造体如下: struct st3{ char a; int b;};//那么依据咱们第1节所说的规定,在32位零碎下,它就应该是8个字节的。假如地址空间是相似上面这样的: 在没有字节对齐的状况下,变量a就是占用了0x00000001这一个字节,而变量b则是占用了0x00000002~0x000000005这四个字节,那么cpu如果想从内存中读取变量b,首先要从变量b的开始地址0x00000002读到0x0000004,而后再读取一次0x00000005这个字节,相当于读一个int,cpu从内存读取了两次。 而如果进行字节对齐的话,变量a还是占用了0x00000001这一个字节,而变量b则是占用了0x00000005~0x00000008这四个字节,那么cpu要读取变量b的话,就间接一次性从0x00000005读到0x00000008,就一次全副读取进去了。 所以说,字节对齐的根本原因其实在于cpu读取内存的效率问题,对齐当前,cpu读取内存的效率会更快。然而这里有个问题,就是对齐的时候0x00000002~0x00000004这三个字节是节约的,所以字节对齐实际上也有那么点以空间换工夫的意思,具体写代码的时候怎么抉择,其实是看集体的。 3.手动设置对齐什么状况下须要手动设置对齐: 设计不同CPU下的通信协议,比方两台服务器之间进行网络通信,共用一个构造体时,须要手动设置对齐规定,确保两边构造体长度始终;编写硬件驱动程序时寄存器的构造;手动设置对齐形式有两种: 代码里增加预编译标识://用法如下#pragma pack(n)//示意它前面的代码都依照n个字节对齐struct st3{ char a; int b;};#pragma pack()//勾销依照n个字节对齐,是对#pragma pack(n)的一个反向操作//这里计算sizeof(st3)=5下面这两行其实就相似于开车的时候,走到某一段路的时候,发现一个限速60公里的指示牌,过了那一段路当前,又会有解除限速60公里的指示牌。 定义构造体时://用法如下struct bbb{ char a; int b;}__attribute__((packed));//间接依照理论占用字节来对齐,其实就是相当于依照1个字节对齐了//这里计算sizeof(st3)=54.构造体比拟办法能够应用内存比拟函数memcpy进行构造体比拟,但因为构造体对齐可能会有填充位不统一的状况,此时须要留神: 设置为1个字节对齐,使它没有空位;当时对构造体进行初始化;memcpy(char *dest, const char* src, int len); //头文件#include<string.h>

April 22, 2021 · 1 min · jiezi

关于c:计算数组交集

两个数组计算交加题目形容给定两个数组,编写一个函数来计算它们的交加。 示例 示例 1:输出:nums1 = [1,2,2,1], nums2 = [2,2]输入:[2]示例 2:输出:nums1 = [4,9,5], nums2 = [9,4,9,8,4]输入:[9,4]阐明: 输入后果中的每个元素肯定是惟一的。 咱们能够不思考输入后果的程序。办法办法一:bitmap + 与运算1.创立两个bitmap:bitmap1、bitmap2,别离用 nums1、nums2 来初始化 bitmap1、bitmap2:将 nums1中的值作为 bitmap1 的索引,并将该索引对应的值置为 1;bitmap2 同理2.创立一个 result数组,用于保留后果3.bitmap1 和 bitmap2 一一元素进行 与运算,若 与运算后果为1,则将值赋值给 result,同时 (*resultSize)++,result中为 1 的元素就是交加,*resultSize就是交加元素个数毛病:三个大数组,空间开销大办法二:排序 + 比拟1.qsort对两个数组排序2.创立result数组,大小为 nums1Size 和 nums2Size 中较小的那个2.一一比拟 nums1[i]、nums2[j]中的元素 若想等 并且 nums1[i] 不等于 prev,则将 nums1[i] 保留至result中,i++,j++ 若不相等,将 nums1[i]、nums2[j] 中较小的值得索引向后偏移代码bitmap + 与运算#define MAX 2048/** * Note: The returned array must be malloced, assume caller calls free(). */int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){ char *map1 = NULL; char *map2 = NULL; int index = 0; int *result = NULL; *returnSize = 0; map1 = (char *)malloc(MAX*sizeof(char)); if (NULL == map1) { return NULL; } memset(map1, 0x00, MAX); map2 = (char *)malloc(MAX*sizeof(char)); if (NULL == map2) { free(map1); return NULL; } memset(map2, 0x00, MAX); for(index = 0; index < nums1Size; index++) { map1[nums1[index]] = 1; } for(index = 0; index < nums2Size; index++) { map2[nums2[index]] = 1; } result = (int *)malloc(sizeof(int)*MAX); if (NULL == result) { free(map1); return NULL; } memset(result, 0x00, MAX); for (index = 0; index < MAX; index++) { if (map1[index] & map2[index]) { result[*returnSize] = index; (*returnSize)++; } } free(map1); free(map2); return result;}排序int *cmp(const void * a, const void * b){ return *(int *)a - *(int *)b;}/** * Note: The returned array must be malloced, assume caller calls free(). */int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){ int *result = NULL; int i = 0; int j = 0; int pre; /* 这里不能初始化为 0,否则遇到元素值为 0 的时候会出错 */ *returnSize = 0; result = (int *)malloc(sizeof(int) * (nums1Size < nums2Size ? nums1Size : nums2Size)); if (NULL == result) { return NULL; } qsort(nums1, nums1Size, sizeof(int), cmp); qsort(nums2, nums2Size, sizeof(int), cmp); while (i < nums1Size && j < nums2Size) { if (nums1[i] == nums2[j] && pre != nums1[i]) { result[(*returnSize)++] = nums1[i]; pre = nums1[i]; i++; j++; } else { nums1[i] < nums2[j] ? i++ : j++; } } return result;}int main(){ return 0;}

April 19, 2021 · 2 min · jiezi

关于c:const-和指针

const 和指针const 也能够和指针变量一起应用,这样能够限度指针变量自身,也能够限度指针指向的数据。const 和指针一起应用会有几种不同的程序,如下所示: const int *p1;int const *p2;int * const p3;在最初一种状况下,指针是只读的,也就是 p3 自身的值不能被批改;在后面两种状况下,指针所指向的数据是只读的,也就是 p1、p2 自身的值能够批改(指向不同的数据),但它们指向的数据不能被批改。 当然,指针自身和它指向的数据都有可能是只读的,上面的两种写法可能做到这一点: 纯文本复制 const int * const p4;int const * const p5;const 和指针联合的写法多少有点让初学者摸不着头脑,大家能够这样来记忆:const 离变量名近就是用来润饰指针变量的,离变量名远就是用来润饰指针指向的数据,如果近的和远的都有,那么就同时润饰指针变量以及它指向的数据。

April 16, 2021 · 1 min · jiezi

关于c:简单的逆序的三位数

思路:拆散出每个数字如将整数217分来到即为:712/100=>7712/10%10=>1 或者 712%100/10=>1712%10=>2 将原数的百位变成个位,个位变成百位,即2100+110+7 = 217 代码如下: int a;scanf("%d", &a);int b = a/100 + (a%100/10)*10 + (a%10)*100;printf("%d",b);return 0;或者 int n,a,b,c,m;scanf("%d",&n);a = n/100;b = n/10%10;c = n%10;m = c*100 + b *10 + a;printf("%d\n",m);return 0;留神:题目要求输出700时,输入为7,而非007,所以printf("%d%d%d\n",c,b,a);是不行的

April 10, 2021 · 1 min · jiezi

关于asm:实操体验-CPU-的流水线多发射

前言前文 <一行机器指令感触下内存操作到底有多慢> 中,咱们体验到了 CPU 流水线阻塞带来的数量级性能差别。过后只是依据机器码,剖析推断进去的,这次咱们做一些更小的试验来剖析验证。 入手之前,咱们先理解一些背景。在 \<CPU 提供了什么> 一文中介绍过,CPU 对外提供了运行机器指令的能力。那 CPU 又是如何执行机器指令的呢? CPU 是如何执行机器指令的一条机器指令,在 CPU 外部也分为好多个细分步骤,逻辑上来说能够划分为这么五个阶段: 获取指令解析指令执行执行拜访内存后果写回流水线作业例如间断的 ABCD 四条指令,CPU 并不是先残缺的执行完 A,才会开始执行 B;而是 A 取指令实现,则开始解析指令 A,同时持续取指令 B,顺次类推,造成了流水线作业。 现实状况下,充分利用 CPU 硬件资源,也就是让流水线上的每个器件,始终放弃工作。然而实际上,因为各种起因,CPU 没法残缺的跑满流水线。 比方: 跳转指令,可能跳转执行另外的指令,并不是固定的程序执行。例如这样的跳转指令,可能接下来就须要跳转到 400553 的指令。je 400553对于这种分支指令,CPU 有分支预测技术,基于之前的后果预测本次分支的走向,尽量减少流水线阻塞。 数据依赖,前面的指令,依赖后面的指令。例如上面的两条指令,第二条指令的操作数 r8 依赖于第一条指令的后果。mov r8,QWORD PTR [rdi]add r8,0x1这种时候,CPU 会利用操作数前推技术,尽量减少阻塞期待。 多发射古代简单的 CPU 硬件,其实也不只有一条 CPU 流水线。简略从逻辑上来了解,能够假如是有多条流水线,能够同时执行指令,然而也并不是简略的反复整个流水线上的所有硬件。 多发射能够了解为 CPU 硬件层面的并发,如果两条指令没有前后的程序依赖,那么是齐全能够并发执行的。CPU 只须要保障执行的最终后果是合乎冀望的就能够,其实很多的性能优化,都是这一个准则,通过优化执行过程,然而放弃最终后果统一。 实际体验实践须要联合实际,有理论的体验,能力更清晰的了解原理。 这次咱们用 C 内联汇编来构建了几个用例来领会这其中的差别。 基准用例#include <stdio.h>void test(long *a, long *b, long *c, long *d) { __asm__ ( "mov r8, 0x0;" "mov r9, 0x0;" "mov r10, 0x0;" "mov r11, 0x0;" ); for (long i = 0; i <= 0xffffffff; i++) { } __asm__ ( "mov [rdi], r8;" "mov [rsi], r9;" "mov [rdx], r10;" "mov [rcx], r11;" );}int main(void) { long a = 0; long b = 0; long c = 0; long d = 0; test(&a, &b, &c, &d); printf("a = %ldn", a); printf("b = %ldn", b); printf("c = %ldn", c); printf("d = %ldn", d); return 0;}咱们用如下命令才执行,只须要 1.38 秒。留神,须要应用 -O1 编译,因为 -O0 下,基准代码自身的开销也会很大。 ...

April 5, 2021 · 3 min · jiezi

关于c:使程序语言支持变量

上面咱们来让计算器程序反对变量的应用,使得程序能够设置和获取变量的值。从当初开始我将不掩藏咱们要实现的是一个程序语言,因为出自计算器所以命名为 bkcalclang 这次的代码以上一篇《使计算器反对语句块》的代码为根底编写,如果发现不相熟当下的内容能够回顾一下之前的篇章。 代码清单【go语言为例】package mainimport ( "fmt" "strconv" "io/ioutil" "./bklexer")var ValueDict map[string]float64type Node interface { Eval() float64}type Block struct { statements []Node}func NewBlock() *Block { return &Block{}}func (block *Block) AddStatement(statement Node) { block.statements = append(block.statements, statement)}func (block *Block) Eval() { for _, statement := range block.statements { statement.Eval() }}type Number struct { value float64}func NewNumber(token *BKLexer.Token) *Number { value, _ := strconv.ParseFloat(token.Source, 64) return &Number{value: value}}func (number *Number) Eval() float64 { return number.value}type Name struct { name string}func NewName(token *BKLexer.Token) *Name { return &Name{name: token.Source}}func (name *Name) Eval() float64 { if value, found := ValueDict[name.name]; found { return value; } return 0.}type BinaryOpt struct { opt string lhs Node rhs Node}func NewBinaryOpt(token *BKLexer.Token, lhs Node, rhs Node) *BinaryOpt { return &BinaryOpt{opt: token.Source, lhs: lhs, rhs: rhs}}func (binaryOpt *BinaryOpt) Eval() float64 { lhs, rhs := binaryOpt.lhs, binaryOpt.rhs switch binaryOpt.opt { case "+": return lhs.Eval() + rhs.Eval() case "-": return lhs.Eval() - rhs.Eval() case "*": return lhs.Eval() * rhs.Eval() case "/": return lhs.Eval() / rhs.Eval() } return 0}type Assign struct { name string value Node}func NewAssign(token *BKLexer.Token, value Node) *Assign { return &Assign{name: token.Source, value: value}}func (assign *Assign) Eval() float64 { value := assign.value.Eval() ValueDict[assign.name] = value return value}type Echo struct { value Node}func NewEcho(value Node) *Echo { return &Echo{value: value}}func (echo *Echo) Eval() float64 { value := echo.value.Eval() fmt.Println(":=", value) return value}func parse(lexer *BKLexer.Lexer) *Block { block := NewBlock() token := lexer.NextToken() for token.TType == BKLexer.TOKEN_TYPE_NEWLINE { token = lexer.NextToken() } for token.TType != BKLexer.TOKEN_TYPE_EOF { statement := parse_statement(lexer) if statement == nil { return nil; } token = lexer.GetToken() if token.TType != BKLexer.TOKEN_TYPE_NEWLINE && token.TType != BKLexer.TOKEN_TYPE_EOF { return nil; } block.AddStatement(statement) for token.TType == BKLexer.TOKEN_TYPE_NEWLINE { token = lexer.NextToken() } } return block}func parse_statement(lexer *BKLexer.Lexer) Node { token := lexer.GetToken() if token.Name == "SET" { name := lexer.NextToken() if name.Name != "NAME" { return nil } token = lexer.NextToken() if token.Name != "ASSIGN" { return nil } lexer.NextToken() value := parse_binary_add(lexer) if value == nil { return nil } return NewAssign(name, value) } else if token.Name == "ECHO" { lexer.NextToken() value := parse_binary_add(lexer) if (value == nil) { return nil } return NewEcho(value) } return parse_binary_add(lexer)}func parse_binary_add(lexer *BKLexer.Lexer) Node { lhs := parse_binary_mul(lexer) if lhs == nil { return nil } token := lexer.GetToken() for token.Source == "+" || token.Source == "-" { lexer.NextToken() rhs := parse_binary_mul(lexer) if rhs == nil { return nil } lhs = NewBinaryOpt(token, lhs, rhs) token = lexer.GetToken() } return lhs}func parse_binary_mul(lexer *BKLexer.Lexer) Node { lhs := factor(lexer) if lhs == nil { return nil } token := lexer.GetToken() for token.Source == "*" || token.Source == "/" { lexer.NextToken() rhs := factor(lexer) if rhs == nil { return nil } lhs = NewBinaryOpt(token, lhs, rhs) token = lexer.GetToken() } return lhs}func factor(lexer *BKLexer.Lexer) Node { token := lexer.GetToken() if token.Name == "LPAR" { lexer.NextToken() expr := parse_binary_add(lexer) if expr == nil { return nil } token := lexer.GetToken() if token.Name != "RPAR" { return nil } lexer.NextToken() return expr } if token.Name == "NUMBER" { number := NewNumber(token) lexer.NextToken() return number } if token.Name == "NAME" { name := NewName(token) lexer.NextToken() return name } return nil}func main() { lexer := BKLexer.NewLexer() lexer.AddRule("\\d+\\.?\\d*", "NUMBER") lexer.AddRule("[\\p{L}\\d_]+", "NAME") lexer.AddRule("\\+", "PLUS") lexer.AddRule("-", "MINUS") lexer.AddRule("\\*", "MUL") lexer.AddRule("/", "DIV") lexer.AddRule("\\(", "LPAR") lexer.AddRule("\\)", "RPAR") lexer.AddRule("=", "ASSIGN") lexer.AddIgnores("[ \\f\\t]+") lexer.AddIgnores("#[^\\r\\n]*") lexer.AddReserve("set") lexer.AddReserve("echo") bytes, err := ioutil.ReadFile("../test.txt") if err != nil { fmt.Println("read faild") return } code := string(bytes) lexer.Build(code) result := parse(lexer) if result == nil { fmt.Println("null result") return } ValueDict = make(map[string]float64) result.Eval()}引入须要应用的包import ( "fmt" "strconv" "io/ioutil" "./bklexer")fmt 打印输出strconv 字符串转换io/ioutil 读取文件./bklexer 用于词法解析申明用于存储变量值的字典var ValueDict map[string]float64咱们会应用一个map类型的对象来存取值,并以此实现变量赋值和取值的操作。 ...

April 2, 2021 · 5 min · jiezi

关于c:使用malloccallocfree和realloc在C中进行动态内存分配

因为C是一种结构化语言, 因而它具备一些固定的编程规定。其中之一包含更改数组的大小。数组是存储在间断内存地位的我的项目的汇合。 能够看出, 上述数组的长度(大小)为9。然而, 如果须要更改此长度(大小), 该怎么办。例如, 如果存在只须要在此数组中输出5个元素的状况。在这种状况下, 残余的4个索引只会节约该数组中的内存。因而须要将数组的长度(大小)从9缩小到5。 采取另一种状况。在这里, 有9个元素组成的数组, 所有9个索引均已填充。然而须要在此数组中再输出3个元素。在这种状况下, 还须要3个索引。因而, 阵列的长度(大小)须要从9更改为12。 此过程称为C中的动态内存调配. 因而, C动态内存调配能够定义为在运行时更改数据结构(如Array)的大小的过程。 C提供了一些性能来实现这些工作。 C下定义了4个提供的库函数<stdlib.h>头文件, 以不便C编程中的动态内存调配。他们是: malloc()calloc()自在()realloc()让咱们更具体地钻研它们。 C malloc()办法" malloc"or"内存调配"C语言中的办法用于动态分配具备指定大小的单个大内存块。它返回void类型的指针, 该指针能够转换为任何模式的指针。它应用默认垃圾值初始化每个块。 语法如下: ptr = (cast-type*) malloc(byte-size)例如: ptr =(int )malloc(100 sizeof(int));因为int的大小为4个字节, 因而此语句将调配400个字节的内存。并且, 指针ptr保留调配的存储器中的第一个字节的地址。 如果空间有余, 调配将失败并返回NULL指针。 例子: #include <stdio.h>#include <stdlib.h> int main(){ // This pointer will hold the // base address of the block created int * ptr; int n, i; // Get the number of elements for the array n = 5; printf ( "Enter number of elements: %dn" , n); // Dynamically allocate memory using malloc() ptr = ( int *) malloc (n * sizeof ( int )); // Check if the memory has been successfully // allocated by malloc or not if (ptr == NULL) { printf ( "Memory not allocated.n" ); exit (0); } else { // Memory has been successfully allocated printf ( "Memory successfully allocated using malloc.n" ); // Get the elements of the array for (i = 0; i < n; ++i) { ptr[i] = i + 1; } // Print the elements of the array printf ( "The elements of the array are: " ); for (i = 0; i < n; ++i) { printf ( "%d, " , ptr[i]); } } return 0;}输入如下: ...

March 31, 2021 · 5 min · jiezi

关于c:HackingC-机翻阅读记录-Chapter3Custom-Types-–-Part-1

集体笔记向+google机翻(机翻的确很多细节形容不清,看不懂记得参考原文)原文参见:https://hackingcpp.com/cpp/be... Custom Types – Part 1Simple Classes / Custom TypesPointersDestructorsExceptionsNoexcept

March 30, 2021 · 1 min · jiezi

关于c:如何在C语言中动态分配2D数组代码示例

以下是在堆上创立2D数组(或动态分配2D数组)的不同办法。 在以下示例中, 咱们思考了"[R‘作为行数, ‘C‘作为列数, 咱们创立了一个2D数组, 其中r = 3, c = 4和以下值 1 2 3 4 5 6 7 8 9 10 11 121)应用单个指针:一种简略的办法是应用简略的指针算法调配大小为r * c的存储块和拜访元素。 #include <stdio.h>#include <stdlib.h> int main(){ int r = 3, c = 4; int *arr = ( int *) malloc (r * c * sizeof ( int )); int i, j, count = 0; for (i = 0; i < r; i++) for (j = 0; j < c; j++) *(arr + i*c + j) = ++count; for (i = 0; i < r; i++) for (j = 0; j < c; j++) printf ( "%d " , *(arr + i*c + j)); /* Code for further processing and free the dynamically allocated memory */ return 0;}输入如下: ...

March 29, 2021 · 3 min · jiezi

关于c:C-系统标准库

c零碎的规范库里的syscall零碎调用号都在iOS零碎库/usr/include里的sys/syscall.h里

March 29, 2021 · 1 min · jiezi

关于c++:C-STL标准库概述容器

一、定义STL,英文全称 sdard template library,中文可译为规范模板库或者泛型库,其蕴含有大量的模板类和模板函数,是 C++ 提供的一个根底模板的汇合,用于实现诸如输出/输入、数学计算等性能。简略来讲,STL是一些容器、算法和其余一些组件的汇合。二、组成构造STL是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 局部形成,其中前面 4 局部是为前 2 局部服务的,它们各自的含意如下图所示。 C++ 规范中,STL被组织为 13 个头文件。 三、容器容器(container)是用于存放数据的类模板。可变长数组、链表、均衡二叉树等数据结构在 STL 中都被实现为容器。容器中能够寄存根本类型的变量,也能够寄存对象。对象或根本类型的变量被插入容器中时,理论插入的是对象或变量的一个复制品。容器分为两大类,即程序容器和关联容器;也能够分为三大类:序列容器、排序容器和哈希容器。1、程序容器(序列容器) 程序容器或序列容器,其特点就是元素在容器中的地位同元素的值无关,即容器不会对存储的元素进行排序。将元素插入容器时,指定在什么地位(尾部、头部或两头某处)插入,元素就会位于什么地位。STL规范库中所有的序列式容器,包含 array、vector、deque、list 和 forward_list。2、关联容器(排序容器、有序关联容器) 关联容器分为 set(汇合)和 map(映射表)两大类,及其衍生体 multiset 和 multimap。这些容器的底层机制均以 RB-tree(红黑树)实现,均衡二叉树中的一种(还包含AVL-tree、AA-tree)。RB-tree 也是一个独立容器,但并不凋谢应用。SGI STL 还提供一个不在规范规格的关联式容器 hash_table(散列表),以及以 hash_table 为底层机制而实现的 hash_set散列汇合、hash_map散列映射表、hash_multiset散列多键汇合、hash_multimap散列多键映射表。关联容器中每个元素都有一个键值key和一个实值value。关联容器内的元素是排序的。插入元素时,容器会按肯定的排序规定将元素放到适当的地位上,因而插入元素时不能指定地位。默认状况下,关联容器中的元素是从小到大排序(或按关键字从小到大排序)的,而且用`<`运算符比拟元素或关键字大小。因为是排好序的,所以关联容器在查找时具备十分好的性能。3、无序关联容器(哈希容器) 无序关联式容器,它们常被称为“无序容器”、“哈希容器”或者“无序关联容器”。无序容器是 C++ 11 规范才正式引入到 STL 规范库中的,如果要应用该类容器,则必须抉择反对 C++ 11 规范的编译器。和关联式容器一样,无序容器也应用键值对(pair 类型)的形式存储数据。然而它们有着实质上的不同:关联式容器的底层实现采纳的树存储构造,更确切的说是红黑树结构;无序容器的底层实现采纳的是哈希表的存储构造。C++ STL 底层采纳哈希表实现无序容器时,会将所有数据存储到一整块间断的内存空间中,并且当数据存储地位发生冲突时,解决办法选用的是“链地址法”(又称“开链法”)。基于底层实现采纳了不同的数据结构,因而和关联式容器相比,无序容器具备以下 2 个特点:①无序容器外部存储的键值对是无序的,各键值对的存储地位取决于该键值对中的键;②和关联式容器相比,无序容器善于通过指定键查找对应的值(均匀工夫复杂度为 O(1));但对于应用迭代器遍历容器中存储的元素,无序容器的执行效率则不如关联式容器。和关联式容器一样,无序容器只是一类容器的统称,其蕴含有 4 个具体容器,别离为 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset。这4种无序容器的名称,仅是在关联式容器名称的根底上,增加了 "unordered_"。以 map 和 unordered_map 为例,其实它们仅有一个区别,即 map 容器内存会对存储的键值对进行排序,而 unordered_map 不会。也就是说,C++ 11 规范的 STL 中,在已提供有 4 种关联式容器的根底上,又新增了各自的“unordered”版本(无序版本、哈希版本),进步了查找指定元素的效率。理论场景中如果波及大量遍历容器的操作,倡议首选关联式容器;反之,如果更多的操作是通过键获取对应的值,则应首选无序容器。留神,因为哈希容器直到 C++ 11 才被正式纳入 C++ 规范程序库,而在此之前,“民间”流传着 hash_set、hash_multiset、hash_map、hash_multimap 版本,不过该版本只能在某些反对 C++ 11 的编译器下应用(如 VS),有些编译器(如 gcc/g++)是不反对的。4、容器适配器 ...

March 26, 2021 · 1 min · jiezi

关于c:C程序打印带有星形图案的锐角三角形

解题思路绿色部份打印的是左直角三角形 红色部份打印的是左倒置直角三角形 /*打印如下:************************* */#include <stdio.h>int main(void){ int i; int j; for (i = 1; i <= 5; i++) { for (j = 1; j <= i; j++) { printf("*"); } printf("\n"); } for (i = 1; i <= 5; i++) { for (j = 1; j <= 5 - i; j++) { printf("*"); } printf("\n"); } return 0;}

March 24, 2021 · 1 min · jiezi

关于loadrunner:Error-27723-Step-download-timeout-120-seconds-has-expired

软件LoadRunner Controller(我的LoadRunner是12.55版本的) 报错Action.c(116): Error -27723: Step download timeout (120 seconds) has expired when downloading resource "https://ieonline.microsoft.com/iedomainsuggestions/ie11/suggestions.zh-CN". Set the "Step Timeout caused by resources is a warning" Run-Time Setting to Yes/No to have this message as a warning/error, respectively [MsgId: MERR-27723]解决关上Runtime Setting   单击Internet Protocol下的Preferences。单击图中的Options按钮   顺次批改HTTP下的HTTP-request connect timeout(sec)、HTTP-request receive timeout(sec)以及General下的Step download timeout(sec)的值120为600或更大的数字 补充如果你方才曾经设置过了并运行了脚本A,当初你运行脚本B时可能还会报这个错,Controller仿佛不会保留你的Runtime Setting设置,也就是说即使你这次设置过了,将120改为600了,下次它可能还会变回120 参考Step download timeout (120 seconds)是什么问题?

March 23, 2021 · 1 min · jiezi

关于c:用C程序打印空心星形金字塔

解题思路用10*10网格 第1行打印9个空格 第2行打印8个空格 第3行打印7个空格 以此类推 从第1行开始列如果(j==1||j==i)则打印星与空格,否则打印两个空格,最初一行打印10个星和空格 /*打印如下: * * * * * * * * * * * * * * * * ** * * * * * * * * * */#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 = 1; j <= i; j++) { if (j == 1 || j == i) { printf("* "); } else if (i == 10) { printf("* "); } else { printf(" "); } } printf("\n"); } return 0;}

March 23, 2021 · 1 min · jiezi

关于c++:网络编程IO复用函数

I/O复用使得程序可能同时监听和解决多个文件描述符,进步程序的效率。支流的零碎调用次要有select,poll和epoll。 select零碎调用原型int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* expectfds, struct timeval* timeout);其中,nfds为select监听的最大文件描述符个数+1,fd_set是一个数据结构,其本质上是一个整形数组,数组中的每一个位都标记一个文件描述符,而fd_set的容量是由内核决定的,即select能同时解决的文件数量是无限的。 fd_set操作的一些宏定义: FD_ZERO(fd_set *fdset) —— 革除fdset所有的位FD_SET(int fd, fd_set *fdset) —— 设置fd位FD_CLR(int fd, fd_set *fdset) —— 革除fd位int FD_ISET(int fd, fd_set *fdset) —— 测试fd位是否被设置

March 22, 2021 · 1 min · jiezi

关于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=1;j<=i;j++){ printf("* "); } printf("\n"); } return 0;}

March 18, 2021 · 1 min · jiezi

关于c:Socket编程基础知识

罕用的数据结构及函数地址转换类主机字节序与网络字节序的转换主机序通常为小端,网络字节序为大端。下列函数在<netinet/in.h>包中: usinged long int htonl(unsigned long int hostlong) —— 主机序转网络序(host to network long)usinged short int htons(unsigned short int hostlong) —— 主机序转网络序(host to network short)usinged long int ntohl(unsigned long int hostlong) —— 网络序转主机序(network to host long)usinged short int ntohl(unsigned short int hostlong) —— 网络序转主机序(network to host short)IP地址转换函数计算机可能辨认二进制的IP地址,但生存中咱们罕用点分十进制来示意IPv4地址,用十六进制示意IPv6地址,于是须要一些函数对他们进行一些转换,这些函数位于<arpa/inet.h>包中。 in_addr_t inet_addr(const char* strprt) —— 将点分十进制IPv4地址转化成网络字节序的地址int inet_aton(const char cp, struct in_addr inp) —— 同上,将后果存在指针里char* inet_ntoa(struct in_addr in) —— 将二进制后果转为点分十进制int inet_pton(int af, const char src, void dst) —— 将十六进制地址转为二进制地址,并存在指针。其中af示意地址族,能够是AF_NET或AF_NET6。const char inet_ntop(int af, const void src, char* dst, socklen_t cnt) —— 将二进制地址转换为十六进制地址,其中cnt指定指标存储单元大小socket地址构造通用socket地址——sockaddr#include <bits/socket.h>struct sockaddr { sa_family_t sa_family; char sa_data[14];}其中,sa_family是地址族,包含:PF_UNIX(UNIX本地区协定族)、PF_INET(TCP/IPv4地址协定族)、PF_INET6(IPv6地址协定族)。留神:AF_xxx和PF_xxx在socket.h中值雷同,因而常混用。sa_data用于寄存socket地址值,不同协定的地址具备不同含意和长度。PF_UNIX寄存的是文件的路径名(疑难:在PF_UNIX中,socket是以一个什么样的模式存在的?),长度可达到108字节;在PF_INET中,寄存16bit的端口号和32bit的IPv4地址;在AF_INET6中,寄存的是128bit的IPv6地址、16bit端口号以及32bit的ID。因为14字节显然无奈容下所有地址,因而linux中sockaddr定义为: ...

March 17, 2021 · 1 min · jiezi

关于c++:CC-性能优化背后的方法论TMAM

开发过程中咱们多少都会关注服务的性能,然而性能优化是绝对比拟艰难,往往须要多轮优化、测试,属于费时费力,有时候还未必有好的成果。然而如果有较好的性能优化办法领导、工具辅助剖析能够帮忙咱们疾速发现性能瓶颈所在,针对性地进行优化,能够事倍功半。 性能优化的难点在于找出要害的性能瓶颈点,如果不借助一些工具辅助定位这些瓶颈是十分艰难的,例如:c++程序通常大家可能都会借助perf /bcc这些工具来寻找存在性能瓶颈的中央。性能呈现瓶颈的起因很多比方 CPU、内存、磁盘、架构等。本文就仅仅是针对CPU调优进行调优,即如何榨干CPU的性能,将CPU吞吐最大化。(实际上CPU出厂的时候就曾经决定了它的性能,咱们须要做的就是让CPU尽可能做有用功),所以针对CPU利用率优化,实际上就是找出咱们写的不够好的代码进行优化。 一、示例先敬上代码: #include <stdlib.h> #define CACHE_LINE __attribute__((aligned(64))) struct S1 { int r1; int r2; int r3; S1 ():r1 (1), r2 (2), r3 (3){} } CACHE_LINE; void add(const S1 smember[],int members,long &total) { int idx = members; do { total += smember[idx].r1; total += smember[idx].r2; total += smember[idx].r3; }while(--idx); } int main (int argc, char *argv[]) { const int SIZE = 204800; S1 *smember = (S1 *) malloc (sizeof (S1) * SIZE); long total = 0L; int loop = 10000; while (--loop) { // 不便比照测试 add(smember,SIZE,total); } return 0; }注:代码逻辑比较简单就是做一个累加操作,仅仅是为了演示。编译+运行: ...

March 17, 2021 · 3 min · jiezi

关于c:用C程序打印星形棱形

解题思路用5*10网格 第0行打印5个空格和5个星 第1行打印4个空格和5个星 第2行打印3个空格和5个星 /*打印如下: ***** ***** ***** ***** ***** */#include <stdio.h>int main(){ int i; int j; for (i = 0; i < 5; i++) { for (j = 0; j < 10 - 5 - i; j++) { printf(" "); } for (j = 0; j < 5; j++) { printf("*"); } printf("\n"); } return 0;}

March 17, 2021 · 1 min · jiezi

关于c++:C防止头文件被重复引入的3种方法

在之前咱们具体介绍了 C 语言中如何应用宏定义(#ifndef / #define / #endif)来无效防止头文件被反复 #include,此形式在 C++ 多文件编程中也很罕用。 举个例子,如下是一个 C++ 我的项目,其外部含有 school.h 和 student.h 这 2 个头文件以及 main.cpp 源文件,其各自蕴含的代码为: //student.hclass Student { //......};//school.h#include "student.h"class School { //......private: Student stu[50];};//main.cpp#include "student.h"#include "school.h"int main() { //...... return 0;}运行此我的项目会发现,编译器报“Student 类型重定义”谬误。这是因为在 school.h 文件中曾经 #include 了一次 "student.h",而在 main.cpp 主程序又同时 #include 了 "school.h" 和 "student.h",即 Student 类的定义被引入了 2 次,C++不容许同一个类被反复定义。 有小伙伴可能想到,既然 School.h 文件中曾经引入了 Student 类,那去掉 main.cpp 主程序引入的 student.h 文件不就能够了吗?这样的确能够防止反复引入 Student 类,但此形式并不适用于所有“反复引入”的场景。 C++ 多文件编程中,解决“屡次 #include 导致反复引入”问题的形式有以下 3 种。 ...

March 16, 2021 · 2 min · jiezi

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

解题思路用10*10网格 第1行打印9个空格1个星 第2行打印8个空格 第3行打印7个空格 以此类推 从第1行开始列如果(j==1||j==i)则打印星,否则打印空格,最初一行打印10个星 /*打印如下: * ** * * * * * * * * * * * * * *********** */#include <stdio.h>int main(){ int i; int j; for (i = 1; i <= 10; i++) { for (j = 1; j <= 10 - i; j++) { printf(" "); } for (j = 1; j <= i; j++) { if (j == 1 || j == i) { printf("*"); } else if (i == 10) { printf("*"); } else { printf(" "); } } printf("\n"); } return 0;}

March 16, 2021 · 1 min · jiezi

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

## 解题思路用10*10网格 行号从1开始计算 每行列号等于1或者列号等于行号打印1个星,最初一行打印10个星,否则打印空格 /*打印如下:**** ** ** ** ** ** ** *********** */#include <stdio.h>int main(){ int i; int j; for (i = 1; i <= 10; i++) { for (j = 1; j <= i; j++) { if (j == 1 || j == i) { printf("*"); } else if (i == 10) { printf("*"); } else { printf(" "); } } printf("\n"); } return 0;}

March 15, 2021 · 1 min · jiezi

关于c:用C程序打印空心星形-右倒置直角三角形

解题思路用10*10网格 第0行打印0个空格10个星 第1行打印1个空格 第2行打印2个空格 以此类推 从第1行开始列如果(j==0||j==10-i-1)则打印星,否则打印空格 /*打印如下:********** * * * * * * * * * * * * * * ** * */#include <stdio.h>int main(){ int i; int j; for (i = 0; i < 10; i++) { for (j = 0; j < i; j++) { printf(" "); } for (j = 0; j < 10; j++) { if (i == 0) { printf("*"); } else if (j == 0 || j == 10 - i - 1) { printf("*"); } else { printf(" "); } } printf("\n"); } return 0;}

March 14, 2021 · 1 min · jiezi

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

解题思路用10*10网格 第一行9个空格(10-1行号)和一个数字(1行号) 第二行8个空格(10-2行号)和两个数字(2行号) 第三行7个空格(10-3行号)和三个数字(3行号) 第四行6个空格(10-4行号)和四个数字(4行号) 以此类推… 从法则来看,每行打印的都与行号无关 /*打印如下: 1 12 123 1234 12345 123456 1234567 12345678 12345678912345678910 */#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 = 1; j <= i; j++) { printf("%d", j); } printf("\n"); } return 0;}

March 13, 2021 · 1 min · jiezi

关于c:linux工具netstat-工具使用

March 13, 2021 · 0 min · jiezi