关于c:详解双向链表的基本操作C语言

@[TOC] 1.双向链表的定义上一节学习了单向链表单链表详解。明天学习双链表。学习之前先对单向链表和双向链表做个回顾。单向链表特点: 1.咱们能够轻松的达到下一个节点, 然而回到前一个节点是很难的. 2.只能从头遍历到尾或者从尾遍历到头(个别从头到尾)双向链表特点 1.每次在插入或删除某个节点时, 须要解决四个节点的援用, 而不是两个. 实现起来要艰难一些 2.绝对于单向链表, 必然占用内存空间更大一些. 3.既能够从头遍历到尾, 又能够从尾遍历到头双向链表的定义: 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,别离指向间接后继和间接前驱。所以,从双向链表中的任意一个结点开始,都能够很不便地拜访它的前驱结点和后继结点。下图为双向链表的结构图。 从上中能够看到,双向链表中各节点蕴含以下 3 局部信息: 指针域:用于指向以后节点的间接前驱节点; 数据域:用于存储数据元素。 指针域:用于指向以后节点的间接后继节点;双向循环链表的定义: 双向链表也能够进行首尾连贯,形成双向循环链表,如下图所示在创立链表时,只须要在最初将收尾相连即可(创立链表代码中曾经标出)。其余代码稍加改变即可。双链表的节点构造用 C 语言实现为: /*随机数的范畴*/#define MAX 100/*节点构造*/typedef struct Node{ struct Node *pre; int data; struct Node *next;}Node;2.双向链表的创立 同单链表相比,双链表仅是各节点多了一个用于指向间接前驱的指针域。因而,咱们能够在单链表的根底轻松实现对双链表的创立。 须要留神的是,与单链表不同,双链表创立过程中,每创立一个新节点,都要与其前驱节点建设两次分割,别离是: 将新节点的 prior 指针指向间接前驱节点; 将间接前驱节点的 next 指针指向新节点; 这里给出创立双向链表的 C 语言实现代码: Node* CreatList(Node * head,int length){ if (length == 1) { return( head = CreatNode(head)); } else { head = CreatNode(head); Node * list=head; for (int i=1; i<length; i++) /*创立并初始化一个新结点*/ { Node * body=(Node*)malloc(sizeof(Node)); body->pre=NULL; body->next=NULL; body->data=rand()%MAX; /*间接前趋结点的next指针指向新结点*/ list->next=body; /*新结点指向间接前趋结点*/ body->pre=list; /*把body指针给list返回*/ list=list->next; } } /*加上以下两句就是双向循环链表*/ // list->next=head; // head->prior=list; return head;}3.双向链表的插入 依据数据增加到双向链表中的地位不同,可细分为以下 3 种状况:1.增加至表头 将新数据元素增加到表头,只须要将该元素与表头元素建设双层逻辑关系即可。 换句话说,假如新元素节点为 temp,表头节点为 head,则须要做以下 2 步操作即可: temp->next=head; head->prior=temp; 将 head 移至 temp,从新指向新的表头; 将新元素 7 增加至双链表的表头,则实现过程如下图所示:2.增加至表的两头地位 同单链表增加数据相似,双向链表两头地位增加数据须要通过以下 2 个步骤,如下图所示: 新节点先与其间接后继节点建设双层逻辑关系; 新节点的间接前驱节点与之建设双层逻辑关系;3.增加至表尾 与增加到表头是一个情理,实现过程如下: 找到双链表中最初一个节点; 让新节点与最初一个节点进行双层逻辑关系; ...

December 16, 2020 · 2 min · jiezi

关于c:单链表的冒泡快排选择插入归并等多图详解

上节介绍了链表的基本操作[史上最全单链表的增删改查反转等操作汇总以及5种排序算法(C语言)]()这节介绍链表的5种排序算法。@[TOC] 0.稳固排序和原地排序的定义稳固排序: 假设在待排序的记录序列中,存在多个具备雷同的关键字的记录,若通过排序,这些记录的绝对秩序放弃不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳固的;否则称为不稳固的。像冒泡排序,插入排序,基数排序,归并排序等都是稳固排序原地排序: 基本上不须要额定辅助的的空间,容许大量额定的辅助变量进行的排序。就是在原来的排序数组中比拟和替换的排序。像抉择排序,插入排序,希尔排序,疾速排序,堆排序等都会有一项比拟且替换操作(swap(i,j))的逻辑在其中,因而他们都是属于原地(旧址、就地)排序,而合并排序,计数排序,基数排序等不是原地排序 1.冒泡排序根本思维: 把第一个元素与第二个元素比拟,如果第一个比第二个大,则替换他们的地位。接着持续比拟第二个与第三个元素,如果第二个比第三个大,则替换他们的地位.... 咱们对每一对相邻元素作同样的工作,从开始第一对到结尾的最初一对,这样一趟比拟替换下来之后,排在最右的元素就会是最大的数。除去最右的元素,咱们对残余的元素做同样的工作,如此反复上来,直到排序实现。具体步骤: 1.比拟相邻的元素。如果第一个比第二个大,就替换他们两个。 2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最初一对。这步做完后,最初的元素会是最大的数。 3.针对所有的元素反复以上的步骤,除了最初一个。 4.继续每次对越来越少的元素反复下面的步骤,直到没有任何一对数字须要比拟。工夫复杂度:O(N2)空间复杂度:O(1)稳固排序:是原地排序:是 Node *BubbleSort(Node *phead){ Node * p = phead; Node * q = phead->next; /*有几个数据就-1;比方x 个i<x-1*/ for(int i=0;i<5;i++) { while((q!=NULL)&&(p!=NULL)) { if(p->data>q->data) { /*头结点和下一节点的替换,要非凡解决,更新新的头head*/ if (p == phead) { p->next = q->next; q->next = p; head = q; phead = q; /*这里切记要把p,q换回来,失常的话q应该在p的后面,进行的是p,q的比拟 *然而通过指针域替换之后就变成q,p.再次进行下一次比拟时, *就会变成q,p的数据域比拟。如果本来p->data > q->data,则进行替换。变成q->data和p->data比拟, *不会进行替换,所以排序就会谬误。有趣味的能够调试下。 */ Node*temp=p; p=q; q=temp; } /*解决两头过程,其余数据的替换状况,要寻找前驱节点if (p != phead)*/ else { /*p,q的那个在前,那个在后。指针域的连贯画图好了解一点*/ if (p->next == q) { /*寻找p的前驱节点*/ Node *ppre = FindPreNode(p); /*将p的下一个指向q的下一个*/ p->next = q->next; /*此时q为头结点,让q的下一个指向p,连接起来*/ q->next = p; /*将原来p的前驱节点指向当初的q,当初的q为头结点*/ ppre->next = q; Node*temp=p; p=q; q=temp; } else if (q->next == p) { Node *qpre = FindPreNode(q); q->next = p->next; p->next = q; qpre->next = p; Node*temp=p; p=q; q=temp; } } } /*地址挪动*/ p = p->next; q = q->next; } /*进行完一轮比拟后,从头开始进行第二轮*/ p = phead; q = phead->next; } head = phead; return head;}2.疾速排序根本思维 咱们从数组中抉择一个元素,咱们把这个元素称之为中轴元素吧,而后把数组中所有小于中轴元素的元素放在其右边,所有大于或等于中轴元素的元素放在其左边,显然,此时中轴元素所处的地位的是有序的。也就是说,咱们无需再挪动中轴元素的地位。 从中轴元素那里开始把大的数组切割成两个小的数组(两个数组都不蕴含中轴元素),接着咱们通过递归的形式,让中轴元素右边的数组和左边的数组也反复同样的操作,直到数组的大小为1,此时每个元素都处于有序的地位。具体步骤: 1.从数列中挑出一个元素,称为 "基准"(pivot); 2.从新排序数列,所有元素比基准值小的摆放在基准后面,所有元素比基准值大的摆在基准的前面(雷同的数能够到任一边)。在这个分区退出之后,该基准就处于数列的两头地位。这个称为分区(partition)操作; 3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;工夫复杂度:O(NlogN)空间复杂度:O(logN)稳固排序:否原地排序:是 ...

December 16, 2020 · 5 min · jiezi

关于c:史上最全单链表的增删改查反转等操作汇总以及5种排序算法C语言

[TOC] 1.筹备工作首先蕴含头文件,定义链表构造体,产生随即链表的范畴,定义全局头尾节点。 #include <stdio.h>#include <stdlib.h>#include <string.h>#define MAX 10/*定义链表*/typedef struct Node { int data; struct Node *next; }Node;/*定义全局头尾节点*/Node *head = NULL;Node *end = NULL;2.创立链表/*依据传入的参数增加链表节点*/int CreatList(int a){ /*定义长期构造体并调配空间*/ Node *temp = (Node *)malloc(sizeof(Node)); if (temp ==NULL) { printf("malloc error!"); return -1; } else { /*给数据类型赋值*/ temp->data = a; temp->next = NULL; /*如果链表长度为0*/ if (head == NULL) { head = temp; end = temp; } else { end->next = temp; end = temp; } } }3.打印链表/*打印链表*/void PrintList(Node *temp){ if(temp == NULL) { printf("Empty List!\r\n"); } while (temp) { printf("%d",temp->data); temp = temp->next; if(temp) printf("->"); } printf("\r\n");}4.在元素前面插入元素向链表中削减元素,依据增加地位不同,可分为以下 3 种状况:1.插入到链表的头部(头节点之后),作为首元节点;2.插入到链表两头的某个地位;3.插入到链表的最末端,作为链表中最初一个数据元素; ...

December 16, 2020 · 4 min · jiezi

关于c:C语言服务器编程必备常识

入门蕴含了正确的头文件只能编译通过,没链接正确的库链接会报错。一些罕用的库gcc会主动链接。库的缺省门路/lib /usr/lib /usr/local/lib不晓得某个函数在那个库能够nm -o /lib *.so | grep 函数名man sin 会列出蕴含的头文件和链接的库名。man 2 sin 2示意零碎调用,3示意c库函数一旦子过程被创立,父子过程一起从fork处被创立。创立子过程为了抢夺资源。重定向用dup2函数kill -l查看信号品种pthread_mutex不跨过程,ipc中的信号量跨过程,但linux不反对无名信号量。信号灯的主要用途是爱护临界资源。多过程拜访共享内存,用信号量同步。alarm(5)5秒后向本人发送SIGALARM信号,缺省解决是完结过程,不自定义就会完结过程。通过对信号集加减信号,确定信号屏蔽字。在信号处理程序被调用时,操作系统建设的新信号屏蔽字包含正在被递送的信号,如果此时这个信号再次发生,将阻塞到前一个解决完,屡次产生不排队只解决一次。 sa_mask会被加到信号屏蔽字中。netstat -an|grep A |grep ESTABLISHED | grep B,查看ip为A的服务器是否在端口B建设了连贯因为咱们的连贯都是常连贯,故能够依照客户端与服务器端建设的连贯端口进行判断。IP协定是网络层协定,次要发送数据包。UDP基于IP协定,用在传输层。TCP协定建设在IP协定之上,牢靠的、按程序发送的。TCP连贯三次握手:客户机向服务器发包。服务器给客户机回包。客户机收到包,向服务器发送确认信息实现连贯。服务器收到确认信息也实现连贯。ioctl能够管制所有文件描述符的状况。循环服务器:UDP服务器,UDP是非面向连贯的,没有一个客户机能够老是占着服务器。TCP循环服务器一次只能解决一个,close后能力解决下一个。TCP并发服务器:fork子过程来解决。创立子过程耗费资源。并发服务器:多路IO复用。当咱们创立一个失常的TCP套接字的时候,咱们只解决内容,不负责TCP头部和ip头部,本人创立头部应用setsockopt。网络程序个别是多过程加上多线程。g++参数-pg产生gprof性能信息,gprof如同是g++自带的(gdb)make使你能不退出gdb就能产生就从新产生可执行文件 , shell 不退出gdb就执行shellfile a.out能够在gdb模式下载入程序。服务器端:socket->bind->listen【建设监听队列,监听socket】->【返回连贯socket】accept【从监听队列取申请,胜利时返回新的socket,与传入的第一参数不同,标识被承受的这个连贯】客户端:socket->connect进阶linux中代替数据库管理文件可能会呈现文件数超过linux可治理数的问题。tcpip协定族:下层协定应用上层协定提供的服务。应用层的货色最初须要在内核中实现,会须要利用空间和内核空间的切换。IP数据太长要分片。IP协定的外围是数据报路由。路由表、跳转、自动更新。socket含意ip:port。称其为socket地址。字节序是按字节思考的,和位无关。cpu累加器一次装载4字节(32位机)。网络上传的肯定是大端字节序,各主机按本身状况转化。java虚拟机采纳大端字节序。不同输出调用两次函数,如果发现前面后果笼罩后面后果,阐明函数不可重入。函数外部如果用动态变量存储后果,就不可重入。将一个地址和socket绑定称为给socket命名。0-1023端口普通用户不能应用,有默认用处。accept只是从监听队列中取出连贯,不管连贯处于何种状态。connect(fd,..)一旦连贯建设胜利,fd就惟一标识了这个连贯,客户端就能够读写fd和服务器通信。对socket执行close只缩小连接数,fork会使援用数加1。无论如何都要终止连贯用shutdown。read和write同样实用于socket。用于TCP数据流的是send recv。UDP数据是recvfrom sendto。没有过程读管道的时候【例如close(fd[0])了】还往管道写数据将引发SIGPIPE。把STDOUT_FILENO敞开,dup(连贯socket),这时dup返回最小可用描述符1【返回的文件描述符和原有描述符指向雷同文件】,此时printf回返回给客户端,而不是打印在屏上。sendfile将实在文件传给socket。splice用于在两个文件描述符间挪动数据,零拷贝,用于socket和管道之间相互定向。tee用于两个管道之间复制数据。IO处理单元是一个专门的接入服务器,它实现负载平衡。申请队列是零碎外部各单元之间通信形式的形象,个别实现为池。阻塞和非阻塞是对文件描述符而言的。非阻塞IO个别和IO告诉机制一起应用,如IO复用或SIGIO信号。IO复用自身是阻塞的,提高效率是因为同时监听多个事件。同步就是协同步调,按预约的先后秩序进行运行。解决客户连贯就是读写描述符,就是IO,所以IO单元被定义为接入服务器。并发不适用于计算密集型,因为工作切换会升高效率,实用于IO密集型,如常常读写文件、拜访数据库。池就是事后动态分配资源,到时能够疾速应用。防止了对内核的频繁拜访。晋升性能办法:池、防止数据复制、上下文切换【线程数大于cpu数时】和锁。读写锁能够缩小锁的粒度实用于读多写少的状况。epoll须要应用一个额定的描述符保护事件表。 EPOLLONESHOT确保只有一个线程解决某个socket。sigaction构造体中的sa_mask设置信号掩码,确切的说是在过程原有信号掩码的根底上减少信号掩码,以指定哪些信号不能发送给本过程。sigset_t 每个元素的每个位示意一个信号,所以雷同的信号只能示意一次。子过程有和父过程雷同的信号掩码,但挂起信号集【发送然而被阻塞的信号】为空,就是说阻塞的信号是不可能发给子过程的。应用程序应用信号集sigset_t前,应调用sigemptyset或sigfillset一次,否则信号集初始状态不分明。最小工夫堆能够达到定时器的成果。waitpid用NOHANG就是非阻塞的了。socketpair创立全双工管道的零碎调用。对信号量的操作成为P(传递,进入临界区)V(开释,退出临界区)。最简略的二进制信号量,只有0和1.用一个一般变量模仿是不行的,因为检测和减1无奈原子实现。linux上的线程应用clone零碎调用创立的过程模仿的。目前能够实现跨过程的线程同步被pthread_cancel的线程能够决定是否容许被勾销以及如何勾销。销毁一个曾经加锁的互斥量将导致不可知的结果。互斥量属性设置中能够设置跨过程共享互斥量。pthread_mutexattr_setpshared()加锁-》pthread_cond_wait暗含解锁,确保能检测到条件变量的任何变动。有些函数不可重入次要是因为外部应用了动态变量。多线程程序中的一个线程调用fork,只复制调fork的那个线程。互斥量的状态也继承,此时容易呈现死锁。所有线程共享信号处理函数,共享过程的信号。所以须要专门线程解决所有信号。过程池:典型的是3-10个。线程池中的线程数量应该和cpu数量差不多。通信【通信:传递数据】父子过程间能够应用管道,多线程间应用一个全局数据即可。pthread_create当线程函数是类的成员函数时,必须为动态函数【确保没对象时也能够应用】,因为动态成员函数只能拜访动态成员,要拜访动静成员须要函数外部用单例或将类的对象作为参数传给函数。SA_RESTART被信号中断的零碎调用再信号处理完结后继续执行。将线程池或过程池中个数缩小为1,便于调试逻辑。而后逐渐减少数量,看同步。过程类[i].fd 通过给不同i的fd传递数据,调用不同的过程工作。m_sub_process[i].pid=fork()【fork了maxnum次】。线程池:线程函数一起都启动,启动后进入while(!stop)循环,一直的锁队列,取工作。POSIX线程只有互斥量的客人可能解锁它。线程的堆栈受限。线程完结形式要么从线程函数return,要么调用pthread_exit,进入终止态,直到被拆散或被连贯。创立不须要连贯的线程应该应用detachstate属性建设线程使其主动拆散。pthread_join会阻塞调用者,直到被join的线程完结,join返回被连贯的线程也拆散,所以只能被join一次,下一次就谬误了。应用条件变量时必须保障如果有线程期待,则该线程期待后必然会收到信号(if/while)条件变量能够使线程处于期待状态而不耗费资源。条件变量必须跟一个互斥变量一起应用,因为条件变量就是共享的全局数据??【条件和锁联合独特爱护共享数据】status = pthread_cond_wait(&alarm_cond, &alarm_mutex);没有条件变量,程序员可用应用轮询某个变量来实现停等-告诉同步,然而十分耗费系统资源。如果确定线程不须要被Join, 则申明为Detached能够节俭系统资源pthread_self取得本身的ID,只能通过ID操作线程。main是主线程,主线程进行所有线程也进行,main中调用pthread_exit,这样过程就必须期待所有线程完结能力终止。通过向pthread_t(ID)=pthread_create传递线程函数地址和函数参数来创立线程。留神以后线程从pthread_create返回前,新创建的线程可能曾经运行结束了。舀水桶相似一个互斥量:桶用来爱护“舀水”临界区【拜访临界资源(共享数据?)的那段程序是临界区】。或者将桶了解为:用来确保一次只能由一个人舀水的不变量。在访问共享数据的代码段四周加锁互斥量,则一次只能有一个线程进入该代码段。pthread_mutex_t示意互斥量,不能拷贝,能够拷贝指针。当调用pthread_mutex_lock时,如果互斥量曾经被锁住,线程将被阻塞。调用pthread_mutex_trylock时不会阻塞,会返回EBASY,能够做其余的事件去。互斥量的实质是串行执行。解决死锁的两种办法:一,规定加锁程序;二,trylock如果不行回退,解锁所有已加锁的互斥量sched_yield()将处理器交给另一个期待解决的线程,如果没有期待解决的线程。立刻返回。sleep()能够确保其余线程有机会运行。依照相同的程序解锁,有助于缩小线程做回退操作的可能。因为同一个线程函数中加锁程序是一样的。对于不同的线程函数程序应该不重要线程运行于解锁和阻塞之间时,其余线程能力扭转共享数据状态。此时共享状态的扭转,本线程是无奈晓得的。->须要条件变量。队列满,队列空,满空就是条件变量。动静初始化的条件变量须要pthread_cond_destroy来开释。动态初始化的不用开释。开释前确保其余线程不应用他。在阻塞线程之前,条件变量期待操作pthread_cond_wait将解锁互斥量,从新返回线程之前,会再次锁住互斥量。子线程只在pthread_cond_wait期待的短时间内能够加锁,批改共享数据,而后解锁。pthread_cond_timedwait的意思就是我在这里等time工夫,如果工夫内条件变量变了,或者不变,我都要跳出while(谓词)的循环,按状况解决。pthread_cond_wait和pthread_cond_signal必须同时产生能力胜利。互斥量:条件变量是 一对多的关系当线程调用pthread_create时,她所能看到的内存值也是它建设的线程能看到的,之后的线程不肯定能看到。线程解锁互斥量时所看到的的数据,也能被起初间接锁住雷同互斥量的线程看到。解锁后写入的数据,不用被其余线程看见(因为那不是用来同步的数据,没必要所有人看见,同步就应该加锁)。线程终止,勾销,从启动函数返回,pthread_exit时看到的数据,可能被连贯该线程的其余线程看到。终止后写入的数据不会被看到。线程发信号或播送时看到的内存数据,能够被唤醒线程看到。之后写入的不会。线程调配的堆栈和堆空间是公有的,除非传给其余线程指针。register(寄存器变量)和auto变量(大部分变量默认auto)(static变量的生命期长)中的数据可随时读取,像在同步程序中一样两个处理器将各自的高速缓存中的数据写入主存的程序是不肯定的,即便写到相应高速缓存的程序有先后之分。同一线程写数据也未必依照程序刷新进内存,这使得其余线程读取后果不对。锁住互斥量->内存屏障->内存屏障->解锁互斥量应用线程的形式:流水线、工作组(工作线程在数据的不同局部操作)、C/S。pthread_attr_setdetachstate (&_attr, PTHREAD_CREATE_DETACHED);阐明在创立线程后,咱们不在须要应用线程ID。不变量(Invariant):程序所做的一些假如,特地是指变量之间的关系。断定条件(Predicates):形容不变量状态的逻辑表达式。pthread_kill(thdid, SIGTERM)给特定线程发信号

December 15, 2020 · 1 min · jiezi

关于c:不同类型数据运算的总结

前几天在学习交换群里,有个小伙伴问了一个问题,是对于有符号整数和无符号字符数的运算的问题。对于这部分,我集体了解的也有点问题,当初来做个总结回顾。有符号整型和无符号数整型举例#include <stdio.h>#include <stdlib.h>int main(){ int a = -1; unsigned int b = 1; printf("%d", a > b); return 0;} 输入后果为:1 解析 无符号整型和有符号整型比拟时,有符号整型会转化成无符号整型。因而,-1转化为无符号数为0xFFFFFFFF,所以a>b,输入1。 有符号字符型和无符号字符型举例#include <stdio.h>#include <stdlib.h>int main(){ char a = -1; unsigned char b = 1; printf("%d", a > b); return 0;} 输入后果:0 解析 要解释以上这个景象,首先要明确整型晋升: 整型晋升是C程序设计语言中的一项规定:在表达式计算时,各种整形首先要晋升为int类型,如果int类型不足以示意则要晋升为unsigned int类型;而后执行表达式的运算。 整型晋升的意义在于:表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度个别就是int的字节长度,同时也是CPU的通用寄存器的长度。因而,即便两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的规范长度。通用CPU(general-purpose CPU)是难以间接实现两个8比特字节间接相加运算(尽管机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,而后能力送入CPU去执行运算。(起源百度百科) 举个例子 #include <stdio.h>#include <stdlib.h>int main(){ char a = '2'; char b = '2'; printf("%d\r\n", sizeof(a+b)); char c = a+b; printf("%c\r\n", c); printf("%d\r\n", sizeof(c)); return 0;} 输入后果为:4 d 1。 ...

December 15, 2020 · 1 min · jiezi

关于c:C语言太简单这14道C语言谜题你能答对几个

本文展现了14个C语言的迷题以及答案,代码应该是足够分明的,而且有相当的一些例子可能是咱们日常工作可能会见失去的。通过这些迷题,心愿你能更理解C语言。 如果你不看答案,不晓得是否有把握答复各个谜题?让咱们来试试。 1 上面的程序并不见得会输入"hello-std-out",你晓得为什么吗? #include#includeint main(){ while(1) { fprintf(stdout,"hello-std-out"); fprintf(stderr,"hello-std-err"); sleep(1); } return 0;}参考答案 stdout和stderr是不同设施描述符。stdout是块设施,stderr则不是。对于块设施,只有当上面几种状况下才会被输出:遇到回车;缓冲区满;flush被调用。而stderr则不会。 2 上面的程序看起来是失常的,应用了一个逗号表达式来做初始化。惋惜这段程序是有问题的。你晓得为什么吗? #includeint main(){ int a = 1,2; printf("a : %dn",a); return 0;}参考答案 这个程序会失去编译出错(语法出错)。逗号表达式是没错,可是在初始化和变量申明时,逗号并不是逗号表达式的意义。这点要辨别,要批改下面这个程序,你须要加上括号:"int a = (1,2);"。 3 上面的程序会有什么样的输入呢? #includeint main(){ int i=43; printf("%dn",printf("%d",printf("%d",i))); return 0;}参考答案 程序会输入4321,你晓得为什么吗?要晓得为什么,你须要晓得printf的返回值是什么。printf返回值是输入的字符个数。 4 上面的程序会输入什么? #includeint main(){ float a = 12.5; printf("%dn", a); printf("%dn", (int)a); printf("%dn", *(int *)&a); return 0;}参考答案 该项程序输入:"0 12 1095237632"。 起因是:浮点数是4个字节,12.5f转成二进制是:01000001010010000000000000000000,十六进制是:0x41480000,十进制是:1095237632。所以,第二和第三个输入置信大家也晓得是为什么了。 而对于第一个,为什么会输入0,咱们须要理解一下float和double的内存布局,如下: • float: 1位符号位(s)、8位指数(e),23位尾数(m,共32位)。 ...

December 14, 2020 · 2 min · jiezi

关于c:S3C2440从NAND-Flash启动和NOR-FLASH启动的问题

1.为什么NAND FLASH不能间接运行程序 NAND FLASH自身是连贯到了控制器上而不是系统总线上。CPU运行机制为:CPU启动后是要取指令执行的,如果是SROM、NOR FLASH 等之类的,CPU 通过地址线发个地址就能够获得指令并执行,NAND FLASH不行,因为NAND FLASH 是管脚复用,它有本人的一套时序,这样CPU无奈获得能够执行的代码,也就不能初始化零碎了。 NAND FLASH是程序存取设施,不可能被随机拜访,程序就不可能分支或跳转,这样你如何去设计程序。 U-BOOT 反对ARM、 PowerPC等多种架构的处理器,也反对Linux、NetBSD和VxWorks等多种操作系统,次要用来开发嵌入式零碎初始化代码 bootloader。bootloader是芯片复位后进入操作系统之前执行的一段代码,实现由硬件启动到操作系统启动的过渡,为运行操作系统提供根本的运行环境,如初始化CPU、堆栈、初始化存储器零碎等,其性能相似于PC机的BIOS。 2.为什么s3c2440能够从NAND FLASH启动 椐理解 NOR FLASH 是容量小,速度快,稳定性好,适宜做程序存储器。 NAND FLASH 总容量大,适宜做数据存储器是不能从NAND FLASH启动的,NAND FLASH是的读写时序是不能间接有ARM硬件产生的,要读写NAND FLASH是要通过程序来实现的,很显著能看进去的就是NAND FLASH只有8个数据、地址复用的数据地址接口。2410/2440能够间接从NAND FLASH启动的,因为它把NAND后面的4K映射到了RAM的空间 2.1 Flash品种 NOR FLASH地址线和数据线离开,来了地址和管制信号,数据就进去。 NAND Flash地址线和数据线在一起,须要用程序来管制,能力出数据。 艰深的说,就是光给地址不行,要先命令,再给地址,能力读到NAND的数据。而且都是在一个总线实现的。 论断是:ARM无奈从NAND间接启动。除非装载完程序,能力应用NAND Flash.装载程序只能从mask rom 或者Nor flash。 三星的2410能够从NAND FLASH启动程序,它会把第一块的前4KB复制到外部SRAM中而后从SRAM执行,也就是说,你须要编写一个长度小于4K的疏导程序,作用是将主程序拷贝到SDRAM中运行(NAND FLASH地址不是线性的,程序不能间接运行,必须拷贝到线性RAM中) 3.NAND启动和NOR启动: 须要检测硬件启动形式,启动形式是由硬件OM0管脚决定的,软件无奈检测硬件电平状态,但能够依据CPU的启动个性来检测。 3.1 NAND启动: 如果配置为NAND FLASH启动(启动模式选择开关拔到nand端,此时OM0管脚拉低)S3C2440的NAND控制器会主动把NAND FLASH中的前4K代码数据搬到外部SRAM中(地址为0x40000000),同时还把这块SRAM地址映射到了0x00000000地址。CPU从0x00000000地位开始运行程序。 【从NAND FLASH启动CPU时,CPU会通过外部的硬件将NAND FLASH开始的4KB数据复制到称为“Steppingstone”的4KB的外部RAM中(起始地址为0),而后跳到地址0开始执行】 3.2 NOR启动: 如果配置为NOR FLASH启动(启动模式选择开关拔到nor端,此时OM0管脚拉高),0x00000000就是NOR FLASH理论的起始地址,NOR FLASH中的程序就从这里开始运行,不波及到数据拷贝和地址映射。 ...

December 14, 2020 · 1 min · jiezi

关于c:本文帮你在Unix玩转C语言

shell是一种非凡的应用程序(命令行解释器),他为运行其余应用程序提供了一个接口。posix标准了操作系统是什么样每个过程都有一个工作目录(又叫当前目录),相对路径都是从工作目录开始解释。Ctrl+D是文件完结字符read读指定字节数;fgets是读取一行三个过程管制函数:fork exec waitpid。 waitpid【此函数获取信息,开释资源】父过程期待子过程终止,能够失去子过程何时终止。system函数是在exec外包了一层。execlp要求参数以null完结,换行符不能够线程id只在它所属过程内起作用,在另一个过程中无意义,能够应用线程id援用相应的线程。一个用户能够属于多至16个组ctrl+c中断键,ctrl+\退出键,等价于kill函数。kill(pid, SIGTERM)向另一过程发信号,发动信号的必须是该过程的所有者。(gdb)set follow-fork-mode child使gdb进入子过程,事实证明不设置(默认调试父过程)这句无奈进入pid==0的语句块。子过程exit后无奈再设置断点gdb信息失落,此时run可能启动的不是父过程而是孙过程。fork会复制fork开始直到函数完结的代码【共享代码注释,但复制全副变量】日历工夫:1970至今秒数,time_t类型用于保留这种工夫。过程工夫:cpu工夫,clock_t类型用于保留这种工夫。零碎cpu工夫是过程执行内核程序的工夫。执行用户指令的工夫是用户cpu工夫。两者之和是cpu工夫。时钟工夫【墙上钟工夫】,是过程运行的工夫总量,和过程数无关。time ls【ls可换成任意程序名】 查看工夫。clock_t times(struct tms* buf)胜利返回墙上钟工夫【必须应用相对值,做差】库函数不肯定调用零碎调用。应用程序能够间接调用零碎调用,也能够通过C库函数调用零碎调用。ISO C规范有24个头文件(包含stdlib.h,stdio.h)。接口即协定。很多程序须要为门路调配存储区守护过程:后盾运行且不与终端相连接的一种过程。与文件或目录无关的选项用sysconf确定,与文件或目录无关的选项用pathconf,fpathconf确定。unix零碎的大多数文件I/O只须要用到5个函数:open close read write lseek,都是不带缓冲的I/O。不带缓冲指的是read write都调用内核的一个零碎调用。不带缓冲的io不是iso c的组成部分,是posix的组成部分。对内核而言,所有的关上的文件都通过文件描述符(非负整数)援用。0 1 2 别离是输出 输入 谬误 的描述符。文件描述符变动范畴0-OPEN_MAX(示意每个过程能够关上OPEN_MAX个文件)。open函数:int fileId【返回最小的未用文件描述符数值】 = open(tmpPtr->_fileName【文件名】,O_RDWR【读、写关上】|O_CREAT【如果不存在则创立】, 0666【配合O_CREATE指定新文件拜访权限】);close(fileId);敞开文件同时开释过程加在该文件上的所有记录锁。过程终止时内核主动敞开它关上的文件。返回文件偏移量【偏移量始终存在,读、写操作从它指向的地位开始】=lseek(fileId,offset【每一个关上的文件都有一个以后文件偏移量,默认0,除非指定O_APPEND】,SEEK_SET【将偏移量设为文件开始处offset字节】)lseek返回-1阐明文件描述符对应的文件是管道、fifo或网络套接字。某些设施容许负的偏移量。od -c 文件名 【-c示意以字符形式打印文件内容】 ls -ls 查看文件占用多少个磁盘块nRead【返回读到字节数】 = read(flag_fd【文件描述符】, buffer【读取数据到buffer中】, length【一次读取字节数】) 【胜利返回前,偏移量减少读到的字节数】int bytes_write【返回写入字节数】 = write(fileHandle,ptr,writeSize【写入字节数】) 【写操作从以后偏移量开始,胜利后偏移量主动减少写入字节数】测量文件读写因为缓存机制,在第一次之后可能不精确。每个过程都有一张关上文件描述符表->文件表(以后文件偏移量)->v节点信息可能有多个文件描述符指向同一文件表项,多个文件表项指向一个v节点表。多过程读同一个文件没有问题,然而写同一个文件会有问题->原子操作。open中用O_CREAT和O_EXCL能够将测试和创立合并为一个原子操作。原子操作指多步组成的操作要么执行完所有步骤,要么一步也不执行。先lseek再write不可能是原子操作。两个函数间内核能够挂起过程。pread(..., off_t offset) pwrite(..., off_t offset) 相当于顺序调用lseek和read,与顺序调用的区别:无奈中断、不更新文件指针O_APPEND形式关上文件,每次write,文件偏移量主动定位到文件尾。新的文件描述符 = dup(int filedes) dup2(int filedes【被复制】,int filedes2【指定数值】) 复制现存的文件描述符,与参数filedes共享同一个文件表项。fcntl(..)也能够复制文件描述符。sync将块缓冲区排入写队列,不等理论写磁盘。fsync对繁多文件起作用,等写磁盘完结返回,更新属性。fdatasync只影响文件的数据局部。fcntl(..)返回值和命令无关,能够返回文件状态,文件描述符。能够批改文件状态。5<>temp示意在文件描述符5上关上文件供读写。终端I/O是 ioctl的最大应用方面。digit1 > &digit2示意要将digit1重定向至描述符2的同一个文件。shell从左到右解决命令struct stat sA【stat构造体蕴含磁盘号,所有者,拜访批改工夫等属性】; int retA【返回小于0示意文件不存在】 = stat(fileNameA.c_str(),&sA【stat函数将填写sA】); ls -l就是应用的stat(...)函数lstat(...)的加强性能是检测符号链接文件类型信息蕴含在stat构造的st_mode成员中,有上面几种类型。一般文件【蕴含某种数据的文件,数据是文本还是二进制对内核而言无区别,对文件内容的解释由解决该文件的应用程序进行。例外:二进制可执行文件恪守内核了解的对立格局】 目录文件【蕴含其余文件的名字以及指向与这些文件无关信息的指针】 块设施文件【磁盘,提供对设施带缓冲的拜访】 字符设施文件【键盘,提供对设施不带缓冲的拜访】 FIFO 又名管道文件,shell里的竖线 | 【用于过程间通信】 套接字【这种文件用于过程间的网络通信,也可用于一台机上过程间的非网络通信】 符号链接【这种文件类型指向另一个文件】 ...

December 10, 2020 · 3 min · jiezi

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

这是CSAPP的第二个试验,次要让咱们了解代码的机器级示意,最重要的是了解每个寄存器的作用以及如何应用这些寄存器。本次的试验内容有点艰涩难懂,对于这些内容多看下习惯就好了。 本次试验中的bomb文件中共有7个炸弹问题(6个显式的和1个暗藏的),每条问题只有输出正确的答案能力进入下一题,否则则会触发爆炸。通过浏览bomb文件的汇编代码了解各个问题的运作形式,推出正确的输出答案。暗藏的问题须要通过gdb间接调用解决。 我的编译环境:Ubuntu 16.04,gcc 5.4.0。 筹备工作 从官网下载到试验,解压后一共三个文件,具体如下图所示。 readme中没写什么有用的内容,bomb文件是编译实现的文件,bomb.c是本次试验的源码,关上看下,大略浏览了一遍,一共有phase_1 ~ phase_6 6个炸弹,从命令行输出的内容必须要和phase函数中的统一,否则就会爆炸退出程序。phase函数并没有给出源码,所以无奈得悉其冀望的字符串是什么。给了bomb可执行文件,咱们就把这个文件反汇编下,从反汇编推算下其内容是什么。 首先应用objdump -d bomb > bomb.asm命令生成反汇编文件。 先运行bomb文件,提醒没有权限,我的文件是从windwos拷贝到Linux虚拟机中的,所以会报这个谬误。执行chmod +777 bomb 赋予权限。如下图所示。 而后轻易输出一些内容看下会有什么结果,如下图所示,提醒曾经爆炸。 phase_1 上面从main函数开始剖析下反汇编。 0000000000400da0 <main>: 400da0: 53 push %rbx 400da1: 83 ff 01 cmp $0x1,%edi #if (argc == 1) 400da4: 75 10 jne 400db6 <main+0x16> # 不相等就跳转到400db6 400da6: 48 8b 05 9b 29 20 00 mov 0x20299b(%rip),%rax # 603748 <stdin@@GLIBC_2.2.5> 400dad: 48 89 05 b4 29 20 00 mov %rax,0x2029b4(%rip) # 603768 <infile> 相等就读取输出 400db4: eb 63 jmp 400e19 <main+0x79> #跳转到initialize_bomb 400db6: 48 89 f3 mov %rsi,%rbx 400db9: 83 ff 02 cmp $0x2,%edi #else if (argc == 2) 400dbc: 75 3a jne 400df8 <main+0x58> #不相等跳转到400df8 400dbe: 48 8b 7e 08 mov 0x8(%rsi),%rdi 400dc2: be b4 22 40 00 mov $0x4022b4,%esi 400dc7: e8 44 fe ff ff callq 400c10 <fopen@plt> 400dcc: 48 89 05 95 29 20 00 mov %rax,0x202995(%rip) # 603768 <infile> 400dd3: 48 85 c0 test %rax,%rax 400dd6: 75 41 jne 400e19 <main+0x79> #跳转到initialize_bomb 400dd8: 48 8b 4b 08 mov 0x8(%rbx),%rcx 400ddc: 48 8b 13 mov (%rbx),%rdx 400ddf: be b6 22 40 00 mov $0x4022b6,%esi 400de4: bf 01 00 00 00 mov $0x1,%edi #传参 400de9: e8 12 fe ff ff callq 400c00 <__printf_chk@plt> #printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]); 400dee: bf 08 00 00 00 mov $0x8,%edi 400df3: e8 28 fe ff ff callq 400c20 <exit@plt> #exit(8); 400df8: 48 8b 16 mov (%rsi),%rdx 400dfb: be d3 22 40 00 mov $0x4022d3,%esi 400e00: bf 01 00 00 00 mov $0x1,%edi 400e05: b8 00 00 00 00 mov $0x0,%eax #传参 400e0a: e8 f1 fd ff ff callq 400c00 <__printf_chk@plt> #printf("Usage: %s [<input_file>]\n", argv[0]); 400e0f: bf 08 00 00 00 mov $0x8,%edi 400e14: e8 07 fe ff ff callq 400c20 <exit@plt> #exit(8); 400e19: e8 84 05 00 00 callq 4013a2 <initialize_bomb> #调用initialize_bomb(); 400e1e: bf 38 23 40 00 mov $0x402338,%edi 400e23: e8 e8 fc ff ff callq 400b10 <puts@plt> #printf("Welcome to my fiendish little bomb. You have 6 phases with\n"); 400e28: bf 78 23 40 00 mov $0x402378,%edi 400e2d: e8 de fc ff ff callq 400b10 <puts@plt> #printf("which to blow yourself up. Have a nice day!\n"); 400e32: e8 67 06 00 00 callq 40149e <read_line> #调用read_line(); 400e37: 48 89 c7 mov %rax,%rdi #传参 400e3a: e8 a1 00 00 00 callq 400ee0 <phase_1> #调用phase_1(); 400e3f: e8 80 07 00 00 callq 4015c4 <phase_defused> #调用phase_defused(); 400e44: bf a8 23 40 00 mov $0x4023a8,%edi 400e49: e8 c2 fc ff ff callq 400b10 <puts@plt> #printf("Phase 1 defused. How about the next one?\n"); 400e4e: e8 4b 06 00 00 callq 40149e <read_line> #调用read_line(); 400e53: 48 89 c7 mov %rax,%rdi #传参 400e56: e8 a1 00 00 00 callq 400efc <phase_2> #调用phase_2(); 400e5b: e8 64 07 00 00 callq 4015c4 <phase_defused> 400e60: bf ed 22 40 00 mov $0x4022ed,%edi 400e65: e8 a6 fc ff ff callq 400b10 <puts@plt> 400e6a: e8 2f 06 00 00 callq 40149e <read_line> 400e6f: 48 89 c7 mov %rax,%rdi 400e72: e8 cc 00 00 00 callq 400f43 <phase_3> #调用phase_3(); 400e77: e8 48 07 00 00 callq 4015c4 <phase_defused> 400e7c: bf 0b 23 40 00 mov $0x40230b,%edi 400e81: e8 8a fc ff ff callq 400b10 <puts@plt> 400e86: e8 13 06 00 00 callq 40149e <read_line> 400e8b: 48 89 c7 mov %rax,%rdi 400e8e: e8 79 01 00 00 callq 40100c <phase_4> #调用phase_4(); 400e93: e8 2c 07 00 00 callq 4015c4 <phase_defused> 400e98: bf d8 23 40 00 mov $0x4023d8,%edi 400e9d: e8 6e fc ff ff callq 400b10 <puts@plt> 400ea2: e8 f7 05 00 00 callq 40149e <read_line> 400ea7: 48 89 c7 mov %rax,%rdi 400eaa: e8 b3 01 00 00 callq 401062 <phase_5> #调用phase_4(); 400eaf: e8 10 07 00 00 callq 4015c4 <phase_defused> 400eb4: bf 1a 23 40 00 mov $0x40231a,%edi 400eb9: e8 52 fc ff ff callq 400b10 <puts@plt> 400ebe: e8 db 05 00 00 callq 40149e <read_line> 400ec3: 48 89 c7 mov %rax,%rdi 400ec6: e8 29 02 00 00 callq 4010f4 <phase_6> #调用phase_4(); 400ecb: e8 f4 06 00 00 callq 4015c4 <phase_defused> 400ed0: b8 00 00 00 00 mov $0x0,%eax 400ed5: 5b pop %rbx 400ed6: c3 retq 大略剖析了下主函数,次要还是传参和函数的调用,想要得出后果还是要看phase_1 ~ phase_6这些函数的反汇编。 ...

December 10, 2020 · 15 min · jiezi

关于c:深入理解计算机系统读书笔记-第三章-程序的机器级表示

本章次要介绍了计算机中的机器代码——汇编语言。当咱们应用高级语言(C、Java等)编程时,代码会屏蔽机器级的细节,咱们无奈理解到机器级的代码实现。既然有了高级语言,咱们为什么还须要学习汇编语言呢?学习程序的机器级实现,能够帮忙咱们了解编译器的优化能力,能够让咱们理解程序是如何运行的,哪些局部是能够优化的;当程序受到攻打(破绽)时,都会波及到程序运行时管制信息的细节,很多程序都会利用零碎程序中的破绽信息重写程序,从而取得零碎的控制权(蠕虫病毒就是利用了gets函数的破绽)。特地是作为一名嵌入式软件开发的从业人员,会常常接触到底层的代码实现,比方Bootloader中的时钟初始化,重定位等都是用汇编语言实现的。尽管不要求咱们应用汇编语言写简单的程序,然而要求咱们要可能浏览和了解编译器产生的汇编代码。@[toc] 程序编码计算机的形象模型 在之前的《深刻了解计算机系统》(CSAPP)读书笔记 —— 第一章 计算机系统漫游文章中提到过计算机的形象模型,计算机利用更简略的形象模型来暗藏实现的细节。对于机器级编程来说,其中两种形象尤为重要。第一种是由指令集体系结构或指令集架构( Instruction Set Architecture,ISA)来定义机器级程序的格局和行为,它定义了处理器状态、指令的格局,以及每条指令对状态的影响。大多数ISA,包含x86-64,将程序的行为形容成如同每条指令都是按程序执行的,一条指令完结后,下一条再开始。处理器的硬件远比形容的精密简单,它们并发地执行许多指令,然而能够采取措施保障整体行为与ISA指定的程序执行的行为完全一致。第二种形象是,机器级程序应用的内存地址是虚拟地址,提供的内存模型看上去是一个十分大的字节数组。存储器零碎的理论实现是将多个硬件存储器和操作系统软件组合起来。 汇编代码中的寄存器 程序计数器(通常称为“PC”,在x86-64中用号%rip示意)给出将要执行的下一条指令在内存中的地址。 整数寄存器文件蕴含16个命名的地位,别离存储64位的值。这些寄存器能够存储地址(对应于C语言的指针)或整数数据。有的寄存器被用来记录某些重要的程序状态,而其余的寄存器用来保留长期数据,例如过程的参数和局部变量,以及函数的返回值。 条件码寄存器保留着最近执行的算术或逻辑指令的状态信息。它们用来实现管制或数据流中的条件变动,比如说用来实现if和 while语句 一组向量寄存器能够寄存个或多个整数或浮点数值 对于汇编中罕用的寄存器倡议看我整顿的嵌入式软件开发面试知识点中的ARM局部,外面具体介绍了Arm中罕用的寄存器和指令集。 机器代码示例 如果咱们有一个main.c文件,应用 gcc -0g -S main.c能够产生一个汇编文件。接着应用gcc -0g -c main.c就能够产生指标代码文件main.o。通常,这个.o文件是二进制格局的,无奈间接查看,咱们关上编辑器能够调整为十六进制的格局,示例如下所示。 53 48 89 d3 e8 00 00 00 00 48 89 03 5b c3 这就是汇编指令对应的指标代码。从中失去一个重要信息,即机器执行的程序只是一个字节序列,它是对一系列指令的编码。机器对产生这些指令的源代码简直无所不知。 反汇编简介 要查看机器代码文件的内容,有一类称为反汇编器( disassembler)的程序十分有用。这些程序依据机器代码产生一种相似于汇编代码的格局。在 Linux零碎中,应用命令 objdump -d main.o能够产生反汇编文件。示例如下图。 在右边,咱们看到依照后面给出的字节顺序排列的14个十六进制字节值,它们分成了若干组,每组有1~5个字节。每组都是一条指令,左边是等价的汇编语言 其中一些对于机器代码和它的反汇编示意的个性值得注意 x86-64的指令长度从1到15个字节不等。罕用的指令以及操作数较少的指令所需的字节数少,而那些不太罕用或操作数较多的指令所需字节数较多设计指令格局的形式是,从某个给定地位开始,能够将字节惟一地解码成机器指令。例如,只有指令 push%rbx是以字节值53结尾的反汇编器只是基于机器代码文件中的字节序列来确定汇编代码。它不须要拜访该程序的源代码或汇编代码反汇编器应用的指令命名规定与GCC生成的汇编代码应用的有些轻微的差异。在咱们的示例中,它省略了很多指令结尾的‘q’。这些后缀是大小批示符,在大多数状况中能够省略。相同,反汇编器给ca11和ret指令增加了‘q’后缀,同样,省略这些后缀也没有问题。数据格式 Intel用术语“字(word)”示意16位数据类型。因而,称32位数为“双字( double words)”,称64位数为“四字( quad words)。下表给出了C语言根本数据类型对应的x86-64示意。 C申明Intel数据类型汇编代码后缀大小(字节)char字节b1short字w2int双字l4long四字q8char*四字q8float单精度s4double双精度18访问信息操作数批示符整数寄存器 不同位的寄存器名字不同,应用的时候要留神。 ...

December 9, 2020 · 3 min · jiezi

关于c:深入理解计算机系统读书笔记-第二章-信息的表示和处理

本章次要钻研了计算机中无符号数,补码,浮点数的编码方式,通过钻研数字的理论编码方式,咱们可能理解计算机中不同类型的数据可示意的值的范畴,不同算术运算的属性,能够晓得计算机是如何解决数据溢出的。理解计算机的编码方式,对于咱们写出能够逾越不同机器,不同操作系统和编译器组合的代码具备重要的帮忙。@[TOC] 信息存储为什么会有二进制?二进制有什么含意和劣势? 对于有10个手指的人类来说,应用十进制表示法是很天然的事件,然而当结构存储和解决信息的机器时,二进制值工作得更好。二值信号可能很容易地被示意、存储和传输。例如,能够示意为穿孔卡片上有洞或无洞、导线上的高电压或低电压,或者顺时针或逆时针的磁场。对二值信号进行存储和执行计算的电子电路非常简单和牢靠,制造商可能在一个独自的硅片上集成数百万甚至数十亿个这样的电路。孤立地讲,单个的位不是十分有用。然而,当把位组合在一起,再加上某种解释,即赋予不同的可能位模式以含意,咱们就可能示意任何无限汇合的元素。比方,应用一个二进制数字零碎,咱们可能用位组来编码非正数。通过应用规范的字符码咱们可能对文档中的字母和符号进行编码。 计算机的三种编码方式 无符号:无符号(unsigned)编码基于传统的二进制表示法,示意大于或者等于零的数字。 补码:补码(two' s-complement)编码是示意有符号整数的最常见的形式,有符号整数就是能够为正或者为负的数字。 浮点数:浮点数( floating-point)编码是示意实数的迷信记数法的以2为基数的版本。 整数&浮点数 在计算机中,整数的运算合乎运算符的交换律和结合律,溢出的后果会示意为正数。整数的编码范畴比拟小,然而其后果示意是准确的。 浮点数的运算是不可联合的,并且其溢出会产生非凡的值——正无穷。浮点数的编码范畴大,然而其后果示意是近似的。 造成上述不同的起因次要是因为计算机对于整数和浮点数的编码格局不同。 虚拟内存&虚拟地址空间 大多数计算机应用8位的块,或者字节(byte),作为最小的可寻址的内存单位,而不是拜访内存中独自的位。机器级程序将内存视为一个十分大的字节数组,称为虚拟内存( virtual memory)。内存的每个字节都由一个惟一的数字来标识,称为它的地址(address),所有可能地址的汇合就称为虚拟地址空间( virtual address space)。 指针是由数据类型和指针值形成的,它的值示意某个对象的地位,而它的类型示意那个地位上所存储对象的类型(比方整数或者浮点数)。C语言中任何一个类型的指针值对应的都是一个虚拟地址。C语言编译器能够依据不同类型的指针值生成不同的机器码来拜访存储在指针所指向地位处的值。然而它生成的理论机器级程序并不蕴含对于数据类型的信息。 二进制&十进制&十六进制二进制转十六进制(分组转换) 四位二进制能够示意一位十六进制。二进制和十六进制的相互转换方法如下表所示。这里就不开展解说了。 十六进制173A4C二进制000101110011101001001100十进制转十六进制Gamma公式展现 $\Gamma(n) = (n-1)!\quad\foralln\in\mathbb N$ 是通过 Euler integral 设x为2的非负整数n次幂时,也就是$x = {2^n}$。咱们能够很容易地将x写成十六进制模式,只有记住x的二进制示意就是1前面跟n个0(比方$1024 = {2^{10}}$,二进制为10000000000)。十六进制数字0代表4个二进制0。所以,当n示意成i+4j的模式,其中0≤i≤3,咱们能够把x写成结尾的十六进制数字为1(i=0)、2(i=1)、4(i=2)或者8(i=3),前面跟随着j个十六进制的0。比方,$2048 = {2^{11}}$,咱们有n=11=3+4*2,从而失去十六进制示意为0x800。上面再看几个例子。 n${2^{n}}$(十进制)${2^{n}}$(十六进制)95120x20019(3+4*4)5242880x8000014(2+4*2)163840x400016(0+4*4)655360x1000017(1+4*4)1310720x200005(1+4*1)320x207(3+4*1)1280x80 十进制转十六进制还能够应用另一种办法:辗转相除法。反过来,十六进制转十进制能够用相应的16的幂乘以每个十六进制数字。 虚拟地址的范畴 每台计算机都有一个字长( word size),指明指针数据的标称大小( nominal size)。因为虚拟地址是以这样的一个字来编码的,所以字长决定的最重要的零碎参数就是虚拟地址空间的最大大小。也就是说,对于一个字长为w位的机器而言,虚拟地址的范畴为0~${2^{w}}$-1 。程序最多拜访${2^{w}}$个字节。 16位字长机器的地址范畴:0~65535(FFFF)32位字长机器的地址范畴:0~4294967296(FFFFFFFF,4GB) 64位字长机器的地址范畴:0~18446744073709551616(1999999999999998,16EB) 32位编译指令:gcc -m32 main.c 64位编译指令:gcc -m64 main.c C语言根本数据类型的典型大小(字节为单位)有符号无符号32位64位[signed] charunsigned char11shortunsigned short22intunsigned int44longunsigned long48int32_tuint32_t44int64_tuint64_t88char* 48float 44double 88 留神:根本C数据类型的典型大小调配的字节数是由编译器如何编译所决定的,并不是由机器位数而决定的。本表给出的是32位和64位程序的典型值。 ...

December 8, 2020 · 5 min · jiezi

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

本文是CSAPP第二章的配套试验,通过应用无限的运算符来实现负数,正数,浮点数的位级示意。通过实现这13个函数,能够使咱们更好的了解计算机中数据的编码方式。筹备工作 首先去官网Lab Assignments取得试验相干的文件(也能够加我QQ获取教学视频、PPT等内容)在每个试验文件的README中都具体介绍了如何批改程序,编译程序等。倡议仔细阅读,有不明确的能够留言,看到后会及时回复。 我的编译环境:Ubuntu 16.04,gcc 5.4.0。 编译时会报如下谬误。 <img src="https://gitee.com/dongxingbo/Picture/raw/master//%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B3%BB%E7%BB%9F/datalab_%E6%8A%A5%E9%94%991.png" alt="image-20201026150615228" /> 执行以下命令,装置64位包。 sudo apt-get purge libc6-devsudo apt-get install libc6-devsudo apt-get install libc6-dev-i386 再次编译,没有报错,失常。 题目bitXor思路 德摩根律,也叫反演。 代码/* * bitXor - x^y using only ~ and & * Example: bitXor(4, 5) = 1 * Legal ops: ~ & * Max ops: 14 * Rating: 1 */int bitXor(int x, int y) { return ~(x & y) & ~(~x & ~y);}tmin思路 补码的最小值0x80000000 ...

December 6, 2020 · 6 min · jiezi

关于c:粗暴的给你的phpsrc源码加点东西

PS:本文不是钻研php-src源码而是间接粗犷的给你一些仿佛没啥卵用的后果或是十分有用的后果 *你平时有没有遇到如下状况: 1 接的外包我的项目做好了,后果对方死活不给钱? 2 xx不发工资,我的项目做好了 3 当这个php-src源码【你加了点货色当前】你能够施展好多货色 如植入点广告,攻打,监控,调试。。。。 4 其它骚操作 * 5 数据泄露了,服务器平白无故少了货色,多了货色,服务器平白无故进行。 当你能加点货色当前,千万不要拿这些小聪明放在公司的我的项目中应用,也不要做好事!!!毕竟年轻人还是讲点码德! 【周末无聊写点文档混日子这样子】 fpm运行调试 1 从github下载源码php-src 【轻易哪个版本】下载源码后,解压而后依据INSTALL.md文件的装置流程【当前记得读README.md,INSTALL.md文件,百度只会给你广告】 2而后批改以下文件,加点货色【批改的是php_cli.c文件】 3再批改fm_request.c 增加一些测试代码 下面两个文件批改好当前,在管制终端之下编译装置【什么?不晓得什么是管制终端?那你得补一下根底了】 4 而后复制粘贴【INSTALL.md】文件里的装置命令进行编译装置,而后要等一小伙儿。 5 而后咱们通过浏览器拜访 而后失去如下后果[如果相干概念你不分明能够分割我,给你点货色康康] 以上呢是咱们深度在php-src源码里批改相干源码,而后进行编译后写进去的。那么当初咱们在测试一下cli模式运行时。是怎么样的。 cli运行形式测试 ./php demo.php 之后,如下后果 这样呢咱们的php运行在cli模式下或是通过浏览器去拜访php-fpm下的worker过程时,咱们能获取相干的数据了。 上面呢我暴力点 把php-fpm运行时的数据【这里演示对方机器编译装置了php+nginx,当对方的利用有用户拜访时,会把数据发到我的私人服务器上,我只拿一下它申请的文件名,申请形式,当然对方机器的源码,数据库所有数据,以及各种敏感数据,或者是能够间接管制对方机器,毕竟在源码中咱们能够间接应用c操作linux的所有!】 改好之后,我从新编译装置。 而后测试如下 总结:当你的服务器数据泄露时,如果你对linux下开发不相熟,对linux api不相熟,多过程,网络方面,终端...理解不够深刻的话,遇到这种,可能你真的查不进去。 你在php脚本中找是不事实的,毕竟他人动了php-src源码,比方比拟火的docker,在外面动手脚也非常容易,并且docker查起来更麻烦。

December 5, 2020 · 1 min · jiezi

关于c:嵌入式Linux1百问网课后题

第四篇:嵌入式Linux利用开发基础知识第六章:文件显示 - 6.3 中文字符的点阵显示批改 lcd_put_chinese 函数,能够指定字符色彩。实现 lcd_put_str 函数, 能够输入混合的中英文字符,比方“中国 china”,反对主动换行。成果:读取指定文件中英文数据到lcd显示文件:show_chinses.c #include <linux/fb.h>#include <sys/mman.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <stdio.h>extern const unsigned char fontdata_8x16[]; // fontdata_8x16 独自在 fontdata.cstatic int fd_fb = 0;static unsigned char *fb_base = NULL;static struct fb_var_screeninfo var = {0};static int screen_size = 0;static int line_width = 0;static int pixel_width = 0;static int fd_hzk16;static struct stat hzk_stat = {0};static unsigned char *hzkmem = {0};void lcd_put_pixel(int x, int y, unsigned int color){ unsigned char *pen_8 = fb_base + y * line_width + x * pixel_width; unsigned short *pen_16 = (unsigned short*)(pen_8); unsigned int *pen_32 = (unsigned int*)(pen_8); unsigned int red = 0; unsigned int green = 0; unsigned int blue = 0; switch (var.bits_per_pixel) { case 8: *pen_8 = color; break; case 16: red = (color >> 16) & 0xff; green = (color >> 8) & 0xff; blue = (color >> 0) & 0xff; color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); *pen_16 - color; break; case 32: *pen_32 = color; break; default: printf("can't open %dbpp\n", var.bits_per_pixel); break; }}void lcd_put_ascii(int x, int y, char c, unsigned int color){ unsigned char *dots = (unsigned char*)(&fontdata_8x16[c * 16]); int i = 0; int b = 0; for (i=0; i<16; ++i) { for (b=7; b>=0; --b) { if (dots[i] & (1 << b)) lcd_put_pixel(x+7-b, y+i, color); else lcd_put_pixel(x+7-b, y+i, 0xffffff); } }}void lcd_put_ascii_str(int x, int y, const unsigned char *str, unsigned int color){ unsigned int index = 0; while(*str) { if (*str == '\r' || *str == '\n') { y += 16; index = 0; } else { lcd_put_ascii(x + index, y, *str, color); index += 8; } ++str; }}void lcd_put_chinese(int x, int y, const unsigned char *str, unsigned int color){ unsigned int area = str[0] - 0xa1; unsigned int where = str[1] - 0xa1; unsigned char *dots = hzkmem + (area * 94 + where) * 32; unsigned char byte = 0; int i = 0; int j = 0; int b = 0; for (i=0; i<16; ++i) { for (j=0; j<2; ++j) { byte = dots[i*2 + j]; for (b = 7; b>=0; --b) { if (byte & (1 << b)) lcd_put_pixel(x+j*8+7-b, y+i, color); else lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); } } }}void lcd_put_str(int x, int y, const unsigned char*str, unsigned int color){ unsigned int index = 0; while (*str) { if (*str <= 0xa1) { if (*str == '\r' || *str == '\n') { y += 16; index = 0; } else { lcd_put_ascii(x + index, y, *str, color); index += 8; } str++; } else { lcd_put_chinese(x + index, y, str, color); index += 16; str+=2; } }}int test_show(const unsigned char *path){ int fd_src = 0; unsigned char *srcmem = NULL; struct stat src_stat = {0}; fd_src = open(path, O_RDONLY); if (fd_src < 0) { printf("can't open %s\n", path); return -1; } if(fstat(fd_src, &src_stat)) { printf("can't get src_stat\n"); return -1; } srcmem = (unsigned char*)mmap(NULL, src_stat.st_size, PROT_READ, MAP_SHARED, fd_src, 0); if (srcmem == (void*)-1) { printf("can't mmap for srcmem\n"); return -1; } lcd_put_str(10, 10, srcmem, 0x11ee88); munmap(srcmem, src_stat.st_size); close(fd_src); }int main(int argc, char **argv){ int i = 0; fd_hzk16 = open("HZK16", O_RDONLY); if(fd_hzk16 < 0) { printf("can't open HZK16\n"); return -1; } if(fstat(fd_hzk16, &hzk_stat)) { printf("can't get fstat\n"); return -1; } hzkmem = (unsigned char*)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0); if (hzkmem == (void*)-1) { printf("can't mmap for hzkmem\n"); return -1; } fd_fb = open("/dev/fb0", O_RDWR); if(fd_fb < 0) { printf("can't open /dev/fb0\n"); return -1; } if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) { printf("can't get fd_fb var\n"); return -1; } screen_size = var.xres * var.yres * var.bits_per_pixel / 8; pixel_width = var.bits_per_pixel / 8; line_width = var.xres * var.bits_per_pixel / 8; fb_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); if (fb_base == (void*)-1) { printf("cant mmap for fb_base\n"); return -1; } memset(fb_base, 0xff, screen_size); //==================================================== if (argc == 1) { // lcd_put_ascii(100, 100, 'A', 0); // lcd_put_ascii_str(100, 100, "Hello word\nHow are you", 0x567894); // lcd_put_chinese(200, 200, "中", 0x123456); lcd_put_str(100, 100, "A中A\nhahah", 0x11ee88); } else { test_show(argv[1]) ; } //==================================================== munmap(fb_base, screen_size); munmap(hzkmem, hzk_stat.st_size); close(fd_fb); close(fd_hzk16); return 0;}编译运行: ...

November 28, 2020 · 4 min · jiezi

关于c:嵌入式Linux1百问网课后题

第四篇:嵌入式Linux利用开发基础知识第六章:文件显示 - 6.3 中文字符的点阵显示批改 lcd_put_chinese 函数,能够指定字符色彩。实现 lcd_put_str 函数, 能够输入混合的中英文字符,比方“中国 china”,反对主动换行。成果:读取指定文件中英文数据到lcd显示文件:show_chinses.c #include <linux/fb.h>#include <sys/mman.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <stdio.h>extern const unsigned char fontdata_8x16[]; // fontdata_8x16 独自在 fontdata.cstatic int fd_fb = 0;static unsigned char *fb_base = NULL;static struct fb_var_screeninfo var = {0};static int screen_size = 0;static int line_width = 0;static int pixel_width = 0;static int fd_hzk16;static struct stat hzk_stat = {0};static unsigned char *hzkmem = {0};void lcd_put_pixel(int x, int y, unsigned int color){ unsigned char *pen_8 = fb_base + y * line_width + x * pixel_width; unsigned short *pen_16 = (unsigned short*)(pen_8); unsigned int *pen_32 = (unsigned int*)(pen_8); unsigned int red = 0; unsigned int green = 0; unsigned int blue = 0; switch (var.bits_per_pixel) { case 8: *pen_8 = color; break; case 16: red = (color >> 16) & 0xff; green = (color >> 8) & 0xff; blue = (color >> 0) & 0xff; color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); *pen_16 - color; break; case 32: *pen_32 = color; break; default: printf("can't open %dbpp\n", var.bits_per_pixel); break; }}void lcd_put_ascii(int x, int y, char c, unsigned int color){ unsigned char *dots = (unsigned char*)(&fontdata_8x16[c * 16]); int i = 0; int b = 0; for (i=0; i<16; ++i) { for (b=7; b>=0; --b) { if (dots[i] & (1 << b)) lcd_put_pixel(x+7-b, y+i, color); else lcd_put_pixel(x+7-b, y+i, 0xffffff); } }}void lcd_put_ascii_str(int x, int y, const unsigned char *str, unsigned int color){ unsigned int index = 0; while(*str) { if (*str == '\r' || *str == '\n') { y += 16; index = 0; } else { lcd_put_ascii(x + index, y, *str, color); index += 8; } ++str; }}void lcd_put_chinese(int x, int y, const unsigned char *str, unsigned int color){ unsigned int area = str[0] - 0xa1; unsigned int where = str[1] - 0xa1; unsigned char *dots = hzkmem + (area * 94 + where) * 32; unsigned char byte = 0; int i = 0; int j = 0; int b = 0; for (i=0; i<16; ++i) { for (j=0; j<2; ++j) { byte = dots[i*2 + j]; for (b = 7; b>=0; --b) { if (byte & (1 << b)) lcd_put_pixel(x+j*8+7-b, y+i, color); else lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); } } }}void lcd_put_str(int x, int y, const unsigned char*str, unsigned int color){ unsigned int index = 0; while (*str) { if (*str <= 0xa1) { if (*str == '\r' || *str == '\n') { y += 16; index = 0; } else { lcd_put_ascii(x + index, y, *str, color); index += 8; } str++; } else { lcd_put_chinese(x + index, y, str, color); index += 16; str+=2; } }}int test_show(const unsigned char *path){ int fd_src = 0; unsigned char *srcmem = NULL; struct stat src_stat = {0}; fd_src = open(path, O_RDONLY); if (fd_src < 0) { printf("can't open %s\n", path); return -1; } if(fstat(fd_src, &src_stat)) { printf("can't get src_stat\n"); return -1; } srcmem = (unsigned char*)mmap(NULL, src_stat.st_size, PROT_READ, MAP_SHARED, fd_src, 0); if (srcmem == (void*)-1) { printf("can't mmap for srcmem\n"); return -1; } lcd_put_str(10, 10, srcmem, 0x11ee88); munmap(srcmem, src_stat.st_size); close(fd_src); }int main(int argc, char **argv){ int i = 0; fd_hzk16 = open("HZK16", O_RDONLY); if(fd_hzk16 < 0) { printf("can't open HZK16\n"); return -1; } if(fstat(fd_hzk16, &hzk_stat)) { printf("can't get fstat\n"); return -1; } hzkmem = (unsigned char*)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0); if (hzkmem == (void*)-1) { printf("can't mmap for hzkmem\n"); return -1; } fd_fb = open("/dev/fb0", O_RDWR); if(fd_fb < 0) { printf("can't open /dev/fb0\n"); return -1; } if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) { printf("can't get fd_fb var\n"); return -1; } screen_size = var.xres * var.yres * var.bits_per_pixel / 8; pixel_width = var.bits_per_pixel / 8; line_width = var.xres * var.bits_per_pixel / 8; fb_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); if (fb_base == (void*)-1) { printf("cant mmap for fb_base\n"); return -1; } memset(fb_base, 0xff, screen_size); //==================================================== if (argc == 1) { // lcd_put_ascii(100, 100, 'A', 0); // lcd_put_ascii_str(100, 100, "Hello word\nHow are you", 0x567894); // lcd_put_chinese(200, 200, "中", 0x123456); lcd_put_str(100, 100, "A中A\nhahah", 0x11ee88); } else { test_show(argv[1]) ; } //==================================================== munmap(fb_base, screen_size); munmap(hzkmem, hzk_stat.st_size); close(fd_fb); close(fd_hzk16); return 0;}编译运行: ...

November 28, 2020 · 4 min · jiezi

关于c:C语言的角落这些C语言不常用的特性你知道吗

变长参数列表 <stdarg.h> 头文件定义了一些宏,当函数参数未知时去获取函数的参数 变量:typedef va_list 宏: va_start() va_arg() va_end() va_list类型通过stdarg宏定义来拜访一个函数的参数表,参数列表的开端会用省略号省略 (va_list用来保留va_start,va_end所需信息的一种类型。为了拜访变长参数列表中的参数,必须申明va_list类型的一个对象 ) 咱们通过初始化(va_start)类型为va_list的参数表指针,并通过va_arg来获取下一个参数。 【例子:】 求任意个整数的最大值: 可变长数组 历史上,C语言只反对在编译时就能确定大小的数组。程序员须要变长数组时,不得不用malloc或calloc这样的函数为这些数组调配存储空间,且波及到多维数组时,不得不显示地编码,用行优先索引将多维数组映射到一维的数组。 ISOC99引入了一种能力,容许数组的维度是表达式,在数组被调配的时候才计算出来。 留神:如果你须要有着变长大小的长期存储,并且其生命周期在变量外部时,可思考VLA(Variable Length Array,变长数组)。但这有个限度:每个函数的空间不能超过数百字节。因为C99指出边长数组能主动存储,它们像其余主动变量一样受限于同一作用域。即使规范未明确规定,VLA的实现都是把内存数据放到栈中。VLA的最大长度为SIZE_MAX字节。思考到指标平台的栈大小,咱们必须更加审慎小心,以保障程序不会面临栈溢出、下个内存段的数据损坏的难堪场面。 case反对范畴取值(gcc扩大个性) MinGW编译通过 非部分跳转setjmp和longjmp 在C中,goto语句是不能逾越函数的,而执行这类跳转性能的是setjmp和longjmp宏。这两个宏对于解决产生在深层嵌套函数调用中的出错状况是十分有用的。 此即为:非部分跳转。非部分指的是,这不是由一般C语言goto语句在一个函数内施行的跳转,而是在栈上跳过若干调用帧,返回到以后函数调用门路的某个函数中。 `#include <setjmp.h> int setjmp(jmp_buf env) ; /设置调转点/ void longjmp(jmp_bufenv, int val) ; /跳转/` setjmp参数env的类型是一个非凡类型jmp_buf。这一数据类型是某种模式的数组,其中寄存 在调用longjmp时能用来复原栈状态的所有信息。因为需在另一个函数中援用env变量,所以应该将env变量定义为全局变量。 longjmp参数val,它将成为从setjmp处返回的值。(很神奇吧。setjmp依据返回值可晓得是哪个longjmp返回来的) 举荐本人的linuxC/C++交换群:812855908!整顿了一些集体 感觉比拟好的学习书籍、视频材料以及大厂面经视频共享在群文件外面,有须要的 能够自行添加哦!~ volatile属性 如果你有一个主动变量,而又不想它被编译器优化进寄存器,则可定义其为有volatile属性。这样,就明确地把这个值放在存储器中,而不会被优化进寄存器。 setjmp会保留以后栈状态信息,也会保留此时寄存器中的值。(longjmp会回滚寄存器中的值) 【如果要编写一个应用非部分跳转的可移植程序,则必须应用volatile属性】 · IO缓冲问题 缓冲输入和内存调配 当一个程序产生输入时,可能立刻看到它有多重要?这取决于程序。 例如,终端上显示输入并要求人们坐在终端后面答复一个问题,人们可能看到输入以晓得该输出什么就显得至关重要了。另一方面,如果输入到一个文件中,并最终被发送到一个行式打印机,只有所有的输入最终可能达到那里是重要的。 立刻安顿输入的显示通常比将其临时保留在一大块一起输入要低廉得多。因而,C实现通常容许程序员管制产生多少输入后在理论地写出它们。 这个管制通常约定为一个称为setbuf()的库函数。如果buf是一个具备适当大小的字符数组,则 setbuf(stdout, buf); 将通知I/O库写入到stdout中的输入要以buf作为一个输入缓冲,并且等到buf满了或程序员间接调用fflush()再理论写出。缓冲区的适合的大小在中定义为BUFSIZ。 因而,上面的程序解释了通过应用setbuf()来讲规范输出复制到规范输入: 可怜的是,这个程序是谬误的,因为一个轻微的起因。 要晓得故障出在哪,咱们须要晓得缓冲区最初一次刷新是在什么时候。答案:主程序实现之后,库将管制交回到操作系统之前所执行的清理的一部分。在这一时刻,缓冲区曾经被开释了! (即main函数栈清空之后) ...

November 24, 2020 · 2 min · jiezi

关于c:C函数设计原则

函数设计准则函数从意义上应该是一个独立功能模块函数名要在肯定水平上反映函数的工能函数函数名要可能体现参数的意义尽量避免在函数中应用全局变量,无状态函数,当函数参数不应该在函数体外部被批改时,应加上const申明如果参数是指针,且仅作输出参数,则应加上const申明不能省略返回值类型,如果函数没有返回值,那么应申明为void类型对参数进行有效性查看,对于指针参数的查看尤为重要不要返回指向“栈内存”的指针,栈内存在函数体完结时主动开释函数体的规模要小,尽量管制在80行代码之内雷同的输出对应雷同的输入,防止函数带有“记忆”性能防止函数有过多的参数,参数个数尽量管制在4个以内有时候函数不须要返回值,但为了减少灵活性,如反对链式表白,能够附加返回值char s[64];int len = strlen(strcpy(s,"android"));函数名与返回值类型在语义上不可抵触

November 19, 2020 · 1 min · jiezi

关于c:CC宏替换详解

根本模式#define name replacement_text 通常状况下,#define 指令占一行,替换文本是 define 指令行尾部的所有残余局部,但也能够把一个较长的宏定义分成若干行,这时须要在待续的行开端加上一个反斜杠符 ``。 宏定义也能够带参数,这样能够对不同的宏调用应用不同的替换文本。例: #define max(A, B) ((A) > (B) ? (A) : (B)) 宏开展中的陷阱认真考虑一下 max 的展开式,其中的表达式会被计算两次,因而如果表达式中蕴含自增运算符或输出/输入等行为,则会呈现不正确的状况,例如上述的宏 max: max(i++, j++) // wrong 另外还须要留神,适当应用圆括号以保障计算秩序的正确性,例如: #define square(x) x * x // wrong 当用 square(z+1) 调用该宏定义时会出错。 #undef在头文件 <stdio.h> 中,getchar 与 putchar 函数在理论中经常被定义为宏,这样能够防止解决字符时调用函数所需的运行时开销。<ctype.h> 头文件中定义的函数也经常是通过宏实现的。 能够通过 #define 勾销名字的宏定义,这样做能够保障后续的调用是函数调用,而不是宏调用: #undef getcharint getchar(void) { ... } 宏参数、# 和 ##如果在宏定义的替换文本中,参数名以 # 作为前缀则后果将被扩大为由理论参数替换该参数的带引号的字符串。例如,能够将它与字符串连贯运算联合起来编写一个调试打印宏: #define dprint(expr) printf(#expr " = %gn", expr) 应用语句 ...

November 18, 2020 · 1 min · jiezi

关于c:C语言中的状态机设计

本文不是关于软件状态机的最佳设计合成实际的教程。我将重点关注状态机代码和简略的示例,这些示例具备足够的复杂性,以便于了解个性和用法。 背景大多数程序员罕用的设计技术是无限状态机(FSM)。设计人员应用此编程构造将简单的问题合成为可治理的状态和状态转换。有无数种实现状态机的办法。 A switch语句提供了状态机最容易实现和最常见的版本之一。在这里,每个案例在switch语句成为一个状态,实现如下所示: switch (currentState) { case ST_IDLE: // do something in the idle state break; case ST_STOP: // do something in the stop state break; // etc...} 这种办法当然适宜于解决许多不同的设计问题。然而,在事件驱动的多线程我的项目上应用时,这种模式的状态机可能是十分无限的。 第一个问题是管制哪些状态转换是无效的,哪些是有效的。无奈强制执行状态转换规则。任何过渡都能够在任何时候进行,这并不是特地可取的。对于大多数设计,只有多数转换模式是无效的。现实状况下,软件设计应该强制执行这些预约义的状态序列,并避免不必要的转换。当试图将数据发送到特定状态时,会呈现另一个问题。因为整个状态机位于单个函数中,因而向任何给定状态发送额定数据都是艰难的。最初,这些设计很少适宜在多线程零碎中应用。设计器必须确保状态机是从单个控制线程调用的。 为什么要用国家机器?应用状态机实现代码是解决简单工程问题的一种十分不便的设计技术。状态机将设计合成为一系列步骤,或在状态机术语中称为状态。每个状态都执行一些广义的工作。另一方面,事件是一种刺激,它导致状态机在状态之间挪动或过渡。 举一个简略的例子,我将在本文中应用它,假如咱们正在设计电机控制软件。咱们想启动和进行电机,以及扭转电机的速度。很简略。向客户端软件公开的电机管制事件如下: 设定速度-设定电机以特定速度行驶站住-进行马达这些事件提供了以任何速度启动电机的能力,这也意味着扭转曾经挪动的电机的速度。或者咱们能够齐全进行马达。对于电机管制模块,这两个事件或性能被认为是内部事件.然而,对于应用咱们的代码的客户机来说,这些只是一般的函数。 这些事件不是状态机状态。解决这两个事件所需的步骤是不同的。在这种状况下,各州是: 闲散-马达不是旋转的,而是静止的鸿鹄之志启动-从死胡同启动马达开启电动机电源设定电机转速变速-调整曾经挪动的马达的速度扭转电机转速停-进行挪动的马达敞开电动机电源进入闲置状态能够看出,将电机管制合成为离散状态,而不是繁多的性能,咱们能够更容易地治理如何操作电机的规定。 每个状态机都有“以后状态”的概念。这是状态机以后所处的状态。在任何给定的时刻,状态机只能处于繁多状态。特定状态机实例的每个实例在定义时都能够设置初始状态。然而,该初始状态在对象创立期间不执行。只有发送到状态机的事件才会导致执行状态函数。 为了图形化地阐明状态和事件,咱们应用状态图。上面的图1显示了电机管制模块的状态转换。框示意状态,连贯箭头示意事件转换。列出事件名称的箭头是内部事件,而未装璜的行被认为是外部事件。(本文前面将介绍外部事件和内部事件之间的差别。) 图1:电机状态图 如您所见,当事件在状态转换中呈现时,所产生的状态转换取决于状态机的以后状态。当SetSpeed事件呈现,例如,电机在Idle状态,则转换为Start状态。然而,同样的SetSpeed以后状态为Start将电机转换为ChangeSpeed状态。您还能够看到,并非所有的状态转换都是无效的。例如,马达不能从ChangeSpeed到Idle而不须要先通过Stop状态。 简而言之,应用状态机捕捉和执行简单的交互,否则可能很难传递和实现。 内外事件正如我后面提到的,事件是导致状态机在状态之间转换的刺激。例如,按下按钮可能是一个事件。事件能够分为两类:内部事件和外部事件。内部事件,在其最根本的级别上,是对状态机模块的函数调用.这些函数是公共的,从内部调用,或者从内部代码调用到状态机对象。零碎中的任何线程或工作都能够生成内部事件。如果内部事件函数调用导致状态转换产生,则状态将在调用方的控制线程内同步执行。另一方面,外部事件是由状态机自身在状态执行期间自行生成的。 典型的场景由生成的内部事件组成,该事件同样能够归结为模块的公共接口中的函数调用。依据正在生成的事件和状态机的以后状态,执行查找以确定是否须要转换。如果是这样,状态机将转换到新状态,并执行该状态的代码。在状态函数的开端,执行查看以确定是否生成了外部事件。如果是这样,则执行另一个转换,并且新的状态有机会执行。此过程将持续进行,直到状态机不再生成外部事件,此时原始内部事件函数调用将返回。内部事件和所有外部事件(如果有的话)在调用者的控制线程中执行。 一旦内部事件启动状态机执行,它不能被另一个内部事件中断,直到内部事件和所有外部事件曾经实现执行,如果应用锁。这个运行到实现模型为状态转换提供了一个多线程平安的环境。能够在状态机引擎中应用信号量或互斥量来阻止可能同时拜访同一状态机实例的其余线程。见源代码函数_SM_ExternalEvent()对于锁的地位的正文。 事件数据生成事件时,它能够抉择附加事件数据,以便在执行过程中由状态函数应用。事件数据是一个const或者不是-const 指向任何内置或用户定义的数据类型的指针。 一旦状态实现执行,事件数据就被认为用完了,必须删除。因而,发送到状态机的任何事件数据都必须通过SM_XAlloc()。状态机引擎主动开释调配的事件数据。SM_XFree(). 状态转变当生成内部事件时,执行查找以确定状态转换操作过程。事件有三种可能的后果:新状态、疏忽事件或不能产生。新状态会导致转换到容许执行的新状态。转换到现有状态也是可能的,这意味着以后状态被从新执行。对于被疏忽的事件,不执行任何状态。然而,事件数据(如果有的话)将被删除。最初一种不可能产生的可能性是保留在事件在状态机的以后状态下有效的状况下应用的。如果产生这种状况,软件就会呈现故障。 在此实现中,执行验证转换查找不须要外部事件。假如状态转换是无效的。您能够查看无效的外部和内部事件转换,但实际上,这只会占用更多的存储空间,并且只会产生很少的益处。验证转换的真正须要在于异步的内部事件,在这些事件中,客户端可能导致事件在不适当的工夫产生。一旦状态机执行,它就不能被中断。它处于公有实现的管制之下,因而没有必要进行转换查看。这使设计人员能够自在地通过外部事件更改状态,而无需更新转换表。 状态机模块状态机源代码蕴含在_StateMachine.c_和_StateMachine.h_档案。上面的代码显示了局部题目。这个StateMachine 报头蕴含各种预处理器多行宏,以简化状态机的实现。 enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN = 0xFF };typedef void NoEventData;// State machine constant datatypedef struct{ const CHAR* name; const BYTE maxStates; const struct SM_StateStruct* stateMap; const struct SM_StateStructEx* stateMapEx;} SM_StateMachineConst;// State machine instance datatypedef struct { const CHAR* name; void* pInstance; BYTE newState; BYTE currentState; BOOL eventGenerated; void* pEventData;} SM_StateMachine;// Generic state function signaturestypedef void (*SM_StateFunc)(SM_StateMachine* self, void* pEventData);typedef BOOL (*SM_GuardFunc)(SM_StateMachine* self, void* pEventData);typedef void (*SM_EntryFunc)(SM_StateMachine* self, void* pEventData);typedef void (*SM_ExitFunc)(SM_StateMachine* self);typedef struct SM_StateStruct{ SM_StateFunc pStateFunc;} SM_StateStruct;typedef struct SM_StateStructEx{ SM_StateFunc pStateFunc; SM_GuardFunc pGuardFunc; SM_EntryFunc pEntryFunc; SM_ExitFunc pExitFunc;} SM_StateStructEx;// Public functions#define SM_Event(_smName_, _eventFunc_, _eventData_) _eventFunc_(&_smName_##Obj, _eventData_)// Protected functions#define SM_InternalEvent(_newState_, _eventData_) _SM_InternalEvent(self, _newState_, _eventData_)#define SM_GetInstance(_instance_) (_instance_*)(self->pInstance);// Private functionsvoid _SM_ExternalEvent(SM_StateMachine* self, const SM_StateMachineConst* selfConst, BYTE newState, void* pEventData);void _SM_InternalEvent(SM_StateMachine* self, BYTE newState, void* pEventData);void _SM_StateEngine(SM_StateMachine* self, const SM_StateMachineConst* selfConst);void _SM_StateEngineEx(SM_StateMachine* self, const SM_StateMachineConst* selfConst);#define SM_DECLARE(_smName_) extern SM_StateMachine _smName_##Obj; #define SM_DEFINE(_smName_, _instance_) SM_StateMachine _smName_##Obj = { #_smName_, _instance_, 0, 0, 0, 0 }; #define EVENT_DECLARE(_eventFunc_, _eventData_) void _eventFunc_(SM_StateMachine* self, _eventData_* pEventData);#define EVENT_DEFINE(_eventFunc_, _eventData_) void _eventFunc_(SM_StateMachine* self, _eventData_* pEventData)#define STATE_DECLARE(_stateFunc_, _eventData_) static void ST_##_stateFunc_(SM_StateMachine* self, _eventData_* pEventData);#define STATE_DEFINE(_stateFunc_, _eventData_) static void ST_##_stateFunc_(SM_StateMachine* self, _eventData_* pEventData) ...

November 11, 2020 · 5 min · jiezi

关于c:单链表反转

#include <stdio.h>#include <stdlib.h>struct ListNode { int val; struct ListNode* next;};void display(struct ListNode* head){ struct ListNode* current = head; while(current != NULL) { printf("%d\n", current->val); current = current->next; }}void push(struct ListNode* head, int val){ struct ListNode* current = head; while(current->next != NULL) { current = current->next; } current->next = (struct ListNode*)malloc(sizeof(struct ListNode)); current->next->val = val; current->next->next = NULL;}struct ListNode* traverse(struct ListNode* head){ if (head == NULL) { return NULL; } struct ListNode* newHead = NULL; while(head != NULL) { struct ListNode* temp = (struct ListNode*)malloc(sizeof(struct ListNode)); temp->val = head->val; if (newHead == NULL) { temp->next = NULL; } else { temp->next = newHead; } newHead = temp; head = head->next; } return newHead;}int main(int argc, char const *argv[]){ struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode)); if (head == NULL) { return 1; } head->val = 1; head->next = NULL; push(head, 2); push(head, 3); push(head, 4); push(head, 5); push(head, 6); display(head); struct ListNode* newHead = traverse(head); printf("\n"); display(newHead);}

November 9, 2020 · 1 min · jiezi

关于c:C语言重点指针篇一文让你完全搞懂指针-从内存理解指针-指针完全解析

注:这篇文章好好看完肯定会让你把握好指针的实质 C语言最外围的常识就是指针,所以,这一篇的文章主题是「指针与内存模型」 说到指针,就不可能脱离开内存,学会指针的人分为两种,一种是不理解内存模型,另外一种则是理解。 不理解的对指针的了解就停留在“指针就是变量的地址”这句话,会比拟胆怯应用指针,特地是各种高级操作。 而理解内存模型的则能够把指针用得炉火纯青,各种 byte 随便操作,让人直呼 666。 一、内存实质编程的实质其实就是操控数据,数据寄存在内存中。 因而,如果能更好地了解内存的模型,以及 C 如何治理内存,就能对程序的工作原理洞若观火,从而使编程能力更上一层楼。 大家真的别认为这是空话,我大一整年都不敢用 C 写上千行的程序也很抗拒写 C。 因为一旦上千行,经常出现各种莫名其妙的内存谬误,一不小心就产生了 coredump...... 而且还无从排查,剖析不出起因。 相比之下,那时候最喜爱 Java,在 Java 里轻易怎么写都不会产生相似的异样,顶多偶然来个 NullPointerException,也是比拟好排查的。 直到起初对内存和指针有了更加粗浅的意识,才缓缓会用 C 写上千行的我的项目,也很少会再有内存问题了。(过于自信 「指针存储的是变量的内存地址」这句话应该任何讲 C 语言的书都会提到吧。 所以,要想彻底了解指针,首先要了解 C 语言中变量的存储实质,也就是内存。 1.1 内存编址计算机的内存是一块用于存储数据的空间,由一系列间断的存储单元组成,就像上面这样, 每一个单元格都示意 1 个 Bit,一个 bit 在 EE 业余的同学看来就是高下电位,而在 CS 同学看来就是 0、1 两种状态。 因为 1 个 bit 只能示意两个状态,所以大佬们规定 8个 bit 为一组,命名为 byte。 并且将 byte 作为内存寻址的最小单元,也就是给每个 byte 一个编号,这个编号就叫内存的地址。 这就相当于,咱们给小区里的每个单元、每个住户都调配一个门牌号: 301、302、403、404、501...... 在生活中,咱们须要保障门牌号惟一,这样就能通过门牌号很精准的定位到一家人。 ...

November 7, 2020 · 5 min · jiezi

关于c:选择排序

#include <stdio.h>void dispaly(int* arr, int length){ for (int i = 0; i < length; ++i) { printf("%d%s", arr[i], i == length - 1 ? " " : ", "); } printf("\n");}void swap(int* a, int* b){ int temp = *a; *a = *b; *b = temp;}void select(int* arr, int length){ for (int i = 0; i < length - 1; ++i) { int minIndex = i; for (int j = i + 1; j < length; ++j) { if (arr[j] < arr[minIndex]) { minIndex = j; } } if (i == minIndex) { continue; } swap(&arr[i], &arr[minIndex]); }}int main(int argc, char const *argv[]){ int arr[] = {3, 4, 2, 6, 7, 1, 7, 7, 21, 87, 22, 66, 11}; int length = sizeof(arr) / sizeof(int); dispaly(arr, length); select(arr, length); dispaly(arr, length); return 0;}

November 6, 2020 · 1 min · jiezi

关于c:冒泡排序

#include <stdio.h>#include <stdlib.h>void display(int* arr, int length){ for (int i = 0; i < length; ++i) { printf("%d%s", arr[i], i == length - 1 ? " " : ", "); } printf("\n");}void swap(int* a, int* b){ int temp = *a; *a = *b; *b = temp; }void bubble(int* arr, int length){ for (int i = 0; i < length - 1; ++i) { for (int j = 0; j < length - 1 - i; ++j) { if (arr[j] > arr[j + 1]) { swap(&arr[j], &arr[j + 1]); } } }}int main(int argc, char const *argv[]){ int arr[] = {2, 3, 5, 21, 2, 3, 999, 2, 34, 87}; int length = sizeof(arr) / sizeof(int); display(arr, length); bubble(arr, length); display(arr, length); return 0;}

November 6, 2020 · 1 min · jiezi

关于c:被称为程序员试金石的指针真的没有那么难不信的话你来看看

很多敌人放弃C语言都是因为指针,说什么指针的*号很厌恶啦、分不清址与值啦,当然了,最烦的还是链表结点,原本链表操作就让人烦了,再加上指针这个货色真是让初学的敌人苦不堪言,最初放弃了C语言转投其余语言的怀抱,然而只有了解指针的实质,那么把握指针只是工夫问题,这篇文章就跟大家一起来看看C语言的指针到底是何方神圣。文末放了一张c/c++入门学习纲要,有须要的敌人能够看一下。 有什么不了解的中央欢送进群973961276来跟大伙一下交换,或者间接到课堂上面对面求教c/c++ 我的项目实战,闲话少说,咱们开启注释。 一、指针到底是什么?计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样,例如 int 占用 4 个字节,char 占用 1 个字节。为了正确地拜访这些数据,必须为每个字节都编上号码,就像门牌号、身份证号一样,每个字节的编号是惟一的,依据编号能够精确地找到某个字节。下图是 4G 内存中每个字节的编号(以十六进制示意):咱们将内存中字节的编号称为地址(Address)或指针(Pointer)。地址从 0 开始顺次减少,对于 32 位环境,程序可能应用的内存为 4GB,最小的地址为 0,最大的地址为 0XFFFFFFFF。上面的代码演示了如何输入一个地址: #include <stdio.h>int main(){int a = 100;char str[20] = "c.biancheng.net";printf("%#X, %#Xn", &a, str);return 0;} 运行后果: 0X28FF3C, 0X28FF10 %#X示意以十六进制模式输入,并附带前缀0X。a 是一个变量,用来寄存整数,须要在后面加&来取得它的地址;str 自身就示意字符串的首地址,不须要加&。 C语言中有一个控制符%p,专门用来以十六进制模式输入地址,不过 %p 的输入格局并不对立,有的编译器带0x前缀,有的不带,所以此处咱们并没有采纳。一切都是地址C语言用变量来存储数据,用函数来定义一段能够重复使用的代码,它们最终都要放到内存中能力供 CPU 应用。数据和代码都以二进制的模式存储在内存中,计算机无法从格局上辨别某块内存到底存储的是数据还是代码。当程序被加载到内存后,操作系统会给不同的内存块指定不同的权限,领有读取和执行权限的内存块就是代码,而领有读取和写入权限(也可能只有读取权限)的内存块就是数据。CPU 只能通过地址来获得内存中的代码和数据,程序在执行过程中会告知 CPU 要执行的代码以及要读写的数据的地址。如果程序不小心出错,或者开发者无意为之,在 CPU 要写入数据时给它一个代码区域的地址,就会产生内存拜访谬误。这种内存拜访谬误会被硬件和操作系统拦挡,强制程序解体,程序员没有解救的机会。CPU 拜访内存时须要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,它们都会被替换成地址。编译和链接过程的一项重要工作就是找到这些名称所对应的地址。假如变量 a、b、c 在内存中的地址别离是 0X1000、0X2000、0X3000,那么加法运算c = a + b;将会被转换成相似上面的模式: 0X3000 = (0X1000) + (0X2000); ( )示意取值操作,整个表达式的意思是,取出地址 0X1000 和 0X2000 上的值,将它们相加,把相加的后果赋值给地址为 0X3000 的内存变量名和函数名为咱们提供了不便,让咱们在编写代码的过程中能够应用易于浏览和了解的英文字符串,不必间接面对二进制地址,那场景几乎让人解体。须要留神的是,尽管变量名、函数名、字符串名和数组名在实质上是一样的,它们都是地址的助记符,但在编写代码的过程中,咱们认为变量名示意的是数据自身,而函数名、字符串名和数组名示意的是代码块或数据块的首地址。 ...

November 6, 2020 · 3 min · jiezi

关于c:数据结构与算法-CPHP-二分查找实现

c语言实现://c语言实现二分搜寻#include <stdio.h>int binarySearch(int *p, int size, int target){ int left = 0; int right = size - 1; int mid = (left + right)/2; while(left <= right){ mid = (left + right)/2; if (p[mid] == target) { return mid; }else{ if (p[mid] > target) { right = mid - 1; }else{ left = left + 1; } } } return -1;}int main(){ int nums[12] = {0,1,2,3,4,5,6,7,8,9,100,200}; int size = sizeof(nums) / sizeof(nums[0]); int res = binarySearch(nums, size,100); printf("res:%d\n",res); return 0;}PHP实现://PHP二分搜寻//这里应用了PHP7的强类型语法function index(array $nums, int $target): int{ $left = 0; $len = count($nums); $right= $len - 1; $ans = -1; while ( $left <= $right) { $mid = floor(($left + $right)/2); if ($target == $nums[$mid]) { return $mid; }elseif ($target > $nums[$mid]) { $left = $mid + 1; }else{ $right = $mid - 1; } } return $ans;}

November 5, 2020 · 1 min · jiezi

关于c:C语言实现快速选择

#include <stdio.h>#include <stdlib.h>int partition(int* arr, int low, int high) { if (low >= high) { return -1; } int i = low; int j = high; int pivot = arr[low]; while(i < j) { while(i < j && arr[j] >= pivot) { j--; } if (i < j) { arr[i++] = arr[j]; } while (i < j && arr[i] < pivot) { i++; } if (i < j) { arr[j--] = arr[i]; } } arr[i] = pivot; return i;}int kthSmallest(int* arr, int low, int high, int k) { while(1) { int position = partition(arr, low, high); int index = k - 1; if (position == index) { return arr[position]; } else if (position > k) { high = position - 1; } else { low = position + 1; } }}int main(int argc, char const *argv[]){ int arr[] = {23, 322, 1, 54, 86, 2, 90, 102, 66, 233, 6, 98}; int length = sizeof(arr) / sizeof(int); int k = 3; printf("K-th smallest element is %d\n", kthSmallest(arr, 0, length - 1, k)); return 0;}

November 5, 2020 · 1 min · jiezi

关于c:C语言实现快速排序

#include <stdio.h>#include <stdlib.h>void display(int arr[], int length){ for (int i = 0; i < length; i++) { printf("%d%s", arr[i], i == length - 1 ? " " : ", "); } printf("\n");}int partition(int* arr, int low, int high) { if (low >= high) { return -1; } int i = low; int j = high; int pivot = arr[low]; while(i < j) { while(i < j && arr[j] >= pivot) { j--; } if (i < j) { arr[i++] = arr[j]; } while (i < j && arr[i] < pivot) { i++; } if (i < j) { arr[j--] = arr[i]; } } arr[i] = pivot; return i;}void quick_sort(int* arr, int low, int high){ if (low >= high) { return; } int index = partition(arr, low, high); quick_sort(arr, low, index - 1); quick_sort(arr, index + 1, high);}int main(int argc, char const *argv[]){ int arr[] = {23, 322, 1, 54, 86, 2, 90, 102, 66, 233, 6, 98}; int length = sizeof(arr) / sizeof(int); display(arr, length); quick_sort(arr, 0, length - 1); display(arr, length); return 0;}

November 5, 2020 · 1 min · jiezi

关于c:快速排序

实现计划一 #include <stdio.h>#include <stdlib.h>void display(int arr[], int length){ for (int i = 0; i < length; i++) { printf("%d%s", arr[i], i == length - 1 ? " " : ", "); } printf("\n");}void quick_sort(int* arr, int low, int high){ if (low >= high) { return; } int i = low; int j = high; int pivot = arr[low]; while (i < j) { while(i < j && arr[j] >= pivot) { j--; } if (i < j) { arr[i++] = arr[j]; } while(i < j && arr[i] < pivot) { i++; } if (i < j) { arr[j--] = arr[i]; } } arr[i] = pivot; quick_sort(arr, low, i-1); quick_sort(arr, i + 1, high);}int main(int argc, char const *argv[]){ int arr[] = {23, 322, 1, 54, 86, 2, 90, 102, 66, 233, 6, 98}; int length = sizeof(arr) / sizeof(int); display(arr, length); quick_sort(arr, 0, length - 1); display(arr, length); return 0;}实现计划二 ...

November 5, 2020 · 2 min · jiezi

关于c:模拟函数递归调用压栈过程

操作系统版本Linux e982ba054bfa 4.9.125-linuxkit x86_64 x86_64 x86_64 GNU/Linuxgcc version 6.4.0 运行与mac下的docker 执行代码//递归函数调用实现斐波那契int fib(int n){ if (n <= 2) { return 1; } else { return fib(n - 1) + fib(n - 2); }}int main(){ fib(4); return 0;}编译gcc fib.c -g -o fib //-g选项使指标文件fib蕴含程序的调试信息开始gdb fib(gdb) start //拉起被调试程序,并执行至main函数的开始地位Temporary breakpoint 1 at 0x40050f: file fib.c, line 14.Starting program: /home/work/fib Temporary breakpoint 1, main () at fib.c:1414 fib(4);查看以后栈层信息及rbp,rsp的值 (gdb) info f //打印出以后栈层的信息Stack level 0, frame at 0x7fffffffe6e0: rip = 0x40050f in main (fib.c:14); saved rip = 0x7ffff7a303d5 source language c. Arglist at 0x7fffffffe6d0, args: Locals at 0x7fffffffe6d0, Previous frame's sp is 0x7fffffffe6e0 Saved registers: rbp at 0x7fffffffe6d0, rip at 0x7fffffffe6d8(gdb) info registers rbp rsp //以后rbp rsp的值rbp 0x7fffffffe6d0 0x7fffffffe6d0rsp 0x7fffffffe6d0 0x7fffffffe6d0查看以后函数的汇编信息,能够看到以后汇编指令执行到0x000000000040050f(貌似我的跟他人的如同不一样,具体能够以本人的为准),我这里曾经执行完main的frame初始化了 ...

November 5, 2020 · 6 min · jiezi

关于c:看完这篇你还能不懂C语言C内存管理

起源:公众号(c语言与cpp编程) C 语言内存治理指对系统内存的调配、创立、应用这一系列操作。在内存治理中,因为是操作系统内存,使用不当会造成毕竟麻烦的后果。本文将从零碎内存的调配、创立登程,并且应用例子来举例说明内存治理不当会呈现的状况及解决办法。 一、内存在计算机中,每个应用程序之间的内存是互相独立的,通常状况下应用程序 A 并不能拜访应用程序 B,当然一些非凡技巧能够拜访,但此文并不具体进行阐明。例如在计算机中,一个视频播放程序与一个浏览器程序,它们的内存并不能拜访,每个程序所领有的内存是分区进行治理的。 在计算机系统中,运行程序 A 将会在内存中开拓程序 A 的内存区域 1,运行程序 B 将会在内存中开拓程序 B 的内存区域 2,内存区域 1 与内存区域 2 之间逻辑分隔。 1.1 内存四区在程序 A 开拓的内存区域 1 会被分为几个区域,这就是内存四区,内存四辨别为栈区、堆区、数据区与代码区。 栈区指的是存储一些长期变量的区域,长期变量包含了局部变量、返回值、参数、返回地址等,当这些变量超出了以后作用域时将会自动弹出。该栈的最大存储是有大小的,该值固定,超过该大小将会造成栈溢出。 堆区指的是一个比拟大的内存空间,次要用于对动态内存的调配;在程序开发中个别是开发人员进行调配与开释,若在程序完结时都未开释,零碎将会主动进行回收。 数据区指的是次要寄存全局变量、常量和动态变量的区域,数据区又能够进行划分,分为全局区与动态区。全局变量与动态变量将会寄存至该区域。 代码区就比拟好了解了,次要是存储可执行代码,该区域的属性是只读的。 1.2 应用代码证实内存四区的底层构造因为栈区与堆区的底层构造比拟直观的体现,在此应用代码只演示这两个概念。首先查看代码察看栈区的内存地址分配情况: include<stdio.h>int main() { int a = 0; int b = 0; char c='0'; printf("变量a的地址是:%dn变量b的地址是:%dn变量c的地址是:%dn", &a, &b, &c); } 运行后果为: 咱们能够察看到变量 a 的地址是 2293324 变量 b 的地址是 2293320,因为 int 的数据大小为 4 所以两者之间距离为 4;再查看变量 c,咱们发现变量 c 的地址为 2293319,与变量 b 的地址 2293324 距离 1,因为 c 的数据类型为 char,类型大小为 1。在此咱们察看发现,明明我创立变量的时候程序是 a 到 b 再到 c,为什么它们之间的地址不是减少而是缩小呢?那是因为栈区的一种数据存储构造为先进后出,如图: ...

November 4, 2020 · 6 min · jiezi

关于c:从源码编译-sdl2dll

自己对C++开发齐全不相熟,但一个python我的项目须要的缘故,要批改SDL2的源码,网上搜材料,发现对于SDL源码编译的内容少得可怜,而且对C++的门外汉太不敌对了,导致走了很多弯路。 尝试了各种环境: - 本机 Win10+VS_2019: 生成胜利,不能用(提醒不是无效的win32程序)- 本机 Win10+VS_2015: 生成失败,找不到windows sdk- 本机 Win10+MinGW: make失败, 有效的提示符- 虚拟机 WinXP+MinGW: 装置失败, 无奈下载须要的库文件- 虚拟机 Win7+VS_2013:生成失败, 找不到windows sdk- 虚拟机 Win7+MinGW: 装置失败, 无奈下载须要的库文件(起初从本机copy后装置胜利, 但make又失败了)- **虚拟机 Win7+VS_2012**:生成胜利,批改平台为x64可用最初在Win7+vs_2012环境胜利了,却发现仅仅是平台选项的问题,返回去在“本机 Win10+VS_2019”环境下批改配置, 也胜利了。不过应该还是2012环境下生成的兼容性更好一些,2019的这个版本太新了。 上面具体记述过程 装置visual studio 2012 express (免费版,之后的版本都叫community了),留神肯定要下载DVD版的,exe版十分小,要在装置的时候下载须要的包,常常下载不下来。VS2012官网下载 / VS2012百度网盘下载-提取码:q138)下载SDL2源码。SDL2 官网下载 / SDL2 百度网盘下载-提取码:8n7z 用VS2012关上我的项目,因为SDL源码中曾经做好了vs的solution,间接找到解决方案文件关上就好了,地位在[SDL ROOT]/VisualC/SDL.sln。如下图所示:右击SDL2这个我的项目间接“生成”就能够了,生成后果会输出在console中。要留神的是:依据运行环境是32位和64位的,须要调整平台(platform)选项。 右击我的项目,抉择属性:点击配置管理器别离抉择release和debug版本对应的平台,32位选win32,64位选x64生成的时候,debug版带调试信息,但文件大,性能较差,release版更适宜生产运行时应用。===

November 4, 2020 · 1 min · jiezi

关于c:C-语言实现一个简单的-web-服务器

说到 web 服务器想必大多数人首先想到的协定是 http,那么 http 之下则是 tcp,本篇文章将通过 tcp 来实现一个简略的 web 服务器。 起源:公众号(c语言与cpp编程) 本篇文章将着重解说如何实现,对于 http 与 tcp 的概念本篇将不过多解说。 一、理解 Socket 及 web 服务工作原理既然是基于 tcp 实现 web 服务器,很多学习 C 语言的小伙伴可能会很快的想到套接字 socket。socket 是一个较为形象的通信过程,或者说是主机与主机进行信息交互的一种形象。socket 能够将数据流送入网络中,也能够接管数据流。 socket 的信息交互与本地文件信息的读取从外表特色上看相似,但其中所存在的编写复杂度是本地 IO 不能比较的,但却有类似点。在 win 下 socket 的交互交互步骤为:WSAStartup 进行初始化--> socket 创立套接字--> bind 绑定--> listen 监听--> connect 连贯--> accept 接管申请--> send/recv 发送或接收数据--> closesocket 敞开 socket--> WSACleanup 最终敞开。 理解完了一个 socket 的根本步骤后咱们理解一下一个根本 web 申请的用户惯例操作,操作分为:关上浏览器-->输出资源地址 ip 地址-->失去资源。当指标服务器接管到该操作产生掉申请后,咱们能够把服务器的响应流程步骤看为:取得 request 申请-->失去申请要害数据-->获取要害数据-->发送要害数据。服务器的这一步流程是在启动socket 进行监听后能力响应。通过监听得悉接管到申请,应用 recv 接管申请数据,从而依据该参数失去进行资源获取,最初通过 send 将数据进行返回。 ...

October 30, 2020 · 5 min · jiezi

关于c:CC后台研发需要点亮哪些技能树

在快完结的秋季招聘中,后盾开发或服务器开发的岗位需要一度炽热,甚至超过了算法岗。不少同学从诸神傍晚的算法岗战场上退下,转向更偏差工程能力的后盾开发岗,从而造成后盾开发岗位竞争的大暴发。 后盾开发工程师支流应用的编程语言有C++、Java、PHP以及目前缓缓风行的Golang等。正好看到题主的发问,就将以C++的角度,讲讲如何学习和筹备后盾开发的岗位。 一、语言根底 无论是C++开发还是Java开发,对于一个码农而言,最重要的就是对于编程语言的相熟。同样,无论从事哪种类型的岗位,首当其冲的就是要把握好语言根底。 C++是一门博大精深的编程语言,不仅领有继承于C语言的过程化程序设计思维,还蕴含有面对对象(OOP)的设计理念。弱小而又简单。相对来说,C++的学习老本较高,语言外面的坑较多。语言根底的学习路线如下: 1 语法根底 重点把握:(务必相熟底层机制原理) 指针和援用的概念指针与内存关系程序编译过程static、const、#define的用法和区别C和C++区别内存模型内存中的栈和堆调配2 面对对象根底 (务必相熟底层机制原理) 面向对象了解析构函数构造函数拷贝结构多态纯虚函数和虚函数虚函数实现机制虚函数表拜访限定符 public、private、protected继承原理、虚继承、菱形继承动态绑定和动静绑定new/delete和malloc/free重载、重写和暗藏3 语法进阶 (务必相熟底层机制原理) 智能指针左值、右值援用和move语义类型转换形式罕用的设计模式线程平安的单例模式内存溢出和内存透露C++11新个性动态链接库和动态链接库4 STL规范模板库 (务必能进行源码分析) 迭代器、空间配置器了解罕用容器特点、用法以及底层实现vector、list、deque、set、map、unorderedmap5 举荐书籍 《C++Primer》可作为工具书,顺手查阅《EffectiveC++》深刻理解C++的程序设计标准《STL源码分析》分析STL的源码底层,十分具备学习价值有精力还能够看《深度摸索C++对象模型》《more EffecticeC++》二、算法与数据结构 对于普通人而言,算法的学习最重要的是可能造成根本的算法思维,懂得从程序设计的角度对高重复性的操作做优化。这其中根本算法思维的把握和罕用数据结构的了解是必不可少。这方面的学习更偏向于多看多想多练。 1 常见算法类型 (务必可能手撕代码) 排序算法(冒泡、插入、抉择、快排、希尔、堆排、归并、桶排、基数、计数)、字符串操作、数组操作、递归、回溯、分治、动静布局等2 罕用数据结构 (务必相熟底层原理和实现) 链表、栈、队列、树(二叉树、均衡二叉树、红黑树、B树、B+树、哈夫曼树、字典树)、跳表、图[外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-hxJLkMWK-1604040668260)(/img/bVcIdoh)] 3 举荐书籍 《大话数据结构》适宜入门学习《剑指offer》必刷66题《算法导论》尽量看,能啃完就是大神三、计算机网络 网络相干的货色不是很多,关键在于对常见网络协议簇的意识和了解,以及一些惯例操作底层设计实现的分析。比方: | 输出http://www.baidu.com会产生什么 | 微信扫描登录会产生什么 1 重点把握知识点 OSI七层模型TCP/IP五层模型TCP/IP协定总结TCP、UDP区别TCP三次握手、四次挥手TCP状态转换TCP状态中TIME_WAITTCP连贯建设须要为什么不是两次握手TCP第三次握手失败会呈现什么TCP长连贯和短链接及优缺点TCP拥塞管制-慢启动、拥塞防止、快重传、快启动TCP如何保障可靠性传输TCP如何解决粘包、拆包问题TCP为什么牢靠UDP如何实现TCP牢靠传输IP地址和子网掩码ARP解析过程DNS原理HTTP状态码HTTP1.0、HTTP1.1、HTTP2.0区别HTTP和HTTPS区别HTTPS加密过程非对称加密和对称加密算法Nagle算法2 举荐书籍 《计算机网络自顶向下办法》教材书,可撒手边查阅《TCP/IP详解》重点理解TCP、IP、UDP协定实现四、数据库 数据库的个别应用其实不难,然而对于不同数据库的个性、实现机制、利用场景和性能优化方面却可能难倒一大批面试者。同样数据库自身也是十分好的我的项目实例,往往可能从中学习到许多程序设计的思维和模式。因而,对数据库要明确怎么用、为什么用、怎么用得好这几个方面的问题。 1 重点把握 数据库类别关系型数据库和非关系型数据库区别MySQL: SQL常见语句MySQL内链接,外链接(左链接、右链接、全链接)MySQL索引类型和原理MySQL事务实现原理ACIDMySQL数据存储引擎MySQL主从复制原理、作用和实现MySQL日记零碎redo log、binlog、undo logMVCC实现原理Sql优化思路范式实践数据库高并发解决办法Redis: Redis反对的数据类型Redis长久化Redis 架构模式主从复制一致性哈希算法2 举荐书籍 《高性能 Mysql》可能加深对Mysql的了解和应用《Redis设计与实现》比拟全面的书,能够多看看五、操作系统 操作系统的问题会集中在过程和线程,然而这一类的问题往往会以凋谢题的模式呈现。次要考查的是对操作系统组件以及运行过程的了解。比方: | 开机登录零碎产生了什么? | 复制粘贴是怎么操作的? 1 重点把握 物理内存和虚拟内存缓存IO和间接IO作业调度算法线程和过程过程和线程的调度线程的创立和完结线程状态线程间通信与线程同步机制互斥锁和信号量线程池消费者和生产者死锁并发和并行2 举荐书籍 《深刻了解计算机系统》很全面的书,这一本就够用了六、Linux零碎 对Linux零碎的纯熟应用是后盾开发/服务器开发的必备技能点。这年头,不会几个Linux指令都不好意思说本人是敲代码的。(客户端和前端的同学示意不服)不管怎样,对于Linux零碎的把握无论在哪个方向上,都会有用武之地的。 ...

October 30, 2020 · 1 min · jiezi

关于c:C语言学习笔记zhangpf

这段时间很想要把学习的过程总结、记录一下,因为脑子里切实没留下什么,所以只好记在这里。。。思维导图我本人感觉真是没什么用的货色。。。可我还是记。。。QAQ 1.一些概念性的货色,算法、面向过程/对象、了解编程语言 2.简略的语法,根本数据类型、变量、类型转换 3.这里是各种运算符了 4.分支抉择构造,if—else 5.永远地for循环 6.什么是数组??? 7.数组、字符数组&字符串 8.函数、函数的调用,还有参数(形参&实参),局部变量&全局变量。这里我第一次了解高内聚低耦合(我当初又含糊了我丢QAQ) 学的真是十分的通俗了,诚实说就是又看了遍语法,就是为了帮忙前面了解java的。。。大一的C学的真是太辣鸡了,我的问题我的问题!哈哈哈哈哈!~~~~

October 21, 2020 · 1 min · jiezi

关于c:memset为什么只能赋0-和-1

在C语言中文网中查到,memset()的原型为: include <string.h>void memset(void s, int c, unsigned long n);函数的性能为:将s指向的前n个字节单元,每个字节单元赋值为c。例子:int p = (int) malloc(10 * sizeof(int));memset(p, 0, 10 * sizeof(int));//能够用int p = (int) calloc(10, sizeof(int)); 代替,此例子为了阐明memset的原理。 以下内容出自:https://blog.csdn.net/a474617...0和-1的二进制示意别离为0000 0000以及1111 1111,大家留神到1个int变量是占4个字节的,而memset每次只能给1个字节的内存单元赋值,然而因为刚好0000 0000 和1111 1111的高位低位都一样,所以memset就“凑巧”实现了每4个字节赋32位0和1。 因为1的二进制示意为0000 0001 所以4个字节连起来就是0000 0001 0000 0001 0000 0001 0000 00001,转换成10进制也就是16843009。这就阐明并不是每一个10进制数都能正确用memset赋值。大家能够多尝试几个数。

October 15, 2020 · 1 min · jiezi

关于c:C-语言指针详解

1为什么应用指针如果咱们定义了 char a=’A’ ,当须要应用 ‘A’ 时,除了间接调用变量 a ,还能够定义 char *p=&a ,调用 a 的地址,即指向 a 的指针 p ,变量 a( char 类型)只占了一个字节,指针自身的大小由可寻址的字长来决定,指针 p 占用 4 个字节。 但如果要援用的是占用内存空间比拟大货色,用指针也还是 4 个字节即可。 应用指针型变量在很多时候占用更小的内存空间。变量为了示意数据,指针能够更好的传递数据,举个例子: 第一节课是 1 班语文, 2 班数学,第二节课颠倒过去, 1 班要上数学, 2 班要上语文,那么第一节课下课后须要怎么作调整呢?计划一:课间 1 班学生全都去 2 班, 2 班学生全都来 1 班,当然,走的时候要携带上书本、笔纸、零食……局面一片狼藉;计划二:两位老师课间调换教室。 显然,计划二更好一些,计划二相似应用指针传递地址,计划一将内存中的内容从新“复制”了一份,效率比拟低。 在数据传递时,如果数据块较大,能够应用指针传递地址而不是理论数据,即进步传输速度,又节俭大量内存。一个数据缓冲区 char buf[100] ,如果其中 buf[0,1] 为命令号, buf[2,3] 为数据类型, buf[4~7] 为该类型的数值,类型为 int ,应用如下语句进行赋值: *(short*)&buf[0]=DataId;*(short*)&buf[2]=DataType;*(int*)&buf[4]=DataValue;数据转换,利用指针的灵便的类型转换,能够用来做数据类型转换,比拟罕用于通信缓冲区的填充。指针的机制比较简单,其性能能够被集中从新实现成更抽象化的援用数据模式函数指针,形如: #define PMYFUN (void*)(int,int) ,能够用在大量分支解决的实例当中,如某通信依据不同的命令号执行不同类型的命令,则能够建设一个函数指针数组,进行散转。在数据结构中,链表、树、图等大量的利用都离不开指针。2 指针是什么?操作系统将硬件和软件联合起来,给程序员提供的一种对内存应用的形象,这种形象机制使得程序应用的是虚拟存储器,而不是间接操作和应用实在存在的物理存储器。所有的虚拟地址造成的汇合就是虚拟地址空间。 内存是一个很大的线性的字节数组,每个字节固定由 8 个二进制位组成,每个字节都有惟一的编号,如下图,这是一个 4G 的内存,他一共有 4x1024x1024x1024 = 4294967296 个字节,那么它的地址范畴就是 0 ~ 4294967296 ,十六进制示意就是 0x00000000~0xffffffff ,当程序应用的数据载入内存时,都有本人惟一的一个编号,这个编号就是这个数据的地址。指针就是这样造成的。 ...

October 13, 2020 · 4 min · jiezi

关于c:C-语言与-C-学习路线

有人说:“C生万物,编程之本”,这一点都没有错! C语言是最靠近计算机的语言,很多工夫,咱们都会发现,C语言是十分有必要学习的。 C语言数据类型、变量、内存布局、指针根底;字符串、一维数组、二维数组;一级指针,二级指针,三级指针,N级指针概念,指针数组和数组指针;构造体、文件的应用;动静库的封装和设计;函数指针回调函数。配套视频:轻松把握C语言视频教程(会打字就能学会)全国计算机等级考试二级c语言视频教61节课浙大翁恺C语言入门C语言进步深入浅出七日成蝶之C语言数据结构经典合集高级视频课程公众号回复C语言获取C++高级编程面向对象编程思维; 类的封装,结构和析构、动态成员、对象治理; 类的结构(有参构造函数、无参结构、拷贝结构、默认构造函数)和析构; 对象动静治理、友元函数、友元类、操作符重载; C++编译器对象治理模型剖析; 类对象的动静治理(new/delete); 友元函数和友元类; 类的继承、多继承及其二义性、虚继承; 多态(概念、意义、原理分析、多态案例); 虚函数、纯虚函数、抽象类(面向抽象类编程思维案例); 函数模板、类模板,模板的继承; C++类型转换; C++输入输出流(规范I/O 文件I/O 字符流I/O); C++异样解决(异样机制、异样类型、异样变量、异样层次结构、规范异样库); 常见罕用的IDE开发工具诸如Windows平台VC系列:VC++6.0(比拟古老) ;Visual Studio2013, Visual Studio 2015,Visual Studio2019;Mac平台的XCode系列,还有CodeBlock,另附一些高级编辑器Notepad++,EditPlus,UE等一些开发工具的罕用设置和一些常见快捷键的应用。 此阶段的学习难度系数不大,把握这些内容之后,能够做些简略的小我的项目。当然了如果你想用这些技能找工作的话的确是比拟艰难的。这时你还应该在加把劲进一步学习第二阶段。 配套视频:猎豹网校 C++ Primer初级教程 C++外围编程_打造你的外围编程技能 东南大学程序设计 公众号回复C++获取 C/C++开发进阶这一阶段的指标才是达到C/C++软件工程师开发行业的根本要求,这个阶段是咱们走向C/C++开发的进阶之路,更是一个让本人找份薪水比拟体面的工作的筹码。 如果在此局部遇到不懂的或者没有见过的知识点和名词,可先将本文珍藏,供当前细细研读。 那么这个阶段,咱们又应该把握什么呢,持续往下看: 1.C++进阶之STL STL = Standard Template Library,即规范模板库。这是进步开发效率的极品工具。通过学习此阶段,应把握泛型编程技巧,了解容器类在C++语言中的利用模式,以及熟练掌握全副STL类的应用办法。 2.C++进阶之设计模式 决定一个我的项目成败最重要的因素是我的项目总体的设计,通过本阶段的学习,可把握面向对象编程中重要的一环,是编码前建模的技巧所在。单例模式;工厂模式;代理模式;迭代模式等,这些都是你要把握的哦。 3.C++进阶之数据结构根底 这是所有编程语言中最应该学习的局部,程序组成的根底之一。 顺序存储、链式存储、循环链表;双向链表、栈(程序和链式)、队列(程序和链式);栈的利用、树基本概念及遍历、二叉树;排序算法、并归算法、抉择、插入、疾速、希尔。4.C++进阶之UI界面开发 把握QT类库构架,图形界面开发模型;把握QT开发技巧,音讯机制,图形处理;把握QT网络编程,UDP,TCP应用形式;把握QT文件解决形式,序列化;把握QT在windows,linux,ios,android不同平台下的移植技术。5.C++进阶之Unix/Linux网络服务器 把握Unix/Linux平台开发方式;纯熟应用零碎调用;纯熟Unix/Linux内存治理,过程,线程调度;相熟网络服务器开发方式,纯熟编写TCP,UCP网络服务程序;把握同步/异步IO模型在网络编程中的应用形式。⑥ C++进阶之数据库开发 把握SQL语言的实用技巧。Oracle,MySQL数据库的应用形式。 如果你能熟练掌握以上列出的技能,具备解决简单问题和技术难点的能力,而且你能独立开发一些比较复杂的功能模块,那么很荣幸地通知你,你曾经达到中级程度,薪资过万对你来说几乎是小菜一碟。 配套视频:猎豹网校 C++ Primer中级教程MFC进阶教程深入浅出版公众号回复C++获取C++开发高级读到此处的你,置信你有更高的指标。即是当下煊赫一时的全栈开发工程师,既晓前端,又通后盾。疾速定位问题,解决问题对他们来说已是小菜一碟,就是人们常说的神秘大牛,只在公司技术攻关的时候才会才看到他们的身影。 ①此阶段软件开发工作所需的常识和技能绝对较难,高级软件工程师编码熟练度和规范性须要达到肯定要求; ②具备肯定的我的项目能力(包含调试能力、文档编写能力、测试能力等)和综合技术素质(包含对软件生命周期的了解、对设计模式的了解、必备的行业常识和教训等);. ③理解支流的后盾技术和前后端合作形式,能从全局角度了解我的项目的整个生命周期。 如果你能熟练掌握以上三个阶段的常识技能,那么你就能够满足C++开发行业的高级需要。 配套视频:猎豹网校 C++ Primer高级教程C++传智播客C/C++根底+待业班第五期完整版面试题公众号回复C++获取2 电子书500本包含:C语言、C++、Linux、Android、PHP、JAVA、HTML

October 12, 2020 · 1 min · jiezi

关于c:cc内存分区

栈区(stack)由编译器主动调配开释。寄存局部变量的值等。 堆区(heap)由程序员手动调配开释。寄存new或malloc进去的对象。 全局区(动态区)(static)编译器编译时即分配内存,程序完结后由零碎开释。寄存全局变量和动态变量。文字常量区编译器编译时即分配内存,程序完结后由零碎开释。寄存常量字符串。程序代码区寄存函数体的二进制代码。

October 10, 2020 · 1 min · jiezi

关于c:FreeBSD12-安装dwm-st

装置前提默认你曾经装置好了FreeBSD零碎并能够失常运行同时应用非root用户登录零碎,即你领有一个非root用户装置 X Windows$ sudo pkg install xorg个别状况下 FreeBSD 是没有 sudo 命令,所以须要进入 root 用户装置 sudo . $ pkg install sudo 在 sudo 文件中批改你用户的权限 $ visudo在 vi 编辑器下的失常模式中应用 /ALL 搜寻,定位到 root ALL(ALL) ALL ,在这一句的后一行增加: 用户名 ALL(ALL) ALL其中用户名就是你本人设定的用户名,替换就好,而后从新应用命令装置 xorg . 装置dwm对于 dwm 的任何问题能够拜访他的官网: DWM 应用 git 从官网将源文件克隆下来,如果没有 git 能够先装置: $ sudo pkg install git$ git clone https://git.suckless.org/dwm$ cd dwm进入 dwm 的源文件夹,这外面蕴含了如下文件: 在这个时候,咱们就要留神了,因为 FreeBSD 零碎与 Linux 零碎之间的些许不同,所以须要批改一下源文件下的 config.mk 文件: $ vi config.mk批改: ...

October 9, 2020 · 1 min · jiezi

关于c:C-递归函数分析

递归的数学思维递归是一种数学上分而自治的思维递归须要有边界条件当边界条件不满足时,递归持续进行当边界条件满足时,递归进行递归函数函数体外部能够调用本人递归函数函数体中存在自我调用的函数递归函数是递归的数学思维在程序设计中的利用递归函数必须有递归进口函数的有限递归将导致程序栈溢出而解体递归函数设计示例一用递归的办法编写函数求字符串长度“D.T.software”strlen(s) = 1 + strlen(s + 1) *s != '0'strlen(s) = 0 *s == '0'例子47-1: #include "stdio.h"int strlen_r(char *s){ if(*s != '\0') { return 1 + strlen_r(s+1); } else { return 0; }}int main(){ printf("%d\n",strlen_r("abc")); printf("%d\n",strlen_r("")); return 0;}输入后果: 30递归函数设计示例二斐波拉契数列递归解法-1,1,2,3,5,8,13,21,...数学公式:fac(n) = fac(n-1) + fac(n-2) n>=3;fac(n) = 1 n==2;fac(n) = 1 n==1;例子47-2: #include "stdio.h"int fac(int n){ if(n == 1) { return 1; } else if(n == 2) { return 1; } else if(n >= 3) { return fac(n - 1) + fac(n - 2); }} int main(){ printf("%d\n",fac(1)); printf("%d\n",fac(2)); printf("%d\n",fac(5)); return 0;}输入后果: ...

October 8, 2020 · 1 min · jiezi

关于c:数据结构绪论

数据结构是一门钻研非数值计算程序设计中操作对象,以及这些对象之间的关系和操作的学科。 基本概念和术语: 数据:形容客观事物属性的数、字符以及能输出到计算机且能被计算机程序辨认和解决的符号的汇合。数据是计算机程序加工的原材料。数据元素:数据的根本单位,通常作为一个整体进行思考和解决,用于残缺的形容一个对象。如:一条学生记录。数据项:组成数据元素的不可分割的最小单位。如:学生记录中的姓名、学号、性别等。数据对象:性质雷同的数据元素的汇合,数据的一个子集。数据结构:相互之间存在着一种或多种特定关系的数据元素的汇合,包含逻辑构造、存储构造、数据的运算。构造就是指数据元 素之间的关系。数据类型:一个值和定义在此汇合上的一组操作的总称。原子类型(其值不可再分)、构造类型(其值能够再分为若干重量)、抽象数据类型。抽象数据类型:由用户定义、示意利用问题的数学模型,以及定义在模型上的一组基本操作的总称。数据对象、其关系的汇合、其基本操作的汇合。逻辑构造和存储构造: 逻辑构造:指数据元素之间的逻辑关系,与数据的存储无关,是独立于计算机的。分为线性构造(线性表)和非线性构造(汇合、树形构造、图状构造)。存储构造:又称物理构造,指数据结构在计算机中的存储示意(映像),包含数据元素的示意和关系的示意,它依赖于计算机语言。存储构造次要有:顺序存储、链式存储、散列存储(Hash存储)、索引存储。数据的运算:包含运算的定义和实现。运算的定义是针对逻辑构造的,指出运算的性能;运算的实现是针对存储构造的,指出运算的根本操作步骤。算法: 定义:是对特定问题求解步骤的形容,是指令的无限序列,每条指令示意一个或多个操作步骤。五大个性: 有穷性:算法的步骤有穷,每一步的执行工夫有穷。确定性:算法中每一条指令必须有确定的含意,不会产生二义性,对于雷同的输出只能有雷同的输入。可行性:算法中的操作都能够通过曾经实现的根本运算执行无限次来实现。输出:一个算法有零个或多个输出。输入:一个算法有一个或多个输入。必须有输入。四大规范: 正确性:能正确的解决问题。可读性:首先是便于人们了解和互相交换,其次是机器的可执行性。难懂的算法易于暗藏谬误,难于调试和批改。健壮性:输出非法数据时,算法能适当地做出正确反馈或进行相应解决,不会产生一些莫名其妙的输入后果。高效性:工夫高效(算法设计正当,执行效率高,用工夫复杂度来度量)和空间高效(占用存储容量正当,用空间复杂度来度量)。渐进工夫复杂度:T(n) = O(f(n)) 采纳事先剖析估算法,不是预先统计法。语句频度T(n)是指该语句在算法中被反复执行的次数。问题规模n是指算法求解问题输入量的多少。工夫复杂度取决于:问题规模和待输出数据的性质。根本语句:反复执行次数和算法的执行工夫成正比的语句,个别是最深层循环内的语句。个别思考最坏工夫复杂度,运行工夫不会比它更长,所以是上界。均匀工夫复杂度:指输出实例以等概率呈现的状况,算法计算量的加权平均值。O(f(n))+O(g(n))= O(max(f,g));O(f(n)) · O(g(n))= O(f · g)。O(1) < O(log2n) < O(n) < O(nlog2n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)常量阶:算法的执行工夫是一个与问题规模n无关的常数,即便常数再大,算法的工夫复杂度都是O(1)。线性阶、平方阶、立方阶、对数阶。渐进空间复杂度:S(n) = O(f(n)) 一个程序在执行时,除了须要存储空间来寄存自身所用的指令、常数、变量和输出数据之外,还须要一些对数据进行操作的辅助存储空间。算法原地工作是指算法所须要的辅助空间为常量,即O(1)。同一个算法,实现语言的级别越高,执行效率越低。

October 5, 2020 · 1 min · jiezi

关于c:AIX和Linux-C语言编译器差别

xlc++ 和 g++AIX上应用的是xlc++编译器,Linux上应用的是g++编译器。 对C规范中没有严格定义的行为,两个编译器的解决形式不肯定雷同,代码在两个平台运行会有不一样的体现。导致在一个平台运行失常,另一个平台可能就是bug了。 问题集中在以下几个方面 危险代码示例xlc++编译器g++编译器解决办法s=”12”sprintf( r,"%04s",s );s有余四位左侧补’0’ r=”0012”s有余四位左侧补空字符' 'r=”  12”减少查看代码char p= r;for (; p == ' '; ++p)*p='0';文件描述符file=NULL时,写入内容fprintf(file, "…");无奈写入,程序持续向下执行程序解体减少判断if (file != NULL)memcpy越界复制数组数据变量间地址扩散,数组自身越界,对其余变量无影响,个别能够出失常后果。变量间地址严密相邻,数组自身越界,邻近定义的变量值也会被笼罩,后果异样。查看对应地位复制内存长度,防止越界构造体s蕴含double型变量a,应用前没初始化a默认值0.0a默认值为超大负数,导致程序解体减少初始化解决memset(&s, '0', sizeof(s));32位零碎 和 64位零碎如果两个平台一个是32位版本另一个是64位版本,有些变量的长度会发生变化。须要留神的中央如下表格: 变量类型32位零碎64位零碎危险代码及解决long4字节8字节防止应用sizeof(long)来获取长度数值指针4字节8字节代码中一些memcpy和strncpy的第三个参数为sizeof(指针),迁徙后造成复制内存长度变动(4变为8)后果异样。须要依据状况批改,防止应用sizeof对指针这种长度随平台变动的变量进行操作。当数组名作为参数时也相当于一个指针,也不应进行sizeof操作。

October 4, 2020 · 1 min · jiezi

关于c:C函数与宏分析

函数与宏宏是由预处理器间接替换开展的,编译器不晓得宏的存在函数是由编译器间接编译的实体,调用行为由编译器决定屡次应用宏会导致最终可执行程序的体积增大函数是跳转执行的,内存中只有一份函数体存在宏的效率比函数要高,因为是间接开展,无调用开销函数调用时会创建活动记录,效率不如宏例子46-1: #include "stdio.h"#define RESET(p, len) \ while(len > 0 ) \ (char*p)[--len] = 0void reset(void*p, int len){ while(len > 0) ((char*)p)[--len] = 0;}int main(){ int array_1[] ={1,2,3,4,5}; int len = sizeof(array_1); int i = 0; reset(array_1,len); for(i = 0;i < 5;i ++) { printf("array[%d] = %d\n",i ,array_1[i]); } return 0 ;}输入后果: array[0] = 0array[1] = 0array[2] = 0array[3] = 0array[4] = 0宏的效率比函数稍高,然而副作用微小宏是文本替换,参数无奈进行类型查看能够用函数实现的工程相对不必宏宏的定义中不能呈现递归定义例子46-2: #include "stdio.h"#define ADD(a,b) a+b#define MUL(a,b) a*b#define MIN(a,b) ((a)<(b)?(a):(b))int main(){ int i = 1; int j = 10; printf("%d\n",MUL(ADD(1,2),ADD(3,4))); //间接文本替换 1+2*3+4 = 11 printf("%d\n",MIN(i++,j)); return 0;}输入后果:112宏的妙用用于生成一些常规性的代码封装函数,加上类型信息小结:宏和函数不是竞争对手宏可能承受任何类型的参数,效率高,易出错函数的参数必须是固定类型,效率稍低,不易出错宏能够实现函数不能实现的性能

September 19, 2020 · 1 min · jiezi

关于c:和C死磕到底

明天写的代码是真的dertypedef struct name{ int a; int b;}elem; 能够这样申明变量struct name data1;data1.a=3;也能够这样elem data2;data2.a=1; 脑子短路写成elem.a=1;看了半天没发现什么不对也是醉了

September 19, 2020 · 1 min · jiezi

关于c:C函数参数的秘密下

调用约定当函数调用产生时参数会传递给被调用的函数而返回值会被返回给函数调用者调用约定形容参数如何传递到栈中以及栈的保护形式参数传递程序调用栈清理调用约定是预约义可了解为调用协定调用约定通常用于库调用和库开发的时候例子45-1: #include <stdio.h>float average(int array1[],int size){ int i = 0; float avr = 0; for(i = 0;i < size; i ++) { avr += array1[i]; } return avr/size;}int main(){ int array1[] = {1,2,3,4,5}; printf("%f\n",average(array1,5));}输入后果: 3.000000可变参数C语言中能够定义参数可变的函数参数可变函数的实现依赖于stdarg.h头文件va_list-参数汇合va_arg-取具体参数值va_start-示意参数拜访的开始va-end-标识参数拜访的完结例子45-2: #include <stdio.h>#include "stdarg.h"float average(int n,...){ va_list args; //可变参数汇合 int i = 0; float sum = 0; va_start(args,n); //可变参数拜访开始 for(i = 0;i < n; i ++) { sum += va_arg(args,int); //从va_arg取具体参数值,取n个 } va_end(args);//参数曾经取完了 return sum/n;}int main(){ printf("%f\n",average(5,1,2,3,4,5)); printf("%f\n",average(4,1,2,3,4)); return 0;}输入后果: ...

September 4, 2020 · 1 min · jiezi

关于c:C函数参数的秘密上

函数参数函数参数在实质上与局部变量雷同在栈上调配空间函数参数的初始值是函数调用时的实参值函数参数的求值程序依赖于编译器的实现int k = 1;printf("%d,%d\n",k++,k++);例子44-1: #include "stdio.h"int func(int i,int j){ printf("i = %d,j = %d\n",i,j); return 0;}int f(){ return 6;}int g(){ return 2;}int main(){ int k = 1; func(k++,k++); printf("%d\n",k); k = f() * g(); printf("%d\n",k); return 0;}输入后果: i = 2,j = 1312函数形参的运算依赖于编译器,不是人为的设想从左往右。 程序中的程序点程序中存在肯定的程序点程序点指的是在执行过程中批改变量值的最晚时刻在程序达到程序点的时候,之前所做的所有操作必须实现C语言中的程序点每个残缺表达式完结时,即分号处&&,||,?,以及逗号表达式的每个参数计算之后函数调用时所有实参值实现后(进入函数体之前)例子44-2: #include "stdio.h"int main(){ int k = 2; int a= 1; k = k++ + k++; printf("k = %d\n",k); if(a-- && a) { printf("a = %d\n",a); }}输入后果: ...

September 3, 2020 · 1 min · jiezi

关于c:C函数的意义

C语言的函数函数的由来程序 = 数据 + 算法C程序 = 数据 + 函数函数的意义C语言中的模块化面向过程的程序设计面向过程是一种以过程为核心的编程思维首先将负责的问题合成为一个个容易解决的问题合成过后的问题能够依照步骤一步步实现函数是面向过程在C语言中的体现解决问题的每个步骤能够用函数来实现申明和定义申明和意义在于通知编译器程序单元的存在定义则明确批示程序单元的意义C语言中通过extern进行程序单元的申明一些程序单元在申明时能够疏忽extern 小结:函数是面向过程思维在C语言中的体现面向过程是由上至下合成问题的设计办法程序中的定义和申明齐全不同C语言中通过extern对程序单元进行申明

September 2, 2020 · 1 min · jiezi

关于c:C内存操作经典问题分析二

常见内存谬误构造体成员指针未初始化构造体成员指针未调配足够的变量内存调配胜利,但并未初始化内存操作越界例子42-1: #include "stdio.h"#include "malloc.h"void test(int* p,int size){ int i = 0; for(i = 0; i < size; i++) { printf("%d\n",p[i]); }// free(p);}void func(unsigned int size){ int *p = (int*)malloc(size *sizeof(int)); int i = 0; if(size % 2 != 0) { return; } for(i = 0; i < size; i++) { p[i] = i; printf("%d\n",p[i]); } free(p);}int main(){ int* p = (int*)malloc(5 * sizeof(int)); //在main函数外面申请的动态内存,就要在main函数外面开释 test(p , 5); free(p); func(9); func(9); return 0;}输入后果: ...

September 2, 2020 · 1 min · jiezi

关于c:C内存操作经典问题分析一

野指针指针变量中的值是非法的内存地址,进而造成野指针野指针不是NULL指针,是指向不可用内存地址的指针NULL指针并无危害,很好判断,也很好调试C语言中无奈判断一个指针所保留的地址是否非法野指针的由来部分指针变量没有初始化指针所指向的变量在指针之前被销毁应用曾经开释过的指针进行了谬误的指针运算进行了谬误的强制类型转换例子41-1: #include"stdio.h"#include"malloc.h"int main(){ int*p1 = (int*)malloc(40); int*p2 = (int*)1234567; //整型值强制类型转化为指针类型,谬误的强制类型转化 int i = 0; printf("%p\n",p1); for(i = 0;i < 40;i ++) { *(p1 + i) = 40 - i; //40个字节只有十个整型,循环40次,40*4 = 160个整型,动态内存不够 } //因为指针运算产生了野指针,改写了非法的内存地址 free(p1); printf("%p\n",p1); for(i = 0;i < 40;i ++) { p1[i] = p2[i]; //应用了曾经开释的内存空间 } return 0;}输入后果:谬误批改后正确代码: #include"stdio.h"#include"malloc.h"int arr[40] = {1,2,3,4,5,6,7};int main(){ int*p1 = (int*)malloc(40*sizeof(int)); int*p2 = (int*)arr; int i = 0; printf("%p\n",p1); for(i = 0;i < 40;i ++) { *(p1 + i) = 40 - i; } free(p1); p1 = NULL; printf("%p\n",p1); for(i = 0;i < 40;i ++) { p1[i] = p2[i]; } return 0;}输入后果: ...

September 2, 2020 · 1 min · jiezi

关于c:C语言内存泄露很严重如何应对

摘要:通过介绍内存透露问题原理及检视办法,心愿后续可能从编码检视环节就杜绝内存透露导致的网上问题产生。1. 前言最近部门不同产品接连呈现内存透露导致的网上问题,具体表现为单板在现网运行数月当前,因为内存耗尽而导致单板复位景象。一方面,内存透露问题属于低级谬误,此类问题脱漏到现网,影响很坏;另一方面,因为内存透露问题很可能导致单板运行固定工夫当前就复位,只能通过批量降级能力解决,理论影响也很顽劣。同时,接连呈现此类问题,尤其是其中一例问题还是咱们老员工批改引入,阐明咱们不少员工对内存透露问题意识还是不够粗浅的。本文通过介绍内存透露问题原理及检视办法,心愿后续可能从编码检视环节就杜绝此类问题产生。 阐明:预防内存透露问题有多种办法,如增强代码检视、工具检测和内存测试等,本文汇集于开发人员能力晋升方面。 2. 内存透露问题原理2.1堆内存在C代码中的存储形式内存透露问题只有在应用堆内存的时候才会呈现,栈内存不存在内存透露问题,因为栈内存会主动调配和开释。C代码中堆内存的申请函数是malloc,常见的内存申请代码如下: char *info = NULL; /**转换后的字符串**/ info = (char*)malloc(NB_MEM_SPD_INFO_MAX_SIZE); if( NULL == info) { (void)tdm_error("malloc error!\n"); return NB_SA_ERR_HPI_OUT_OF_MEMORY; }因为malloc函数返回的实际上是一个内存地址,所以保留堆内存的变量肯定是一个指针(除非代码编写极其不标准)。再反复一遍,保留堆内存的变量肯定是一个指针,这对本文宗旨的了解很重要。当然,这个指针能够是单指针,也能够是多重指针。 malloc函数有很多变种或封装,如g_malloc、g_malloc0、VOS_Malloc等,这些函数最终都会调用malloc函数。 2.2堆内存的获取办法看到本大节题目,可能有些同学有纳闷,上一大节中的malloc函数,不就是堆内存的获取办法吗?确实是,通过malloc函数申请是最间接的获取办法,如果只晓得这种堆内存获取办法,就容易掉到坑里了。个别的来讲,堆内存有如下两种获取办法: 办法一:将函数返回值间接赋给指针,个别表现形式如下: char *local_pointer_xx = NULL;local_pointer_xx = (char*)function_xx(para_xx, …);该类波及到内存申请的函数,返回值个别都指针类型,例如: GSList* g_slist_append (GSList *list, gpointer data)办法二:将指针地址作为函数返回参数,通过返回参数保留堆内存地址,个别表现形式如下: int ret; char *local_pointer_xx = NULL; /**转换后的字符串**/ ret = (char*)function_xx(..., &local_pointer_xx, ...);该类波及到内存申请的函数,个别都有一个入参是双重指针,例如: __STDIO_INLINE _IO_ssize_tgetline (char **__lineptr, size_t *__n, FILE *__stream)后面说通过malloc申请内存,就属于办法一的一个具体表现形式。其实这两类办法的实质是一样的,都是函数外部间接申请了内存,只是传递内存的办法不一样,办法一通过返回值传递内存指针,办法二通过参数传递内存指针。 2.3内存透露三要素最常见的内存透露问题,蕴含以下三个因素: 因素一:函数内有部分指针变量定义; 因素二:对该部分指针有通过上一大节中“两种堆内存获取办法”之一获取内存; 因素三:在函数返回前(含失常分支和异样分支)未开释该内存,也未保留到其它全局变量或返回给上一级函数。 ...

September 1, 2020 · 1 min · jiezi

关于c:二分搜索的陷阱

二分搜寻无处不在,然而写好一个二分搜寻却还须要规避一些陷阱。第一篇二分搜寻论文在1946年就发表了,然而第一个没有谬误的二分搜寻程序却直到1962年才呈现。 问题:给定一个有序数组,蕴含【1...n】,以及一个目标值。搜寻目标值并返回。 int guessNumber(int n, int target){ int left = 1, right = n, m = 0; while (left <= right) { m = left + (right - left)/2; if (m == target) return m; if(m > target) right = m ; else left = m ; } return m;}程序看起来非常简单,且对于绝大多数数据都能执行正确。然而,如果输出是 2,2数组内容为;[1,2],搜寻 2.left = 1 right = 2 。 m = 1此时会陷入死循环。 正确程序: int guessNumber(int n, int target){ int left = 1, right = n, m = 0; while (left <= right) { m = left + (right - left)/2; if (m == target) return m; if(m > target) right = m - 1; else left = m + 1; } return m;}举例 1.......m........n如果m < target 阐明target在后半局部,则新的区间应为[m+1,n],而非[m,n].同理,如果m > target, 阐明target在前半部分,新区间应为:[1,m-1],而非[1,m]. ...

August 30, 2020 · 1 min · jiezi

关于c:大话编码格式

什么是编码格局从一个小问题引入咱们在学习C语言的时候,有一道必做的题目是将大写字母转换成小写,置信有点根底的同学都能不加思索的写出上面的代码: char toLower(char upper){ if (upper >= 'A' && upper <= 'Z'){ return upper + 32; }else{ return upper; }} 要问为什么是这段代码?咱们往往也能说得出:因为大小写字母在ASCII码上正好相差32(字符'a'为97, 字符'A'为65)。咱们在进行字符初始化的时候,往往会将字符初始化为'\0'。因为'\0'在ASCII码中对应的数值是0。咱们理所应当地晓得char型字符对应的范畴是0~127,因为ASCII码的范畴就是0~127。然而有没有想过,为什么是ASCII码?所谓的ASCII码,又到底是什么? 编码格局介绍要说起ASCII码,不得不说起编码格局。咱们晓得,对于计算机来说,咱们在屏幕上看到的千姿百态的文字、图片、甚至视频是不能间接辨认的,而是要通过某种形式转换为0和1组成的二进制的机器码,最终被计算机辨认(0为低电平,1为高电平)。对于数字来说,有一套十分成熟的转换计划,就是将十进制的数字转换为二进制,就能间接被计算机辨认(如5转换为二进制是 0000 0101)。然而对于像ABCD这样的英文字母,还有!@#$这样的特殊符号,计算机是不能间接辨认的,所以就须要有一套通用的规范来进行标准。这套标准就是ASCII码。ASCII码应用127个字符,示意A~Z等26个大小写字母,蕴含数字0~9,所有标点符号以及特殊字符,甚至还有不能在屏幕上间接看到的比方回车、换行、ESC等。依照这套SACII的编码标准,就很容易的晓得,'\0'代表的是0, 'A'代表的是65,而'a'代表的是97,'A'和'a'之间正好相差了32。ASCII码尽管只有127位,但根本实现了对所有英文的反对。所以为什么说char类型只占1个字节?因为char型最大的数字是127,转成二进制也不过是0111 1111,只须要1个字节就能示意所有的char型字符,因而char只占1个字节。然而随着计算机的遍及,计算机岂但要解决英文,还有汉字、甚至希腊文字、韩文、日文等诸多文字,这时,127个字符必定不够了,这时就引入了Unicode的概念。Unicode是一个编码字符集,它根本涵盖了世界上绝大多数的文字(只有极少数没有蕴含),在Unicode中文对照表中能够查看一些汉字的Unicode字符集。比方,汉字”七“在Unicode示意为十六进制0x4e03,示意成二进制位0100 1110 0000 0011,占了15位,至多须要两个字节能力放得下,有些更简单的生僻字,可能占用的字节数甚至不止两位。这就面临着一个问题,当一个中英文夹杂的字符串输出到电脑的时候,计算机是如何晓得它到底是什么的?就像下面的0100 1110 0000 0011,它到底是示意的是0100 1110和0000 0011两个ASCII字符,还是汉字”七“?计算机并不知道。所以就须要一套规定来通知计算机,到底该依照什么来解析。这些规定,就是字符编码格局。其中就包含以下几种。 ASCIIUTF-8GBKGB2312GB18030BIG5ISO8859编码格局分类ASCIIASCII 编码后面曾经介绍过,此处就不再多说了。它应用0~127这128位数字代表了所有的英文字母以及数字、标点、特殊符号和键盘上有但屏幕上看不见的非凡按键。它的长处是仅用128个数字就实现了对英文的完满反对,然而毛病也同样显著,不反对中文等除英文以外的其余语言文字。因而,ASCII码根本能够看做是其余字符编码格局的一个子集,其余字符编码都是在ASCII码的根底上实现了肯定的扩大,但毫无意外地,都实现了对ASCII码的兼容。 UTF-8在汉字环境下,UTF-8能够说是最常见的编码。它是Windows零碎默认的文本编码格局。UTF-8是一种变长的编码方式,最大能够反对到6位。这就意味着他能够无效地节俭空间(在前面介绍GBK的时候,会讲GBK是固定长度的编码方式)。那么,UTF8是如何晓得以后所要表白的字符是几个字节呢?在UTF8中,它以首字节的高位作为标识,用来区别以后字节的长度。其规定大抵如下: **1字节 0xxxxxxx (范畴:0x00-0x7F)2字节 110xxxxx 10xxxxxx (范畴:0x80-0x7ff)3字节 1110xxxx 10xxxxxx 10xxxxxx (范畴:0x800-0xffff)4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (范畴:0x10000-0x10ffff) 5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx**如下面的汉字”七“的unicode码是0x4e03,在0x800-0xffff区间,所以是3字节,用UTF-8示意就是11100100 10111000 10000011(十六进制示意为0xe4b883)。"七"的Unicode码是0100 1110 0000 0011,可为什么是这个数呢?依据3字节的填充规定,从右往左,顺次填充x的地位: ...

August 28, 2020 · 3 min · jiezi

关于c:C程序设计语言第2版-新版

C程序设计语言(第2版 新版) 下载地址 https://pan.baidu.com/s/1Y2MAci2hO_XSSwapD5FbAA 扫码上面二维码关注公众号回复 100035获取分享码 本书目录构造如下: 第1章 导言 1.1 入门 1.2 变量与算术表态式 1.3 for语句 1.4 符号常量 1.5 字符输出/输入 1.6 数组 1.7 函数 1.8 参数——传值调用 1.9 字符数组 1.10 内部变量与作用域 第2章 类型、运算符与表达式 2.1 变量名 2.2 数据类型及长度 2.3 常量 2.4 申明 2.5 算术运算符 2.6 关系运算符与逻辑运算符 2.7 类型转换 2.8 自增运算符与自减运算符 2.9 按位运算符 2.10 赋值运算符与表达式 2.11 条件表达式 2.12 运算符优先级与求值秩序 第3章 控制流 3.1 语句与程序块 3.2 if-else语句 3.3 else-if语句 3.4 switch语句 ...

August 28, 2020 · 1 min · jiezi

关于c:双指针在数组操作中的应用

数组作为一种简略直观的数据结构,当须要对其操作时,应用双指针有时能够带来出奇的成果。 问题:给定一个数组,将所有0挪动到数组的尾部,同时放弃非零元素的绝对程序。【0,1,0,3,12】挪动完当前为->[1,3,12,0,0] 应用双指针,一个快指针遍历数组,一个慢指针用来存储非零数据。 void moveZeroes(int* nums, int numsSize){ int fast = 0, slow = 0; while (fast < numsSize) { if (nums[fast] != 0) { nums[slow] = nums[fast]; if (fast > slow) nums[fast] = 0; slow++; } fast++; }}将非零数据,存入数组的前段,赋值操作结束当前nums[fast]曾经没有用途了将其置0,须要留神的是slow等于fast的状况,防止将slow笼罩。

August 28, 2020 · 1 min · jiezi

关于c:C-Templates

C++ Templates 下载地址 https://pan.baidu.com/s/1kIb3G2vY1ogS-zp6JS1h6w 扫码上面二维码关注公众号回复 100029获取分享码 本书目录构造如下: 第1局部 根底 第2章 函数模板 第3章 类模板 第4章 非类型模板参数 第5章 技巧性基础知识 第6章 模板实战 第7章 模板术语 第2局部 深刻模板 第8章 深刻模板根底 第9章 模板中的名称 第10章 实例化 第11章 模板实参演绎 第12章 特化与重载 第13章 将来的方向 第3局部 模板与设计 第14章 模板的多态威力 第15章 trait与policy类 第16章 模板与继承 第17章 metaprogram 第18章 示意式模板 第4局部 高级应用程序 第19章 类型辨别 第20章 智能指针 第21章 tuple 第22章 函数对象和回调 附录A 一处定于准则 附录B 重载解析

August 27, 2020 · 1 min · jiezi

关于c:链表中的哨兵sentinel

哨兵节点广泛应用于树和链表中,如伪头、伪尾、标记等,它们是纯性能的,通常不保留任何数据,其次要目标是使链表标准化,如使链表永不为空、永不无头、简化插入和删除。 问题:删除链表中等于给定值val的所有节点。1,如果节点在两头,只需将该节点进行删除即可。2,如果节点在头部,则问题将有点简单,不能间接将头节点删掉,这样会造成链表的失落。通过设置哨兵节点,用来长期保留头部节点,最初在计算结束当前,返回该节点。代码如下: /** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */struct ListNode* removeElements(struct ListNode* head, int val){ if (head == NULL) return NULL; struct ListNode *ret = (struct ListNode*)malloc(sizeof(struct ListNode)); ret->next = head; struct ListNode *pre = ret; struct ListNode *cur = head; while (cur != NULL) { if (cur->val == val) { pre->next = cur->next; cur = cur->next; } else { cur = cur->next; pre = pre->next; } } return ret->next;}应用两个指针,一个示意前一个节点,一个示意以后节点。当以后节点须要删除时,pre->next = cur->next; ...

August 27, 2020 · 1 min · jiezi

关于c:C-程序设计语言第2版

C 程序设计语言(第2版) 下载地址 https://pan.baidu.com/s/1FrNNDDuWycRD0g_ZfhkQnA 扫码上面二维码关注公众号回复 100027获取分享码 本书目录构造如下: 第1章 导言 1.1 入门 1.2 变量与算术表态式 1.3 for语句 1.4 符号常量 1.5 字符输出/输入 1.6 数组 1.7 函数 1.8 参数——传值调用 1.9 字符数组 1.10 内部变量与作用域 第2章 类型、运算符与表达式 2.1 变量名 2.2 数据类型及长度 2.3 常量 2.4 申明 2.5 算术运算符 2.6 关系运算符与逻辑运算符 2.7 类型转换 2.8 自增运算符与自减运算符 2.9 按位运算符 2.10 赋值运算符与表达式 2.11 条件表达式 2.12 运算符优先级与求值秩序 第3章 控制流 3.1 语句与程序块 3.2 if-else语句 3.3 else-if语句 3.4 switch语句 3.5 whil循环与for特环 3.6 do-while循环 3.7 break语句与continue语句 3.8 goto语句与标号 第4章 涵数与程序结构 第5章 指针与数组 第6章 构造 第7章 输出与输入 第8章 UNIX零碎接口 附录A 参考手册 附录B 规范库 附录C 变更小结

August 27, 2020 · 1 min · jiezi

关于c:C-Primer-Plus第五版

C Primer Plus(第五版) 下载地址 https://pan.baidu.com/s/19eSyUSVTYZRpMukVZ2hLIw 扫码上面二维码关注公众号回复 100026获取分享码 本书目录构造如下: 第1章 概览 1.1 C语言的起源 1.2 应用C语言的理由 1.3 C语言的倒退方向 1.4 计算机工作的基本原理 1.5 高级计算机语言和编译器 1.6 应用C语言的7个步骤 1.7 编程机制 1.8 语言规范 1.9 本书的组织构造 1.10 本书体例 1.11 总结 1.12 复习题 1.13 编程练习 第2章 C语言概述 2.1 C语言的一个简略实例 2.2 实例阐明 2.3 一个简略程序的构造 2.4 使程序可读的技巧 2.5 更进一步 2.6 多个函数 2.7 调试 2.8 关键字和保留标识符 2.9 要害概念 2.10 总结 2.11 复习题 2.12 编程练习 第3章 数据和C 3.1 示例程序 ...

August 27, 2020 · 3 min · jiezi

关于c:linux-查看程序函数占用时间gprof

理论工作中常常会遇到程序执行工夫迟缓,但对于那局部执行迟缓无奈确定。 应用GNU profiler(gprof)工具可对程序的函数调用次数,函数占用工夫,准确展现。是程序开发的一个有用的工具。 用法:在编译时退出 gcc -pg 选项。失常运行程序后,会产生一个gmon.out文件.通过如下命令可查看程序运行中各函数调用次数以及运行工夫。 $ gprof app gmon.out > report.txt输入后果会展现在report文件中。

August 26, 2020 · 1 min · jiezi

关于c:C语言指针详解

指针问题: 指针是什么? 简略来说就是咱们应用指针变量,通过这个指针变量拜访该数值 请看上面的示例代码 int num = 7;// 创立指针变量int *p = &num ;printf("%d\n", *p);在上述代码中,能够看出指针变量申明的办法及如何创立指针 指针变量的申明(申明指针时类型肯定要雷同,防止一些谬误)类型 *指针变量名 = &变量名 其中&是取地址符的意思 上面通过简略示例来演示指针的根本应用办法 int a = 1, b = 5;swap(int &a, &b); // 调用函数void swap(int *a, int *b){ int temp = *a; *a = *b; *b = temp; printf("a = %d\nb = %d\n", *a, *b);}// result: a = 5, b = 1在下面代码中,函数申明咱们应用了地址符指向了两个值,在函数中,咱们通过一个长期变量实现替换变量。试想下,如果咱们没有通过指针来实现替换,会胜利么? 上面是我没有指针指向变量而意料之外的谬误 swap(int a, int b);void swap(int a, int b){ int temp = a; a = b; b = temp; printf("a = %d\nb = %d\n", a, b)}// result: a = 1, b = 1通过下面测试得悉,这样在产生转换时,不能够失去咱们想要的后果 ...

August 23, 2020 · 1 min · jiezi

关于c:C程序的内存布局

程序与过程程序与过程不同程序是动态的概念,表现形式为一个可执行文件过程是动静的概念,程序由操作系统加载运行后失去过程每个程序能够对应多个过程每个过程只能对应一个程序程序文件的个别布局文件布局在内存中的映射File Header 、.text、.data、.bss、a.out栈、堆、.bss、.data、.text、未映射区域、a.out过程的地址空间 程序的内存布局各个段的作用堆栈段在程序运行后才正式存在,是程序运行的根底.bss段寄存的是未初始化的全局变量和动态变量.text段寄存的是程序中的可执行代码.data段保留的是曾经初始化了的全局变量和动态变量.rodata段存放程序中的常量值,如字符串常量(只读存储区)程序术语的对应关系动态存储区通常指程序中的.bss和.data只读存储区通常指程序中的.rodata局部变量所占空间为栈上的空间动静空间为堆中的空间程序可执行代码寄存于.text段思考:面试中的小问题同是全局变量和动态变量,为什么初始化和未初始化的保留在不同段中?读取速率不同,把初始化与未初始化分凋谢, 可能无效的进步映射效率,读取效率。 小结:程序源码在编译后对应可执行程序中的不同存储区程序和过程不同,程序动态概念,过程是动静概念堆栈段是程序运行的根底,只存在于过程空间中程序可执行代码寄存于.text段,是只读的.bss和.data段用于保留全局变量和动态变量

August 22, 2020 · 1 min · jiezi

关于c:Linux驱动入门之如何写一个字符驱动

这是博主第一次写开放式技术性文章,写这篇文章的目标既是为了总结学到的常识,也是为了能帮忙老手,因为在学习的过程中也是面向某度编程,导致呈现了很多的坑,既有相干帖子的问题,也有本人的问题,我会将其中的坑一一注明,尽可能地让大家在少踩坑的过程中写出一个驱动。一、一个要解决加减法问题的驱动题目: 通过内核驱动程序,在安卓终端实现加减运算 要求:a. 算法在内核驱动中实现,通过ioctl调用;       b. 应用程序调用驱动算法实现加减法运算,在安卓终端输出 ./fun 3 + 5,输入后果8; 要解决这个问题,咱们就要理解linux驱动的相干常识、编译工具的应用、安卓调试工具ADB的应用、linux驱动调试办法以及Makefile的编写等常识,重在利用怎么调用驱动这个过程。二、基础知识大梳理 linux驱动常识(1)linux驱动分类 linux中所有皆文件,linux零碎把硬件设施标记为非凡的文件,分为以下三种:字符设施就是LCD、触摸屏等设施,块设施就是如U盘、SD卡、电脑硬盘等设施,而网络设备就是常见的网卡设施、蓝牙设施等。依据题目要求,这次的驱动就以一个简略的字符驱动开始。(2)利用怎么调用驱动 !两幅图都援用自两位前辈的帖子,再次感激前浪的开源分享精力!先是草根老师的图,比拟具体地介绍了应用程序怎么通过形象函数open()、read()、wiite()、ioctl()函数实现关上文件获取文件描述符、寻找对应inode,继而创立struct file,保留文件信息,确认设施信息,拷贝cdev地址信息,进而通过这些信息找到咱们所写的底层write()、ioctl()函数,实现相应的性能,数据从应用层、VFS虚构文件系统再到驱动层再最终传达到硬件设施,通过比照crystal_guang老师第二幅图,能够理解VFS层的实现根底就是库以及内核。2.设么是内核态?什么是用户态图片来自于在此期间的笔记,曾经很充沛了,咱们这次次要的工作就是实现用户态到内核态的切换,从切换形式可知咱们是通过零碎调用来应用驱动的。 怎么写仍然拿出草根老师的图: 驱动的实现具体就是三部,分为底层(本人写的)open()、write()、read()、ioctl()函数的实现,struct file_operations、初始化函数以及退出函数,具体程序放到前面。 实现驱动计算得出后果有2种方法: (1)、计算后用return返回计算值,间接输入后果 (2)、开拓空间保留,而后用read()读取 既然要玩,果然要谋求刺激,咱们抉择第二种办法。我的程序参考了草根老师的驱动程序,程序写的十分丑陋,而且草根老师程序足足更改了三次,欠缺了很多,最初的版本能够驱动多个同类设施并且能够动静申请节点,然而帖子有些内容不全面而且也有问题,这也是我写这篇文章的初衷,那就是写一个真真能够调用的驱动而不是输入hello,我会在最初把他们的博客内容放在最初,通过浏览我的以及他们的博客,置信前人能够很顺利写出一个能够跑的字符驱动。来聊聊ioctl()ioctl函数是文件构造中的一个属性重量,就是说如果你的驱动程序提供了对ioctl的反对,用户就能够在用户程序中应用ioctl函数来管制设施的I/O通道,它须要传递三个参数,蕴含文件动静信息的文件构造体,命令以及参数。第一个在应用程序里反映的就是文件描述符,cmd则是判断语句里须要用到的,linux为cmd设计了具体的规定以及命令生成函数以便于程序人员开发本人的命令,第三个参数就用来传递参数,既能够传递整数也能够传递指针,然而指针的传递须要在程序外面进行测验,否则有可能造成零碎奔溃。CMD的常识须要大家本人学习一下,命令的规定比较复杂,相对来说网上材料也比拟多,大家齐全能够本人学习一下,而且ioctl()函数在 2.6当前的内核中改版了,须要留神一下。5.什么是编译?什么是穿插编译?什么是连贯 因为大家很多都是从IDE时代过去的,对编译的概念很含糊,然而到linux环境中必须要理解这些常识。 6.Makefile、.ko文件 Makefile文件用来让大家实现主动编译,从而生成驱动文件.ko,具体的语法以及常识,大家能够本人去学习一下,这些也是一名嵌入式工程师的必修课。上面贴一下我的Makefile KERN_DIR = /studio/...all: make -C $(KERN_DIR) M=`pwd` modules CROSS_COMPILE=aarch64-linux-gnu-clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.orderobj-m += in_proc.o其中KERN_DIR就是你所用linux零碎kernel内核的门路,须要提一下的是,在make命令之前须要先编译一下内核。7.ADB工具应用 次要用一下几条指令: ADB connect IPADB rootADB remountADB push windows门路 /bin补充一条 ADB devices ADB didconnect,因为ADB常常断连贯所以须要查看和排除连贯问题二、避坑指南 ioctl版本ioctl在改版之后也有2种,次要是为了解决零碎兼容的问题,分为compat_ioctl与unlocked_ioctl,安卓零碎也分为32位与64位版本,32位零碎,内核版本64位则须要应用compat_ioctl,如果零碎和内核版本都是32位或者64位则会调用unlock_ioctl,这一点如果不留神就会发现调用ioctl时候会报错,须要大家留神。 编译工具在安卓跑驱动的时候须要用专门的工具来编译应用程序,还有穿插编译生成驱动文件的编译器,都有具体的版本要求,须要大家联合本人的设施抉择。三、源码浏览 开始带大家读代码,大部分的问题都藏在代码中或者能够通过梳理代码解决,RTFSC!先上驱动代码 #include <linux/init.h>#include <linux/module.h>#include <linux/cdev.h>#include <linux/fs.h> //函數指針寄存#include <linux/device.h>#include <linux/slab.h>#include <asm/uaccess.h>#include <linux/kernel.h>#include "in_proc.h"//指定的主设施号#define MAJOR_NUM 250/*struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count;}; *///注册本人的设施号struct mycdev { int len; unsigned int buffer[50]; struct cdev cdev;};MODULE_LICENSE("GPL"); //許可說明//设施号static dev_t dev_num = {0};//全局gcdstruct mycdev *gcd;//设施类struct class *cls;//取得用户传递的数据,依据它来决定注册的设施个数static int ndevices = 1;module_param(ndevices, int, 0644);MODULE_PARM_DESC(ndevices, "The number of devices for register.\n");//关上设施static int dev_fifo_open(struct inode *inode, struct file *file){ struct mycdev *cd; //用struct file的文件公有数据指针保留struct mycdev构造体指针 cd = container_of(inode->i_cdev,struct mycdev,cdev); //该函数能够依据构造体成员获取构造体地址 file->private_data = cd; printk( KERN_CRIT "open success!\n"); return 0;}//读设施static ssize_t dev_fifo_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos){ int n ; int ret; unsigned int *kbuf; struct mycdev *mycd = file->private_data; //printk(KERN_CRIT "read *ppos : %lld\n",*ppos); //光标地位 if(*ppos == mycd->len) return 0; //申请大小 > buffer残余的字节数 :读取理论记的字节数 if(size > mycd->len - *ppos) n = mycd->len - *ppos; else n = size; //从上一次文件地位指针的地位开始读取数据 kbuf = mycd->buffer+*ppos; //拷贝数据到用户空间 ret = copy_to_user(ubuf,kbuf,n*sizeof(kbuf)); if(ret != 0) return -EFAULT; //更新文件地位指针的值 *ppos += n; printk(KERN_CRIT "dev_fifo_read success!\n"); return n;}//写设施static ssize_t dev_fifo_write(struct file *file, const char __user *ubuf, size_t size, loff_t *ppos){ int n; int ret; unsigned int *kbuf; struct mycdev *mycd = file->private_data; printk("write *ppos : %lld\n",*ppos); //曾经达到buffer尾部了 if(*ppos == sizeof(mycd->buffer)) return -1; //申请大小 > buffer残余的字节数(有多少空间就写多少数据) if(size > sizeof(mycd->buffer) - *ppos) n = sizeof(mycd->buffer) - *ppos; else n = size; //从上一次文件地位指针的地位开始写入数据 kbuf = mycd->buffer + *ppos; //拷贝数据到内核空间 ret = copy_from_user(kbuf, ubuf, n*sizeof(ubuf)); if(ret != 0) return -EFAULT; //更新文件地位指针的值 *ppos += n; //更新dev_fifo.len mycd->len += n; printk("dev_fifo_write success!\n"); return n;}//linux 内核在2.6当前,曾经废除了ioctl函数指针构造,取而代之的是unlocked_ioctllong dev_fifo_unlocked_ioctl(struct file *file, unsigned int cmd,unsigned long arg){ int ret = 0; struct mycdev *mycd = file->private_data; struct ioctl_data val; //定义构造体 printk( KERN_CRIT "in dev_fifo_ioctl,sucessful!\n"); if(_IOC_TYPE(cmd) != DEV_FIFO_TYPE) //检测命令 { printk(KERN_CRIT "CMD ERROR\n"); return -EINVAL; } /*if(_IOC_NR(cmd) > DEV_FIFO_NR) { printk(KERN_CRIT "CMD 1NUMBER ERROR\n"); return -EINVAL; }*/ switch(cmd){ case DEV_FIFO_CLEAN: printk("CMD:CLEAN\n"); memset(mycd->buffer, 0, sizeof(mycd->buffer)); break; case DEV_FIFO_SETVALUE: printk("CMD:SETVALUE\n"); mycd->len = arg; break; case DEV_FIFO_GETVALUE: printk("CMD:GETVALUE\n"); ret = put_user(mycd->len, (int *)arg); break; case DEV_FIFO_SUM: //求和 printk( KERN_CRIT "It is CMD:SUM!!!!\n"); //查看指针是否平安|获取arg传递地址 if(copy_from_user(&val,(struct ioctl_data*)arg,sizeof(struct ioctl_data))){ ret = -EFAULT; goto RET; } //printk( KERN_CRIT "CMD:SUM!!!!!\n"); memset(mycd->buffer,0,DEV_SIZE); //清空区域 val.buf[1] = val.buf[0]+val.buf[2]; //计算结果 //printk(KERN_CRIT "result is %d\n",val.buf[1]); memcpy(mycd->buffer,val.buf,sizeof(val.buf)*val.size); /*for(i = 0;i < 50;i++) printk( KERN_CRIT "%d\n",mycd->buffer[i]);*/ mycd->len = val.size; file->f_pos = 0; break; case DEV_FIFO_DEC: //求差 printk(KERN_CRIT "It is CMD:DEC!!!!\n"); //查看指针是否平安|获取arg传递地址 if(copy_from_user(&val,(struct ioctl_data*)arg,sizeof(struct ioctl_data))){ ret = -EFAULT; goto RET; } memset(mycd->buffer,0,DEV_SIZE); //清空区域 val.buf[1] = val.buf[0]-val.buf[2]; //计算结果 memcpy(mycd->buffer,val.buf,sizeof(val.buf)*val.size); /*printk(KERN_CRIT"size is :%d\n",val.size); for(i = 0;i < 50;i++) printk( KERN_CRIT "%d\n",mycd->buffer[i]);*/ mycd->len = val.size; file->f_pos = 0; break; default: return -EFAULT; //谬误指令 } RET: return ret;}//设施操作函数接口static const struct file_operations fifo_operations = { .owner = THIS_MODULE, .open = dev_fifo_open, .read = dev_fifo_read, .write = dev_fifo_write, .compat_ioctl = dev_fifo_unlocked_ioctl,};//模块入口int __init dev_fifo_init(void){ int i = 0; int n = 0; int ret; struct device *device; gcd = kzalloc(ndevices * sizeof(struct mycdev), GFP_KERNEL); //内核内存失常调配,可能通过睡眠来期待,此函数等同于kmalloc() if(!gcd){ return -ENOMEM; //內存溢出 } //设施号 : 主设施号(12bit) | 次设施号(20bit) dev_num = MKDEV(MAJOR_NUM, 0); //动态注册设施号 ret = register_chrdev_region(dev_num,ndevices,"dev_fifo"); if(ret < 0){ //动态注册失败,进行动静注册设施号 ret = alloc_chrdev_region(&dev_num,0,ndevices,"dev_fifo"); if(ret < 0){ printk("Fail to register_chrdev_region\n"); goto err_register_chrdev_region; } } //创立设施类 cls = class_create(THIS_MODULE, "dev_fifo"); if(IS_ERR(cls)){ ret = PTR_ERR(cls); goto err_class_create; } printk("ndevices : %d\n",ndevices); for(n = 0;n < ndevices;n ++){ //初始化字符设施 cdev_init(&gcd[n].cdev,&fifo_operations); //增加设施到操作系统 ret = cdev_add(&gcd[n].cdev,dev_num + n,1); if (ret < 0){ goto err_cdev_add; } //导出设施信息到用户空间(/sys/class/类名/设施名) device = device_create(cls,NULL,dev_num + n,NULL,"dev_fifo%d",n); if(IS_ERR(device)){ ret = PTR_ERR(device); printk("Fail to device_create\n"); goto err_device_create; } } printk("Register dev_fito to system,ok!\n"); return 0;err_device_create: //将曾经导出的设施信息除去 for(i = 0;i < n;i ++){ device_destroy(cls,dev_num + i); }err_cdev_add: //将曾经增加的全副除去 for(i = 0;i < n;i ++) { cdev_del(&gcd[i].cdev); }err_class_create: unregister_chrdev_region(dev_num, ndevices);err_register_chrdev_region: return ret;}void __exit dev_fifo_exit(void){ int i; //删除sysfs文件系统中的设施 for(i = 0;i < ndevices;i ++){ device_destroy(cls,dev_num + i); } //删除零碎中的设施类 class_destroy(cls); //从零碎中删除增加的字符设施 for(i = 0;i < ndevices;i ++){ cdev_del(&gcd[i].cdev); } //开释申请的设施号 unregister_chrdev_region(dev_num, ndevices);}module_init(dev_fifo_init);module_exit(dev_fifo_exit);接下来是利用代码: ...

August 20, 2020 · 4 min · jiezi

关于c:指针到底是个什么鬼怎么用

学数据结构时,创立链表,他定义了个二级指针,很懵逼,所以就有了这篇博客。指针的 * 有啥用?在我看来就像打排位匹配对手一样,什么样的段位匹配什么样的对手。就比方我是黑铁4,就匹配不到最强王者。 理解你定义的变量,是怎么来的:首先咱们来看根本数据类型,int a = 100, a在内存中,是怎么保留的呢,首先开拓一个内存,地址如:0X61,而后他代表的值是100。 而后再来看 一级指针变量 int *p1 = &a,咱们首先还是须要去内存中开拓一个空间,而后如果 p1这个指针的值是 0X62 ,而后留神了,咱们也给p1这个指针,赋值了的,所以他就指向 a 的地址: 0X61。 最初就是二级指针变量了, int **p2 = &p1, 如上,**p2的地址值为 0X63,他指向的值就应该是 p1的地址值 。 如下图: 总结一下:这时候你应该悟了,其实每个变量(无论是指针变量还是根本数据类型)都应该有两个值,一个是本人的地址值,一个是装(贮存)的值,只不过根本类型和指针装的不一样,一个装数据,一个装地址。 对于int a来说,他的地址是0X61,他装的是100,而对于一级指针int *p1来说,他的地址是0X62,而他装的是 a的地址0X61,对于int **p2来说,他的地址是0X63,而他装的是 *p1的地址 0X62; 柯南工夫到:当初就来揭秘,指针后面那个 * 有啥用(咱们要晓得这玩意有啥用,最无利的证据,就是用代码输入后果): #include <stdio.h>int main(){ int a =100; int *p1 = &a; int **p2 = &p1; int ***p3 = &p2; printf("%d, %d, %d, %d\n", a, *p1, **p2, ***p3); printf("&p2 = %#X, p3 = %#X\n", &p2, p3); printf("&p1 = %#X, p2 = %#X, *p3 = %#X\n", &p1, p2, *p3); printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\n", &a, p1, *p2, **p3); return 0;}100, 100, 100, 100&p2 = 0X28FF3C, p3 = 0X28FF3C&p1 = 0X28FF40, p2 = 0X28FF40, *p3 = 0X28FF40&a = 0X28FF44, p1 = 0X28FF44, *p2 = 0X28FF44, **p3 = 0X28FF44咱们从后果,再联合书上讲的,就精炼一下嘛,&示意取取变量的地址符号,*则示意取值符号,。 ...

August 16, 2020 · 1 min · jiezi

关于c:学C语言和C它有毛关系吗

这是最近一周工夫几个读者小伙伴所提的问题,我棘手截了两个图。 实不相瞒,这类问题之前也常常看到,然而我忘了截图了。 每次遇到这种问题,看起来很简略,然而打字一时半会还真说不清,想想明天周末了,写一篇文章来对立聊聊吧,如果小伙伴们有不同认识,也欢送批评指正,评论区见。 本文在开源我的项目:https://github.com/hansonwang99/JavaCollection 中已收录,外面蕴含不同方向的自学编程路线、面试题汇合/面经、及系列技术文章等,资源继续更新中...” C语言和C++到底是什么关系?首先C++和C语言原本就是两种不同的编程语言,但C++的确是对C语言的裁减和延长,并且对C语言提供后向兼容的能力。对于有些人说的C++齐全就蕴含了C语言的说法还是有点顺当的。 C++一开始被本贾尼·斯特劳斯特卢普(Bjarne Stroustrup)创造时,起初被称为“C with Classes”,即「带类的C」。很显著它是在C语言的根底上裁减了类class等面向对象的个性和机制。然而起初通过一步步订正和很屡次演变(如下图所示),最终才造成了现如今这个反对一系列重大个性的宏大编程语言。 就像经典书籍《Effective C++》一结尾就说的,现如今咱们提到C++,都应该视其为一个宏大的「语言联邦」,最起码蕴含如下几个重要的组成部分: 面向过程编程面向对象编程泛型编程元编程函数式编程STL规范库这其中的第一局部「面向过程编程」,正是C++提供的向后兼容C语言的局部,所以你能看到市面上在售的大部分讲C++编程的书,一开始前几个章节根本都是在讲「面向过程编程」的内容,包含但不限于:数据类型、变量、运算符、表达式、语句、判断、循环、函数、指针等等这些内容。 不学C语言能间接学C++吗?还是像后面所说,C++编程语言的第一大重要组成部分就是「面向过程编程」,而这正是C语言老大哥的畛域。即便没有学过C语言,一上来就间接学习C++的小伙伴,应该也难逃『面向过程』这一部分的内容。因为市面上在售的大部分讲C++编程的书,开始的章节都在讲「面向过程编程」的内容。 从实践上来说,学C++前并不一定非得学C语言,然而有C语言底子再去学C++往往更具劣势,最起码「面向过程编程」这一部分内容可能驾轻就熟。 然而遗憾的是,即便是《C++ Primer》这种700多页厚的权威C++书籍,结尾也只有很少一部分在讲「面向过程编程」,所以对于面向过程这一部分的讲述是必定没有专门讲C语言的书籍分析得粗疏和全面的,不然也不会有专门讲指针相干的《C和指针》这类书籍的呈现了。 所以集体倡议是在学C++之前,C语言的根底还是尽量要夯实,必定是有帮忙的。 C学得好的,学习C++是否更具劣势?是的。 最起码学C++时,外面的「面向过程」这一部分内容能够说驾轻就熟了。 C++能代替C语言吗?既然C++这么弱小,蕴含这么多模块和范式,而且也简直蕴含了C语言面向过程这一部分的内容,那为啥还要学C语言呢?都间接学习C++它不香嘛? 是的,C++很弱小没错,但那些弱小的范式和机制自身带来的包袱就不轻,也的确给学习者造成了不小的累赘,甚至劝退了很多人。 而反观C语言,C语言自身就是一个把能力、性能、效率和学习老本衡量得十分极致的一种编程语言,以至于大学阶段必开的程序设计课程里根本都有C语言的身影。 而且C语言的应用领域极度宽泛,上到操作系统底层的原生接口,下到一般的应用层开发,C语言都有着不小的功绩。以至于这么多年来,在Tiobe编程语言排行榜里,C语言都是居高位不下。 而且2020开年C语言重回巅峰王座,一举夺得「2019年度编程语言」。尽管这只是一个看起来很无聊的排名,但多多少少能阐明一些事件。 所以无论是过来,当初,甚至是将来,近50岁的C语言老将军仍然永不为奴。 只有C++这种面向对象的语言才适宜大型项目吗?C++的呈现确实是为了更不便地开发大型应用程序,毕竟面向对象编程里的很多重要思维和机制都对大型项目和简单零碎所要求的我的项目工程化、代码复用性/扩展性/可维护性等提供了弱小的撑持。 然而摆在眼前的事实通知咱们,即使是C语言,也照样能够构建出极其简单的零碎和软件。上到Linux这种旷世平凡的操作系统内核,小到被各个公司重度依赖的Redis、Nginx等开源软件或框架,都是C语言的代表作品。 所以有时候咱们不得不抵赖的是,大家所说的形象能力更多的是看写这个程序的人,而并非编程语言自身。 小 结好啦,扯得有点多了,总结一下就是: C语言和C++是两个不同的编程语言,只不过内容上有肯定的重叠;C语言是一门很弱小的编程语言,我感觉有机会还是要学一下;一般来说,有了C语言的根底,上手C++也会更快;C++和C各有各的选用思考和利用场景,并没有谁更好一说,学不学看本人的趣味和本身技术倒退的考量*书籍举荐最初聊一聊学习C语言和C++的书籍吧。 集体感觉如果想零碎学习这两门语言,最好还是得看一下经典的书籍。 对于C语言学习书籍,最最权威的当然是C语言的发明者Dennis M. Ritchie所著的《The C Programming Language》(它也有中文版的),除此之外《C Primer Plus》也很零碎全面。指针局部强化能够零碎浏览《C和指针》。 对于C++的学习书籍,最权威的当属C++的发明者Bjarne Stroustrup大佬所著的《The C++ Programming Language》,然而很显著这本书不适宜初学者,更加适宜的还得是《C++ Primer》,也很零碎全面。至于再深刻能够持续浏览诸如《Effective C++》、《STL源码分析》、《深度摸索C++对象模型》等书籍。 这些书读完,成神之路便可由此开启。 本文在开源我的项目:https://github.com/hansonwang99/JavaCollection 中已收录,外面蕴含不同方向的自学编程路线、面试题汇合/面经、及系列技术文章等,资源继续更新中...” 每天提高一点点 慢一点能力更快

August 15, 2020 · 1 min · jiezi

关于c:读书笔记c指针数组

最近在重新学习下c语言,浏览《c程序语言设计》。读到5.10节, 实现一个“find -x -n 模式”命令,x示意打印所有与模式不匹配得文本,-n示意打印行号。看似一个简略得编程。 书中在解决-nx命令选项的代码如下(通过删减): #include <stdio.h>int main(int argc,char *argv[]){ char c; while(--argc&&(*++argv)[0]=='-'){ while(c=*++argv[0]){ switch(c){ case 'x': printf("x\n"); break; case 'n':~~~~ printf("n\n"); break; default : printf("userage not right\n"); } } }}(++argv)[0] 和++ argv[0] 看起来真的着实有点晕啊。相必很多人跟我一样,刚开始看到这几行代码不晓得啥意思。遂记录下来,供我当前参考,避免遗记。 main得参数argv的定义:char * argv[],argv 是一个字符指针,指向一个char \*的数组。如下图: ++argv代表什么呢?因为* 和++ 的的优先级雷同,联合性是从右到左,因而*++argv首先计算++argv 。++argv将argv指针下移到下一个元素,在这里指向数组的第二个元素的地位。++argv 那么是第二个元素的值,是一个指向char的指针,如下图。 晓得了*++argv 那么(*++argv)[0],其实相当于*((*++argv)+0),是数组第二个元素的指向的第一个char的值。 如果第一个字符是‘-’,那么持续向下执行while(c=++argv[0]),判断是否是规定的属性,退出响应的解决形式。在上图的根底上,++argv[0]是什么呢。因为[]的运算符优先级高于++和* 因而,首先是计算argv[0],这个值是数组的值,是数组第二个元素的值。++argv[0]就是将指针指向下一个字符的地位,*++argv[0]

August 13, 2020 · 1 min · jiezi

关于c:C-程序结构

原文链接,还有更多编程语言教程 C 程序结构c程序由头文件和函数组成。C语言程序执行过程中最根本的三种构造: 程序构造:从上到下、从左到右、从小到大,一条条执行直到完结指令完结程序分支构造:须要理解if、else、elseif、switch case的用法循环构造:for循环、while循环、do..while循环C 程序次要包含以下局部: 预处理器指令、函数、变量、 语句 & 表达式、 正文 用C语言在线运行工具运行代码咱们来看一段简略的代码,能够输入单词 "Hello World": #include <stdio.h>int main(){ /* 我的第一个 C 程序 */ printf("Hello, World! \n"); return 0;}将下面的代码粘贴到在线运行工具中, 点击运行按钮就能够看到程序运行后打印输出的内容 Hello, World!。 C语言在线运行工具地址: http://c.jsrun.net 咱们接下来解说一下下面这段程序: 第1行 #include <stdio.h> 是预处理器指令,通知 C 编译器在理论编译之前要蕴含 stdio.h 文件。 第2行 int main() 是主函数,从这里开始执行程序。 第4行 /.../ 会被编译器疏忽,两头的内容就是正文。 第5行 printf(...) 是 C 中另一个可用的函数,会在屏幕上显示音讯 "Hello, World!"。 第6行 return 0; 终止 main() 函数,并返回值 0。 在本地编译 & 执行 C 程序咱们来看看接下来如何把源代码保留在一个文件中,以及如何编译并运行它。上面是简略的步骤: ...

August 12, 2020 · 1 min · jiezi

关于c:Qt-自定义组件

问题反馈邮箱:1508539502@qq.comBannerLabel 滚动横幅部件仓库源文件 成果 个性可随机增加图片门路及提示信息汇合可清空图片门路及提示信息汇合可设置图片切换工夫可设置提示信息文字色彩可设置指示器地位 右边 + 两头 + 左边可设置鼠标悬停进行播放可设置组件固定大小(须要与图片尺寸匹配)图片切换时,可收回切换信号以后图片被点击时,可收回点击信号不依赖其它自定义组件,易集成应用示例 /* BannerLabel example begin */ BannerLabel *bannerLabel = new BannerLabel(this); bannerLabel->setTextColor(Qt::green); bannerLabel->setIndicatorPosition(BannerLabel::IndicatorLeft); bannerLabel->setInterval(2000); bannerLabel->setHoverStop(true); bannerLabel->setFixedSize(800, 400); const QList<std::pair<QString, QString>>&& imagepairlst = { std::make_pair("C:/Users/DELL/Desktop/bannerLabel/banner-image/0", "离离原上草!"), std::make_pair("C:/Users/DELL/Desktop/bannerLabel/banner-image/1", "一岁一枯荣!"), std::make_pair("C:/Users/DELL/Desktop/bannerLabel/banner-image/2", "野火烧不尽!"), std::make_pair("C:/Users/DELL/Desktop/bannerLabel/banner-image/3", "春风吹又生!"), std::make_pair("C:/Users/DELL/Desktop/bannerLabel/banner-image/5", "Banner 横幅组件!"), }; bannerLabel->addImage(imagepairlst); connect(bannerLabel, &BannerLabel::imageChange, [=](int index, const QString &imagePath, const QString &tipText){ qDebug() << "imageChange: " << index << " " << imagePath << " " << tipText; }); connect(bannerLabel, &BannerLabel::imageclicked, [=](int index, const QString &imagePath, const QString &tipText){ qDebug() << "imageclicked: " << index << " " << imagePath << " " << tipText; }); /* BannerLabel example end */PopupMessageBox 屏幕右下角信息提醒对话框仓库源文件 ...

August 10, 2020 · 1 min · jiezi

关于c:c语言-结构体中定义函数

通常咱们在写c代码的时候,可能心愿能在构造体上定义函数,然而c语言中不反对间接定义函数,咱们能够通过定义函数指针的形式来实现// 上面是一个简略的实例#include <stdio.h>#include <stdlib.h> /* 定义一个构造体,外面有三个成员,是三个函数指针 ,后面千万不要加static,这里并没有分配内存*/struct prt_fn { int (*add) (int a, int b); int (*sub) (int a, int b); int (*mult) (int a, int b);};static int add(int a, int b){ return a + b;}static int sub(int a, int b){ return a - b;}static int mult(int a, int b){ return a * b;}int main(){ int a = 10, b = 5; // 初始化构造体 struct prt_fn pfn = { add, sub, mult }; printf("a + b = %d\n", pfn.add(a, b)); printf("a - b = %d\n", pfn.sub(a, b)); printf("a * b = %d\n", pfn.mult(a, b)); return 0;}

August 5, 2020 · 1 min · jiezi

关于c:STM32F4-HAL库-UART相关API介绍

STM32F4 HAL库 UART相干API介绍本文绝大部分翻译自ST的官网用户手册 Description of STM32F4 HAL and LL driversUSART 与 UART 的区别在于有没有同步通信的性能。USART: 通用同步异步收发器 ; UART: 通用异步收发器。 当进行异步通信时,这两者是没有区别的。这个同步通信性能能够把USART当做SPI来用,比方用USART来驱动SPI设施。 同步(阻塞模式)是指:发送方收回数据后,等接管方发回响应当前才发下一个数据包的通信形式。 异步(非阻塞模式)是指:发送方收回数据后,不等接管方发回响应,接着发送下个数据包的通信形式。 其中SPI IIC为同步通信 UART为异步通信, usart为同步&异步通信。 参考:https://blog.csdn.net/anbaixi... 硬件相干常识STM32F427/STM32F429 共有4个USART与4个UART,如下表 序号U(S)ART_RX引脚U(S)ART_TX引脚U(S)ART_CK引脚USART_ CTS引脚USART_ RTS引脚USART1PA10PA9PA8PA11PA12USART2PA3/PD6PA2/PD5PA4/PD7PA0/PD3PA1/PD4USART3PB11/PC11/PD9PB10/PC10/PD8PB12/PC12/PD10PB13/PD11PB14/PD12UART4PA1/PC11PA0/PC10 UART5PD2PC12 USART6PC7/PG9PC6/PG14PC8/PG7PG13/PG15PG8/PG12UART7PE7PE8 UART8PE0PE1 RT: Receive Data 接收数据TX: Transmit Data 发送数据CK: Clock (同步)时钟硬件流控制 RTS: Request To Send 申请发送数据CTS: Clear To Send 容许发送数据 参见: UART通信中流控RTS和CTS的了解 https://blog.csdn.net/u013797... 相干构造体变量USRT_InitTypeDef该构造体定义了用于初始化UART的一些相干参数typedef struct{ uint32_t BaudRate; //波特率 uint32_t WordLength;//字长 取值参考 UART_Word_Length 宏定义 uint32_t StopBits; //进行位 取值参考 UART_Stop_Bits 宏定义 uint32_t Parity; //奇偶校验模式 取值参考 UART_Parity 宏定义 uint32_t Mode; //收发模式 取值参考 UART_Mode 宏定义 uint32_t HwFlowCtl; //是否关上硬件流控制 取值参考 UART_Hardware_Flow_Control 宏定义 uint32_t OverSampling;//是否关上过采样模式 取值参考 UART_Over_Sampling 宏定义 .}UART_InitTypeDef;UART_HandleTypeDef 该构造体定义的则是UART句柄(集体了解为用于操作UART)的一些参数该构造体中只有*Instance与Init两个成员变量是须要咱们配置的typedef struct{ USART_TypeDef *Instance; /*!< UART registers base address */ // UART/USART相干寄存器的地址 曾经在HAL库中定义完 参数为 U(S)ARTx x=1...8 UART_InitTypeDef Init; /*!< UART communication parameters */ // UART初始化参数构造体 uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */ // 缓存指针 uint16_t TxXferSize; /*!< UART Tx Transfer size */ // 缓存指针指向的数据的大小(字节) uint16_t TxXferCount; /*!< UART Tx Transfer Counter */ // 缓存指针指向的数据的的数量(字节) uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */ uint16_t RxXferSize; /*!< UART Rx Transfer size */ uint16_t RxXferCount; /*!< UART Rx Transfer Counter */ DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters * // DMA句柄 DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */ HAL_LockTypeDef Lock; /*!< Locking object */ // 锁对象 __IO HAL_UART_StateTypeDef State; /*!< UART communication state */ // UART通信状态 __IO uint32_t ErrorCode; /*!< UART Error code */ // 错误码}UART_HandleTypeDef;HAL Locked The HAL lock is used by all HAL APIs to prevent accessing by accident shared resources.HAL库中的API通过该参数来判断某个API是否正在执行,如__HAL_LOCK(__HANDLE__)与__HAL_UNLOCK(__HANDLE__)所实现的 ...

August 3, 2020 · 5 min · jiezi

关于c:经验栈C与是德科技信号发生器Keysight-RF-Signal-GeneratorsN9310A通信操作

1、前言2、C#代码2.1 参考C#与泰克示波器(Tektronix oscilloscope)通信操作中C#代码;2.2 能够用是德科技提供的通信代码;2.3 应用Ivi.Visa.Interop类;3、简略指令参考资料 # 1、前言 这次应用的仪器是是德科技(keysight)的射频信号发生器,型号为N9310A,来一张侧面照。 2、C#代码2.1 参考C#与泰克示波器(Tektronix oscilloscope)通信操作中C#代码;2.2 能够用是德科技提供的通信代码;环境:IO 程序库套件 我的项目-援用-增加-程序集-Keysight.Visa  using System;  using Ivi.Visa;  using Keysight.Visa;    namespace IdnSample  {   class IdnSample   {   static void Main(string\[\] args)   {   GpibSession session \= new GpibSession ("MyInstr",   Ivi.Visa.AccessModes.None,   2000);     try   {   IMessageBasedFormattedIO io \= session.FormattedIO;   io.PrintfAndFlush("\*IDN?");//发送查问设施指令   string\[\] response \= new string\[\] { "", "", "", "" };   io.Scanf("%,s", out response);//读取信息,以逗号或空格划分字符串   Console.WriteLine("Manufacturer: {0}", response\[0\]);   Console.WriteLine("Instrument Model: {0}",   response\[1\].TrimEnd(new char\[\] {'\\n'}));   if (response.Length \> 2)   {   Console.WriteLine("Firmware Revision: {0}",   response\[2\].TrimEnd(new char\[\] {'\\n'}));   }   if (response.Length \> 3)   {   Console.WriteLine("Serial Number: {0}",   response\[3\].TrimEnd(new char\[\] {'\\n'}));   }   }   catch   {   Console.WriteLine("\*IDN? query failed");   }   finally   {   ession.Dispose();   session \= null;   }   Console.WriteLine("Press any key to end...");   Console.ReadKey();   }   }  }2.3 应用Ivi.Visa.Interop类;  ...

July 27, 2020 · 2 min · jiezi

关于c:FFmpeg小点记AVPKTFLAGDISCARD

记录一下踩到的 AV_PKT_FLAG_DISCARD 一个坑。 背景做一个 取帧 的性能,可能传入指定工夫,并获取该工夫点的帧。 实现步骤如下: 获取视频流的 duration 。 因为局部格局的素材须要做适配,所以这里是通过取 pkt->pts + pkt->duration 的最大值来计算的。依据 duration 做容错,大于或等于duration 时,就取 duration 工夫点的帧。取帧的判断条件是: if (frame->pts <= clock && clock < frame->pts + frame->duration) { // 采纳的是左闭右开的形式 [pts, pts+duration) 取帧}问题看似都比拟失常,然而奇怪的事件呈现了,视频流的 duration 为 6133 (即: {pkt->pts: 6100, pkt->duration: 33})。然而解码进去最初一帧 AVFrame 的 pts 却是 6067。 这就导致了依照上述的取帧形式始终取不到工夫点为 大于6100 的帧。 解决经排查,发现在计算视频流 duration 的时候,最初一个 AVPacket 的 flags 字段中蕴含了 AV_PKT_FLAG_DISCARD 标记。该标记的作用如下: /** * Flag is used to discard packets which are required to maintain valid * decoder state but are not required for output and should be dropped * after decoding. **/#define AV_PKT_FLAG_DISCARD 0x0004大抵的意思能够简略的了解为:带有该标记的AVPacket所携带的数据为解码器相干的信息,不会被解码出一幅图像。 ...

July 24, 2020 · 1 min · jiezi

关于c:实操案例字符串哈希表操作

摘要:当遇到C语言库没有字符串哈希表的时候,该如何进行操作。有考C语言可信编程认证的共事常常会问到,C语言库没有字符串哈希表操作,那考试遇到了怎么办。尽管历次考试的题目中没有必须要用到C语言哈希表的题目(至多我都能用惯例C做进去),然而还须要防患未然,这里给出一道有代表性的题目,能够尝试做做看:https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/ 给定一个字符串 s 和一些长度雷同的单词 words。找出 s 中恰好能够由 words 中所有单词串联造成的子串的起始地位。 留神子串要与 words 中的单词齐全匹配,两头不能有其余字符,但不须要思考 words 中单词串联的程序。示例:输出: s = "barfoothefoobarman", words = ["foo","bar"]输入:[0,9]解释:从索引 0 和 9 开始的子串别离是 "barfoo" 和 "foobar" 。输入的程序不重要, [9,0] 也是无效答案。这题不思考编程语言的话,用哈希表会比较简单,那要是用C语言的话,能够本人撸个哈希表用,凑合这类题目还是入不敷出的。 思路的话参考https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-6/中的解法二,这里只讲下怎么最简略结构一个哈希表。 首先是选取哈希函数,这里我用的是djb2算法,参考http://www.cse.yorku.ca/~oz/hash.html,碰撞率相当低,散布均衡,实现也很简略,就两三行代码,记住要害数字(5381和33)。 If you just want to have a good hash function, and cannot wait, djb2 is one of the best string hash functions i know. it has excellent distribution and speed on many different sets of keys and table sizes.Language- 代码 ...

July 20, 2020 · 2 min · jiezi

C指针阅读技巧分析

指针浏览技巧解析左右法令从最里层的圆括号中未定义的标识符看起首先往右看,再往左看遇到圆括号或方括号能够确定局部类型,并调转方向反复2,3步骤,直到浏览完结1.int(*p)(int) ==>p为最里层圆括号未定义标识符==>p为指针,指向函数,指向的函数有一个int参数,返回值为int2.int(p1)(int ,int(f)(int)); ==>p1为指针,指向函数,指向的函数有int,f为第二个参数,它是函数指针,指向的函数参数是int,返回值为int;返回值为int3.int(p2[5])(int); ==>p2为数组,有5个元素,这5个元素为指针,指向函数,函数类型为int(int*)4.int((p3)[5])(int); ==>p3为指针,数组指针,指向的数组有5个元素,这5个元素为指针,是函数指针,指向的函数类型int(int)5.int((p4)(int))(int) ==>p4为指针,函数指针,参数为int,返回值为指针,是函数指针,指向的函数类型int(int)6.int((p5)(int))[5]; ==>p5为指针,函数指针,参数为int,返回值为指针,指向数组,指向的数组类型为int[5] typedef int(ArrayType)[5];typedef ArrayType(Functype)(int);Functype* p5; 小结:左右法令总结于编译器对指针变量的解析过程指针浏览练习的意义在于了解指针的组合定义可通过typedef简化简单指针的定义

July 15, 2020 · 1 min · jiezi

CCJava到Python编程入门学习什么语言好

摘要:回顾编程语言几十年来的兴衰起伏,仿佛也折射了整个信息产业的变迁沦亡,想要在技术的洪流里激流勇进,找准并学精一两门编程语言更加显得至关重要。最近,TIOBE更新了7月的编程语言榜单,长年霸榜的C、Java和Python仍然蝉联前三位。万万没想到的是,R语言竟然冲到了第八位,创下了史上最佳记录。而且后续随着业内对数据统计和开掘需要的上涨,R语言热度颇有些势不可挡的架势。 然而作为程序员吃饭的工具,编程语言之间也造成了某种鄙视链,各大论坛里弥漫着剑拔弩张的氛围,众口难调。也难怪有很多初学者会有纳闷,为什么会有这么多编程语言,我到底应该学什么语言? 回顾编程语言几十年来的兴衰起伏,仿佛也折射了整个信息产业的变迁沦亡,想要在技术的洪流里激流勇进,找准并学精一两门编程语言更加显得至关重要。 编程语言的黄金时代“有人不喜爱花括号,开发了Python;有人在一个周末设计了出了JavaScript;有人因为下班太无聊,于是创造了C语言”。对于编程语言的八卦轶事很多,但归根结底,一个编程语言的诞生肯定是需要的推动。 从面向机器的语言、面向过程的语言到面向对象的语言,编程语言的历史也经验了由繁到简。而互联网崛起的90年代,无疑也是编程语言的黄金时代。 套用维基百科的总结,“1990年代未见到有什么重大的翻新,大多都是以前构想的重组或变动。” 但就是这样的一个时代,却是各种编程语言大放异彩的开始。 彼时最支流的编程语言是贝尔实验室两位大佬创造的C/C++语言,互联网的载体计算机的基础设施,都是用这套语言来编写的,包含Linux操作系统、数据库、编译器等等。 这也奠定了C语言在IT畛域的位置,只有有计算机的存在,C/C++必定会有它们的一席之地。 但后来者居上的故事也随时在产生,这里的配角是曾经闭幕的今日巨头网景和Sun。 1994年,网景公布了一款真正的网络浏览器Navigator,但受限于过后的技术限度,Navigator更像是一个本地展现页面,不具备任何交互性能。巧的是,Sun公司在1995年正式向市场推出他们开发的Java(即Oak语言),特点是“一次编写,到处运行”,网景看中了这个潜力股,抉择与Sun结盟。 过后的Java能够作为脚本嵌入到网页中实现交互性能,即咱们所熟知的Java Applet。然而,网景认为Java作为网页脚本还是过于简单,程序员Brendan Eich就在10天内,集百家之长,开发了Javascript,现在所有浏览器前端库都须要基于JavaScript实现。 计算机的遍及、互联网的崛起,Web利用的呈现,Ruby、PHP等语言也悉数退场。 如知乎作者“强哥学堂”述,“这段时间,尽管没有发明编程语言,然而因为大量企业的参加,编程语言失去了空前的倒退,造成了明天的编程语言的根本状态。也使得软件开发越来越标准化,规模化。” 与此同时,还有一些“小众”的编程语言,正蓄势待发中,期待着“爆红”的机会。 苹果的搅局,生态很要害2010年iPhone 4问世,乔布斯用智能手机改写了之后的互联网倒退轨迹。iOS和Android撑持起的智能手机市场,让挪动互联网成为相对的舞台配角。 苹果创始了一个全新的生态,作为iOS的利用开发语言,始于1984年的Objective-C从籍籍无名走向声名大噪,依靠各种iPhone、iPad应用程序的开发,Objective-C曾一度挺进TIOBE排行榜的Top 3。 不过,苹果在2014年的WWDC上推出了新的编程语言Swift,反对编写macOS/OS X、iOS、iPadOS、watchOS和tvOS的软件应用,并在第二年发表开源、反对Linux。 自此之后,Objective-C的市场份额逐年递加,从2014年的12%降落到2016年的1%。从下图也能够看出,新语言Swift从0开始,这几年始终处于稳定增长中,往年1月也冲进了TIOBE榜单的第九位。 尽管一门计算机语言从编译器、语法、根底库到在程序员的圈子中遍及,并不是短时间能够一蹴而成,但苹果依然以一己之力将Objective-C和Swift带入了编程语言的支流市场,由此可见,编程语言和一个产品的生态规模非亲非故。 微软和Visual Basic的关系也是如此,挪动互联网时代之前,VB已经依附可视化的开发环境,成为最煊赫一时的桌面开发工具,并一度领有300万开发者。而且借助Windows操作系统市场劣势,VB现在也始终放弃着居高不下的热度(遗憾的是,微软示意不打算持续演变 Visual Basic了)。 回到挪动互联网时代,另一边的Android抉择的是Java,随着Android营垒的越来越宏大,Java始终是TIOBE榜单上的第一,问题最差也能拿个第二。 一个编程语言的长盛不衰,除了它自身的劣势之外,其生态的建设也至关重要,毕竟用的人多了,也就能自成一派,这一点在Python的风行中体现的酣畅淋漓。 大数据+AI时代,Python一骑绝尘当学Python要从娃娃抓起的口号开始风行时,编程语言从IT圈子进入到公众圈。 大数据、AI的时代,Python能够说是一骑绝尘,从数据分析、机器学习、Web开发到爬虫开发都能够在Python的帮忙下十分轻松的解决。 统计分析人员能够用Python解决数据爬取和剖析的问题,大学生能够用Python解决租房的问题…… 最要害的是,Python的学习门槛比C++,Java都要低,简洁的语法、成熟的第三方库,即便是非程序员群体,也能在短时间内速成。 用于统计分析、绘图、数据挖掘的R语言也是如此,赶上了数据利用剖析的潮流,天然能从泛滥编程语言中怀才不遇。 针对往年7月榜单R语言创纪录的问题,TIOBE的 CEO Paul Jansen 明确示意有两大趋势起着关键作用:一是商业统计语言和软件包(如 SAS、Stata 和 SPSS)的时代曾经完结,大学和钻研机构采纳Python和R进行统计分析;二是与COVID-19相干的钻研须要进行大量统计和数据挖掘,所以易于学习和应用的统计编程语言热度回升。 再比方诞生于2010年,由Mozilla主导开发的Rust语言,其更加强调安全性、存储器配置、以及并发解决等个性。去年Facebook推出的区块链我的项目Libra,就抉择了Rust语言来构建,因为它足够平安。 Rust的排名也从2012年的200名开外,在最新一期TIOBE榜单上冲进了Top20。 咱们以互联网的崛起为分界线,能够看到各个编程语言的此消彼长,必然随同着新的产品和技术。适应新技术倒退的编程语言,即使时隔几十年,也能够从新焕发荣耀。而且随着开源社区的壮大,一门优良的编程语言能够博采众家之长,成长为后起之秀。 结语如果你想以编程语言立生,倡议你先学习Java,而后是Python和C++,因为这三种编程语言,基本上能够解决咱们工作中遇到的大部分问题。 所以,无妨先试试华为云推出的Java开发全栈成长打算,让你0根底入门,构建Java技能体系,向全栈工程师迈进! 最初,如C#之父Anders Hejlsberg曾总结:如果要我概括在将来十年编程语言会变成什么样,首先,我认为编程语言应该变得更加“申明式”,咱们须要设法为语言引入一些如元编程,函数式编程的能力,同时可能也要寻找让用户有方法扩大语法,使他们能够结构畛域特定语言等等。不过总体来说我想强调的是,对于编程语言,新的范式则是“多范式”编程语言。 本文局部内容有参考以下文章: 1、《IT行业激荡30年->编程语言变迁史》 https://zhuanlan.zhihu.com/p/106808045 2、《计算机和编程语言的倒退历史》 https://blog.csdn.net/abc6368765/article/details/83990756 点击关注,第一工夫理解华为云陈腐技术~

July 15, 2020 · 1 min · jiezi

uftrace在arm平台的交叉编译使用介绍

uftrace概述uftrace 工具可用来跟踪和剖析用 C/C++ 编写的程序。它深受 Linux 内核的 ftrace 框架(尤其是函数图跟踪器)的启发,并反对用户空间程序。它反对各种命令和过滤器来帮忙分析程序的执行和性能。它的个性如下: 它能跟踪可执行文件中的每个函数并显示持续时间,它也能够跟踪内部库调用(入口和进口),通常也反对内部库嵌套的内部库或者外部函数调用。它能够在性能级别显示具体的执行流程,并报告哪个函数的开销最高,它还显示了与执行环境相干的各种信息。能够设置过滤器,以便在跟踪时排除或蕴含特定函数。此外,它还能够保留和显示函数参数和返回值。它反对多过程和多线程应用程序。如果内核有启用函数图跟踪器(CONFIG_FUNCTION_GRAPH_TRACER=y),那么它还能够跟踪内核函数(应用 -k 选项)。实际toolchain:gcc-9.1.0-2019.11-x86_64_arm-linux-gnueabihf 下codegit clone https://github.com/namhyung/uftrace.git编译./configure --prefix=$PWD/output --host=arm-linux-gnueabihf make -j5make install生成布局为: 运行环境筹备板子挂载共享目录ifconfig eth0 upifconfig eth0 xxx.xxx.xxx.xxxmount -t nfs xxx.xxx.xxx.xxx:/xxx /mnt -o nolock将生成的 output/* 拷贝到共享目录的对应地位,板子上布局如下: 编译二进制程序要带上 -pg 或者 -finstrument-functions,本文全程以上面的程序为例子://multi_thread.c#include <pthread.h>#include <unistd.h>void *thread_funcA(void *arg){ printf("%s:%d\n", __FUNCTION__, __LINE__); sleep(5); printf("%s:%d\n", __FUNCTION__, __LINE__); return NULL;}void *thread_funcB(void *arg){ printf("%s:%d\n", __FUNCTION__, __LINE__); sleep(10); printf("%s:%d\n", __FUNCTION__, __LINE__); return NULL;}int main(void){ pthread_t thidA; pthread_t thidB; printf("%s:%d\n", __FUNCTION__, __LINE__); pthread_create(&thidA, NULL, thread_funcA, "funcA"); printf("%s:%d\n", __FUNCTION__, __LINE__); pthread_create(&thidB, NULL, thread_funcB, "funcB"); printf("%s:%d\n", __FUNCTION__, __LINE__); pthread_join(thidA, NULL); printf("%s:%d\n", __FUNCTION__, __LINE__); pthread_join(thidB, NULL); printf("%s:%d\n", __FUNCTION__, __LINE__); return 0;}编译如下: ...

July 11, 2020 · 2 min · jiezi

Programiz-中文系列教程翻译完成

原文:Programiz协定:CC BY-NC-SA 4.0 欢送任何人参加和欠缺:一个人能够走的很快,然而一群人却能够走的更远。 在线浏览ApacheCN 学习资源目录Programiz C 语言教程 C 简介 C 关键字和标识符C 变量,常量和字面值C 数据类型C 输入输出(I/O)C 编程运算符C 简略示例C 流程管制 C if...else语句C for循环C while和do...while循环C break和continueC switch语句C goto申明C 管制流程示例C 函数 C 函数C 用户定义的函数C 编程中用户定义函数的类型C 递归C 存储类C 函数示例C 数组 C 数组C 多维数组将数组传递给 C 中的函数C 编程指针C 指针数组和指针之间的关系C 按援用调用:应用指针C 动态内存调配C 数组和指针示例C 字符串 C 编程字符串应用库函数进行 C 编程中的字符串操作C 编程中的字符串示例构造与联结 构造构造和指针C 构造与函数C 联结C 构造示例C 文件 C 文件解决C 文件示例其余主题 枚举C 预处理器和宏C 规范库函数C 示例 C 程序:打印金字塔和图案C 程序:查看数字是否为质数C 程序:查看数字是否为回文C 程序:HelloWorldC 程序:打印整数(由用户输出)C 程序:相加两个整数C 程序:将两个浮点数相乘C 程序:查找字符的 ASCII 值C 程序:商和余数C 程序:查找int,float,double和char的大小C 程序:long关键字演示C 程序:替换两个数字C 程序:查看数字是偶数还是奇数C 程序:查看字符是元音还是辅音C 程序:查找三个数字中最大的数字C 程序:查找二次方程的根C 程序:查看平年C 程序:查看数字是负数还是正数C 程序:查看字符是否为字母C 程序:计算自然数之和C 程序:查找数字阶乘C 程序:生成乘法表C 程序:显示斐波那契数列C 程序:查找两个数字的 GCDC 程序:查找两个数字的 LCMC 程序:应用循环从 A 到 Z 显示字符C 程序:计算整数中的位数C 程序:反转数字C 程序:计算数字的幂C 程序:显示两个距离之间的质数C 程序:查看阿姆斯特朗数C 程序:在两个距离之间显示阿姆斯特朗数C 程序:显示数字因数C 程序:应用switch...case制作一个简略的计算器C 程序:应用函数显示区间内的质数C 程序:应用用户定义的函数查看质数或阿姆斯特朗数C 程序:查看一个数字是否能够示意为两个质数之和C 程序:应用递归查找自然数之和C 程序:应用递归查找数字的阶乘C 程序:应用递归查找 GCDC 程序:将二进制数转换为十进制,反之亦然C 程序:将八进制数转换为十进制,反之亦然C 程序:将二进制数转换为八进制,反之亦然C 程序:应用递归来反转句子C 程序:应用递归计算幂C 程序:应用数组计算平均值C 程序:查找数组中的最大元素C 程序:计算标准差C 程序:应用多维数组相加两个矩阵C 程序:应用多维数组将两个矩阵相乘C 程序:查找矩阵的转置C 程序:通过将矩阵传递给函数来将两个矩阵相乘C 程序:应用指针拜访数组元素C 程序:应用按援用调用以循环程序替换数字C 程序:应用动态内存调配查找最大数字C 程序:查找字符串中字符的频率C 程序:计算元音,辅音等的数量C 程序:删除字符串中除字母之外的所有字符C 程序:查找字符串的长度C 程序:连贯两个字符串C 程序:不应用strcpy()复制字符串C 程序:按字典程序(字典程序)对元素进行排序C 程序:应用程序存储学生信息C 程序:应用构造相加两个间隔(以英寸-英尺零碎为单位)C 程序:通过将构造传递给函数来相加两个复数C 程序:计算两个时间段之间的差别C 程序:应用构造存储学生信息C 程序:在构造中动静存储数据C 程序:将句子写入文件C 程序:从文件中读取一行并显示它C 程序:显示本人的源代码作为输入Programiz C++ 教程 ...

July 11, 2020 · 5 min · jiezi

手动打造一个弹窗程序

在平时的剖析当中,常常会碰到PE构造的文件,尽管 010 Editor 等工具会提供一个模板,把各个局部都具体的标记进去,然而在调试的时候,常常会须要在 VS 等程序框中进行调试,所以,就须要对PE构造有肯定的理解,才可能疾速定位到本人想要的中央。 为了更好的理解PE构造中的每一位的作用,最好的方法就是本人手写一个PE文件,这样对每一个局部的了解,都会清晰很多。 目录 0x00 筹备工作 0x01 结构DOS头 0x02 结构File头 0x03 结构Optional头 0x04 结构节表 0x05 结构导入表 0x06 执行代码 0x00 筹备工作 在开始之前,有一些细节是须要提前思考好的,这些细节对于整个PE构造来说是十分重要的。 因为只须要实现一个弹窗的成果,代码量是非常少的,所以在程序的设计上,一个节表就齐全足够了,同时,咱们心愿保障文件尽可能小,所以将文件对齐设置为200,将内存对齐设置为1000。 再加上头部和对齐的思考,文件就须要占用400个字节了,到内存开展当前就占用2000字节。 注:PE格局中,所有能够被笼罩掉,而不影响程序运行的地位,我都会用CC来填充,这些地位能够写入字节的shellcode等。 0x01 结构DOS头 DOS头部在编辑器中占用了4行,其中的少数数据都是在16位的DOS环境下运行时所必备的,在当初看来,曾经是能够占用的内容,只有两个参数是必须的:e_magic和e_lfanew。 e_magic 这个位是识别性的头部(MZ),这个地位是会被作为一个非法PE文件的检测位。 e_lfanew 用来指向一个新的构造,这个也就是咱们当初来说,最重要的构造,所有的参数信息都是在这个构造中定义的。 在DOS头部前面还有一个Stub数据区,是16位程序的残留数据,是能够去掉的,所以就间接将e_lfanew指向了0x40,在这个地位开始新的构造。 0x02 结构File头 PE标识、File头以及Optional头统称为NT头,这里就不提NT的概念了,PE标识有4字节。 File头有1.4行,有4个重要的参数。 Machine 这是一组宏,示意在什么硬件下运行,肯定要依据理论的状况来进行更改,以后是Intel386,所以填0x014c。 NumberOfSections 形容节表的个数,在后面布局的时候提到了,应用一个节表就足够了。 SizeOfOptionalHeader 形容Optional头的长度,在本人写程序进行剖析的时候,肯定要留神这一点,原版的OD间接应用sizeof来取得了,漠视了这个值是变长的,咱们能够本人来更改,达到反调试的目标,这里填写0xE0。 Characteristics 形容可执行文件的属性,具体参照上面这张图。 最终填写如下 0x03 结构Optional头 Optional头波及到的参数就比拟多了,也是最重要的一个局部 Magic 类型辨认,能够来判断到底是32位还是64位,再或者是其余的,咱们应用32位的,为0x10b AddressOfEntryPoint 程序入口点,因为头部对齐后为200字节,所以程序就从文件的200地位开始写,对应到内存中就是1000,所以填写0x1000 ImageBase 倡议装载地址,这个地址并不是肯定能占住的,如果没有到这个地位的话,会依据重定位表来修改程序中的地址,因为要写一个exe文件,个别默认是0x400000 SectionAlignment 内存对齐,填写0x1000 FileAlignment ...

July 11, 2020 · 1 min · jiezi

C语言编程需要掌握的核心要点有哪些-为你总结了这20个

摘要:C语言作为编程的入门语言,学习者如何疾速把握其外围知识点,面对茫茫书海,仿佛有点迷茫。为了让各位疾速地把握C语言的常识内容,在这里对相干的知识点进行了演绎。引言笔者有十余年的C++开发教训,相比而言,我的C教训只有一两年,C比较简单,简略到《The C Programming Language》(C程序设计语言)只有区区的200多页,相比上千页的C++大部头,不得不说真的很人性化了。 C语言精简的语法集和规范库,让咱们能够把精力集中到设计等真正重要的事件上来,而不是迷失在语法的陆地里,这对于初学者尤其重要。尽管C语言有形象有余的毛病,但我更喜爱它的精美,只须要花大量的工夫,钻研分明它每一个知识点,看任何C源码就不会存在语法上的阻碍,大家须要建设的常识共识足够少,少即是多,少好于多。 我教过6集体编程,教过HTML,教过JAVA,也教过C++。最近,我在教我小孩编程,他只有十岁,很多人倡议我抉择Python,但我最终抉择了C语言,因为C语言简略且弱小,当初看来,如同是个不错的抉择。 类型C是强类型语言,有short、long、int、char、float、double等build-in数据类型,类型是贯通c语言整个课程的外围概念。 struct、union、enum属于c的构造类型,用于自定义类型,裁减类型零碎。 变量变量用来保留数据,数据是操作的对象,变量的变字意味着它能够在运行时被批改。 变量由类型名+变量名决定,定义变量须要为变量分配内存,能够在定义变量的同时做初始化。 int i; float f1 = 0.5, f2= 0.8; 常量const int i = 100; const char* p = "hello world"; 运行中恒定、不可变,编译期便可确定。 数组光有简略变量显然不够,咱们须要数组,它模仿事实中雷同类型的多个元素,这些对象是严密相邻的,通过数组名+地位索引便能拜访每个元素。 二维、三维、高纬数组实质上还是线性的,二维数组通过模仿行列给人立体的感觉,理论存储上还是间断内存的形式。 数组是动态的,在定义的时候,数组的长度就曾经确认,运行中无奈伸缩,所以有时候咱们不得不为应酬裁减多调配一些空间。数组元素不论用多用少,它都在哪里,有时候,咱们会用一个int n去界定数组理论被应用的元素个数。 函数函数封装行为,是模块化的最小单元,函数使得逻辑复用变得可能。 C语言是过程式的,事实世界都能够封装为一个个过程(函数),通过过程串联和编排模仿世界。 用C语言编程,行为和数据是拆散的。调用函数的时候,调用者通过参数向函数传递信息,函数通过返回值向调用者反馈后果。 函数最好是无副作用的,函数内应该尽量避免批改全局变量或者动态局部变量,更好的形式是通过参数传递进来,这样的函数只是逻辑的盒子,它满足线程平安的要求。 有了变量和函数,就能够编写简略的程序了。 管制语句分支:if 、else、else if、switch case、?:循环:while、do while、forbreak、continue、goto、default构造体build-in数据类型不足以描述事实世界,或者用build-in类型形容不够间接,构造体用来模仿复合类型,它赋予了咱们裁减类型零碎的能力,咱们把类型组合到一起构建更简单的类型,而每个被组合的成分就叫成员变量。 构造体内的成分,对象通过点(.)运算符,指针通过箭头(->)拜访成员。 指针C语言的灵魂是指针,指针带来弹性,指针的实质是地址。 须要辨别指针和指针指向的对象,多个指针变量可指向同一个对象,一个指针不能同时指向多个对象。 指针相干的基本操作包含:赋值(批改指针指向),解援用(拜访指针指向的对象),取地址(&variable),指针反对加减运算。 因为指针变量要能笼罩整个内存空间,所以指针变量的长度等于字长,32位零碎下32位4字节,64位零碎下64位8字节。 指针的含意远比上述丰盛,指针跟数组联合便有了指针数组(int* p[n])和数组指针(int (*p)[n]),指针跟函数联合便有了函数指针(ret_type (*pf)(param list)),指针跟const联合便有了const char*/char* const/const char* const,还有指向指针的指针(int **p)。 既能够定义指向build-in数据类型的指针,也能够定义指向struct的指针,void*示意通用(万能)指针,它不能被解援用,也不能做指针算术运算。 函数指针与回调(callback)c source code被编译链接后,函数被转换到可执行程序文件的text节,过程启动的时候,会把text节的内容装载到过程的代码段,代码段是c过程内存空间的一部分,所以任何c函数都会占一块内存空间,函数指针就是指向函数在代码段的第一行汇编指令,函数调用就会跳转到函数的第一个指令处执行。 函数指针常常被用来作为回调(callback),c语言也会用蕴含函数指针成员的构造体模仿OOP,实质上是把C++编译器做的事件,转给程序员来做(C++为蕴含虚函数的类构建虚函数表,为蕴含虚函数的类对象附加虚函数表的指针)。 字符串char*是一类非凡的指针,它被称为c格调字符串,因为它总是以‘\0’作为结尾的标识,所以要标识一个字符串,有一个char*指针就够了,字符串的长度被0隐式指出,跟字符串相干的STD C API大多以str打头,比方strlen/strcpy/strcat/strcmp/strtok。 ...

July 10, 2020 · 2 min · jiezi

NI-IMAQ-Error-Code-一览表

-1074397183 IMG_ERR_NCAP NI-IMAQ cannot perform the requested operation. -1074397182 IMG_ERR_OVRN Too many interfaces open. -1074397181 IMG_ERR_EMEM Not enough memory to perform the operation. -1074397180 IMG_ERR_OSER Operating system error occurred. -1074397179 IMG_ERR_PAR1 Function-specific; see function description. -1074397178 IMG_ERR_PAR2 Function-specific; see function description. -1074397177 IMG_ERR_PAR3 Function-specific; see function description. -1074397176 IMG_ERR_PAR4 Function-specific; see function description. -1074397175 IMG_ERR_PAR5 Function-specific; see function description. -1074397174 IMG_ERR_PAR6 Function-specific; see function description. -1074397173 ...

July 3, 2020 · 20 min · jiezi

C语言在线运行在线编译

C语言在线运行,在线编译 这里推荐一下C语言在线运行工具http://c.jsrun.net/ C 语言是一种通用的、面向过程式的计算机程序设计语言。1 C 语言是一种广泛使用的计算机语言,它与 Java 编程语言一样普及,二者在现代软件程序员之间都得到广泛使用。 当前最新的 C 语言标准为 C18 ,在它之前的 C 语言标准有 C17、C11...C99 等 它可以作为系统设计语言,编写工作系统应用程序,也可以作为应用程序设计语言,编写不依赖计算机硬件的应用程序。 特征: 1、C语言是高级语言。它把高级语言的基本结构和语句与低级语言的实用性结合起来。C 语言可以像汇编语言一样对位、字节和地址进行操作.C语言在线运行,在线编译http://c.jsrun.net/ ”C语言?和:是三目运算符,也叫三元运算符。 语法:表达式1 ? 表达式2 : 表达式3; 函数:先执行表达式1,执行完毕,如果表达式1的结果为真,则执行表达式2,整个表达式的结果就是表达式2的结果,否则执行表达式3,结果是表达式3的结果。 例如:int res=1?10:20; 可以简单理解为表达式1成立吗?成立执行表达式2,否则执行表达式3。

July 3, 2020 · 1 min · jiezi

第34课-多维数组和多维指针

指向指针的指针指针的本质是变量指针会占用一定的内存空间可以定义指针的指针来保存指针变量的地址值`int main(){ int i = 0;int* p = NULL;int** PP = NULL;pp = &p;*pp = &i;return 0;}` 为什么需要指向指针的指针?指针在本质上也是变量对于指针也同样存在传值调用与传址调用例子31-4:` include "stdio.h"include "malloc.h"int reset(char**p, int size, int new_size) //重置动态空间的大小{ int ret = 1;int i = 0;int len = 0;char* pt = NULL;char* tmp = NULL;char* pp = *p;if((p != NULL) && (new_size >0)){ pt = (char*)malloc(new_size); //申请一段new_size内存空间 tmp = pt; len = (size < new_size)? size : new_size; //取小的一个进行复制 for( i = 0;i < len; i ++) { *tmp++ = *pp++; //将传来的数据传给新申请的内存空间 } free(*p); //释放原来的内存空间 *p = pt;}else{ ret = 0;}return ret;}int main(){ char p = (char)malloc(5); //新申请一段内存空间,大小为5 printf("%pn",p); if(reset(&p,5,3)) //传址调用 { printf("%pn",p); } return 0;}`输出结果:00F3126000F31298结果分析:内存空间的地址被改变 ...

July 1, 2020 · 2 min · jiezi

C语言探索之旅-第二部分第十一课练习题和习作

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:http://www.jianshu.com/p/30d4...《C语言探索之旅》全系列 内容简介前言练习题练习题答案习作第三部分第一课预告1. 前言上一课和上上一课我们完成了一个游戏: C语言探索之旅 | 第二部分第九课:实战"悬挂小人"游戏C语言探索之旅 | 第二部分第十课:实战"悬挂小人"游戏答案这一课我们来做一些练习题,还有一个小的习作。 这些练习题是帮助大家巩固知识的,其实不难。不看答案来完成这 15 道题目吧,都是单选题。 2. 练习题1.什么是全局变量? A. 可以在随处被访问的变量B. 变量类型任意的变量C. 声明在 main 函数里的变量 2.当我们引入标准库的头文件,使用类似如下的哪一种方式? A. #include <time.h>B. #include "time.h"C. #include {time.h}D. #include [time.h] 3.C语言的编译按如下那种顺序进行?(这里其实省略了一步:汇编(用到汇编器,就不赘述了)) A. 预处理 -> 编译 -> 链接B. 编译 -> 链接 -> 预处理C. 链接 -> 预处理 -> 编译D. 预处理 -> 链接 -> 编译 4.如果我写 &variable,我得到的是什么? A. variable 的地址B. variable 的值C. variable 指向的变量的值 5.我们用什么值来初始化指针? A. NOTHINGB. 1C. NULLD. MAINE. 0_ADDRESS ...

June 20, 2020 · 2 min · jiezi

C语言探索之旅-第二部分第十课-实战悬挂小人游戏答案

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/b23...《C语言探索之旅》全系列 内容简介前言解方(1. 游戏的代码)解方(2. 词库的代码)第二部分第十一课预告1. 前言经过上一课 C语言探索之旅 | 第二部分第九课: 实战"悬挂小人"游戏 之后,相信大家都或多或少都写了自己的“悬挂小人”的游戏代码吧。 这一课我们就来"终结"这个游戏吧 (听着怎么有点吓人...)。 "Yes, you are terminated."2. 解方(1. 游戏的代码)如果你开始阅读这里,说明: 或者你写完了游戏,想来看看我们怎么写。或者你没完成这个游戏,想来看看怎么写。不管你是哪种情况,我都会介绍一下如何来完成这个游戏。 “说不说在我,听不听在您”~ 事实上,我自己花了比想象中更多的时间来完成这游戏。 人生总是这样的,“理想丰满,现实骨感;看似美满,人艰不拆”。 但是,我还是坚信大家是有能力独自完成这个小游戏的(如果你认真学习了之前的 C语言课程),可以去查阅网上资料,花点时间(几十分钟,几小时,几天?),这并不是一次竞赛,所以不用着急。 我更希望您花了不少时间,最终实现了这个游戏; 比之您只花 5 分钟,然后就来看答案要好很多。 千万不要觉得我是一蹴而就写成这个游戏的,这个游戏虽小,但也还没简单到可以在脑中构思好一切,然后“下笔如有神”: 我也是一步步写出来的。 我们将会分 2 步来介绍我们的解方: 首先我们会演示如何一步步写游戏的主体部分,一开始我们会只有一个猜测的单词,而且是固定的;我选了 BOTTLE(表示“瓶子”),因为我们要测试对于单词中有大于等于两个相同字母的情况是否处理正确了(BOTTLE 中有 2 个 T)。然后我们会演示如何加入词库的处理程序,以便每一轮游戏可以从词库中随机抽取一个单词。牢记:重要的不是结果,而是我们思考的方式和过程。分析 main 函数大家都知道,我们的 C语言程序都是由 main 函数作为入口的。 我们也不要忘了引入一些标准库的头文件:stdio.h,stdlib.h,ctype.h(为了 toupper 函数)。 因此,我们的程序一开始会是这样的: #include <stdio.h>#include <stdlib.h>#include <ctype.h>int main(int argc, char* argv[]){ return 0;}是不是很简单啊,慢慢来么。 我们的 main 函数将控制游戏的大部分运作,并且调用我们将要写的不少函数。 我们来声明一些必要的变量吧。这些变量也不是一次就能全部想到的,都是写一点,想到一些。“罗马不是一日建成的, 小人也不是一日能悬挂完的”。 #include <stdio.h>#include <stdlib.h>#include <ctype.h>int main(int argc, char* argv[]){ char letter = 0; // 存储用户输入的字母 char secretWord[] = "BOTTLE"; // 要猜测的单词 int letterFound[6] = {0}; // 布尔值的数组。数组的每一个元素对应猜测单词的一个字母。0 = 还没猜到此字母, 1 = 已猜到字母 int leftTimes = 7; // 剩余猜测次数(0 = 失败) int i = 0; // 为了遍历数组,需要一个下标 return 0;}上述的变量中,起到关键作用的就是 letterFound 这个 int 型数组了。这个数组用于表示猜测的单词中哪些字母已经猜到,哪些还没猜到。 ...

June 20, 2020 · 7 min · jiezi

C语言探索之旅-第二部分第九课-实战悬挂小人游戏

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/6cb...《C语言探索之旅》全系列 内容简介前言题目规定优化建议第二部分第十课预告1. 前言第二部分的理论知识基本讲完了。上一课我们经历了很有意思的 C语言探索之旅 | 第二部分第八课:动态分配 。 这一课我们来实战一下,要实现的游戏叫“悬挂小人”。 这个“小人”,不是“君子和小人”的小人。是 little man(小小的人)的意思。读者:“你有必要这么强调吗?简直无聊嘛...” 好的,话休絮烦... 俗语说得好:“实践是必要的!” 对于大家来说这又尤为重要,因为我们刚刚结束了一轮 C语言的高级技术的“猛烈进攻”,需要好好复习一下,消化消化。 不论你多厉害,在编程领域,不实践是永远不行的。尽管你可能读懂了之前的所有课程,但是如果不配合一定的实践,是不能深刻理解的。 以前我大学里入门编程以前看 C语言的书,觉得看懂了,但是一上手要写程序,就像挤牙膏一样费劲。 这次的实战练习,我们一起来实现一个小游戏:“悬挂小人”,或叫 “上吊游戏”。英语叫 HangMan,是挺著名的一个休闲益智游戏。 虽说是游戏,但是比较可惜的是还不能有图形界面 (不过课程后面会说怎么实现在控制台绘制小人,其实也可以实现简陋的“图形化”): 因为 C语言本身不具备绘制 GUI(Graphical User Interface 的缩写,表示“图形用户接口”)的能力,需要引入第三方的库。 悬挂小人游戏是一个经典的字母游戏,在规定步数内一个字母一个字母地猜单词,直到猜出整个单词。 所以我们的游戏暂时还是以控制台的形式(黑框框)与大家见面,当然如果你会图形编程,也可以把这个游戏扩展成图形界面的。 相信不少读者应该见过这个游戏的图形界面版本,就是每猜错一个字母画一笔,直到用完规定次数,小人被“吊死”。 这个实战的目的是让我们可以复习之前学过的所有 C语言知识:指针,字符串,文件读写,结构体,数组,等等,都是好家伙! 2. 题目规定既然是出题目的实战,那么就需要委屈大家按照我的题目要求来编写这个游戏啦。 好,就来公布我们的题目要求: 游戏每一轮有 7 次(次数可以设置,不一定是 7 次)猜测的机会,用完则此轮失败。每轮会从字典中随机抽取一个单词供玩家猜,初始时单词是以若干个星号(*)的方式来表示。说明所有字母都还隐藏着。字典的所有单词储存在一个文本文件中(在 Windows 下通常是 txt 文件,在 Unix/Linux/macOS 下一般可以是任意后缀名的文件)。每猜错一个字母就扣掉一次机会,猜对一个字母不扣除机会数。猜对的字母会显示在屏幕上的单词中,替换掉星号。 一个回合的运作机制假设要猜的单词是 OSCAR。 假设我们给程序输入一个字母 B(猜的第一个字母),程序会验证字母是否在这个单词里。 有两种情况: 所猜的字母在单词中,此时程序会显示这个单词,不是全部显示,而是显示猜到的那些字母,其他的还未猜到的字母用 * 表示。所猜的字母不在单词中(目前的情况,因为字母 B 不在单词 OSCAR 中),此时程序会告诉玩家“你猜错了”,剩余的机会数会被扣除一个。如果剩余机会数变为 0,游戏结束。在图形化的“悬挂小人”(Hangman)游戏中,每猜一次会有一个小人被画出来。我们的游戏,虽然还不能真正实现图形化,但是如果优化一下,也可以在控制台实现类似这样的效果: 假设玩家输入一个 C,因为 C 在单词 OSCAR 中,那么程序不会扣除玩家的剩余机会数,而且会显示已猜到的字母,如下: ...

June 17, 2020 · 3 min · jiezi

C语言探索之旅-第二部分第七课文件读写

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/4ad...《C语言探索之旅》全系列 内容简介前言文件的打开和关闭读写文件的不同方法在文件中移动文件的重命名和删除第二部分第八课预告1. 前言上一课 C语言探索之旅 | 第二部分第六课:创建你自己的变量类型 之后,我们来学习很常用的文件读写。 我们学过了这么多变量的知识,已经知道变量实在是很强大的,可以帮助我们实现很多事情。 变量固然强大,还是有缺陷的,最大的缺陷就是:不能永久保存。 因为 C语言的变量储存在内存中,在你的程序退出时就被清除了,下次程序启动时就不能找回那个值了。 “蓦然回首,那人不在灯火阑珊处...”“今天的你我,怎样重复昨天的故事?这一张旧船票,还能否登上你的破船?” 不能够啊,“涛声不能依旧”啊... 如果这样的话,我们如何在 C语言编写的游戏中保存游戏的最高分呢?怎么用 C语言写一个退出时依然保存文本的文本编辑器呢? 幸好,在 C语言中我们可以读写文件。这些文件会储存在我们电脑的硬盘上,就不会在程序退出或电脑关闭时被清除了。 为了实现文件读写,我们就要用到迄今为止我们所学过的知识: 指针,结构体,字符串,等等。 也算是复习吧。 2. 文件的打开和关闭为了读写文件,我们需要用到定义在 stdio.h 这个标准库头文件中的一些函数,结构,等。 是的,就是我们所熟知的 stdio.h,我们的“老朋友” printf 和 scanf 函数也是定义在这个头文件里。 下面按顺序列出我们打开一个文件,进行读或写操作所必须遵循的一个流程: 调用“文件打开”函数 fopen(f 是 file(表示“文件”)的首字母;open 表示“打开”),返回一个指向该文件的指针。检测文件打开是否成功,通过第 1 步中 fopen 的返回值(文件指针)来判断。如果指针为 NULL,则表示打开失败,我们需要停止操作,并且返回一个错误。如果文件打开成功(指针不为 NULL),那么我们就可以接着用 stdio.h 中的函数来读写文件了。一旦我们完成了读写操作,我们就要关闭文件,用 fclose(close 表示“关闭”)函数。首先我们来学习如何使用 fopen 和 fclose 函数,之后我们再学习如何读写文件。 fopen:打开文件函数 fopen 的原型是这样的: FILE* fopen(const char* fileName, const char* openMode);不难看出,这个函数接收两个参数: fileName:文件名(name 表示“名字”)。是一个字符串类型,而且是 const,意味着不能改变其值。openMode:打开方式(open 表示“打开”,mode 表示“方式”)。表明我们打开文件之后要干什么的一个指标。只读、只写、读写,等等。这个函数的返回值,是 FILE *,也就是一个 FILE(file 表示“文件”)指针。 ...

June 17, 2020 · 5 min · jiezi

C语言探索之旅-第二部分第八课动态分配

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/bbc...《C语言探索之旅》全系列 内容简介前言变量的大小内存的动态分配动态分配一个数组总结第二部分第九课预告1. 前言上一课是 C语言探索之旅 | 第二部分第七课:文件读写 。 经历了第二部分的一些难点课程,我们终于来到了这一课,一个听起来有点酷酷的名字:动态分配。 “万水千山总是情,分配也由系统定”。到目前为止,我们创建的变量都是系统的编译器为我们自动构建的,这是简单的方式。 其实还有一种更偏手动的创建变量的方式,我们称为“动态分配”(Dynamic Allocation)。dynamic 表示“动态的”,allocation 表示“分配”。 动态分配的一个主要好处就是可以在内存中“预置”一定空间大小,在编译时还不知道到底会用多少。 使用这个技术,我们可以创建大小可变的数组。到目前为止我们所创建的数组都是大小固定不可变的。而学完这一课后我们就会创建所谓“动态数组”了。 学习这一章需要对指针有一定了解,如果指针的概念你还没掌握好,可以回去复习 C语言探索之旅 | 第二部分第二课:进击的指针,C语言的王牌! 那一课。 我们知道当我们创建一个变量时,在内存中要为其分配一定大小的空间。例如: int number = 2;当程序运行到这一行代码时,会发生几件事情: 应用程序询问操作系统(Operating System,简称 OS。例如Windows,Linux,macOS,Android,iOS,等)是否可以使用一小块内存空间。操作系统回复我们的程序,告诉它可以将这个变量存储在内存中哪个地方(给出分配的内存地址)。当函数结束后,你的变量会自动从内存中被删除。你的程序对操作系统说:“我已经不需要内存中的这块地址了,谢谢!” (当然,实际上你的程序不可能对操作系统说一声“谢谢”,但是确实是操作系统在掌管一切,包括内存,所以对它还是客气一点比较好...)。可以看到,以上的过程都是自动的。当我们创建一个变量,操作系统就会自动被程序这样调用。 那么什么是手动的方式呢?说实在的,没人喜欢把事情复杂化,如果自动方式可行,何必要大费周章来使用什么手动方式呢?但是要知道,很多时候我们是不得不使用手动方式。 这一课中,我们将会: 探究内存的机制(是的,虽然以前的课研究过,但是还是要继续深入),了解不同变量类型所占用的内存大小。接着,探究这一课的主题,来学习如何向操作系统动态请求内存。也就是所谓的“动态内存分配”。最后,通过学习如何创建一个在编译时还不知道其大小(只有在程序运行时才知道)的数组来了解动态内存分配的好处。准备好了吗?Let's Go ! 2. 变量的大小根据我们所要创建的变量的类型(char,int,double,等等),其所占的内存空间大小是不一样的。 事实上,为了存储一个大小在 -128 至 127 之间的数(char 类型),只需要占用一个字节(8 个二进制位)的内存空间,是很小的。 然而,一个 int 类型的变量就要占据 4 个字节了;一个 double 类型要占据 8 个字节。 问题是:并不总是这样。 什么意思呢? 因为类型所占内存的大小还与操作系统有关系。不同的操作系统可能就不一样,32 位和 64 位的操作系统的类型大小一般会有区别。 这一节中我们的目的是学习如何获知变量所占用的内存大小。 有一个很简单的方法:使用 sizeof()。 虽然看着有点像函数,但其实 sizeof 不是一个函数,而是一个 C语言的关键字,也算是一个运算符吧。 我们只需要在 sizeof 的括号里填入想要检测的变量类型,sizeof 就会返回所占用的字节数了。 ...

June 17, 2020 · 4 min · jiezi

C语言探索之旅-第二部分第六课创建你自己的变量类型

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/39b...《C语言探索之旅》全系列 内容简介前言定义一个 struct结构体的使用结构体指针unionenum总结第二部分第七课预告1. 前言上一课是 C语言探索之旅 | 第二部分第五课:预处理 ,应该是比较轻松的。 这一课将会非常令人激动也很有意思,不过有些难度。 众所周知,C语言是面向过程的编程语言,与 Java,C++,等面向对象的编程语言有所不同。 在面向对象的编程语言中,有类(class)的概念。 C语言是没有类这种“类型”的,但是 C语言就不能“模拟”面向对象编程了吗? 不,只要你设计得好,C语言也可以模拟面向对象编程。 这一课我们要学习的 struct(结构体)的知识就可以使你有能力用 C语言实现“面向对象”。 前面我们学习了指针,数组,字符串和预处理,掌握这些知识你的 C语言水平已经还不错啦。但是我们岂能就此止步,必须 Bigger than bigger~ 除了使用 C语言已经定义的变量类型,我们还可以做一些更厉害的事情:创建你自己的变量类型。 我们可以将其称为“自定义的变量类型”,我们来看三种:struct,union 和 enum。 因为当你需要编写比较复杂的程序时,你会发现创建自定义的变量类型是很重要的。 幸好,这学起来其实也不是特别难。但是大家需要专心学习这一课,因为从下一课开始,我们会一直用到 struct 了。 2. 定义一个 struct什么是 struct 呢? struct 是 structure(表示“结构”)的缩写,所以 struct 的专业术语是“结构体”。 定义:struct 就是一系列变量的集合,但是这些变量可以是不同类型的。这个定义是不是唤起了大家对我们的老朋友数组的怀念啊?数组里面的每个成员都必须是同一个类型的,相比之下 struct 更灵活。 一般来说,我们习惯把 struct 定义在 .h 头文件中,也就是和预处理命令以及函数原型那群“家伙”在一起。 下面就给出一个 struct 的例子: struct 你的struct的名字{ char variable1; short variable2; int otherVariable; double numberDecimal;};可以看到:struct 的定义以关键字 struct 开始,后面接你自定义的 struct 的名称(比如 Dog,Cat,Person,等)。 ...

June 10, 2020 · 6 min · jiezi

C语言探索之旅-第二部分第五课预处理

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/cb8...《C语言探索之旅》全系列 内容简介前言include 指令define 命令宏条件编译总结第二部分第六课预告1. 前言上一课 C语言探索之旅 | 第二部分第四课:字符串 ,我们结束了关于字符串的旅程。 大家在一起经历了前三课:指针、数组和字符串的“疲劳轰炸”之后,这一课回归轻松。 就像刚在沙漠里行走了数日,突然看到一片绿洲,还有准备好的躺椅,清澈的小湖,冷饮,西瓜,一台顶配电脑(又暴露了程序员的本质...)... 脑补一下这个画面还是挺开心的。 前面三课我们一下子学了不少新知识点,虽然我没有那么"善良",但也不至于不给大家小憩的机会啊。 这一课我们来聊聊“预处理器”,这个程序就在编译之前运行。 当然了,虽然这一课不难,可以作为中场休息,但不要认为这一课的内容不重要。相反,这一课的内容非常有用(读者:“你就说哪一课的内容不是非常有用吧...”)。 2. include 指令在这个系列教程最初的某一课里,我们已经向大家解释过:在源代码里面总有那么几行代码是很特别的,称之为预处理命令。 这些命令的特别之处就在于它们总是以 # 开头,所以很容易辨认。 预处理命令有好几种,我们现在只接触了一种:以 #include 开始的预处理命令。 #include 命令可以把一个文件的内容包含到另一个文件中。 在之前的课程里我们已经学习了如何用 #include 命令来包含头文件(以 .h 结尾的)。 头文件有两种,一种是 C语言的标准库定义的头文件(stdio.h,stdlib.h,等),另一种是用户自定义的头文件。 如果要导入 C语言标准库的头文件(位于你安装的 IDE(集成开发环境)的文件夹或者编译器的文件夹里),需要用到尖括号 <>。如下所示:#include <stdio.h>如果要导入用户自己项目中定义的头文件(位于你自己项目的文件夹里),需要用到双引号。如下所示:#include "file.h"事实上,预处理器(preprocessor)在编辑之前运行,它会遍历你的源文件,寻找每一个以 # 开头的预处理命令。 例如,当它遇到 #include 开头的预处理命令,就会把后面跟的头文件的内容插入到此命令处,作为替换。 假设我有一个 C 文件(就是 .c 文件),包含我的函数的实现代码;还有一个 H 文件(就是 .h 文件),包含函数的原型。 我们可以用下图来描绘预处理的时候发生的情况: 如上图所示,H 文件的所有内容都将替换 C 文件的那一行预处理命令(#include "file.h")。 假设我们的 C 文件内容如下所示: #include "file.h"int myFunction(int thing, double stuff){/* 函数体 */}void anotherFunction(int value){/* 函数体 */}我们的 H 文件内容如下所示: ...

June 8, 2020 · 3 min · jiezi

C语言探索之旅-第二部分第四课字符串

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/2be...《C语言探索之旅》全系列 内容简介前言字符类型显示字符字符串其实就是字符的数组字符串的创建和初始化从 scanf 函数取得一个字符串操纵字符串的一些常用函数总结第二部分第五课预告1. 前言上一课 C语言探索之旅 | 第二部分第三课:数组 ,我们结束了关于数组的旅程。 好了,这课我不说“废话”,直接进入主题(但又好像不是我的风格...)。这一课我们还是会涉及一些指针和数组的知识。 字符串,这是一个编程的术语,用来描述“一段文字”。 一个字符串,就是我们可以在内存中以变量的形式储存的“一段文字”。 比如,用户名是一个字符串,“程序员联盟”是一个字符串。 但是我们之前的课说过,呆萌的电脑兄只认得数字,“众里寻他千百度,电脑却只认得数。” 所以实际上,电脑是不认得字母的,但是“古灵精怪”的计算机先驱们是如何使电脑可以“识别”字母呢? 接下来我们会看到,他们还是很聪明的。 2. 字符类型在这个小部分,我们把注意力先集中在字符类型上。 如果你还记得,之前的课程中我们说过:char(有符号字符类型)是用来储存范围从 -128 到 127 的数的;unsigned char(无符号字符类型)用来储存范围从 0 到 255 的数。 注意: 虽然 char 类型可以用来储存数值,但是在 C语言中却鲜少用 char 来储存一个数。通常,即使我们要表示的数比较小,我们也会用 int 类型来储存。当然了,用 int 来储存比用 char 来储存在内存上更占空间。但是今天的电脑基本上是不缺那点内存的,“有内存任性嘛”。char 类型一般用来储存一个字符,注意,是 一个 字符。 前面的课程也提到了,因为电脑只认得数字,所以计算机先驱们建立了一个表格(比较常见的有 ASCII 表, 更完整一些的有 Unicode 表),用来约定字符和数字之间的转换关系,例如大写字母 A 对应的数字是 65。 C语言可以很容易地转换字符和其对应的数值。为了获取到某个字符对应的数值(电脑底层其实都是数值),只需要把该字符用单引号括起来,像这样: 'A'在编译的时候,'A' 会被替换成实际的数值 65。 我们来测试一下: #include <stdio.h>int main(int argc, char *argv[]){ char letter = 'A'; printf("%d\n", letter); return 0;}程序输出: ...

June 4, 2020 · 5 min · jiezi

linux模拟多线程崩溃和多进程崩溃

结论是:多线程下如果其中一个线程崩溃了会导致其他线程(整个进程)都崩溃;多进程下如果其中一个进程崩溃了对其余进程没有影响; 多线程 #include <stdio.h>#include <string.h>#include <stdlib.h>#include <pthread.h>#include <assert.h>void *fun1(void *arg){ printf("fun1 enter\n"); while(1) { printf("%s\n", __FUNCTION__); usleep(1000 * 1000); } printf("fun1 exit\n"); return ((void *)1);}void *fun2(void *arg){ printf("fun1 enter\n"); usleep(1000 * 3000); char * ptr = (char *)malloc(sizeof(char)); printf("ptr1: 0x%x\n", ptr); ptr = NULL; printf("ptr2: 0x%x\n", ptr); free(ptr); memcpy(ptr, "123", 3); printf("ptr3: 0x%x\n", ptr); printf("fun2 exit\n"); return ((void *)2);}int main(void){ pthread_t tid1, tid2; int err; err = pthread_create(&tid1, NULL, fun1, NULL); assert(0 == err); err = pthread_create(&tid2, NULL, fun2, NULL); assert(0 == err); printf("main join ...\n");// getchar(); pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0;}多进程 ...

June 3, 2020 · 1 min · jiezi

C语言探索之旅-第二部分第三课数组

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/7fe...《C语言探索之旅》全系列 内容简介前言内存中的数组定义一个数组遍历一个数组初始化数组把数组传递给函数一些小练习总结第二部分第四课预告1. 前言结束了上一课“指针”的有点艰难的旅程(其实上一课没有讲很深),C语言探索之旅 | 第二部分第二课:进击的指针,C语言的王牌! ,这一课我们来学习数组这个 C语言的重点。 我们将继续“一路向北”,“指”哪打哪。 为什么这么说呢?因为这一课我们还要涉及指针的知识。就如上一课说的,指针的使用几乎是贯穿 C语言的,而且我们也会步步深入指针的学习。 不然指针怎么能被称为 C语言的精华呢?所以“指针啊,天天见”,你以为指针会这么“放过”你么?Too young, too naive... :P 想要现在逃避吗?那可不是成功者的表现哦。 很多学 C语言的朋友,都觉得指针和数组貌似有点类似,又好像不同。有点扑朔迷离的感觉,“情深深,雨濛濛”,纠葛不清,难分难舍。 所以这一课我们就来解惑:到底指针和数组有什么联系和区别呢? 学完这一课相信会有些许拨云见雾的感觉。 在这一课中,我们一起学习如何创建数组这种数据类型(或者说是数据结构)。数组在 C语言中也是极为重要的内容,所以大家不能因为过了指针那一坎,就不“正襟危坐”了。 我们会首先解释一下数组在内存中的机制(配图),对内存的解释始终是很重要的,因为理解好了内存的机制,C语言才能学得扎实。 所以推荐大家花些时间去学习王爽老师编写的《汇编语言》第三版,对于理解 C语言和计算机原理是很有帮助的。汇编语言可能不必学得很深,入门就好。 可以看我以前写的文章 学习汇编对编程有什么帮助?如何学习 。 一个程序员如果能很好地知道自己的程序背后的机理,方能写出稳定、健壮的程序。 2. 内存中的数组数组是在内存中具有连续地址的一系列相同类型的变量的集合。好吧,我知道这个定义“学究气太重,腐儒味更甚”。 简单地说,数组就是“巨大的变量”(怎么听起来那么变扭,幸亏我加了一个“的”字...),其中可以存储一系列相同类型的变量(long,double,int,char,等)。 数组在英语中是 array。array 这个词有这些含义:“数组,阵列;排列,列阵;大批,一系列”。 数组中变量(可以称为数组元素或成员)的数目是固定的(当然也可以构造动态数组,以后再说),它可以包含 2 个,3 个,10 个,25 个,2500 个,甚至更多变量,由你决定存放数目。 下图展示了一个由四个元素组成的数组,首元素的地址是 1700。 当你要创建包含 4 个元素的数组时,其实是首先向操作系统这个“大管家”发出请求:“能否给我在内存中分配一块地址,以存放这四个元素”。 操作系统一般都会应声而起,随传随到,乖乖分配你要的地址。 但是对于数组来说,这四个元素的存放地址是连续的,中间没有间隔,这也是数组的一个特点。 各个元素之间“亲密无间”。如上图所示,四个元素的地址分别是:1700,1701,1702,1703。 每一个地址的区块上存放相同类型的一个数字(说到底所有数据对于计算机来说都是数字么)。 如果数组是 int 类型的,那么每一个数组元素的地址块上就存放了一个 int 类型的数。我们不能在一个数组里既存放 int 型又存放 double 型,鱼与熊掌不可兼得也~ 小结一下,对于数组: 当一个数组被创建时,它就占用了内存上地址连续的一块空间,数组的元素之间是一个接一个的。数组的所有元素(成员)都必须是同一类型的数据。例如 int 型的数组,就只能存放 int 型的变量,而不能存放其他类型的变量。3. 定义一个数组我们来学习如何定义一个数组。首先定义一个包含 4 个 int 类型数据的数组: ...

June 2, 2020 · 3 min · jiezi

C语言探索之旅-第二部分第二课进击的指针C语言的王牌

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/e5e...《C语言探索之旅》全系列 内容简介前言棘手的问题内存,地址的问题指针的使用传递指针给函数谁说“棘手的问题”了?总结第二部分第三课预告1. 前言上一课是 C语言探索之旅 | 第二部分第一课:模块化编程 。 终于来到了这一刻(课)。是的,这一课我们就来学习《C语言探索之旅》的重头戏中的重头戏:指针! 如果把这个系列课程比作寻宝之旅的话,那么指针就是最贵重的那个宝藏。如果把 C语言比作一棵佳美的葡萄树,那么指针就是那累累硕果;如果把 C语言比作太阳系,那么指针就是我们美丽的地球;如果把 C语言比作人的一生,那么指针就是令人神往的爱情;如果一定要在这份爱上加一个期限,我希望是一万年...“不好意思,又跑题了~” 总而言之,言而总之,一起来享用这份精心烹制的指针大餐吧! 在开始这一课前,请深吸一口气,因为这一课可能不会像之前那些课一般“悠哉”了。 指针也许是 C语言设计最出彩的地方了,也是精华部分。如果没有了指针,C语言也会黯然失色。 可能你觉得我有点夸大其词,但是在 C语言的编程中,指针是随处可见的,使用很广泛,所以我们不得不来吃掉这个“烫手山芋”。 不少朋友学 C语言的时候,指针那块总是有点“蹒跚却步”。在这一课里我们会努力使你不再如此。你会发现,指针也没那么难。 好的开始是成功的一半,一起加油吧! 2. 棘手的问题对于初学 C语言的朋友,除了觉得指针有点神秘之外,最大的问题之一可能是:搞明白指针到底是用来干什么的。 对此,我会回答说:“指针绝对是必不可少的,我们会一直使用它,请相信我!” 你可能会丢我砖头(说不定还有西红柿、鸡蛋,等物品),因为我说了等于没说。 好吧,我会给你一个问题,你会发现假如不用指针是不能解决的。这个问题有点类似我们这一课的引子。 在这一课的结尾我们还会重新来谈这个问题,并尝试用我们马上要学到的知识来解决。 问题是这样:我要写一个函数,它返回两个值。 “这不可能!”,你会理直气壮地说。 确实,之前我们学过:一个函数只能通过 return 返回一个值。 int function(){ return value;}如上,我们将函数的返回值类型定为 int,那么就用 return 返回一个 int 类型的值。 我们也可以不返回任何值,只要把函数的返回值类型定为 void : void function(){}你会说:“是啊,要一个函数一次返回两个值,不可能啊,我们不能写两个 return 啊。臣妾做不到啊...” 假设我要写的这个函数是这样: 我们给它输入的参数是一个分钟数,它返回给我们两个值:小时数和分钟数。例如:传给函数 30,它返回 0 小时 30 分钟。传给函数 60,它返回 1 小时 0 分钟。传给函数 90,它返回 1 小时 30 分钟。我们可以试着来写一下这个函数: ...

May 31, 2020 · 5 min · jiezi

C语言探索之旅-第二部分第一课模块化编程

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/207...《C语言探索之旅》全系列 内容简介前言函数原型头文件分开编译变量和函数的作用范围总结第二部分第二课预告1. 前言上一课是 C语言探索之旅 | 第一部分练习题 。 话说上一课是第一部分最后一课,现在开始第二部分的探索之旅! 在这一部分中,我们会学习 C语言的高级技术。这一部分内容将是一座高峰,会挺难的,但是我们一起翻越。 俗语说得好:“一口是吃不成一个胖子的。” 但是一小口一小口,慢慢吃,还是能吃成胖子的嘛。所以要细水长流,肥油慢积,一路上有你(“油腻”)~ 一旦你跟着我们的课程一直学到这一部分的结束,你将会掌握 C语言的核心技术,也可以理解大部分 C语言写的程序了。 到目前为止我们的程序都只是在一个 main.c 文件里捣腾,因为我们的程序还很短小,这也足够了。 但如果之后你的程序有了十多个函数,甚至上百个函数,那么你就会感到全部放在一个 main.c 文件里是多么拥挤和混乱。 正因为如此,计算机科学家才想出了模块化编程。原则很简单:与其把所有源代码都放在一个 main.c 当中,我们将把它们合理地分割,放到不同的文件里面。 2. 函数原型到目前为止,写自定义函数的时候,我们都要求大家暂时把函数写在 main 函数的前面。 这是为什么呢? 因为这里的顺序是一个重要的问题。如果你将自己定义的函数放置在 main 函数之前,电脑会读到它,就会“知道”这个函数。当你在 main 函数中调用这个函数时,电脑已经知道这个函数,也知道到哪里去执行它。 但是假如你把这个函数写在 main 函数后面,那你在 main 函数里调用这个函数的时候,电脑还不“认识”它呢。你可以自己写个程序测试一下。是的,很奇怪对吧?这绝对有点任性的。 那你会说:“C语言岂不是设计得不好么?” 我“完全”同意(可别让 C语言之父 Dennis Ritchie 听到了...)。但是请相信,这样设计应该也是有理由的。计算机先驱们早就想到了,也提出了解决之道。 下面我们就来学一个新的知识点,借着这个技术,你可以把你的自定义函数放在程序的任意位置。 用来声明一个函数的“函数原型”我们会声明我们的函数,需要用到一个专门的技术:函数原型,英语是 function prototype。function 表示“函数”,prototype 表示“原型,样本,模范”。 就好比你对电脑发出一个通知:“看,我的函数的原型在这里,你给我记住啦!” 我们来看一下上一课举的一个函数的例子(计算矩形面积): double rectangleArea(double length, double width){ return length * width;}怎么来声明我们上面这个函数的原型呢? 复制,粘贴第一行。在最后放上一个分号(;)。把这一整行放置在 main 函数前面。很简单吧?现在你就可以把你的函数的定义放在 main 函数后面啦,电脑也会认识它,因为你在 main 函数前面已经声明过这个函数了。 ...

May 30, 2020 · 4 min · jiezi

C语言探索之旅-第一部分练习题

作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。转载请注明出处。原文:https://www.jianshu.com/p/3cd...《C语言探索之旅》全系列 内容简介前言练习题练习题答案第二部分第一课预告1. 前言上一课是 C语言探索之旅 | 第一部分第十一课:函数 。 第一部分课程已经全部结束,在开始第二部分的探索之旅之前,我们必须巩固好第一部分学到的知识。 我为你准备了一些练习题:15 道选择题。 15 道题目(单选)在本课附有答案,但是希望你先做完再看答案。 2. 练习题1.负责将源代码转换成二进制码的程序叫什么?A.扬声器B.编译器C.转码器 2.下面哪一个不是 IDE(集成开发环境)?A.VimB.Visual StudioC.Code::BlocksD.Xcode 3.C语言将哪一个值视为布尔值的 false(假)?A.1B.0C.-1D.-2 4.下面这段代码中的 return 0; 意味着什么? #include <stdio.h>int main(){ printf("Hello world!\n"); return 0;}A.程序没有执行操作B.程序没有正常运行C.程序正常运行 5.以下哪一个是 C语言中的换行符?A.tB.nC.按一下键盘上的回车键就够了 6.如果我有一个变量 bankAccount(银行账户),类型是 long,其值为 6500000,那下面的代码会输出什么? printf("您的银行账户上有 %ld 元\n", bankAccount);A.您的银行账户上有 %ld 元B.您的银行账户上有 6500000 元C.您的银行账户上有 ld 元, bankAccount 7.以下哪一种存储器是在电脑关闭之后不会被清空的?A.寄存器B.高速缓存C.内存D.硬盘 8.经过这个操作,result 变量的值是多少? result = (8 / 3) - 2;A.-2B.0C.1D.2 以下 switch 语句的问题是什么?switch (variable){ case 5: printf("Hello"); case 12: printf("你好"); default: printf("再见");}A.少了 break 语句。B.在 switch 的最后的大括号后面少了一个分号。C.对每一个 case 下面的指令需要用一个大括号括起来。D.default 应该写成 case default 。 ...

May 29, 2020 · 1 min · jiezi

C语言探索之旅-第一部分第十一课函数

作者 谢恩铭,公众号「程序员联盟」。转载请注明出处。原文:https://www.jianshu.com/p/148...《C语言探索之旅》全系列 内容简介前言函数的创建和调用一些函数的实例总结第一部分练习题预告1. 前言上一课是 C语言探索之旅 | 第一部分第十课:第一个C语言小游戏 。 这一课我们将会用函数这个重中之重来结束《C语言探索之旅》的第一部分(基础部分),而第二部分将要迎接我们的就是 C语言的高级技术了。 第二部分会比较难,不过不用担心,我们会循序渐进,一点点地学习。只要方向对,肯花时间,C语言一点也不可怕。 这一课里我们也会给大家讲 C语言程序所基于的原则。 我们将要学习如何将程序分块管理,有点像乐高积木。 其实所有 C语言的大型程序都是小程序块的集合,而这些小程序块我们称之为函数。函数的英语是 function,function 表示“功能;[数]函数”。 在面向对象的语言(如 Java,C++)里面,函数又被称为方法(method)。当然这里我们只讨论 C语言(面向过程的语言),不讨论面向对象的语言。 2. 函数的创建和调用在之前的课程中我们已经学过:所有的 C语言程序都是由 main 函数开始运行的。那时候我们也展示了一个概要图,里面有一些术语: 最上面的部分我们称之为“预处理指令”,很容易辨识,因为以 # 号开头,而且通常总是放在程序的最前面。 下面的部分就是我们要学习的函数了,这里的示例是 main 函数。 前面说过,C语言的程度都是以 main 函数为入口函数的。一个 C程序要运行,必须要有 main 函数。只不过,目前为止我们写的所有程序,包括上一课的小游戏,也只是在 main 函数里面捣鼓而已,我们还没跳出过 main 函数过。 那你也许会问:“这样不好吗?” 答案是:并不是说这样不好,但这并不是 C语言的程序员通常所做的。几乎没有程序员会只在 main 函数的大括号内部写代码。 到目前为止我们所写的程序都还比较短小,但是想象一下如果程序变得很大,代码几千几万甚至上百万行,难道我们还把这些代码都塞在 main 函数里面吗? 所以我们现在来学习如何更好地规划我们的程序。我们要学习将程序分成很多小块,就像乐高积木的每一个小块一样,这些小块搭起来却可以组成很多好玩的形状。 这些程序小块我们称之为函数(function)。 一个函数会执行某些操作,并返回一个值。程序就是一个代码序列,负责完成特定的任务。 一个函数有输入和输出,如下图所示: 我们可以把函数想象成一台制作香肠的机器,在输入那一头你把猪装进去,输出那一头就出来香肠了。这酸爽,不言而喻~ 当我们在程序中调用一个函数的时候,会依次发生三个步骤: 输入:给函数传入一些信息(通过给函数一些参数)。运算:使用输入里传进去的信息,函数就可以完成特定任务了。输出:做完运算后,函数会返回一个结果,被称为输出或者返回值。举个例子,比如我们有个函数叫做 multipleTwo,作用是将输入乘以二,如下所示: 函数的目的是为了让源代码更加结构分明,也节省源代码数目,因为我们就不用每次都输入重复的代码片段而只需要调用函数就好了。 再设想一下: 之后我们可能会想要创建一个叫 showWindow(“显示窗口”)的函数,作用是在屏幕上显示一个窗口。 ...

May 28, 2020 · 4 min · jiezi

C语言探索之旅-第一部分第六课变量的世界三显示变量内容

作者 谢恩铭,公众号「程序员联盟」。转载请注明出处。原文:https://www.jianshu.com/p/497...《C语言探索之旅》全系列 内容简介用 printf 显示变量内容用 scanf 提取程序中的输入总结第一部分第七课预告1. 用 printf 显示变量内容变量相关的内容有点多,经过上一课 C语言探索之旅 | 第一部分第五课:变量的世界(二),变量声明 ,今天我们来学习变量的最后一个知识点:显示变量内容。 在上几课中,我们已经知道了如何用 printf 函数在屏幕上显示内容。但那时候还只是显示一些简单的文字,比如“Hello World”,“你好吗”,“饭吃了没”,等等。 下面我们来学习如何用 printf 函数来显示变量内容。 用 printf 来显示变量内容其实也是类似,只不过在我们要插入变量内容的地方要用一个特殊符号代替,例如: printf("你有 %d 只狗");这里的特殊符号其实是 % 加上一个字母(上例中是 d),这个字母表明要显示什么类型,d 表示要显示整数。下表列出了一些常用的字母和对应的变量类型: 格式类型%dint%ldlong%ffloat%fdouble可以看到用于显示 float(单精度浮点数)和 double(双精度浮点数)的符号是一样的(因为说到底它们都是浮点数嘛)。 在适当的时候,我们还会介绍其他的符号,暂时只需要记得这几个就够了。 我们差不多要完工了。我们在确切的位置指明了我们要显示一个整数,但是我们还没指定要显示哪一个数。所以上面的代码还不完整,必须告诉 printf 函数我们要显示的变量。 其实也很简单,我们只需要在双引号后面再写一个逗号,在逗号后面写上我们要显示的变量的名字,如下: printf("你有 %d 只狗", numberOfDogs);程序运行时,printf 函数就会把 %d 替换成变量 numberOfDogs 的值了。 我们用一个完整的程序来测试一下: #include <stdio.h>#include <stdlib.h>int main(int argc, char *argv[]){ int numberOfDogs = 5; // 一开始,你有5只狗 printf("你有 %d 只狗\n", numberOfDogs); printf("**** 跑了一只狗 ****\n"); numberOfDogs= 4; // 刚跑了一只狗,只有4只了 printf("啊呀,你只剩下 %d 只狗了\n", numberOfDogs); return 0;}运行以上程序,屏幕会显示: ...

May 26, 2020 · 2 min · jiezi

C语言探索之旅-第一部分第三课你的第一个程序

作者 谢恩铭,公众号「程序员联盟」。转载请注明出处。原文:https://www.jianshu.com/p/c73...《C语言探索之旅》全系列 内容简介前言控制台程序还是窗口程序最基础的代码特殊字符注释,很有用总结第一部分第四课预告1. 前言在上一课 C语言探索之旅 | 第一部分第二课:工欲善其事,必先利其器 中我们说过,你可以用自己喜欢的 IDE,或者用文本编辑器、编译器和调试器这样的组合来学习本课程。 不过,我觉得对于初学者,还是有必要演示一下某款 IDE 的具体下载和安装等操作。我就以 Code::Blocks 这款免费开源且跨平台的 IDE 为例吧。 下载 Code::Blocks先去 Code::Blocks 的官方下载页面:http://www.codeblocks.org/dow... 。 2020 年 3 月 29 日 Code::Blocks 终于更新了 20.03 版(不容易啊,上一次的版本还是 2017 年的),不过可惜的是 macOS 的版本还是停留在 13.12(网站上标识的是 13.12,但实际安装之后是 17.12 版)。 Windows 用户请下载 .exe 的文件,一般来说是下载 codeblocks-20.03mingw-setup.exe 这样的最新的 .exe 的安装文件。记得要有 mingw 字样的,才包含 GCC 编译器和 GDB 调试器。Linux 用户请下载对应你的操作系统的版本。目前有 Debian、Ubuntu 和 RedHat 系。Ubuntu 可以用 apt 命令来安装,请看官方文档。苹果 macOS 用户请下载最新版的 dmg 文件,例如 CodeBlocks-13.12-mac.zip。不过这个版本有点旧了,可能会有 bug。安装运行 Code::BlocksCode::Blocks 的安装很简单,和安装 QQ 这样的普通软件没多大区别,一路默认到底就好。 ...

May 26, 2020 · 2 min · jiezi

C语言探索之旅-第一部分第四课变量的世界一内存那档事

作者 谢恩铭,公众号「程序员联盟」。转载请注明出处。原文:https://www.jianshu.com/p/e29...《C语言探索之旅》全系列 内容简介前言不同类型的存储器内存第一部分第五课预告1. 前言经过上一课 C语言探索之旅 | 第一部分第三课:你的第一个程序 ,我们已经知道怎么在屏幕上显示文字了。 暂时我们也就只能做这么多了。而这是正常的,因为我们还没学习很重要的编程知识:变量。 变量相关的知识点比较多,我们会分为几课来学习: 内存那档事;声明变量;显示变量内容和提取输入内容。这一课我们学习电脑内存的知识。 每一个正常人都有记忆,我们的老朋友电脑也不例外,只不过它比我们“厉害”,它的记忆(存储器)有好几种。 为什么电脑要有好几种存储器呢?单单一种还不够吗? 不够:主要问题是我们想要电脑有一个既快捷(能够很快提取信息)又容量大(能够存储很多数据)的存储器。 然而,你会哑然失笑,因为目前我们还没有一种兼具快捷和大容量两种特性的存储器。更准确地说,高速存储器太贵,所以容量只能低一点。 因此,我们只能给电脑配备: 或者是很高速但是低容量的存储器;或者是大容量但是低速的存储器。2. 不同类型的存储器以下列出电脑的存储器类型,从速度最快到速度最慢排序: 寄存器(Register):位于处理器(Processor)上,存取速度非常快(因为“近水楼台先得月”)。高速缓存(Cache):用于链接寄存器和内存。内存(Memory):这是我们编程时最常打交道的存储器,也是平时我们买电脑时很关心的数据之一。硬盘(Hard Disk):你肯定不陌生,就是平时 Windows 电脑里的 C 盘,D 盘之类的(当然还有很多其他类型的硬盘,比如 U 盘,EEPROM,等),我们在里面存储文件,照片,视频,等。正如上面所说,我们这里把存储器从最快(寄存器)到最慢(硬盘)做了分类。你应该可以推断出寄存器相比硬盘来说,容量要小很多。 寄存器只能存储少量的数据,而硬盘则可以存储大量的数据。 当我们说一个存储器慢的时候,其实是相对电脑的处理速度而言的。 读取硬盘大概要耗费 8 毫秒,对于一台电脑来说那已经很慢了(现在普通的家用电脑的运算速度都已经可以达到每秒几十亿次了,1 GHz 对应每秒 10 亿次;如果你的电脑主频是 2.5 GHz,那运算速度就是每秒 25 亿次;现在家用的双核电脑通常都能达到每秒 50 亿次运算(5 GHz)左右的水平)。 上面所说的内容,我们需要掌握什么呢? 事实上,只需要关心一部分就够了。要知道在编程时,我们基本上只和内存打交道。我们在之后的课程中也会学习如何读写硬盘,在硬盘上创建文件。至于寄存器和高速缓存,我们基本不关心,这是电脑的事。 当然,在一些很底层的语言中,比如汇编语言(Assembly,简称 ASM),我们经常会用到寄存器。 我以前学生时代跟着王爽老师的《汇编语言》第二版学习了汇编,那本书写得挺好。 但是我想说的是,用汇编哪怕只是做个乘法,也是很不容易的。幸运的是,在 C语言(或其他大部分语言)中做乘法很简单。 还要记住一个很重要的知识点:上述四种存储器类型中,只有硬盘是可以永久保存数据的。其他的存储器(寄存器,高速缓存,内存)都是暂时性的存储器:当你关闭电脑时,这些存储器会被清空。 幸好,当你重新开启电脑时,硬盘里的数据始终存在,所以你的电脑还知道自己姓甚名谁。 3. 内存既然我们以后的大部分时间都要跟内存打交道,那岂能不来认识认识这位朋友呢? 我们用变焦镜头的形式慢慢道来。 一开始看到的是我们的电脑工作环境的整体图片: 上图中的鼠标,键盘,显示屏,你应该都不陌生吧,还有右边那个大家伙:主机箱。 我们关心的是主机箱,因为我们的内存在这里面。 当然,我们的硬盘,寄存器和高度缓存也都在这个主机箱里。 我们来开盖看看里面的内容吧: 看上去有点凌乱,不过不用担心,我们并不需要知道各部分的功能,我们只需要关心我们这一章的主角:内存。 它在哪呢?聪明如你可能已经发现了,我用红色框标识的那个区域就是内存所在。 ...

May 26, 2020 · 1 min · jiezi

C语言探索之旅-第一部分第二课工欲善其事必先利其器

作者 谢恩铭,公众号「程序员联盟」。转载请注明出处。原文:https://www.jianshu.com/p/60c...《C语言探索之旅》全系列 内容简介前言编程的必要工具选择你的 IDE总结第一部分第三课预告1. 前言上一课是 C语言探索之旅 | 第一部分第一课:什么是编程? ,这一课我们就正式进入 C语言编程的正题了。 这一课我们将回答以下问题: 我们需要什么软件来编程呢?这一课大家可以轻松地度过,因为没有什么难点(不过也有不少知识点)。我们会花点时间来认识一些常用的编程软件。 应该好好享受这一课,因为下一课开始我们就要一起写程序了,所以该喝咖啡的喝咖啡,该吃烤鸡的吃烤鸡,休息好了,才能上阵。 2. 编程的必要工具依你看,什么软件对编程来说是必要的呢?如果你认真学了上一课,那你至少可以说出一种吧。 对了,就是编译器。这个重要的程序可以把你的源代码(用高级语言(比如 C语言)写的指令)转换成电脑可以理解的二进制码(只包含 0 和 1 的,例如 01100110001111011101010)。 上一课我们也提了一下,每种高级语言都有对应的编译器(当然对于 Python 这样的解释性语言,就不需要编译了),光是 C语言的编译器就有很多。 比较常用的有 GCC(GNU C Compiler。GNU 是国际著名的自由软件基金会,GNU 是“GNU is Not Unix”(GNU 不是 Unix)的递归缩写法。 老外总是那么顽皮,起名字也喜欢整一些编程的知识,比如 Linux 就是“Linux Is Not Unix”(Linux 不是 Unix)的递归。要不就用很多动物的名字或图标,特别有意思)。 除了编译器,我们还需要什么工具呢? 就不卖关子了吧,下面列出编程的基本工具: 文本编辑器:Text Editor。用来写源代码的工具。理论上 Windows 操作系统的记事本或者 Linux 下的 Nano 编辑器都可以作为文本编辑器来使用。但是,最好还是用一些更高级一点的文本编辑器,比如可以支持代码高亮显示,方便你在代码间跳转,等。个人推荐的文本编辑器是 VS Code、Vim 或 Emacs。这几个文本编辑器真的非常强,跨平台(Windows、Linux、macOS,等),可以自由定制,各种快捷键组合,有的还可以收发邮件,看视频,浏览网页,浏览 PDF,没有做不到,只有想不到(略有夸张...),可以让你编辑代码非常高效。Vim 和 Emacs 是老牌的文本编辑器,粉丝很多。VS Code 是微软在 2015 年发布的一款免费开源的现代化轻量级代码编辑器,现在也非常火。编译器:Compiler。已经提过了,用来转换(或叫“编译”)你的源代码成为二进制码。调试器:Debugger。用来跟踪、发现程序里的错误的工具。这里出现一个新名词,调试(debug)。bug 是英语“臭虫,虫子”的意思,de 在英语里是一个表示“分离,除掉”的前缀,所以 debug 的意思就是“除臭虫”。一般把程序里的错误或者缺陷叫做 bug,据说是因为传奇女程序员 Grace Hopper(格莱斯.霍普)有一次在 MARK II 计算机(就是那种早期的比较庞大的电脑)中发现了一只飞蛾,这只飞蛾导致了这台电脑死机(夹在电脑的继电器之间),霍普用镊子把飞蛾夹出来,用透明胶布贴到笔记本上,并注明“第一个发现虫子(bug)的实例”。从此,人们就沿用了这个有趣的称呼,用 debug 来表示排除程序错误的行为。现在霍普的笔记本,连同那只“呆萌”的飞蛾,陈列在美国历史博物馆里。在 Linux 下,常用的调试器是 GDB(GNU Debugger)。 ...

May 26, 2020 · 2 min · jiezi

C语言探索之旅-第一部分第一课什么是编程

作者 谢恩铭,公众号「程序员联盟」。转载请注明出处。原文:https://www.jianshu.com/p/7f8...内容简介开宗明义什么是编程?总结第一部分第二课预告1. 开宗明义不知道为什么,一直对 C语言有一种很深厚的“情怀”(类似老罗对锤子手机的那种)。 也许因为 C语言是很多前辈谆谆教诲说一定要学一下的一门编程语言;也许因为 C语言自 1972 年诞生以来历经数十年依然独领风骚,位列编程语言排行榜前三;也许因为几乎所有操作系统(Windows、Unix、Linux,macOS,Android、iOS,等),底层都有大量的 C语言代码;也许因为 C语言在嵌入式领域是主要的编程语言;也许因为在诸多编程语言中,C语言的入门是比较难的;也许因为需要 5 到 10 年的时间,你才能说自己精通了 C语言;也许因为学好了 C语言,一般就能轻松入门其他语言。有太多也许,但是可以肯定的是:C语言是一门充满魅力的编程语言。 C语言的发明人,“C语言之父” Dennis Ritchie 在 2011 年去世。很低调,没有太多新闻报道。同年史蒂夫.乔布斯去世,各界报道铺天盖地(当然了,乔布斯也是很伟大的人)。 我想说:应该多纪念那些低调付出的人。 Dennis Ritchie 除了 C语言这项伟大发明(Windows、Linux 和 macOS 操作系统,底层几乎都用到了 C语言),还与 Ken Thompson 一起研发了 Unix 操作系统(虽然大部分工作是 Ken Thompson 起步的,不过后来 Dennis 用自己发明的 C语言重写了 Unix 操作系统)。 要知道,Unix 操作系统成为了后来举世闻名的 Linux 操作系统的参考对象,Android 手机底层就是用的修改过的 Linux 系统。苹果的 macOS 操作系统底层也是类 Unix 系统。我们用的 iPhone 和 iPad 等的操作系统,底层大部分也是 C语言编写的。 不为了提高编程水平,光为了感受大师的风采,光为了探寻一下我们每天都在接触的这门语言,也要学一下这位了不起的人物的传世杰作:C语言。 在这个系统课程中,我将带大家一起探索 C语言的奇妙世界。 2. 什么是编程?你也许听说过编程,也许你想要真正明白编程到底是怎么一回事。 ...

May 26, 2020 · 1 min · jiezi