关于c:uint32t考据
uint32_t: u=unsigned(无符号); int=integer(整数); 32=32bits(32位); t=typedef。 这是从C99起定义于<stdint.h>中的"定宽整数类型(Fixed-width integer types)"1。 相似的共有四个: uint8_t, uint16_t, uint32_t, uint64_t,是出于跨平台的须要。 Fixed width integer types (since C99) ↩
uint32_t: u=unsigned(无符号); int=integer(整数); 32=32bits(32位); t=typedef。 这是从C99起定义于<stdint.h>中的"定宽整数类型(Fixed-width integer types)"1。 相似的共有四个: uint8_t, uint16_t, uint32_t, uint64_t,是出于跨平台的须要。 Fixed width integer types (since C99) ↩
LIS回顾本题是LIS的进阶版本,上周课上老师举了LIS做例子,在解答本题前,先将LIS的代码贴在这里 class Solution {public: int lengthOfLIS(vector<int>& nums) { auto n = (int)nums.size(); vector<int> dp(n, 1); for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { if (nums[j] < nums[i]) { dp[i] = max(dp[i], dp[j] + 1); } } } return *max_element(dp.begin(), dp.end()); }};LIS最长回升子序列是一道经典的动静布局问题,解DP问题的关键在于定义正确的状态(有最优子结构)并找到正确的状态转移方程,在LIS中,状态转移方程为 $$LIS_j=max(LIS_i)+1,where\ 0\le i<j,\&\& \ nums[i] < nums[j] $$ 最长递增子序列的个数上面思考第637题,求最长递增子序列的个数要求最长递增子序列的个数,首先要晓得最长子序列的长度,在LIS的代码中,DP数组代表的就是以第i个数结尾的最长子序列的长度。那么遍历一遍DP数组,咱们就能够晓得最长子序列结尾的那个数有多少种抉择,也就是 vector<int> dp;int cnt = 0;int maxlen = *max_element(dp.begin(), dp.end());for(auto d:dp){ if(d == maxlen) cnt ++;}那么,如果咱们曾经晓得最初一位,那么倒数第二位的取值又有多少种状况呢?这启发我能够应用DFS广度优先搜寻,穷举最长LIS所有可能的状况,代码如下 ...
Summary1)C语言中的单引号用来示意字符字面量;字符字面量的实质是一个整数; 2)C语言中的双引号用来示意字符串字面量;字符串字面量的实质是一个指针(内存地址); 3)低于0x08048000这个值的内存地址都是不能间接拜访的,拜访就会段谬误; 4)留神:char类型和char*类型要用正确的字面量进行初始化;尽管char a = ""; char* s = 'a';都能编译的过,但应用的时候就会出意想不到的段谬误。 5)字符字面量和字符串字面量的混同应用在C编译器中能够编的过,但要留神warning,工程中对于所有的warning肯定都要当成error来解决。 单引号和双引号C语言中的单引号用来示意字符字面量;字符字面量的实质是一个整数,'a'在内存中占1个字节,'a'+1 示意'a'的ASCII码值加1,后果为'b'。 C语言中的双引号用来示意字符串字面量;字符串字面量的实质是一个内存地址(地址值),"a"在内存中占有2个字节,"a"+1 示意指针运算,后果指向"a"中的结束符'\0'。 代码浏览: char* p1 = 1;char* p2 = '1';char* p3 = "1";printf("%s, %s, %s", p1, p2, p3);printf('\n');printf("\n");代码解析: p1 用一个整数字面量1进行初始化,有warning,但能够编的过,因为指针实质也是一个32(64)位的整数值p2 用一个字符字面量'1'进行初始化,同p1。字符'1'实质也是一个整数,值为49p3 用一个字符串字面量"1"进行初始化,字符串字面量的实质是一个指针(内存地址),没有问题printf(param1, ...)的第一个参数是一个指针,地址值printf('\n')能够编的过,然而warning,同p2,应用10作为一个地址值传给printf函数printf("\n")能够编的过,应用一个字符串字面量作为参数,换行综上:p1 p2 printf('\n')都是能够编的过,然而有问题的语句,在进行打印的应用就会段谬误,因为应用了谬误的地址值,拜访了不该拜访的内存,野指针!!! 代码浏览: char c = " "; while(c == "\t" || c == " " || c == "\n") { scanf("%c", &c); }代码解析: c的初始化:应用一个只有空格字符的字符串字面量进行初始化;实际上是用一个指针(内存地址值)进行初始化因为char类型占1个字节,指针类型占4字节(32位),所以会产生截断再去进行while循环的判断,第一次就为false,不会进到循环中所以要正确判断字符类型和字符串类型,确保应用的类型是正确的:批改,将"\t"等都改成'\t'。本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。
本文首发于 2015-05-04 14:50:16概述一个由 C/C++ 编译的程序占用的内存分为以下几个局部: 栈区(stack):由编译器主动调配、开释,寄存函数的参数值、局部变量的值等,其操作形式相似于数据结构中的栈。个别大家常说的堆栈和栈是一样的,就是栈(stack),而说 堆 时才是堆 heap 。 堆区(heap):个别由程序员调配开释,若程序员不开释,程序完结时由OS回收。留神它与数据结构中的堆是两回事,调配形式倒是相似于链表。全局区(动态区,static):全局变量和动态变量的存储是放在一块的,初始化的全局变量和动态变量在一块区域,未初始化的全局变量和未初始化的动态变量在相邻的另一块区域。程序完结后由零碎开释。文字常量区:常量字符串就是放在这里的。程序完结后由零碎开释。程序代码区:寄存函数体的二进制代码。举例来说: //main.cpp int a = 0; // 全局初始化区 char *p1; // 全局未初始化区 main() { int b; // 栈 char s[] = "abc"; // 栈 char *p2; // 栈 char *p3 = "123456"; // 123456\0 在常量区,p3在栈上。 static int c =0; // 全局(动态)初始化区 // 调配的 10 个和 20 个字节的区域就在堆区。 p1 = (char *)malloc(10); p2 = (char *)malloc(20); strcpy(p1, "123456"); // 123456\0 放在常量区,编译器可能会将它与p3所指向的"123456"优化成同一个地位} 在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数调配失去的就是在堆上。 ...
本文首发于 2015-01-03 21:04:36概述IPv4 中应用 gethostbyname() 函数实现主机名到地址解析,这个函数仅仅反对 IPv4 ,且不容许调用者指定所需地址类型的任何信息,返回的构造只蕴含了用于存储 IPv4 地址的空间。 IPv6中引入了getaddrinfo()的新API,它是协定无关的,既可用于 IPv4 也可用于IPv6 。 getaddrinfo函数可能解决名字到地址以及服务到端口这两种转换,返回的是一个addrinfo的构造(列表)指针而不是一个地址清单。这些addrinfo构造随后可由socket函数间接应用。 getaddrinfo函数把协定相关性平安暗藏在这个库函数外部。应用程序只有解决由getaddrinfo函数填写的套接口地址构造。该函数在 POSIX标准中定义了。 函数阐明蕴含头文件: #include <sys/types.h>#include <sys/socket.h>#include <netdb.h>函数原型: int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );参数阐明: hostname: 一个主机名或者地址串(IPv4 的点分十进制串或者 IPv6 的 16 进制串)。service:服务名能够是十进制的端口号,也能够是已定义的服务名称,如 ftp、http 等。hints:能够是一个空指针,也能够是一个指向某个 addrinfo 构造体的指针,调用者在这个构造中填入对于冀望返回的信息类型的暗示。result:本函数通过 result 指针参数返回一个指向 addrinfo 构造体链表的指针。返回值: 0:胜利;非0:出错。 参数设置在getaddrinfo函数之前通常须要对以下6个参数进行以下设置:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol。 在6项参数中,对函数影响最大的是nodename,sername和hints.ai_flag,而ai_family只是有地址为v4地址或v6地址的区别。ai_protocol个别为0不作改变。 getaddrinfo在理论应用中的几种罕用参数设置: 个别状况下,client/server编程中,server端调用bind(如果面向连贯的还须要listen);client则无需调用bind函数,解析地址后间接connect(面向连贯)或间接发送数据(无连贯)。因而,比拟常见的状况有: 通常服务器端在调用getaddrinfo之前,ai_flags设置AI_PASSIVE,用于bind;主机名nodename通常会设置为NULL,返回通配地址[::]。客户端调用getaddrinfo时,ai_flags个别不设置AI_PASSIVE,然而主机名nodename和服务名servname(更违心称之为端口)则应该不为空。当然,即便不设置AI_PASSIVE,取出的地址也并非不能够被bind,很多程序中ai_flags间接设置为0,即3个标记位都不设置,这种状况下只有hostname和servname设置的没有问题就能够正确bind。上述情况只是简略的client/server中的应用,但理论在应用getaddrinfo和查阅国外开源代码的时候,曾遇到一些将servname(即端口)设为NULL的状况(当然,此时nodename必不为NULL,否则调用getaddrinfo会报错)。 应用须知1)如果本函数返回胜利,那么由result参数指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串联起来的addrinfo构造链表。 struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; struct sockaddr *ai_addr; /* 我认为这个成员是这个函数最大的便当。 */ char *ai_canonname; struct addrinfo *ai_next; };其中,sockaddr构造体为: ...
本文首发于 2014-07-21 15:35:306. 附录6.1. 字节序与网络序6.1.1. 字节序字节序,顾名思义就是字节的高下位寄存程序。 对于单字节,大部分处理器以雷同的程序解决比特位,因而单字节的寄存和传输方式个别雷同。 对于多字节数据,如整型(32位机中个别占4字节),在不同的处理器的寄存形式次要有两种(以内存中 0x0A0B0C0D 的寄存形式为例)。 6.1.1.1. 大字节序(Big-Endian,又称大端序或大尾序)在计算机中,存储介质以上面形式存储整数 0x0A0B0C0D,则称为大字节序: 数据以8bit为单位:低地址方向 -> 0x0A 0x0B 0x0C 0x0D -> 高地址方向 数据以16bit为单位:低地址方向 -> 0x0A0B 0x0C0D -> 高地址方向 其中,最高无效位(MSB,Most Significant Byte)0x0A存储在最低的内存地址处。下个字节0x0B存在前面的地址处。同时,最高的16bit单元0x0A0B存储在低位。 简而言之,大字节序就是高字节存入低地址,低字节存入高地址。 这里讲个词源典故:“endian”一词来源于乔纳森·斯威夫特的小说《格列佛游记》。小说中,小人国为水煮蛋该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开而争执,争执的单方别离被称为 Big-endians 和 Little-endians 。 1980年,Danny Cohen在其驰名的论文"On Holy Wars and a Plea for Peace"中为平息一场对于字节该以什么样的程序传送的争执而援用了该词。 借用下面的典故,设想一下要把熟鸡蛋旋转着稳立起来,大头(高字节)必定在上面(低地址)^_^ 6.1.1.2. 小字节序(Little-Endian,又称小端序或小尾序)在计算机中,存储介质以上面形式存储整数 0x0A0B0C0D 则称为小字节序: 数据以8bit为单位:高地址方向 -> 0x0A 0x0B 0x0C 0x0D -> 低地址方向 数据以16bit为单位:高地址方向 -> 0x0A0B 0x0C0D -> 低地址方向 其中,最低无效位(LSB,Least Significant Byte)0x0D存储在最低的内存地址处。前面字节顺次存在前面的地址处。同时,最低的16bit单元0x0A0B存储在低位。 可见,小字节序就高字节存入高地址,低字节存入低地址。 C语言中的位域构造也要遵循比特序(相似字节序) 。例如: ...
本文首发于 2014-07-10 10:00:41代码#include <stdio.h>#include <stdlib.h>#include <glob.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#define PATHSIZE 1024static int path_noloop(const char *path){ char *pos; pos = strrchr(path,'/');//定位最左边的'/'的地位 if(strcmp(pos+1,".") == 0 || (strcmp(pos+1,"..") == 0)) return 0; return 1;}static int64_t mydu(const char *path){ int i; glob_t globres; int64_t sum; static struct stat statres; static char nextpath[PATHSIZE]; if(lstat(path, &statres) < 0) { perror("lstat()"); return 0;//exit(1); } if(!S_ISDIR(statres.st_mode)) return statres.st_blocks; strncpy(nextpath, path,PATHSIZE); strncat(nextpath, "/*" , PATHSIZE); glob(nextpath,GLOB_NOSORT, NULL, &globres); strncpy(nextpath, path,PATHSIZE); strncat(nextpath, "/.*" , PATHSIZE); glob(nextpath,GLOB_NOSORT|GLOB_APPEND, NULL, &globres); sum = statres.st_blocks; for(i = 0 ;i < globres.gl_pathc ; i++) { if(path_noloop(globres.gl_pathv[i])) sum += mydu(globres.gl_pathv[i]); } return sum;}int main(int argc,char **argv){ if(argc < 2) { fprintf(stderr,"Usage...\n"); exit(1); } printf("%lld 512B blocks\n", (long long int)mydu(argv[1])); return 0;}编译$ gcc -g -Wall testdu.c -o testdu运行testdf执行成果:$ ./testdu /usr/bin1766184 512B blocks原生df执行成果:$ du -sh /usr/bin859M /usr/bin欢送关注我的微信公众号【数据库内核】:分享支流开源数据库和存储引擎相干技术。 ...
本文首发于 2014-07-21 15:32:281. 引言思考上面的构造体定义: typedef struct{ char c1; short s; char c2; int i;}T_FOO;假如这个构造体的成员在内存中是紧凑排列的,且c1的起始地址是0,则s的地址就是1,c2的地址是3,i的地址是4。 当初,咱们编写一个简略的程序: int main(void){ T_FOO a; printf("c1 -> %d, s -> %d, c2 -> %d, i -> %d\n", (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a, (unsigned int)(void*)&a.s - (unsigned int)(void*)&a, (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a, (unsigned int)(void*)&a.i - (unsigned int)(void*)&a); return 0;}运行后输入: c1 -> 0, s -> 2, c2 -> 4, i -> 8为什么会这样?这就是字节对齐导致的问题。 本文在参考诸多材料的根底上,具体介绍常见的字节对齐问题。因成文较早,材料起源大多已不可考,敬请体谅。 2. 什么是字节对齐古代计算机中,内存空间依照字节划分,实践上能够从任何起始地址拜访任意类型的变量,但实际上在拜访特定类型变量时常常在特定的内存地址拜访,这就须要各种类型数据依照肯定的规定在空间上排列,而不是一个接一个地程序寄存,这就是对齐。 3. 对齐的起因和作用不同硬件平台对存储空间的解决上存在很大的不同。某些平台对特定类型的数据只能从特定地址开始存取,而不容许其在内存中任意寄存。例如 Motorola 68000 处理器不容许16位的字寄存在奇地址,否则会触发异样,因而在这种架构下编程必须保障字节对齐。如果不依照平台要求对数据寄存进行对齐,会带来存取效率上的损失。比方32位的 Intel 处理器通过总线拜访(包含读和写)内存数据。每个总线周期从偶地址开始拜访32位内存数据,内存数据以字节为单位寄存。如果一个32位的数据没有寄存在4字节整除的内存地址处,那么处理器就须要2个总线周期对其进行拜访,显然拜访效率降落很多。因而,通过正当的内存对齐能够进步拜访效率。 为使CPU可能对数据进行快速访问,数据的起始地址应具备“对齐”个性。比方4字节数据的起始地址应位于4字节边界上,即起始地址可能被4整除。正当利用字节对齐还能够无效地节俭存储空间。但要留神,在32位机中应用1字节或2字节对齐,反而会升高变量访问速度,因而,须要思考处理器类型。同时,还应思考编译器的类型,在VC/C++和GNU GCC中都是默认是4字节对齐。4. 对齐的分类和准则本大节次要基于 Intel X86 架构介绍构造体对齐和栈内存对齐,位域实质上为构造体类型。 ...
本文首发于 2014-08-04 17:56:55第一章 词法“陷阱”1. =不同于==if(x = y) break;实际上是将y赋给x,再查看x是否为0。 如果真的是这样预期,那么应该改为: if((x = y) != 0) break;2. &和| 不同于 && 和 ||3. 词法剖析中的“贪婪法”编译器将程序分解成符号的办法是:从左到有一个一个字符的读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断曾经读入的两个字符组成的字符床是否可能是一个符号的组成部分;如果可能,持续读入下一个字符,反复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。例如: y = x/*p; 会被解析为:/* 正文符号4. 整型常量010(八进制数) 不同于 10(十进制)。 5. 字符与字符串首先是单引号与双引号的区别: 用单引号括起来的一个字符示意一个整数(ASCII码),而双引号括起来示意一个指针。第二章 语法“陷阱”1. 了解函数申明弄懂(*(void(*)())0)(); //首地址为0的函数。 float (*h)(): h是一个指向返回值为浮点型的函数的指针 所以,(float (*)()) 示意一个“指向返回值为浮点型的函数的指针”的类型转换符。 fp(): 是(*fp)( )的简写。 *fp(): 是 *( (*fp) ( ) )的简写。 ( *0 )( );尽管上式编译器不认,但能够把0转换为指向“返回值为void的”函数的指针,所以0可变为: ( void(*) ( ) ) 0 ,代入(*0)(),失去: (*( void(*) ( ) ) 0) ( )该式子用等价于: ...
本文首发于 2014-07-10 09:48:48代码#include <stdio.h>#include <mntent.h>#include <string.h>#include <sys/vfs.h>static const unsigned long long G = 1024*1024*1024ull;static const unsigned long long M = 1024*1024;static const unsigned long long K = 1024;static char str[20];char* kscale(unsigned long b, unsigned long bs){ unsigned long long size = b * (unsigned long long)bs; if (size > G) { sprintf(str, "%0.2f GB", size/(G*1.0)); return str; } else if (size > M) { sprintf(str, "%0.2f MB", size/(1.0*M)); return str; } else if (size > K) { sprintf(str, "%0.2f K", size/(1.0*K)); return str; } else { sprintf(str, "%0.2f B", size*1.0); return str; }}int main(int argc, char *argv[]){ FILE* mount_table; struct mntent *mount_entry; struct statfs s; unsigned long blocks_used; unsigned blocks_percent_used; const char *disp_units_hdr = NULL; mount_table = NULL; mount_table = setmntent("/etc/mtab", "r"); if (!mount_table) { fprintf(stderr, "set mount entry error\n"); return -1; } disp_units_hdr = " Size"; printf("Filesystem %-15sUsed Available %s Mounted on\n", disp_units_hdr, "Use%"); while (1) { const char *device; const char *mount_point; if (mount_table) { mount_entry = getmntent(mount_table); if (!mount_entry) { endmntent(mount_table); break; } } else continue; device = mount_entry->mnt_fsname; mount_point = mount_entry->mnt_dir; //fprintf(stderr, "mount info: device=%s mountpoint=%s\n", device, mount_point); if (statfs(mount_point, &s) != 0) { fprintf(stderr, "statfs failed!\n"); continue; } if ((s.f_blocks > 0) || !mount_table ) { blocks_used = s.f_blocks - s.f_bfree; blocks_percent_used = 0; if (blocks_used + s.f_bavail) { blocks_percent_used = (blocks_used * 100ULL + (blocks_used + s.f_bavail)/2 ) / (blocks_used + s.f_bavail); } /* GNU coreutils 6.10 skips certain mounts, try to be compatible. */ if (strcmp(device, "rootfs") == 0) continue; if (printf("\n%-20s" + 1, device) > 20) printf("\n%-20s", ""); char s1[20]; char s2[20]; char s3[20]; strcpy(s1, kscale(s.f_blocks, s.f_bsize)); strcpy(s2, kscale(s.f_blocks - s.f_bfree, s.f_bsize)); strcpy(s3, kscale(s.f_bavail, s.f_bsize)); printf(" %9s %9s %9s %3u%% %s\n", s1, s2, s3, blocks_percent_used, mount_point); } } return 0;}编译$ gcc -g -Wall testdf.c -o testdf运行testdf执行成果:$ ./testdfFilesystem Size Used Available Use% Mounted onudev 3.87 GB 0.00 B 3.87 GB 0% /devtmpfs 796.17 MB 980.00 K 795.21 MB 0% /run/dev/vda1 96.75 GB 40.54 GB 56.19 GB 42% /tmpfs 3.89 GB 0.00 B 3.89 GB 0% /dev/shmtmpfs 5.00 MB 0.00 B 5.00 MB 0% /run/locktmpfs 3.89 GB 0.00 B 3.89 GB 0% /sys/fs/cgroup/dev/vda15 104.35 MB 3.86 MB 100.50 MB 4% /boot/efi/dev/loop1 55.50 MB 55.50 MB 0.00 B 100% /snap/core18/2074/dev/loop2 70.62 MB 70.62 MB 0.00 B 100% /snap/lxd/16922/dev/loop4 70.38 MB 70.38 MB 0.00 B 100% /snap/lxd/21029/dev/loop5 32.38 MB 32.38 MB 0.00 B 100% /snap/snapd/12704tmpfs 796.17 MB 980.00 K 795.21 MB 0% /run/snapd/nstmpfs 796.17 MB 0.00 B 796.17 MB 0% /run/user/1000/dev/loop6 55.50 MB 55.50 MB 0.00 B 100% /snap/core18/2128/dev/loop0 32.38 MB 32.38 MB 0.00 B 100% /snap/snapd/12883原生df执行成果:$ df -hFilesystem Size Used Avail Use% Mounted onudev 3.9G 0 3.9G 0% /devtmpfs 797M 980K 796M 1% /run/dev/vda1 97G 41G 57G 42% /tmpfs 3.9G 0 3.9G 0% /dev/shmtmpfs 5.0M 0 5.0M 0% /run/locktmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup/dev/vda15 105M 3.9M 101M 4% /boot/efi/dev/loop1 56M 56M 0 100% /snap/core18/2074/dev/loop2 71M 71M 0 100% /snap/lxd/16922/dev/loop4 71M 71M 0 100% /snap/lxd/21029/dev/loop5 33M 33M 0 100% /snap/snapd/12704tmpfs 797M 0 797M 0% /run/user/1000/dev/loop6 56M 56M 0 100% /snap/core18/2128/dev/loop0 33M 33M 0 100% /snap/snapd/12883欢送关注我的微信公众号【数据库内核】:分享支流开源数据库和存储引擎相干技术。 ...
epoll 是Linux平台下的一种特有的多路复用IO实现形式,与传统的 select 相比,epoll 在性能上有很大的晋升。本文次要解说 epoll 的实现原理,而对于 epoll 的应用能够参考相干的书籍或文章。相干视频举荐面试中正经“八股文”网络原理tcp/udp,网络编程epoll/reactor6种epoll的设计,让你吊打面试官,而且他不能还嘴epoll原理分析以及三握四挥的解决LinuxC++后盾服务器开发架构师收费学习地址 epoll 的创立要应用 epoll 首先须要调用 epoll_create() 函数创立一个 epoll 的句柄,epoll_create() 函数定义如下:int epoll_create(int size);参数 size 是因为历史起因遗留下来的,当初不起作用。当用户调用 epoll_create() 函数时,会进入到内核空间,并且调用 sys_epoll_create() 内核函数来创立 epoll 句柄,sys_epoll_create() 函数代码如下: asmlinkage long sys_epoll_create(int size){ int error, fd = -1; struct eventpoll *ep; error = -EINVAL; if (size <= 0 || (error = ep_alloc(&ep)) < 0) { fd = error; goto error_return; } fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep); if (fd < 0) ep_free(ep);error_return: return fd;}sys_epoll_create() 次要做两件事件:调用 ep_alloc() 函数创立并初始化一个 eventpoll 对象。调用 anon_inode_getfd() 函数把 eventpoll 对象映射到一个文件句柄,并返回这个文件句柄。咱们先来看看 eventpoll 这个对象,eventpoll 对象用于治理 epoll 监听的文件列表,其定义如下: ...
古代计算机数字示意1 概述古代计算机存储和解决信息以二值信号示意。二值信号能够很容易的被示意、存储和传输,例如能够示意为导线上的高电压、低电压。对二值信号进行存储和执行计算的电子电路非常简单牢靠,制造商能够在一个独自的硅片上集成数百万甚至数十亿个这样的电路。 对于数字而言,有三种比拟重要的示意: 无符号(unsigned)编码:对大于等于零的整数进行编码补码(two's-complement)编码:对有符号的整数进行编码,蕴含正负数浮点数(floating-point)编码:对实数进行编码表示,应用以2为基准的迷信记数法形式计算机的示意是应用无限数量的位来对一个数字进行编码,因而,当数字太大以至于不能示意时,运算后果就会溢出(overflow)。 值得注意的是,整数的编码和浮点数的编码解决数字示意有限性的形式不一样: 浮点数编码能够对一个较大范畴的值域进行编码,然而这种示意只是近似的,存在精度缺失整数编码尽管只能编码一个范畴较小的值域,然而是准确的示意,无效范畴内不会存在精度缺失问题。2 信息存储信息在计算机的内存中,往往都是以二进制补码模式存在。对于如何在内存中不便的应用信息,须要两个规定: 信息的地址是什么?如何对信息的字节进行排列?对于被存储为间断字节的信息对象,对象的地址为所应用字节中最小的地址。 2.1 字节程序排列一个信息的字节有两个通用的规定: 大端法(big endian):信息的高无效字节寄存在高地址,地无效字节寄存在地址小端法(litter endian):信息的地无效字节寄存在高地址,高无效字节寄存在低地址例如,存在一个整型变量int x = n,它的十六进制示意为0x12345678。那么内存会给它调配四个字节的存储空间。假如地址值为0x100-0x103。那么就0x100就是低地址,0x103就是高地址。 对于字节的最高无效字节和最低无效字节的程序,是从左往右顺次升高的。12为最高无效字节,顺次类推,78就是最低无效字节。 对于大多数Intel兼容机,都是采纳小端模式;IBM和Oracle大多数机器则是大端模式;对于挪动端来说,无论是Andriod还是IOS都是小端模式。 2.2 字符与字符串的示意2.2.1 字符示意C或者C++中,对字符采纳某种规范编码进行示意,比拟罕用的有ASCII字符码,就是应用1个字节的bit位来对字符进行编码。 遗憾的是ASCII编码仅仅实用于英文文档,对于一些特殊字符以及中文编码不反对。所以Unicode联合会整顿订正了反对宽泛语言编码的根本编码-Unicode对立字符集,应用32位来示意字符。 2.2.2 字符串的示意在C或者C++中,字符串被编码为一个以NULL('\0',值为0)结尾的字符数组。每个字符由规范编码表示。 2.3 整数示意整数用bit位来示意也有两种不同的形式。一种是只能示意非负整数;另外一种是可能示意负整数、零以及正整数。无论哪种形式,在内存中都是以补码模式存在。 2.3.1 无符号整数编码无符号数总是大于等于零,所以无符号数的补码就是它二进制原码自身: unsigned int num = 10;// 原码 int类型占用32bit0000 0000 0000 0000 0000 0000 0000 1010// 补码 内存中寄存的是补码0000 0000 0000 0000 0000 0000 0000 1010无符号整数的所有bit位都是数字无效位。 2.3.2 有符号整数编码对于有符号的整数,将其转换为二进制之后,最高位(最右边)bit位代表符号位,不参加数据表示。其中0示意负数,1示意正数。 对于补码: 负数补码 = 负数二进制原码正数补码 = 正数二进制原码除了符号位,其余位取反 + 1//无符号整数 负数int a = 3;// 3的原码。第一位是符号位,3是负数,所以是00000 0000 0000 0000 0000 0000 0000 0011// 3的补码0000 0000 0000 0000 0000 0000 0000 0011 //无符号整数 符数int a = -3;// -3的原码 第一位符号位,3是正数 所以是11000 0000 0000 0000 0000 0000 0000 0011// 而后是反码,除了符号位,其余位取反1111 1111 1111 1111 1111 1111 1111 1100// 最初是补码:反码 + 1,这就是负3在内存中的模式1111 1111 1111 1111 1111 1111 1111 11012.4 实数示意在计算机中,实数示意办法与整数的示意办法是不同的。 ...
#include <stdio.h>#include <stdlib.h>#define INIT_LEN 6typedef struct node{ int x; struct node * next;}link;link * initLink(link * head){ link * pr = head, *p; int i, num; printf("输出数组%d个元素的值。\n", INIT_LEN); for(i = 0;i < INIT_LEN;i++){ //create node p = (link*)malloc(sizeof(link)); if(p == NULL) exit(EXIT_FAILURE); scanf("%d", &num); p->x = num; p->next = NULL; //link node pr->next = p; pr = p; } return head;}link * deleteLink(link * head, int num){ link * p = head->next, *pr = head; while(p != NULL){ //带头结点的删除 if(p->x == num) break; pr = p; p = p->next; } if(p == NULL){ printf("NOT FOUND!\n"); }else{ pr->next = p->next; p->next = NULL; free(p); } return head;}link * insertLink(link * head, int num){// 带头结点的程序插入 //create node link * pt = (link*)malloc(sizeof(link)); if(pt == NULL) exit(EXIT_FAILURE); pt->x = num; pt->next = NULL; if(head == NULL) head = pt; link * p = head; while(p->next != NULL && p->next->x < num) p = p->next; //when p->next->x >= num pt->next = p->next; p->next = pt; return head;}void printLink(link * head){ link * p = head->next; //头结点内容不输入 while(p != NULL){ printf("%d ",p->x); p = p->next; } printf("\n");}void destoryLink(link * head){ link * p = head; while(p != NULL){ head = p->next; free(p); p = head; }}int main() { int cnt, del, add; link * head = (link*)malloc(sizeof(link)); // 带头结点// head = initLink(head); printf("此链表各个结点的数据域为:"); printLink(head); printf("输出要插入的数据个数:"); scanf("%d",&cnt); printf("输出要插入的数据x: "); for(int i = 0;i < cnt;i++){ scanf("%d", &add); insertLink(head, add); } printf("以后链表各个结点的数据域为:"); printLink(head); printf("输出要删除的数据x: "); scanf("%d", &del); deleteLink(head, del); printf("删除后链表各个结点的数据域为:"); printLink(head); destoryLink(head); return 0; }
后面的调度学习都是默认在单个 CPU 上的调度策略。咱们晓得为了 CPU 之间缩小“烦扰”,每个 CPU 上都有一个工作队列。运行的过程种可能会呈现有的 CPU 很忙,有的 CPU 很闲,如下图所示:为了防止这个问题的呈现,Linux 内核实现了 CPU 可运行过程队列之间的负载平衡。因为负载平衡是在多个核上的平衡,所以在解说负载平衡之前,咱们先看下多核的架构。将 task 从负载较重的 CPU 上转移到负载绝对较轻的 CPU 上执行,这个过程就是负载平衡的过程。多核架构这里以 Arm64 的 NUMA(Non Uniform Memory Access) 架构为例,看下多核架构的组成。从图中能够看出,这是非一致性内存拜访。每个 CPU 拜访 local memory,速度更快,提早更小。因为 Interconnect 模块的存在,整体的内存会形成一个内存池,所以 CPU 也能拜访 remote memory,然而绝对 local memory 来说速度更慢,提早更大。咱们晓得一个多外围的 SOC 片上零碎,内部结构是很简单的。内核采纳 CPU 拓扑构造来形容一个 SOC 的架构,应用调度域和调度组来形容 CPU 之间的档次关系。LinuxC++后盾服务器开发架构师收费学习地址【文章福利】:小编整顿了一些集体感觉比拟好的学习书籍、视频材料共享在群文件外面,有须要的能够自行添加哦!~点击退出(须要自取) CPU 拓扑每一个 CPU 都会保护这么一个构造体实例,用来形容 CPU 拓扑。 struct cpu_topology { int thread_id; int core_id; int cluster_id; cpumask_t thread_sibling; cpumask_t core_sibling;};thread_id: 从 mpidr_el1 寄存器中获取core_id:从 mpidr_el1 寄存器中获取cluster_id:从mpidr_el1寄存器中获取thread_sibling:以后 CPU 的兄弟 thread。core_sibling:以后 CPU 的兄弟Core,即在同一个 Cluster 中的 CPU。能够通过 /sys/devices/system/cpu/cpuX/topology 查看 cpu topology 的信息。cpu_topology 构造体是通过函数 parse_dt_topology() 解析 DTS 中的信息建设的:kernel_init() -> kernel_init_freeable() -> smp_prepare_cpus() -> init_cpu_topology() -> parse_dt_topology()static int __init parse_dt_topology(void){ struct device_node *cn, *map; int ret = 0; int cpu; cn = of_find_node_by_path("/cpus"); ------(1) if (!cn) { pr_err("No CPU information found in DT\n"); return 0; } /* * When topology is provided cpu-map is essentially a root * cluster with restricted subnodes. */ map = of_get_child_by_name(cn, "cpu-map"); ------(2) if (!map) goto out; ret = parse_cluster(map, 0); ------(3) if (ret != 0) goto out_map; topology_normalize_cpu_scale(); /* * Check that all cores are in the topology; the SMP code will * only mark cores described in the DT as possible. */ for_each_possible_cpu(cpu) if (cpu_topology[cpu].cluster_id == -1) ret = -EINVAL;out_map: of_node_put(map);out: of_node_put(cn); return ret;}找到 dts 中 cpu topology 的根节点 "/cpus"找到 "cpu-map" 节点解析 "cpu-map" 中的 cluster以 i.mx8qm 为例,topology 为:”4A53 + 2A72”,dts中定义如下: ...
语句语句根底常见类别表达式语句:表达式后加分号 表达式求值,后抛弃 可能产生副作用 2 + 3; //表达式; int x; x = 3; //表达式求值,返回值x后抛弃 //副作用:x值的扭转空语句:仅蕴含一个分号的语句 罕用在循环体 复合语句(语句体):{ ......} 结尾无分号 { int x = 3; x+=1; std::cout << x << "\n";} //一条复合语句 //造成独立的域(语句域) //域中可定义长期变量,准确管制生命周期 { ...};//两条语句 ;为空语句int x = 2;{ int x = 3; x++;}程序语句与非程序语句程序语句 语义上依照先后顺序执行理论的执行程序可能产生变动,编译器优化/硬件乱序执行等与硬件流水线紧密结合,执行效率较高非程序语句 在执行过程中引入跳转,产生简单变动**分支预测**晋升性能,预测谬误可能导致执行性能升高会限度编译器优化goto 通过标签指定跳转地位限度: 不能跨函数跳转 向前(下)跳转时,不能越过初始化语句向后跳转可能导致对象的销毁与从新初始化goto实质对应汇编中的跳转指令 不足结构性的含意 容易造成逻辑凌乱 防止应用int main(){ int x = 1; if(x) goto label; x++;label: return 0'}分支语句非程序语句分为分支语句与循环语句 if 与 switch ...
一、背景最近为了考研,在学习C语言与数据结构,最开始应用Visual Studio 2019作为编辑器,然而总感觉不习惯; 之前始终应用jetbrains公司的编辑器,正好发现C语言能够用CLion,然而发现不会应用他的调试性能,有些时候为了调试代码,还须要将代码复制到 Visual Studio 2019编辑器中;起初感觉太麻烦了,摸索了一段时间终于找到了CLion的调试办法,将办法记录下来给须要的同学吧。 二、开启调试关上CLion,新建我的项目;接在以此在菜单中选择如下菜单file->settings->Build、Execution、Deployment->Debugger->Data Views->C/C++ 找到Enable NatVis renderes for LLDB Diagnostics 抉择 Verbose,如下图所示 抉择之后,点击下方的OK按钮进行确认。 三、编译代码当初须要在代码中轻易编辑一些代码,代码中须要有变量赋值操作,用于察看debug模式,参考代码如下所示 #include <stdio.h>int main() { int i = 0; while (i < 5) { i++; } return 0;}编写完代码之后,将须要察看的变量标注一下,标注办法是用鼠标点击行号右侧旁边,会呈现小红点,如下图所示。 接下来就能够应用debug模式察看变量的数据了,在编辑器的右侧上方有一个绿色虫子的图标,点击此图标就进入了debug模式。 四、 调试代码在debug模式下,能够看到变量以后在内存中的值,如下图所示 当须要让程序继续执行时,须要点击两头的红色框,这样程序会往下一步执行,同时能够看到变量的值也在发生变化,在最下方能够看到整个程序的变量列表,以及对应值是多少。 作者:汤青松日期:2021-09-14微信:songboy8888
代码 #include <execinfo .h>#include <stdio .h>#include <stdlib .h> void fun1();void fun2();void fun3(); void print_stacktrace(); int main(){ fun3();} void fun1(){ printf("stackstrace begin:\n"); print_stacktrace();} void fun2(){ fun1();} void fun3(){ fun2();} void print_stacktrace(){ int size = 16; void * array[16]; int stack_num = backtrace(array, size); char ** stacktrace = backtrace_symbols(array, stack_num); for (int i = 0; i < stack_num; ++i) { printf("%s\n", stacktrace[i]); } free(stacktrace);}编译 gcc test.cc -rdynamic -g -o test3
相干视频举荐面试中正经“八股文”网络原理tcp/udp,网络编程epoll/reactorepoll 原理分析 以及 reactor 模型利用epoll原理分析以及三握四挥的解决LinuxC++后盾服务器开发架构师收费学习地址彻底学会应用epoll(一)——ET模式实现剖析接上一篇首先,看程序四的例子。l 程序四 #include <unistd.h>#include <iostream>#include <sys/epoll.h>using namespace std;int main(void){ int epfd,nfds; struct epoll_event ev,events[5];//ev用于注册事件,数组用于返回要解决的事件 epfd=epoll_create(1);//只须要监听一个描述符——规范输入 ev.data.fd=STDOUT_FILENO; ev.events=EPOLLOUT|EPOLLET;//监听读状态同时设置ET模式 epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev);//注册epoll事件 for(;;) { nfds=epoll_wait(epfd,events,5,-1); for(int i=0;i<nfds;i++) { if(events[i].data.fd==STDOUT_FILENO) cout<<"hello world!"<<endl; } }};这个程序的性能是只有规范输入写就绪,就输入“hello world!”。运行后果: 咱们发现这将是一个死循环。上面具体分析一下这个程序的执行过程:(1) 首先初始buffer为空,buffer中有空间可写,这时无论是ET还是LT都会将对应的epitem退出rdlist(对应第一节图中的红线),导致epoll_wait就返回写就绪。(2) 程序想规范输入输入”hello world!”和换行符,因为规范输入为控制台的时候缓冲是“行缓冲”,所以换行符导致buffer中的内容清空,这就对应第二节中ET模式下写就绪的第二种状况——当有旧数据被发送走时,即buffer中待写的内容变少得时候会触发fd状态的扭转。所以下次epoll_wait会返回写就绪。之后反复这个过程始终循环上来。咱们再看程序五。程序五绝对程序四这里仅仅去掉了输入的换行操作。即: cout<<"hello world!";运行后果如下: 咱们看到程序成挂起状态。因为第一次epoll_wait返回写就绪后,程序向规范输入的buffer中写入“hello world!”,然而因为没有输入换行,所以buffer中的内容始终存在,下次epoll_wait的时候,尽管有写空间然而ET模式下不再返回写就绪。回顾第一节对于ET的实现,这种状况起因就是第一次buffer为空,导致epitem退出rdlist,返回一次就绪后移除此epitem,之后尽管buffer依然可写,然而因为对应epitem曾经不再rdlist中,就不会对其就绪fd的events的在检测了。程序六int main(void){ int epfd,nfds;struct epoll_event ev,events[5];//ev用于注册事件,数组用于返回要解决的事件epfd=epoll_create(1);//只须要监听一个描述符——规范输入ev.data.fd=STDOUT_FILENO;ev.events=EPOLLOUT;//应用默认LT模式epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev);//注册epoll事件for(;;){ nfds=epoll_wait(epfd,events,5,-1); for(int i=0;i<nfds;i++){ if(events[i].data.fd==STDOUT_FILENO) cout<<"hello world!";}}}; 程序六绝对程序五仅仅是批改ET模式为默认的LT模式,咱们发现程序再次死循环。这时候起因曾经很分明了,因为当向buffer写入”hello world!”后,尽管buffer没有输入清空,然而LT模式下只有buffer有写空间就返回写就绪,所以会始终输入”hello world!”,当buffer满的时候,buffer会主动刷清输入,同样会造成epoll_wait返回写就绪。程序七int main(void) { int epfd,nfds;struct epoll_event ev,events[5];//ev用于注册事件,数组用于返回要解决的事件epfd=epoll_create(1);//只须要监听一个描述符——规范输入ev.data.fd=STDOUT_FILENO;ev.events=EPOLLOUT|EPOLLET;//监听读状态同时设置ET模式epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev);//注册epoll事件for(;;){ nfds=epoll_wait(epfd,events,5,-1); for(int i=0;i<nfds;i++){ if(events[i].data.fd==STDOUT_FILENO) cout<<"hello world!"; ev.data.fd=STDOUT_FILENO; ev.events=EPOLLOUT|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_MOD,STDOUT_FILENO,&ev); //从新MOD事件(ADD有效)} ...
本文首发于 码友网--《创立API服务最小只有4行代码!!!尝新体验ASP.NET Core 6预览版本中公布的最小Web API(minimal APIS)新个性》 概述.NET开发者们大家好,我是Rector。 几天前(美国工夫2021年8月10日),微软官网公布了.NET 6的第7个预览版,其中蕴含了很多新的个性和性能,比方: 优化最小Web API(minimal APIS)模板为生成罕用HTTP响应增加了IResult的接口实现最小操作反对Request,Response等优化了最小主机、模板从查问字符串中获取Blazor组件参数...本文咱们次要来体验最小Web API的性能和个性。最小Web API的目标次要是帮忙C#(或者F#)后端开发者疾速创立微服务项目或者HTTP API服务。 与之前的ASP.NET Core Web API相比,最小Web API在启动模板和框架结构上都有些不同。最小Web API的模板相当简洁,你几须要写4行代码便可实现一个最小Web API我的项目的搭建。 上面咱们从头开始创立一个最小Web API我的项目并体验。 筹备工作在开始创立最小Web API之前,请确保开发环境中已装置了.NET SDK 6.0.100-preview.7.21379.14(以后最新的.NET SDK版本),在Windows操作系统中,你能够应用.NET Cli命令来查看,比方: dotnet --info运行后果如下图: 如果须要查看以后环境已装置的.NET模板,运行如下命令: dotnet new -l运行后果如下: Template Name Short Name Language Tags-------------------------------------------- ------------------- ---------- --------------------------ASP.NET Core Empty web [C#],F# Web/EmptyASP.NET Core gRPC Service grpc [C#] Web/gRPCASP.NET Core Web API webapi [C#],F# Web/WebAPIASP.NET Core Web App razor,webapp [C#] Web/MVC/Razor PagesASP.NET Core Web App (Model-View-Controller) mvc [C#],F# Web/MVCASP.NET Core with Angular angular [C#] Web/MVC/SPAASP.NET Core with React.js react [C#] Web/MVC/SPAASP.NET Core with React.js and Redux reactredux [C#] Web/MVC/SPABlazor Server App blazorserver [C#] Web/BlazorBlazor WebAssembly App blazorwasm [C#] Web/Blazor/WebAssembly/PWAClass Library classlib [C#],F#,VB Common/LibraryConsole Application console [C#],F#,VB Common/Consoledotnet gitignore file gitignore ConfigDotnet local tool manifest file tool-manifest Configglobal.json file globaljson ConfigMSTest Test Project mstest [C#],F#,VB Test/MSTestMVC ViewImports viewimports [C#] Web/ASP.NETMVC ViewStart viewstart [C#] Web/ASP.NETNuGet Config nugetconfig ConfigNUnit 3 Test Item nunit-test [C#],F#,VB Test/NUnitNUnit 3 Test Project nunit [C#],F#,VB Test/NUnitProtocol Buffer File proto Web/gRPCRazor Class Library razorclasslib [C#] Web/Razor/LibraryRazor Component razorcomponent [C#] Web/ASP.NETRazor Page page [C#] Web/ASP.NETSolution File sln SolutionWeb Config webconfig ConfigWindows Forms App winforms [C#],VB Common/WinFormsWindows Forms Class Library winformslib [C#],VB Common/WinFormsWindows Forms Control Library winformscontrollib [C#],VB Common/WinFormsWorker Service worker [C#],F# Common/Worker/WebWPF Application wpf [C#],VB Common/WPFWPF Class library wpflib [C#],VB Common/WPFWPF Custom Control Library wpfcustomcontrollib [C#],VB Common/WPFWPF User Control Library wpfusercontrollib [C#],VB Common/WPFxUnit Test Project xunit [C#],F#,VB Test/xUnit创立最小API程序我的项目在.NET Core中创立程序的形式有多种,能够应用命令行工具执行dotnet new <模板名称> <项目名称>创立,也能够应用IDE(如:Visual Studio, Rider, VS Code)来创立。 ...
Visitor Pattern访问者模式是一种行为模式,容许任意的拆散的访问者可能在管理者管制下拜访所治理的元素。访问者不能扭转对象的定义(但这并不是强制性的,你能够约定为容许扭转)。对管理者而言,它不关怀到底有多少访问者,它只关怀一个确定的元素拜访程序(例如对于二叉树来说,你能够提供中序、前序等多种拜访程序)。 组成Visitor 模式蕴含两个次要的对象:Visitable 对象和 Vistor 对象。此外,作为将被操作的对象,在 Visitor 模式中也蕴含 Visited 对象。 一个 Visitable 对象,即管理者,可能蕴含一系列形态各异的元素(Visited),它们可能在 Visitable 中具备简单的构造关系(但也能够是某种单纯的包容关系,如一个简略的 vector)。Visitable 个别会是一个简单的容器,负责解释这些关系,并以一种规范的逻辑遍历这些元素。当 Visitable 对这些元素进行遍历时,它会将每个元素提供给 Visitor 令其可能拜访该 Visited 元素。 这样一种编程模式就是 Visitor Pattern。 接口为了可能察看每个元素,因而实际上必然会有一个束缚:所有的可被察看的元素具备独特的基类 Visited。 所有的 Visitors 必须派生于 Visitor 能力提供给 Visitable.accept(visitor&) 接口。 namespace hicc::util { struct base_visitor { virtual ~base_visitor() {} }; struct base_visitable { virtual ~base_visitable() {} }; template<typename Visited, typename ReturnType = void> class visitor : public base_visitor { public: using return_t = ReturnType; using visited_t = std::unique_ptr<Visited>; virtual return_t visit(visited_t const &visited) = 0; }; template<typename Visited, typename ReturnType = void> class visitable : public base_visitable { public: virtual ~visitable() {} using return_t = ReturnType; using visitor_t = visitor<Visited, return_t>; virtual return_t accept(visitor_t &guest) = 0; };} // namespace hicc::util场景以一个实例来说,假如咱们正在设计一套矢量图编辑器,在画布(Canvas)中,能够有很多图层(Layer),每一图层蕴含肯定的属性(例如填充色,透明度),并且能够有多种图元(Element)。图元能够是 Point,Line,Rect,Arc 等等。 ...
图标阐明namespace 名称空间interface 接口delegate 委托event 事件enum 枚举internal enum 只供同一程序集外部拜访的枚举public class 类internal class 只供同一程序集外部拜访的类只含get的属性包含get 、set的属性public 办法(函数)public static 办法(函数)private 办法(函数)private static 公有静态方法(函数)公有字段const 常量字段private static 字段internal 字段protected 字段protected override void Finalize(); internalstaticprivate protectedfield 字段(一般字段、const、readonly)
原PRQA动态测试软件产品线(包含QA-C、 QA-C++、QA-Verify等),对立更名为“Helix QAC”,PRQA的代码动态剖析工具可能帮忙企业开发团队进步代码的品质和平安,缩短软件开发所需的工夫,HelixQAC是作为其主打产品目前已广泛应用于汽车、航空航天、电子商务、医疗器械、生产和通信等畛域。 HelixQAC HelixQAC是动态代码剖析工具,根据C和C++编码规定主动扫描代码对规定的违反。开发团队在开发过程的晚期就能够用它来检测缺点,因为此时批改代码是最不便也最经济的。Helix QAC因而自动化强制施行代码编程规范,比方MISRA,保障代码的合规性。 性能个性 ◆ 遵循代码规范 遵循编码和工业规范。Helix QAC主动审查代码,确保它们合乎用户抉择的编码标准。合规性报告可视化地揭示用户哪些代码须要多加注意。Helix QAC反对多种C和C++编码标准,提供相应的合规性模块,也反对规范的客户化定制。 ◆ 查看更多缺点 在开发晚期查看编译器没有发现的要害缺点。Helix QAC为用户的软件建设了准确的行为模型,跟踪代码中的变量值,如同运行时一样。因而这种剖析最大化地笼罩了代码,使误报和漏报最低。它甚至能辨认极其简单的代码引起的问题。 ◆ 进步代码品质 提供任何应用程序的整体品质和平安。Helix QAC辨认必须批改的缺点,提供具体的领导帮忙开发人员批改问题。这是不须要运行程序的。开发人员既然取得了即时的上下文反馈,他们将因而从谬误中取得学习,下一次编写新的代码(或者评审代码)时,能力将失去晋升。 ◆ 协同代码审查 Helix QAC的仪表盘提供了协同代码审查的能力,用户可能在Helix QAC查看出的诊断上增加注解,为其余用户调配须要他们采取的动作。 ◆ 适应数百万行代码 让动态剖析适应你的环境。Helix QAC有能力解决数百万行代码,保障你的产品无论代码由如许简单它都是平安的。 ◆ 重用代码 重用品质信得过的代码。Helix QAC检测代码移植性问题,所以你能重用让你释怀的代码,帮忙你的疾速开发。 ◆ 减速开发过程 升高瓶颈减速开发。Helix QAC能集成在构建零碎和继续集成环境中,尽早且频繁地发现缺点,从而防止了在开发前期往往须要破费甚巨的谬误。它也减速了以后代码的评审,你甚至能够只让它查看新的代码变动,疾速提供反馈。 ◆ 监督整体代码品质 应用Helix QAC的仪表盘监督代码品质。你可能用它监督代码品质度量,取得品质趋势。仪表盘还能帮你为利益相干方创立属于他们的报告。 编程规范合规性 ● MISRA ① MISRA编码标准查看平安要害零碎的潜在问题。MSIRA C和MISRA C++合规性模块指出违反这些规定的代码段。 ② MISRA C模块强制施行MISRA C:1998、MISRA C:2004和MISRA C:2012。 ③ MISRA C++模块强制施行MISRA C++:2008。 ④ 在MISRA规定查看方面,Helix QAC的准确性远高于其余工具。它对规定的违反划分出重大度的优先级,你能够据此批改最重要的问题。 MISRA C MISRA C++ ...
学习c#的过程中,常常遇到这样的语句: public string StudentName { get{return stuName;} set{stuName=value;}} 《Visual c#.net程序设计教程》笔记:在许多面向对象程序设计语言中,属性 { Student stu = new Student(); Console.Write(stu.StudentName); Console.ReadKey(); } } } 下面代码中定义了一个属性StudentName,它蕴含get拜访器和set拜访器。属性StudentName封装了类Student中的字段stuName,字段如果没有加拜访控制符,被默认为private,外界不能间接拜访它,当初外界能够通过StudentNamee属性自在地存取 stuName字段了。 属性的get和set都是可执行的程序语句组合,具备行为的特点;而应用具备get拜访器和set拜访器的属性时候就像应用字段一样,即能够作为左值承受数据,又能够作为右值输入数据,零碎正是依照属性呈现在语句中的地位,主动地抉择是调用get还是调用set。 属性的读写管制 属性中的get和set能够只用一个,如果只有get而没有set,那么这个属性只可读出,不可写入;如果只有set而没有get,那么这个属性是只写入,不可读出。在属性中实现更多功能 既然get和set是程序,那当然能够做更多的事件。一个正当的分工是:设计字段是为了便于外部办法应用,而尽量与外界断绝;设计属性思考的是不便外界的应用,然而不让外界晓得的数据一律不给。具体阐明: set 拜访器与返回 void 的办法相似。它应用称为 value 的隐式参数,此参数的类型是属性的类型。在下例中,set 拜访器被增加到Name 属性: public string Name { get { return name; } set { name = value; }}当对属性赋值时,用提供新值的参数调用 set 拜访器。例如: e1.Name = "Joe"; // The set accessor is invoked here在 set 拜访器中对局部变量申明应用隐式参数名 (value) 是谬误的。 ...
最近得用unity做虚构仿真相干的货色,当然,它的出名还是在游戏界,像王者光荣就是应用 unity 开发的。 应用unity就要学习C#语言,我始终认为C#是C++的降级版本,因为C++是C的改进版,然而C#与前两者的关系就相当于Java和JavaScript,没半毛钱关系。 C语言C语言是一门面向过程的计算机编程语言,与C++、C#、Java等面向对象编程语言有所不同。 C语言的设计指标是提供一种能以繁难的形式编译、解决低级存储器、仅产生大量的机器码以及不须要任何运行环境反对便能运行的编程语言。 并且它是一门非常优良而又重要的语言,Window,Liunx,UXIX零碎都是应用C语言编写的,可见他的重要水平。 C++C++ 是一种动态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,反对过程化编程、面向对象编程和泛型编程。 它被认为是一种中级语言,综合了高级语言和低级语言的特点。 OOP(面向对象)语言特点: 封装性:一个手机是由很多部件形成的,有处理器,存储器,输入输出设置,屏幕,电池等,把这些货色组装在一起就是手机了,这就是封装性。继承性:老子生了儿子,儿子继承了老子的个性,就有了继承性。多态性:我在家是父母的儿子,在公司是老板的员工,在深圳就是深圳人,在面向对象的语言中,调用雷同的办法会有不同的行为,这就是多态。C Sharp又叫C#,是一个古代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由Ecma 和ISO 核准认可的。 至于它的命名,咱们来看看起源: 微软为了解救Visual Basic未实现的事业剽窃jxx语言并起名为 j++被吃官司后,学聪慧了,谎称本人是C语言的改良命名为C#对,剽窃的就是Java语言,当初的C# 和 Java有着惊人的类似。 三者的关系经典案例:把大象放到冰箱里 C:C语言是一个极其高冷的人,因而答复都是冷冷的: 我:你好C语言,我想把大象放到冰箱里,帮我做好不好? C:好 我:那咱们要怎么做呢? C:猜 我:额。。。是不是应该先发明一只大象? C:是 我:怎么发明呢? C:猜 我只好去翻了一下文档,哦,malloc一块内存啊。 我:好的,我用malloc,申请一块空间放大象好不好? C:好 我:额。。。大象怎么结构呢? C:猜 我。。。去看了看大象的构造 我:好吧。。。我定义了一个大象的数据结构,接下来怎么做? C:猜 我心里一阵说不出的感觉。。。 我:哦好吧,我发明一个冰箱,步骤应该和大象差不多。 C:嗯 我翻看了冰箱的构造,而后定义了一个冰箱的struct。 我:好了,冰箱结构进去了,怎么放呢? C:哼 我。。。默默在Stack Overflow上输出如何把大象放进冰箱 C。 我:终于找到答案了,定义一个办法,把大象的鼻子放进去、眼睛放进去、耳朵放进去。OK,都放进去了。C,你输入一下看看吧。 C:烫烫烫烫烫烫烫 我:哈哈哈哈C你终于不高冷了 。。。 我:哎,你咋不谈话了?C?你发烧了吗? 立即一盆水倒上去。 完结。 C++:C++是一个常识渊博的孩子,绝对年老,也没那么高冷。 我:C艹,咱们把大象放冰箱好吗? C++:滚 谈话的不高冷呢? 我:额我错了,敬爱的C++,咱们把大象放冰箱好吧。 C++:好的么么哒,大象的数据类型曾经有人定义好了,冰箱也有人定义好了,你须要别离结构一个哦。 我:好的。 于是我翻看了文档后晓得了用new来结构 我:OK,敬爱的C++,我想结构一个大象。 C++:好的,结构大象的办法有13个,请问你抉择哪一种呢? 我。。。 我:你介绍一下吧。 C++:OK,首先能够在栈下面结构,这么做的益处呢是能够不便资源管理,而且语法也难看一些,能够利用RAII,如果你不晓得什么事RAII,你能够去cppreference下面查一下,当然你也能够在堆上结构,能够结构一只很大很大的大象,还有。。。。。。。 ...
Summary1)编译器会将反斜杠'\'剔除,跟在反斜杠前面的字符主动接续到前一行 2)在接续单词时,反斜杠'\'之后不能有空格,反斜杠'\'的下一行之前也不能有空格 3)接续符'\'适宜用于定义宏代码块,进步可读性 4)C语言中的本义符'\'次要用于示意无回显字符(不会在屏幕上显示的),也能够用于示意惯例字符。 5)当反斜杠'\'作为本义符应用时,必须呈现在单引号或者双引号之间 \n回车换行\t横向跳到下一制表地位\反斜杠符'\'\'单引号符\ddd1~3位八进制数所代表的字符\xdd1~2位十六进制数所代表的字符\a响铃\v竖向跳格\b退格\r回车\f走纸换页接续符和本义符1、接续符'\'上面代码正确么? #incl\ud\e <s\tdio\.h>in\t main(\){ printf(\ "Hello World.\n" )\ ; ret\urn 0;}编译器会将反斜杠'\'剔除,跟在反斜杠前面的字符主动接续到前一行 // 将反斜杠'\'剔除,前面的字符主动接续到前一行的代码如下#include <stdio.h>int main(){ printf( "Hello World.\n" ) ; return 0;}在接续单词时,反斜杠'\'之后不能有空格,反斜杠的下一行之前也不能有空格 // 如果格局像上面这样,urn后面有4个空格,这时候编译就不过了,因为失去的语句是// ret urn 0; ret\ urn 0;接续符'\n'适宜在定义宏代码块时应用 // 用宏代码块实现的替换变量语句#define SWAP(a, b) \{ \ int temp = a; \ a = b; \ b = temp; \}2、本义符'\'C语言中的本义符'\'次要用于示意无回显字符(不会在屏幕上显示的),也能够用于示意惯例字符。\n回车换行\t横向跳到下一制表地位\反斜杠符'\'\'单引号符\ddd1~3位八进制数所代表的字符\xdd1~2位十六进制数所代表的字符\a响铃\v竖向跳格\b退格\r回车f走纸换页当反斜杠'\'作为本义符应用时,必须呈现在单引号或者双引号之间 char* p = "\141 \t \'\a \x62 "; // \141: 八进制141对应十进制97,Ascii中示意字符a // \t: 制表符 // \': 单引号' // \a: 响铃一次 // \x62: 十六进制62对应于十进制98,Ascii中示意字符bprintf("%s\n", p); 本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。 ...
相干视频举荐面试中正经“八股文”网络原理tcp/udp,网络编程epoll/reactorepoll 原理分析 以及 reactor 模型利用epoll原理分析以及三握四挥的解决LinuxC++后盾服务器开发架构师收费学习地址 通过上一剖析,poll运行效率的两个瓶颈曾经找出,当初的问题是怎么改良。首先,如果要监听1000个fd,每次poll都要把1000个fd 拷入内核,太不迷信了,内核干嘛不本人保留曾经拷入的fd呢?答对了,epoll就是本人保留拷入的fd,它的API就曾经阐明了这一点——不是 epoll_wait的时候才传入fd,而是通过epoll_ctl把所有fd传入内核再一起"wait",这就省掉了不必要的反复拷贝。其次,在 epoll_wait时,也不是把current轮流的退出fd对应的设施期待队列,而是在设施期待队列醒来时调用一个回调函数(当然,这就须要“唤醒回调”机制),把产生事件的fd纳入一个链表,而后返回这个链表上的fd。另外,epoll机制实现了本人特有的文件系统eventpoll filesystem 1. 内核数据结构(1) struct eventpoll { spinlock_t lock; struct mutex mtx; wait_queue_head_t wq; /* Wait queue used by sys_epoll_wait() ,调用epoll_wait()时, 咱们就是"睡"在了这个期待队列上*/ wait_queue_head_t poll_wait; /* Wait queue used by file->poll() , 这个用于epollfd本事被poll的时候*/ struct list_head rdllist; /* List of ready file descriptors, 所有曾经ready的epitem都在这个链表外面*/ structrb_root rbr; /* RB tree root used to store monitored fd structs, 所有要监听的epitem都在这里*/ epitem *ovflist; /*寄存的epitem都是咱们在传递数据给用户空间时监听到了事件*/. struct user_struct *user; /*这里保留了一些用户变量,比方fd监听数量的最大值等*/ }; 通过epoll_ctl接口退出该epoll描述符监听的套接字则属于socket filesystem,这点肯定要留神。每个增加的待监听(这里监听和listen调用不同)都对应于一个epitem构造体,该构造体已红黑树的构造组织,eventpoll构造中保留了树的根节点(rbr成员)。同时有监听事件到来的套接字的该构造以双向链表组织起来,链表头保留在eventpoll中(rdllist成员)。 ...
Summary1)编译器在编译过程中应用空格替换整个正文 2)反斜杠'\'是接续符,编译器会将反斜杠剔除,跟在反斜杠前面的字符主动接续到前一行 3)字符串里的//和/**/都会被看做斜杠字符,不会作为正文 4)/*..*/正文不能嵌套应用 5)留神 正文用来形容程序的起因和用意,而不是逐渐形容语句正文要防止二义性,防止缩写正文要精简,防止臃肿正文1. 以下正文代码是否正确? 编译器在编译过程中应用空格代替整个正文字符串字面量里的//和/*..*/不代表正文符号/*..*/正文不能嵌套依据以上规定: Line10正确;Line13的输入为“abcd // efgh”;Line15正确,‘\’是一个接续符,把下一行的内容接到上一行去;Line18谬误,正文替换成立空格,变成了in t i;Line20谬误,/*..*/里不能嵌套2. 正文用来论述起因和用意,而不是形容程序的运行过程反例 int r = n/2; // r是n的一半r++; // 变量r自增13. 正文必须无二义性,起到对代码的提醒作用,防止缩写反例 sad = 0x723; // R.I.P.L.V.B这一行代码的正文,让人看不明确sad是什么,这个值又是什么意思。 4. 正文是对代码的提醒,防止过于臃肿反例 /* * b.s. 09/11/2021 * 这段代码这么写很不优雅 * 代码简单,且工夫、空间复杂度都较高 * 但我当前会去批改这段代码的 * 当初这么做因为交付压力较大 * 肯定 */ if(xx) { } else { if(xx) {} else {} }本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。
相干视频举荐面试中正经“八股文”网络原理tcp/udp,网络编程epoll/reactorepoll 原理分析 以及 reactor 模型利用epoll原理分析以及三握四挥的解决LinuxC++后盾服务器开发架构师收费学习地址1.期待队列在Linux内核中期待队列有很多用处,可用于中断解决、进程同步及定时。咱们在这里只说,过程常常必须期待某些事件的产生。期待队列实现了在事件上的条件期待: 心愿期待特定事件的过程把本人放进适合的期待队列,并放弃管制全。因而,期待队列示意一组睡眠的过程,当某一条件为真时,由内核唤醒它们。期待队列由循环链表实现,由期待队列头(wait_queue_head_t)和期待队列项(wait_queue)组成,其元素(期待队列项)蕴含指向过程描述符的指针。每个期待队列都有一个期待队列头(wait queue head),期待队列头是一个类型为wait_queue_head_t的数据结构定义期待队列头(相干内容能够在linux/include/wait.h中找到)期待队列头构造体的定义: struct wait_queue_head { spinlock_t lock; //自旋锁变量,用于在对期待队列头 struct list_head task_list; // 指向期待队列的list_head}; typedef struct __wait_queue_head wait_queue_head_t;应用期待队列时首先须要定义一个wait_queue_head,这能够通过DECLARE_WAIT_QUEUE_HEAD宏来实现,这是动态定义的办法。该宏会定义一个wait_queue_head,并且初始化构造中的锁以及期待队列。 Linux中期待队列的实现思维如下图所示,当一个工作须要在某个wait_queue_head上睡眠时,将本人的过程管制块信息封装到wait_queue中,而后挂载到wait_queue的链表中,执行调度睡眠。当某些事件产生后,另一个工作(过程)会唤醒wait_queue_head上的某个或者所有工作,唤醒工作也就是将期待队列中的工作设置为可调度的状态,并且从队列中删除。 (2)期待队列中寄存的是在执行设施操作时不能取得资源而挂起的过程定义期待对列: struct wait_queue { unsigned int flags; //prepare_to_wait()里有对flags的操作,查看以得出其含意 #define WQ_FLAG_EXCLUSIVE 0x01 //一个常数,在prepare_to_wait()用于批改flags的值 void * private //通常指向当前任务管制块 wait_queue_func_t func; //唤醒阻塞工作的函数 ,决定了唤醒的形式 struct list_head task_list; // 阻塞工作链表};typedef struct __wait_queue wait_queue_t;【文章福利】:小编整顿了一些集体感觉比拟好的学习书籍、视频材料共享在群文件外面,有须要的能够自行添加哦!(须要自取) 1.select/poll毛病 select/poll的毛病在于: 1.每次调用时要反复地从用户态读入参数。 2.每次调用时要反复地扫描文件描述符。 3.每次在调用开始时,要把以后过程放入各个文件描述符的期待队列。在调用完结后,又把过程从各个期待队列中删除。内核实现2.1 次要数据结构:(1) struct poll_table_entry { struct file filp; wait_queue_t wait;//外部有一个指针指向一个过程 wait_queue_head_t wait_address;//期待队列头部(期待队列有多个wait_queue_t组成,通过双链表连贯)};(2) struct poll_table_page { ...
摘要:C语言中比拟重要的就是指针,它能够用来链表操作,谈到链表,很多时候为此分配内存采纳动态分配而不是动态调配。本文分享自华为云社区《【云驻共创】C语言中动态内存调配的实质》,作者: G-washington。 C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发。只管C语言提供了许多低级解决的性能,但依然放弃着跨平台的个性,因为C语言具备可移植性,可拓展性,可重用性等个性,促使C语言依然在编程语言排行榜上占据肯定无利位置。而C语言中比拟重要的就是指针,它能够用来链表操作,谈到链表,很多时候为此分配内存采纳动态分配而不是动态调配。 内存调配的概念通常定义变量(或对象),编译器在编译时都能够依据该变量(或对象)的类型晓得所需内存空间的大小,从而零碎在适当的时候为他们调配确定的存储空间。这种内存调配称为动态存储调配;有些操作对象只在程序运行时能力确定,这样编译时就无奈为他们预约存储空间,只能在程序运行时,零碎依据运行时的要求进行内存调配,这种办法称为动态存储分配。所有动态存储分配都在堆区中进行。 内存不是取之不尽用之不竭,4g、8g、16g是常见的电脑内存大小,关上工作管理器,能看到不同的利用占据的内存状况。如果一个应用程序占了大部分内存,预计别的利用就资源缓和了,那这个利用可能会被卸载,找个节俭内存的。 内存治理是计算机靠近物理实质的操作,那些程序语言之下的动作,最终都要调动内存来实现。零碎的资源不是有限的,零碎上运行的程序也不是只有这一个,疏忽内存,就会设计出危险的、冗余的代码产品,或者没法更好的交互。 动态内存调配的特点动态内存是绝对动态内存而言的。所谓动静和动态就是指内存的调配形式。动态内存是指在堆上调配的内存,而动态内存是指在栈上调配的内存。动态内存调配的实质就是,什么时候须要一块内存的时候,再调配这块内存;当不再须要某一块内存的时候,就能够把这块内存开释掉。这种灵便的内存调配形式,正好适宜链表这种数据结构。 传统数组的毛病数组与动态内存调配相比有以下毛病: 数组的长度必须当时指定,而且只能是常量,不能是变量。因为数组长度只能是常量,所以它的长度不能在函数运行的过程当中动静地裁减和放大。对于数组所占内存空间程序员无奈手动编程开释,只能在函数运行完结后由零碎主动开释,所以在一个函数中定义的数组只能在该函数运行期间被其余函数应用。而“传统数组”的问题,实际上就是动态内存的问题。然而动态内存就不存在这个问题,因为动态内存是由程序员手动编程释的,所以想什么时候开释就什么时候开释。只有程序员不手动编程开释,就算函数运行完结,动态分配的内存空间也不会被开释,其余函数仍可持续应用它。除非是整个程序运行完结,这时零碎为该程序调配的所有内存空间都会被开释。 动态内存的申请与开释动态内存的申请与开释次要依附两个函数malloc和free。malloc 是一个零碎函数,它是 memory allocate 的缩写。其中memory是“内存”的意思,allocate是“调配”的意思。顾名思义 malloc 函数的性能就是“分配内存”,要调用它必须要蕴含头文件<stdlib.h>。 malloc()函数会向堆中申请一片间断的可用内存空间;若申请胜利 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以咱们在用malloc()函数开拓动态内存之后, 肯定要判断函数返回值是否为NULL;返回值的类型为void型, malloc()函数并不知道间断开拓的size个字节是存储什么类型数据的 ,所以须要咱们自行决定 ,办法是在malloc()前增强制转 ,转化成咱们所需类型 ,如: (int)malloc(sizeof(int)*n). 上面应用 malloc 函数写一个程序,程序的性能是:调用被调函数,将主调函数中动态分配的内存中的数据放大 10 倍。 输入后果是:*p = 100 free是开释函数,在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会主动开释内存 , 如果咱们不手动开释, 直到程序运行完结才会开释, 这样就可能会造成内存透露, 即堆中这片内存中的数据曾经不再应用, 但它始终占着这片空间, 所以当咱们申请的动态内存不再应用时 ,肯定要及时开释 .不过须要留神的是,开释并不是指清空内存空间,而是指将该内存空间标记为“可用”状态,使操作系统在分配内存时能够将它重新分配给其余变量应用。 那么,当指针变量被开释后,它所指向的内存空间中的数据会怎么呢?free 的规范行为只是示意这块内存能够被再调配,至于它外面的数据是否被清空并没有强制要求。不同的编译器解决的形式可能不一样。咱们就看一下 VC++6.0 这个编译器是怎么解决的: 可见在 VC++6.0 中,当指针变量被开释后,尽管它依然是指向那个内存空间的,但那个内存空间中的值将会被从新置一个十分小的正数。动态创建的内存如果不必了必须要开释。留神,一个动态内存只能开释一次。如果开释屡次程序就会解体,因为曾经开释了,不能再开释第二次。 综上所述,malloc 和 free 肯定要成对存在,一一对应。有 malloc 就肯定要有 free,有几个 malloc 就要有几个 free,与此同时,每开释一个指向动态内存的指针变量后要立即把它指向 NULL。 注意事项1)开释一块内存的一部分是不容许的。动态分配的内存必须整块一起开释。然而,realloc函数能够放大一块动态分配的内存,无效地开释它尾部的局部内存。 2)不要拜访曾经被free函数开释了的内存。假设对一个指向动态分配的内存的指针进行了复制,而且这个指针的几份拷贝扩散于程序各处。你无奈保障当你应用其中一个指针时它所指向的内存是不是已被另一个指针开释。还要确保程序中所有应用这块内存的中央在这块内存开释之前进行对它的应用。 3)当动态分配的内存不再须要应用时,应该被开释,这样能够被重新分配应用。分配内存但在应用结束后不开释将引起内存透露(memory leak)。 ...
简介 本文应用 C++20 引入的协程来编写一个 Linux epoll 程序。在此实现中,用户应用异步操作时再也无需提供本人的回调函数。以此处实现的 asyncRead() 为例: 应用 asyncRead() 所需的参数和 read() 大致相同,无需传入回调;asyncRead() 的外部会向 epoll 注册要监听的文件描述符、感兴趣的事件和要执行的回调(由实现提供,而无需使用者传入);当事件未就绪时,co_await asyncRead() 会挂起以后协程;当事件就绪时,epoll 循环中会执行具体的 I/O 操作(此处将其提交到 I/O 线程池中执行),当 I/O 操作实现时,复原协程的运行。1. ThreadPool此处应用了两个线程池: I/O 线程池:用于执行 I/O 操作;工作线程池:用于解决客户端连贯(此处以 tcp 回显程序为例)。此处应用的是本人实现的线程池,具体实现见 https://segmentfault.com/a/11...。 2. IOContextIOContext 类对 Linux epoll 做了简略的封装。 io_context.h: #ifndef IOCONTEXT_H#define IOCONTEXT_H#include <sys/epoll.h>#include <unistd.h>#include <unordered_map>#include <functional>#include "thread_pool.h"using callback_t = std::function<void()>;struct Args{ callback_t m_cb;};class IOContext{public: IOContext(int nIOThreads=2, int nJobThreads=2); // 监听文件描述符 fd,感兴趣事件为 events,args 中蕴含要执行的回调 bool post(int fd, int events, const Args& args); // 提交工作至工作线程池 bool post(const Task& task); // 不再关注文件描述符 fd,并移除相应的回调 void remove(int fd); // 继续监听、期待事件就绪 void run();private: int m_fd; std::unordered_map<int, Args> m_args; std::mutex m_lock; ThreadPool m_ioPool; // I/O 线程池 ThreadPool m_jobPool; // 工作线程池};#endifio_context.cpp: ...
回顾享元模式,思考实作它的各种问题。 Prologue略过 FlyWeight Pattern实践享元模式,是将简单对象的雷同的组成元素抽出并独自保护的一种结构型设计模式。这些雷同的组成元素被称为共享元件,它们在一个独自的容器中被唯一性地治理,而简单对象只需持有到该惟一实例的参考,而无需反复创立这样的雷同的元素,从而可能大幅度地削减内存占用。 以字处理器为例,每个字符都具备独立的、区别于其它字符的非凡属性:例如字体款式,背景、边框、对齐等等。如果一个文档中全副字符都独自存储一份它的所有属性的正本,那么这将会是宏大的内存需要。但思考到一大堆(例如1000个)字符可能都有雷同的“宋体,9pt”这样的属性,那么实际上咱们只须要独自存储一份“宋体,9pt”的字体款式属性,而一个字符只须要一个指向该字体款式属性的指针就能够了,这就比1000个字符的1000个字体款式属性拷贝要节约的多。 相似的案例还有相当多,例如例子零碎中的每个粒子(例如子弹、弹片,或者敌方飞机)都有一些雷同的属性(例如色彩,轮廓等等)占地不小,但值却雷同。 工厂模式很容易想到,咱们能够在一个工厂中就地治理享元对象。当客户以具体值来申请一个享元对象时,工厂会从一个字典中检索享元是否存在,而后返回该元素的参考援用给客户。如果享元尚未存在,那么工厂会创立它,而后在返回援用。 不可变性依照传统的说法,享元模式要求这些雷同的局部(享元,雷同的组成元素)是不可变的。但这并不是铁律。 一个办法是,以一个享元为整体,咱们能够整体批改对象持有的享元参考。 例如咱们正在批改字处理器中的一个单词的字体款式,从“宋体,9pt”改为“黑体,12pt”,那么咱们能够间接批改援用指向。也就是说,咱们提供 character.apply_font_style(font_style& style) 这样的整体批改接口。 另一个办法能够从更细的粒度登程进行批改,例如从“宋体,9pt”改为“宋体,10pt”,但在产生变更时,尝试从工厂中查证新值的参考。也就是说,咱们提供 character.set_font_size(float pt) 这样的接口,但在其实现过程中记得去查证享元工厂(管理器)以求更新外部援用。 C++ 实现传统的享元模式的实现形式有这样的示例代码: namespace hicc::dp::flyweight::basic { /** * flyweight Design Pattern * * Intent: Lets you fit more objects into the available amount of RAM by sharing * common parts of state between multiple objects, instead of keeping all of the * data in each object. */ struct shared_state { std::string brand_; std::string model_; std::string color_; shared_state(const std::string &brand, const std::string &model, const std::string &color) : brand_(brand) , model_(model) , color_(color) { } friend std::ostream &operator<<(std::ostream &os, const shared_state &ss) { return os << "[ " << ss.brand_ << " , " << ss.model_ << " , " << ss.color_ << " ]"; } }; struct unique_state { std::string owner_; std::string plates_; unique_state(const std::string &owner, const std::string &plates) : owner_(owner) , plates_(plates) { } friend std::ostream &operator<<(std::ostream &os, const unique_state &us) { return os << "[ " << us.owner_ << " , " << us.plates_ << " ]"; } }; /** * The flyweight stores a common portion of the state (also called intrinsic * state) that belongs to multiple real business entities. The flyweight accepts * the rest of the state (extrinsic state, unique for each entity) via its * method parameters. */ class flyweight { private: shared_state *shared_state_; public: flyweight(const shared_state *o) : shared_state_(new struct shared_state(*o)) { } flyweight(const flyweight &o) : shared_state_(new struct shared_state(*o.shared_state_)) { } ~flyweight() { delete shared_state_; } shared_state *state() const { return shared_state_; } void Operation(const unique_state &unique_state) const { std::cout << "flyweight: Displaying shared (" << *shared_state_ << ") and unique (" << unique_state << ") state.\n"; } }; /** * The flyweight Factory creates and manages the flyweight objects. It ensures * that flyweights are shared correctly. When the client requests a flyweight, * the factory either returns an existing instance or creates a new one, if it * doesn't exist yet. */ class flyweight_factory { std::unordered_map<std::string, flyweight> flyweights_; std::string key(const shared_state &ss) const { return ss.brand_ + "_" + ss.model_ + "_" + ss.color_; } public: flyweight_factory(std::initializer_list<shared_state> lists) { for (const shared_state &ss : lists) { this->flyweights_.insert(std::make_pair<std::string, flyweight>(this->key(ss), flyweight(&ss))); } } /** * Returns an existing flyweight with a given state or creates a new one. */ flyweight get(const shared_state &shared_state) { std::string key = this->key(shared_state); if (this->flyweights_.find(key) == this->flyweights_.end()) { std::cout << "flyweight_factory: Can't find a flyweight, creating new one.\n"; this->flyweights_.insert(std::make_pair(key, flyweight(&shared_state))); } else { std::cout << "flyweight_factory: Reusing existing flyweight.\n"; } return this->flyweights_.at(key); } void list() const { size_t count = this->flyweights_.size(); std::cout << "\nflyweight_factory: I have " << count << " flyweights:\n"; for (std::pair<std::string, flyweight> pair : this->flyweights_) { std::cout << pair.first << "\n"; } } }; // ... void AddCarToPoliceDatabase( flyweight_factory &ff, const std::string &plates, const std::string &owner, const std::string &brand, const std::string &model, const std::string &color) { std::cout << "\nClient: Adding a car to database.\n"; const flyweight &flyweight = ff.get({brand, model, color}); // The client code either stores or calculates extrinsic state and passes it // to the flyweight's methods. flyweight.Operation({owner, plates}); }} // namespace hicc::dp::flyweight::basicvoid test_flyweight_basic() { using namespace hicc::dp::flyweight::basic; flyweight_factory *factory = new flyweight_factory({ {"Chevrolet", "Camaro2018", "pink"}, {"Mercedes Benz", "C300", "black"}, {"Mercedes Benz", "C500", "red"}, {"BMW", "M5", "red"}, {"BMW", "X6", "white"} }); factory->list(); AddCarToPoliceDatabase(*factory, "CL234IR", "James Doe", "BMW", "M5", "red"); AddCarToPoliceDatabase(*factory, "CL234IR", "James Doe", "BMW", "X1", "red"); factory->list(); delete factory;}其输入后果如同这样: ...
1. 匹配根底用法: #include <iostream>#include <string>#include <regex>int main(){ using namespace std::regex_constants; std::string filename("Foo.txt"); std::regex pattern("[a-z]+\\.txt", ECMAScript|icase); // icase: 疏忽大小写 // filename 是否匹配 pattern bool matched = std::regex_match(filename, pattern); std::cout << std::boolalpha << matched; // true}提取子匹配(分组): std::string filename("foo.txt");std::regex pattern("([a-z]+)\\.([a-z]+)");std::smatch groups;// 如果 filename 匹配 pattern,则将匹配后果存入 groups 中bool matched = std::regex_match(filename, groups, pattern);if (matched){ for (size_t i = 0; i < groups.size(); i++) { /* groups[0] 是整个匹配的内容; groups[1] 是第一匹配分组的内容,以此类推。 */ std::ssub_match group = groups[i]; std::cout << "groups[" << i << "]: " << group.str() << '\n'; }}groups[0]: foo.txtgroups[1]: foogroups[2]: txt2. 搜寻根底用法: ...
Summary1)enum是C语言中的一种自定义类型; enum类型的变量只能取定义时的离散值。 2)enum在C语言中能够定义真正意义上的常量,工程中罕用枚举来定义常量(无名枚举)。 3)sizeof是编译器的内置批示符,sizeof的值在编译期就确定。 4)sizeof是C语言的内置关键字而不是函数 在编译过程中所有的sizeof将被具体的数值替换,括号里的函数或者语句都不会执行程序的执行过程与sizeof没有任何关系5)typedef用于给已有的类型重命名,实质上不能产生新类型,新的类型名不能被unsigned或者signed润饰,原类型名能够在typedef后定义。 enum、typedef、siezof剖析1、enumenum是C语言中的一种自定义类型enum的值是能够依据须要自定义的整形值第一个定义enum值默认为0默认状况下的enum值是在前一个定义值的根底上加1enum类型的变量只能取定义时的离散值 enum Color{ RED , YELLOW = 2, GREEN};int main(){ enum Color cc = GREEN; printf("cc = %d\n", cc); // 3 return 0;}enum中定义的值是C语言中真正意义上的常量在工程中,enum多用于定义整形常量 enum // 无名枚举{ ARRAY_SIZE = 5 // 用来定义数组大小,常量};int arr[ARRAY_SIZE] = {0};2、sizeofsizeof是编译器的内置批示符sizeof的值在编译期就曾经确定sizeof用于计算类型或变量所占的内存大小 sizeof用于类型:sizeof(type)sizeof用于变量:sizeof(var)或者sizeof var int var = 0;printf("%d\n", sizeof(int));printf("%d\n", sizeof(var));printf("%d\n", sizeof var);sizeof是C语言的内置关键字而不是函数 在编译过程中所有的sizeof将被具体的数值替换程序的执行过程与sizeof没有任何关系 // 上面程序的输入是?int var = 0;int size = sizeof(var++);printf("var = %d, size = %d\n", var, size); // 0, 4程序输入var = 0, size = 4,因为sizeof在编译期时候就曾经确定值,即sizeof(var++)被替换成了4,var++也不会再执行。 ...
Summary1)空构造体的大小在不同的编译器下行为不同:gcc编译器下空构造体的大小为0;bcc和vc编译器认为空构造体的语法错误,编译谬误 2)柔型数组的要点: struct SoftArray{ int len; int array[];};sizeof(struct SoftArray) = 4 struct SoftArray* sa = NULL; sa = (struct SoftArray*)malloc(sizeof(struct SoftArray) + 5*sizeof(int)); sa->len = 5;柔性数组的最初一个数组是一个标识符,不占用空间,sizeof(struct SoftArray)失去的是数组长度占用的空间。柔性数组须要从堆空间中申请,能够自在调配想要大小数组,malloc完了之后指定数组的大小。益处在于,不须要在定义的时候指定数组大小,能够在运行时指定大小;带了长度信息,作为函数参数的时候不须要额定指定数组的大小。3)union中的所有成员共享一段内存,大小为成员中最大的一个所占内存。 4)小端系统:低地址存储低位数据;大端系统:低地址存储高位数据。应用union能够判断零碎的大小端。不管大小端系统,在取uu.c的值是,都是从低地址处按字节来取。 struct和union剖析1、struct1.1 空构造体占用多大的内存?struct TS{};sizeof(struct TS) = ?以上的代码,在不同的编译器下,后果不同: gcc编译器下,空构造体的大小为0bcc编译器下,编译谬误vc编译器下,编译谬误这对应了两种解释思路: bcc、vc认为,既然构造体是存储变量的汇合,如果一个构造体里什么都不放,天然是不对的,编译谬误gcc认为,空的构造体里什么变量都没有,天然大小为0;同时也能用空构造体类型来定义变量,大小也为0。1.2 柔性数组柔性数组是数组大小待定的数组C语言中能够由构造体产生柔性数组C语言中构造体的最初一个元素能够是大小未知的数组 struct SoftArray{ int len; int array[];};sizeof(struct SoftArray) = 4柔性数组的用法: struct SoftArray{ int len; int array[];};int main(){ printf("sizeof(struct SoftArr) = %d\n", sizeof(struct SoftArray)); struct SoftArray* sa = NULL; sa = (struct SoftArray*)malloc(sizeof(struct SoftArray) + 5*sizeof(int)); sa->len = 5; for(int i=0; i<sa->len; ++i) { sa->array[i] = i+1; printf("sa->array[%d] = %d\n", i, sa->array[i]); } return 0;}柔性数组的最初一个数组是一个标识符,不占用空间,sizeof(struct SoftArray)失去的是数组长度占用的空间。柔性数组须要从堆空间中申请,能够自在调配想要大小数组,malloc完了之后指定数组的大小。益处在于,不须要在定义的时候指定数组大小,能够在运行时指定大小;带了长度信息,作为函数参数的时候不须要额定指定数组的大小。2、union2.1 union占用的内存C语言中的union在语法上与struc类似union只调配最大成员的空间,所有成员共享这个空间。 ...
曾经写了一篇 谈 C++17 里的 Factory 模式 ,起初又顺便肝了一篇 谈 C++17 里的 Singleton 模式 。看来是得要整一大堆了,对于懒惰的人来说这很麻烦。我不晓得是不是要打算会写残缺个 GoF 的集体了解以及新的的实现,缓缓看吧,做了就做了。回顾下构建者模式,并应答做类库时遇到的构建者模板类应如何实作的问题。 Prologue实际上,就我集体而言,真正地使用 builder pattern,反而是在 Java 开发经验中。流式接口也是如此。 Builder 模式就是为了分步骤结构一个对象用的,看图: FROM: HERE尽管很多时候咱们都只关怀 new 了对象后怎么操作它,然而有的时候有的场景里的确咱们只会关怀怎么 new 这个对象。这时候就是 Builder 了。 Builder Pattern实践Builder 模式是 Creational Patterns 中的一种。在 谈 C++17 里的 Factory 模式 中,咱们曾经介绍过创立型模式了,所以本文不再赘述了。 构建者模式的用意,就在于让你能够分步骤地构建简单对象,它容许你应用雷同(类似)的创立diamanté生产出不同类型和模式的对象。 对于 Builder 模式来说,一个重要的标记,只管这并不是规定但却往往约定俗成,就是以一个 .build() 调用作为完结。例如: auto shape = Builder() .choose(Shape.Rect) // choose a factory .setColor(COLOR.RED) .setBorderWidth(1) .setFill(COLOR.GRAY) .build();canva.place(shape, Position.Default);Builder 模式并非必须得要采纳流式接口。 反而在很多时候咱们须要和交互对象协商一个抉择,并将这个决定设置到 Builder 结构者中。直到全副协商实现之后,才应用 builder.build() 构建出最终产品实例。 ...
题面 原题链接 代码本题有很多做法 动静布局(本人想进去的,工夫性能要比哈希表优良很多): int变量dpIndex,记录整个字符串中第一次只呈现一次的字符下标 int数组present[123],因为'z'的ascii码是122,初始化为全0 dp[i] = 前i个字符串中第一个只呈现一次的字符 动静布局过程 if s[i]!=dp[i-1] dp[i] = dp[i-1] else present[s[dpIndex]] = 1 dpIndex++ until present[s[dpIndex]] != 1 dp[dpIndex] = s[dpIndex] j = dpIndex源代码 class Solution {public: char firstUniqChar(string s) { char result = 'n'; int present[123]; for(int i=0; i<123;i++){ present[i] = 0; } int len = s.size(); if(len==0){ return ' '; } char dp[len]; for(char k:dp){ k=' '; } int dpIndex = 0; dp[0] = s[0]; int j= 1; for(j = 1;j<len;j++){ if(s[j] != dp[j-1]){ dp[j] = dp[j-1]; } else{ present[s[j]] = 1; while(dpIndex<len && present[s[dpIndex]] == 1){ dpIndex++; } if(dpIndex>=len){ return ' '; } dp[dpIndex] = s[dpIndex]; j = dpIndex; // if(j==len-1){ // return ' '; // } cout<<dpIndex<<endl; } } return dp[len-1]; }}; ...
1. 简介#include <unordered_map>template < class Key, // unordered_map::key_type class T, // unordered_map::mapped_type class Hash = std::hash<Key>, // unordered_map::hasher class Pred = std::equal_to<Key>, // unordered_map::key_equal class Alloc = std::allocator<std::pair<const Key, T>> // unordered_map::allocator_type> class unordered_map;unordered_map 具备如下性质: 唯一性:键是惟一的;无序性:键值对是无序存储的,元素被存储在桶中,如果两个元素领有雷同哈希值的键,则它们会被存储于同一桶中;具备常数工夫复杂度(均匀来说)的搜寻、插入、删除操作。2. 自定义键类型如果键是自定义类型,则须要提供相应的哈希函数,来计算键的哈希值。 办法1:显式提供相应的模板参数 #include <iostream>#include <unordered_map>#include <string>struct Key{ std::string first; std::string second;};struct KeyHash{ std::size_t operator()(const Key& k) const { return std::hash<std::string>()(k.first) ^ (std::hash<std::string>()(k.second) << 1); }};struct KeyEqual{ bool operator()(const Key& lhs, const Key& rhs) const { return lhs.first == rhs.first && lhs.second == rhs.second; }};int main(){ Key k1{ "John", "Doe" }, k2{ "Mary", "Sue" }; std::unordered_map<Key, std::string, KeyHash, KeyEqual> m = { { k1, "example"}, { k2, "another"} }; std::cout << m[k1] << '\n'; // example}办法2:显式特化 std::hash<> 模板 ...
我这里只举荐3款自认为比拟好的ide 1.CLionCLion是JetBrains的,代码补全很爽,写起货色来很棘手,PyCharm,WebStrom和GoLand都是神作。然而不自带编译器官网 教程 如何激活?点我! 2.Visual studio很好用,代码补全,智能性能剖析....就是有点大,占空间。然而,一个业余的IDE性能齐备,很好的 Visual Studio有不同版本,社区版收费。不仅反对C/C++,还有C/C++游戏开发等等反对。就是代码补全很蛋疼... 官网 3.Dev C++很不便,能够间接上,然而没有代码补全,且很老官网曾经停运了。。。 当初如果有小伙伴还想用,举荐小熊猫Dev-C++ 总结老手不怕麻烦能够选VS,喜爱丝滑的感觉果决CLion,不爱折腾...DevC++
C++ 规范库提供了如下线程同步机制: 互斥量(反对超时加锁、递归加锁)读写锁(共享互斥量,也反对超时加锁)互斥量包装器(基于 RAII 的思维)条件变量信号量(二元信号量、计数信号量)栅栏(反对重用)调用一次1. 互斥量#include <mutex>mutex:提供根底的互斥性能。 std::mutex mtx;mtx.lock(); // locks the mutex, blocks if the mutex is not availablebool ok = mtx.try_lock(); // tries to lock the mutex, returns if the mutex is not available mtx.unlock(); // unlocks the mutex timed_mutex:在 mutex 的根底上减少了超时加锁的性能。 #include <chrono>using namespace std::chrono_literals;std::timed_mutex mtx;// 以绝对工夫的模式指定超时工夫if (mtx.try_lock_for(100ms)){ // 已加锁}// 以相对工夫的模式指定超时工夫auto now = std::chrono::steady_clock::now();if (mtx.try_lock_until(now + 10s)){ // 已加锁}recursive_mutex:在 mutex 的根底上减少了递归加锁的性能(此时,lock() 函数能够被同一线程在不开释锁的状况下屡次调用)。 std::recursive_mutex mtx;void fun1() { mtx.lock(); // ... mtx.unlock();}void fun2() { mtx.lock(); // ... fun1(); // recursive lock becomes useful here mtx.unlock();};recursive_timed_mutex:在 timed_mutex 的根底上减少了递归加锁的性能。2. 读写锁#include <shared_mutex>shared_mutex ...
Singleton Pattern回顾下单件模式,并思考实现一个通用的单件模板类以达成业务端低代码的指标。 Prologue设计模式中最平民的 Pattern 是哪一个?简直不会有任何一致,那必须是单件模式了。所谓单件模式,是在 C 语言开发历史上经验了各种各样的全局变量失控的折磨后倒退起来的一种技术,得益于 C++ 的封装能力,咱们能够将各种各样的全局变量管控在一个全局动态类(或者说一个类中全都是动态变量的实现形式)中,从而避免任意搁置全局变量带来的灾难性的结果。 我不用在此反复这些结果能有多糟,因为本文不是入门教材,而是实作教训的一个梳理而已。 很显著,动态类只不过是开始,它不是特地好的解决伎俩,因为除了可能集中在一处这一长处之外,这些动态变量还是是予取予求的,此外,晚期(C++11以前)的编译器没有明确和对立的动态变量初始化程序约定,所以你较难解决这些变量的初始化机会,再一个问题是,没有任何伎俩可能让你实现这些变量的懒加载(lazyinit),除非你将它们通通变成指针,那样的话,还会搞出多少事来只有天知道了。 实践根底Singleton 模式是 Creational Patterns 中的一种。在 谈 C++17 里的 Factory 模式 中,咱们曾经介绍过创立型模式了,所以本文不再赘述了。 单件模式的用意,及其根本实现,都是非常简单的,因此也不用消耗笔墨凑字数,间接略过。 Goal咱们想要的是一个可能 lazyinit 或者可能管制起初始化机会的、线程平安的单件模式。 所以上面会介绍 C++11(包含 C++0x)以来的若干可能理论使用的 Singleton 实现计划,但略过了更晚期的实现手法,以及略过了那些顾头不顾尾的示例性实现。嗯,其中一个是这样的: class singleton { static singleton* _ptr; singleton() {} public: singleton* get() { if (_ptr == NULL) _ptr = new singleton(); return _ptr; }};这个实现最为亲民,因为任何人无需任何常识(还是须要会c++的)也能一次性手写胜利,甚至不用放心手误或者其它什么编译谬误。它的弱点,较为显著的就先不提了,有时候会被展现者有意无意疏忽或者覆盖的一个重要弱点是,_ptr 是不会被 delete 的:但使用者会压服本人说我的程序就这么一个指针透露,这个代价是付得起的。 可怕吗?或者也不。这就是真的。 考究一点的家伙,晓得 C 提供了一种 atexit 的伎俩,所以会设法挂载一个退出时的 delete 例程,但还是不能解决在 if(_ptr==null) 这里可能产生的跨线程 data racing 问题。问题在于,一个 C++er 你搞个 C 技法在外面,那它也很不纯净的不是? ...
C#读取XML文件try{ XmlDocument xmlDoc = new XmlDocument();xmlDoc.Load(@"types.xml");XmlElement xmlRoot = xmlDoc.DocumentElement;foreach (XmlNode node in xmlRoot.ChildNodes){ Type type = new Type(); string num = node["num"].InnerText; string desc = node["desc"].InnerText; string content = node["content"].InnerXml;}}catch { }
【五】零根底上手HAL库之—按键内部中断 5.1 前言咱们曾经大抵的理解了Cubemx软件中GPIO的基本操作了,接下来咱们开始进入内部中断的操作,这是一个触目惊心的时刻,为什么呢?在应用Hal库的同时你会感觉到其劣势性,开发效率大大提高带来的快感。 后期筹备 STM32各类型的板子(自己应用F103Rc和F407ZE)。CubeMx软件,Keil\_IDE。5.2 Key按键中断同样的咱们先来看看按键模块的原理图局部: 当按键没有按下时,按键局部相当于断路,PC13的电压相当于电容两端电压为3.3V。 当按键按下时,按键局部相当于短路(即一根导线),PC13的电压和GND地相连为0V。 所以按键从未按下到按下相当于是一个高电平到低电平的跳变,简称为降落沿。所以说咱们在按键中断时抉择的模式就是降落沿触发。 1、新建工程 搜寻或者筛选芯片后,点击Start Project胜利创立一个我的项目。 2、时钟配置 点击RCC进入时钟配置,配置高速时钟为内部晶振,软件主动配置了两个晶振的引脚如图PD0和PD1 3、Debug配置为DAP 4、GPIO配置为中断 ① GPIO模块配置模块② 各引脚配置: PC12/PC13(内部中断模式中断线12和13),共用一个中断向量。PC14/PC15输入模式为LED的两个引脚③ 引脚性能具体配置: GPIO mode :回升沿/降落沿/上降落沿 中断和事件模式。GPIO Pull-up/Pull-down:因为我的板子硬件上有上拉电阻即平时电平为高电平,所以配置为弱上拉模式。User Lable:这里没有应用到用户标签为了不便在图中右半局部看到引脚所配置的性能。④ 输入模式配置:PC14/PC15配置为输入模式,管制LED灯亮灭。⑤ 内部中断线13:PC13为降落沿触发的内部中断模式,KEY1。⑥ 内部中断线12:PC12为降落沿触发的内部中断模式,KEY2。 5、项目管理配置以及代码生成配置这里咱们还是一样配置为最高的时钟频率72M,在对应的框中输出72,按下回车即可见到如下两图的变动。按下回车前如图: 按下回车后如图: 6、项目管理配置以及代码生成配置同样的咱们填写好工程名KEY,抉择好文件门路,以及所用的IDE以及版本号即可 代码生成抉择好这两个后点击右上角按钮,产生代码: 7、业务逻辑代码7.1 API咱们再来看看GPIO模块对应的API函数: /* 初始化和删除初始化函数, HAL_GPIO_Init:cubemx生成代码后主动调用初始化函数 HAL_GPIO_DeInit:解除初始化,不想应用时能够被动应用*/void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);/* 操作GPIO的函数,包含读取,写入,翻转,加锁,中断服务函数以及回调函数 HAL_GPIO_ReadPin:读取引脚电平 返回值为(GPIO_PIN_RESET/GPIO_PIN_SET) HAL_GPIO_WritePin:写入引脚电平 HAL_GPIO_TogglePin:翻转引脚电平 HAL_GPIO_LockPin:所以引脚以后电平,将无奈扭转 HAL_GPIO_EXTI_IRQHandler:引脚的中断服务函数 HAL_GPIO_EXTI_Callback:引脚的中断回调函数 */GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);7.2 编写回调函数void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ if(GPIO_Pin == GPIO_PIN_12) { HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_14); } if(GPIO_Pin == GPIO_PIN_13) { HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_15); }}咱们来看看回调函数是怎么工作的: ...
C++20 引入了 range 来简化对元素序列的解决(能够省略掉许多的循环遍历)。 1. range 和 viewrange range concept 通过提供一个迭代器以及一个哨兵来示意一个元素范畴,以容许对某个类型进行遍历。 template<class T>concept range = requires(T& t) { ranges::begin(t); ranges::end (t);};如,vector 就是一个 range: std::vector<int> vec{ 1, 2, 3 };auto it1 = std::ranges::begin(vec);auto it2 = std::ranges::end(vec);view view 是一个 range,且具备常数工夫复杂度的的拷贝、挪动和赋值操作(如,间接操作一对迭代器、或应用生成器来按需生成元素)。 template<class T>concept view = ranges::range<T> && std::movable<T> && ranges::enable_view<T>;template<class T>inline constexpr bool enable_view = std::derived_from<T, view_base> || /*is-derived-from-view-interface*/<T>;如,可通过 iota 来创立一个 view,相似于 Python 中的 range: auto v1 = std::views::iota(1); // [1, +inf)auto v2 = std::views::iota(1, 10); // [1, 10)for (int i : v2){ std::cout << i << ' ';}2. 范畴工厂会创立一个 view。 ...
Prologue: C++ 中的枚举类型利用以及转换到字符串的加强:AWESOME_MAKE_ENUM,... Original From: HERE因为长期发现须要一个枚举量到字符串的转换器,所以罗唆梳理了一遍古往今来的枚举类型的变动。 于是奇怪的冷常识又减少了。 枚举类型 enum在 cxx11 之前,C/C++ 通过 enum 关键字申明枚举量。 // 匿名全局枚举量enum { DOG, CAT = 100, HORSE = 1000};enum Animal { DOG, CAT = 100, HORSE = 1000};从 cxx11 起,enum 容许应用不同于 integer 的其它数据类型。此时它的语法是这样的: enum 名字(可选) : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... }enum 名字 : 类型 ;所以在必要时能够: enum smallenum: std::int16_t { a, b, c};但类型并不容许大过 int,受制于 CPU 字长。所以类型的反对简直没有任何实用价值,不晓得那堆人怎么想的,看来嵌入式真的很火。 enum class从 cxx11 起,咱们被举荐放弃 enum 改用有作用域的 enum class,也或者 enum struct。这时候申明枚举类型的形式如下: ...
【四】零根底上手HAL库之—GPIO按键3.1前言咱们在上一节曾经晓得了点灯操作,相当于是学会了GPIO输入的配置,接下来是GPIO输出实现按键扫描的操作。 后期筹备 STM32各类型的板子(自己应用F103Rc和F407ZE)。CubeMx软件,Keil\_IDE。 3.2 KEY按键同样的咱们先来看看按键模块的原理图局部: 当按键没有按下时,按键局部相当于断路,PC13的电压相当于电容两端电压为3.3V。 当按键按下时,按键局部相当于短路(即一根导线),PC13的电压和GND地相连为0V。 所以按键从未按下到按下相当于是一个高电平到低电平的跳变,简称为降落沿。 1、新建工程 搜寻或者筛选芯片后,点击Start Project胜利创立一个我的项目。 2、时钟配置 点击RCC进入时钟配置,配置高速时钟为内部晶振,软件主动配置了两个晶振的引脚如图PD0和PD1 3、Debug配置为DAP 4、输出模式配置 ① 抉择如原理图所示的引脚,这里PC14/PC15为LED引脚,PC12/PC13为KEY引脚,别离设置为输入和输出模式② 选中GPIO模块,对引脚模式进行具体配置③ 设置PC12的具体模式, GPIO mode:输出模式。GPIO Pull:由原理图可知,当没有按键按下时为高电平,所以抉择上拉模式。User Label:用户标签,用于标识对应引脚的端口和Pin,不便调用API,这里按键为KEY1/KEY2,灯为LED1/LED2。 5、时钟配置这里咱们还是一样配置为最高的时钟频率72M,在对应的框中输出72,按下回车即可见到如下两图的变动。按下回车前如图: 按下回车后如图: 6、项目管理配置以及代码生成配置同样的咱们填写好工程名KEY,抉择好文件门路,以及所用的IDE以及版本号即可 代码生成抉择好这两个后点击右上角按钮,产生代码: 7、业务逻辑代码7.1 API咱们再来看看GPIO模块对应的API函数: /* 初始化和删除初始化函数, HAL_GPIO_Init:cubemx生成代码后主动调用初始化函数 HAL_GPIO_DeInit:解除初始化,不想应用时能够被动应用*/void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);/* 操作GPIO的函数,包含读取,写入,翻转,加锁,中断服务函数以及回调函数 HAL_GPIO_ReadPin:读取引脚电平 返回值为(GPIO_PIN_RESET/GPIO_PIN_SET) HAL_GPIO_WritePin:写入引脚电平 HAL_GPIO_TogglePin:翻转引脚电平 HAL_GPIO_LockPin:所以引脚以后电平,将无奈扭转 HAL_GPIO_EXTI_IRQHandler:引脚的中断服务函数 HAL_GPIO_EXTI_Callback:引脚的中断回调函数 */GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);main( )函数咱们能够在main.h中看到之前定义的用户标签,如下: ...
【三】零根底上手HAL库之—GPIO点灯3.1前言咱们曾经大抵的理解了Cubemx软件界面的个别操作,懂得如何新建工程和应用步骤了,接下来咱们正式进入应用Hal库开发的旅程,这是一个触目惊心的时刻,为什么呢?在应用Hal库的同时你会感觉到其劣势性,开发效率大大提高带来的快感,来吧咱们先点灯。 后期筹备 STM32各类型的板子(自己应用F103Rc和F407ZE)。CubeMx软件,Keil\_IDE。 3.2 LED流水灯咱们先来看看板子对应流水灯的原理图: 采纳共阴极连贯的LED灯,只有当引脚输入高电平时,发光二极管正向导通发光。咱们晓得了板子硬件的电路结构,那么咱们来看看软件上怎么配置。 1、新建工程: 搜寻或者筛选芯片后,点击Start Project胜利创立一个我的项目 2、时钟配置 点击RCC进入时钟配置,配置高速时钟为内部晶振,软件主动配置了两个晶振的引脚如图PD0和PD1 留神引脚色彩: 绿色 :曾经配置了性能且抉择了模式黄色:配置了性能,但未配置模式灰色:暂未配置米红色:不可配置引脚 3、GPIO引脚配置 ① 找到流水灯对应的引脚PC14(PC15),设置为输入。② 找到GPIO模块设置对应的模式③ 以后我的项目配置的引脚的总览页面④ GPIO模式的具体配置(包含初始化输入电平,输入模式,高低拉模式,引脚速度,引脚性能用户标签) 4、GPIO模式具体配置 ① 以后配置的引脚,这里是PC15② 初始化输入电平,因为咱们的LED灯是共阴极接法,所以输入为高电平时灯会亮③ 输入的模式(推挽、开漏输入),这里应用默认的推挽输入即可。④ 是否高低拉引脚(上拉,下拉),这里应用默认。⑤ 引脚速度(低,中,高速),流水灯对引脚速度没有特殊要求,低速即可。⑥ 用户标签,在初始化实现后,对应的引脚和端口会有对应的宏定义在main.h中生成。 5、下载模式配置(肯定要配置) DAP-LinkJTAG依照本人所应用的下载(调式)形式进行对应的配置,我是用的是DAP所以配置为Serial Wire,如果不进行配置进行一次下载后,再一次下程序可能就下不进了,留神留神肯定要配置。 6、时钟配置这里咱们采纳最简略,最不便,最快捷的配置形式,见下图: 点击Clock Configuration后对应②中填入72(F407最高频率为168,不同芯片能够配置的最高频率不同)按下回车。 而后失去如下模式,示意时钟曾经配置胜利: 7、项目管理配置以及代码生成在相熟Cubemx软件框架中曾经细细的讲过这部分内容,咱们须要配置的是项目名称,我的项目保留地位,所用IDE的配置 抉择如下两个选项后,点击右上角的GNEERATE CODE按钮,产生代码。 在进行大型项目编写时,经常要求模块化编程,以及对文件大小有所束缚,个别状况下,抉择以上两个选项能够大大的优化编程。在代码量和代码可读性上有所优化。8、编写业务代码关上main.c文件,咱们能够看到如下界面,咱们在应用Cubemx和Keil开发时,所有的用户代码都是写入在 /* USER CODE BEGIN 1 *//* USER CODE END 1 */这样的正文之间,而且经常在不必的区间编写不同的代码。 API咱们再来看看GPIO模块对应的API函数: /* 初始化和删除初始化函数, HAL_GPIO_Init:cubemx生成代码后主动调用初始化函数 HAL_GPIO_DeInit:解除初始化,不想应用时能够被动应用*/void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);/* 操作GPIO的函数,包含读取,写入,翻转,加锁,中断服务函数以及回调函数 HAL_GPIO_ReadPin:读取引脚电平 HAL_GPIO_WritePin:写入引脚电平 HAL_GPIO_TogglePin:翻转引脚电平 HAL_GPIO_LockPin:所以引脚以后电平,将无奈扭转 HAL_GPIO_EXTI_IRQHandler:引脚的中断服务函数 HAL_GPIO_EXTI_Callback:引脚的中断回调函数 */GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);main( )函数咱们能够在main.h中看到之前定义的用户标签,如下: ...
【二】零根底上手HAL库之—相熟Cubemx软件的框架在实现了Hal库开发环境的搭建后,就是要开始应用此工具了,那么咱们必定有一些必要的筹备工作要进行,本节内容仅仅解说软件各个模块的性能,想要用好软件,第一步当然就是大体上的相熟软件创立一个工程。如果还没搭建好环境能够看上一节内容: 2.1 后期筹备Stm32f103Rct6芯片的板子Keil5软件以及上一节内容装置好的开发环境(Cubemx、JRE、Hal包) 点击关上软件能够失去如下界面: ①创立,关上,导入我的项目等操作。②调整窗口大小,以及输入值的一些操作。③更新软件,以及一些文献资料查找。④通过芯片类型创立一个我的项目⑤通过板子类型创立一个我的项目⑥查找一个例程 点击主界面④框选的局部,开始通过芯片类型创立一个工程 ①搜寻所须要的芯片。②内核类型。③芯片系列。(F1/F4/F7等等)④芯片系列细分。(103/100等等)⑤芯片封装类型。⑥其余(芯片的主频,Rom,Flash等等)⑦外设资源。⑧通过筛选后的芯片。 双击选取芯片界面中⑧框选的局部,一个依据芯片类型的工程创立实现,能够看到如下工程界面,此界面大抵能够分成如下四块用于配置芯片。 ①Categories(品种)依据外设的品种来抉择配置的外设。②A->Z(序号)依据外设首字母序号来抉择配置的外设。③外设模式的配置以及此模式下的具体参数配置。④芯片引脚配置详情,绿色示意曾经应用 点击Clock Configuaration进入时钟树配置界面,学过stm32规范库的都应该很革除这个时钟树,咱们先晓得有这个货色,下一节会讲到这部分内容。 点击Project Manager进入我的项目配置界面, ①项目名称(尽量不要用中文)。 ②我的项目地址(留神留神!!!! 肯定不要有中文名门路)。 ③编译代码所用的IDE开发(我个别应用Keil进行)。 ④堆栈大小的设置(个别应用默认即可)。 ⑤芯片类型(肯定要和所应用的芯片对应上)。 ⑥固件包版本(不同的版本会有些许差别,但最新版准没错)。 点击Code Generator 选项卡,后可见如下界面: ①复制所有的软件包到工程中。②复制所需的软件包到工程中。③不复制文件,从固件包地位援用相干的文件到工程中(见上一步固件包的默认地位)。④依照类型给每个外设设立独立的源文件(.c)和头文件(.h)。⑤从新生成时备份上一次产生的文件(有谬误时,能够还原上一版本)。⑥从新生成时保留用户区的代码。⑦从新生成时删除原有的文件。⑧没应用的引脚设置为模仿状态引脚。 ①驱动库抉择能够是HAL库和LL库,不晓得二者区别能够看之前内容②是否调用初始化函数,也就是你代码是否调用对应初始化函数。 比方:你GPIO项勾选Not Generate Function Call,你main.c函数中就不会调用MX\_GPIO\_Init这个函数。 ③是否申明为static动态内链接函数,也就勾选时在外文件不能调用此函数。
本文将介绍 FFmpeg 如何播放 RTSP/Webcam/File 流。流程如下: RTSP/Webcam/File > FFmpeg open and decode to BGR/YUV > OpenCV/OpenGL display代码: https://github.com/ikuokuo/rt..., 子模块 rtsp-local-playerFFmpeg 筹备git clone https://github.com/ikuokuo/rtsp-wasm-player.gitcd rtsp-wasm-playerexport MY_ROOT=`pwd`# ffmpeg: https://ffmpeg.org/git clone --depth 1 -b n4.4 https://git.ffmpeg.org/ffmpeg.git $MY_ROOT/3rdparty/source/ffmpegcd $MY_ROOT/3rdparty/source/ffmpeg./configure --prefix=$MY_ROOT/3rdparty/ffmpeg-4.4 \--enable-gpl --enable-version3 \--disable-programs --disable-doc --disable-everything \--enable-decoder=h264 --enable-parser=h264 \--enable-decoder=hevc --enable-parser=hevc \--enable-hwaccel=h264_nvdec --enable-hwaccel=hevc_nvdec \--enable-demuxer=rtsp \--enable-demuxer=rawvideo --enable-decoder=rawvideo --enable-indev=v4l2 \--enable-protocol=filemake -j`nproc`make installln -s ffmpeg-4.4 $MY_ROOT/3rdparty/ffmpeg./configure 手动抉择了:解码 h264,hevc 、解封装 rtsp,rawvideo 、及协定 file ,以反对 RTSP/Webcam/File 流。 其中, Webcam 因于 Linux ,故用的 v4l2。 Windows 可用 dshow, macOS 可用 avfoundation ,详见 Capture/Webcam。 ...
C#学习路线指南破费了几天工夫整顿了C#学习路线图,能够说是纯家养C#程序员成长路线布局,在这里分享下,心愿能够帮忙到想从事C#开发的同学们。自己经历尚浅,有些知识点可能分享不正确,心愿宽广网友评论区补充,我会同步修改更新。题记最近本人始终在想,一个初学者刚开始应该学些什么,该学习到哪种水平找工作才不成问题,能力在做我的项目时熟能生巧不被Bug所困住。 是不是每一个初学者在刚开始学习任何一门编程语言的时候,都会有这样的疑难,都感觉无从下手,没有脉络。反正我过后的确有这样的困惑,在刚开始接触编程的时候,过后幸好有实验室学长和老师的倡议,才缩小了试错老本,能够把更多工夫都破费在正确的事件上,学习效率才会高。 依据我过后的学习状况和当初目前市场上的行情来剖析,列举出新手学习C#编程语言路线指南,心愿对你有帮忙。 计算机根底如果你是本科生的话,在校期间,应该会学习计算机根底,C语言,计算机组成原理,操作系统等对于计算机的课程,我心愿你好好学习,尤其是C语言,它是一种特地容易入门上手和理解编程思维的语言,是面向过程式的开发,首先你得学会应用它的语法做一些简略的逻辑运算,其次能够依据肯定的需要,实现局部非凡算法的演练(这些对你前期学习面向对象开发语言有很大的帮忙)。 如果没有开设这些课程,那么就须要你花工夫去互联网上找相干视频去学习。 举荐书籍: 《C语言程序设计》(第2版)谭浩强版本《大话数据结构》 程杰《数据结构C语言版》 严蔚敏版《深刻了解计算机系统》(美)布赖恩特举荐视频: 【C语言】《带你学C带你飞》(https://www.bilibili.com/vide...)数据结构与算法根底(青岛大学-王卓)(数据结构与算法根底(青岛大学-王卓)_哔哩哔哩_bilibili)[C语言形容]《数据结构和算法》(【C语言形容】《数据结构和算法》_哔哩哔哩_bilibili)C#基础知识根底语法你如果后期学习过C语言的话,那么学习C#基础知识就会感觉很容易,特地好了解。在这个环节你须要认真学习基础知识,你得学会在保障语法不报错的状况下,利用基础知识写一些简略的算法;得纯熟理解语法,应用切当并能够实现一些逻辑代码。 (根底语法、数据类型、变量、循环、办法、数组、汇合、判断、类等) 面向对象开发C语言是面向过程的开发语言,而C#是面对对象的开发语言,在应用C语言写逻辑代码时都是能够依照肯定的顺利逻辑将其实现就OK,而C#就不一样,你在学习语法,实现业务逻辑时得思考程序的可扩展性,鲁棒性等。 你得领有抽象思维,能够将事实中无生命体征的物品进行拟物解决,将它们进行更为形象的提炼,汇总为某个非凡类,让其这个类代表这类物品,并让其领有这个物品的所有属性和行为,这就是面向对象开发。 (继承、封装、多态、接口、抽象类等) 这一部分的学习比拟形象,学会领有抽象思维,让本人能够更好的了解这些知识点。 基础知识进阶这里你将会学习一些高端的编程玩法,可能短期内,你无奈应用到,然而它们更多体现在你的职业前期,当你理论应用框架或者浏览源码的过程中,都会遇到这些知识点,并有可能被它困住。 (Linq、泛型、委托、事件、多线程、Socket开发) 举荐书籍: C#图解教程(第五版) (5星举荐入门教程)C#入门经典 (外面的进阶方面常识介绍的很)CLR via C# (很厚很厚的书,能够看做工具书应用)举荐视频 C#语言入门详解(https://www.bilibili.com/vide...)C#入门到精通(传智播客黑马程序员课程(https://www.bilibili.com/vide...))举荐在线学习网址 菜鸟教程(https://www.runoob.com/csharp...)微软官网文档(https://docs.microsoft.com/zh...) 前端技术在编写后端代码的过程中,咱们或多或少的须要对页面进行解决,那么理解和会应用前端语言是至关重要的。对于前端技术,只须要把握到会用的水平就能够,看得懂,会用,会写就OK。刚开始学习的话就是网页三剑客 HTML+CSS+JavaScript,这里我举荐看网上的菜鸟教程和视频来学习,这样很容易上手,做出货色后就很有满足感。 其次就是Jquery+AJAX技术,它是很老的技术了,目前市面上比拟风行的Vue、Angular、React等框架都是对底层Jquery+AJAX的高度封装实现,所以咱们把握理解Jquery和AJAX技术很有必要。 因为前端技术这几年倒退特地快,各种新技术如雨后春笋般涌现,所以咱们大家在学习的过程中要抓住主次,别什么都学习。这里我举荐大家学习Vue.js(3.0),目前很多公司都在应用它,咱们学好它,对当前找工作会有很大的帮忙。至于其余的前端技术,没有必要全都去学习,等到公司或者说当前我的项目须要应用它的时候,咱们再学习也不迟。置信我,那些货色都是很容易上手的,原理都是通的,就看你会不会活学活用了。 前面可能还得学习Node.js、NPM等用于公布和构建的技术,这些也能够等理论应用的时候再去学习。 举荐书籍 《JavaScript DOM 编程艺术》《JavaScript权威指南》(犀牛书)《深入浅出Node》《HTTP权威指南》举荐教程 菜鸟教程JavaScript教程(JavaScript教程 - 廖雪峰的官方网站 (liaoxuefeng.com))Vue官网文档(介绍 | Vue.js (vuejs.org))数据库对于数据库的学习,须要先从最根本的SQL语句开始,这个能够通过看菜鸟教程来理解和相熟,SQL语句学习的过程中要装置好数据库环境来实际操作,不能只死记硬背公式,要学会死记硬背,在实在的数据库中来体验。 目前数据库分为关系型数据库和非关系型数据库。 关系型数据库:Sql Server、MySQL、Oracle、SQLite、DB2等。 非关系型数据库:MongoDb、Redis、NoSql等。 对于咱们未来从事编码开发的人员来说,我感觉你首先先学会应用Sql Server和MySql数据库,其余的数据库在当前的开发中再去学习也不迟。 就拿Sql Server来说,你学习完后在前面的C#高级常识中,应用ASP.NET MVC或者Web Api技术可立马实际操作利用数据库。你能够先写一些简略的CRUD,实现一些根本的性能,这对你前期的成长至关重要。 举荐书籍 《SQL必知必会》《Microsoft SQL Server 2008技术底细:T-SQL查问》举荐教程 SQL教程(SQL教程 - 廖雪峰的官方网站 (liaoxuefeng.com))菜鸟教程(https://www.runoob.com/csharp...)SQL Server文章目录(https://www.cnblogs.com/Carey...)C#高级常识对于高级常识局部这个环节,你能够试着做出点理论我的项目进去,不论是ADO.NET 还是ASP.NET MVC或者ASP.NET Web API,你都能够先跟着书本或者视频教程,先让本人会用继而模拟看他人的写法,本人也入手进行实现。你能够在Github或者Gitee中找到一些开源的我的项目,比方图书管理系统、博客零碎,论坛等一系列应用高级常识来实现的我的项目,你须要先浏览代码,看他人是如何实现的,从而在仿照他人的实现过程来依照本人的想法编写本人的零碎。(通过这样的实战我的项目,你能够疾速的上手实现,学着并尝试做一个全栈开发,本人一个人从前端到后端再到公布上线,体验下整个软件我的项目的残缺生命周期)。 ADO.NET: 它是最原始的数据库拜访技术,目前根本都不应用,市面上公司都是应用封装好的ORM,然而你也得学习它。你得理解它是如何和数据库进行交换的,尝试着本人做这些步奏,看是否能顺利连贯数据库,并进行一系列的CRUD操作。 ...
Summary 1)const润饰的变量是只读的,实质还是变量。(不管值是否扭转) 2)调配空间:const润饰的局部变量在栈上调配空间。const润饰的全局变量在全局数据区调配空间。(这两点印证了const润饰的依然是变量,因为在内存中仍然会调配空间) 3)const只在编译期有用,在运行期无用。(示意:const润饰的变量,在编译的时候不能放在赋值符号左侧,然而在运行期,就能够通过指针扭转该变量的值) 4)在古代C语言编译器中,批改const全局变量会导致程序解体。因为古代C语言编译器,会将具备全局生命期的const变量放在只读存储区,改了只读存储区的内容,造成解体。规范C语言编译器依然将全局生命期的const变量放在全局数据区,所以是能够扭转的。 5)const润饰函数参数,示意在函数体内不心愿扭转参数的值(形参不能够呈现在赋值符号左侧)。 6)const润饰函数返回值,示意返回值不可扭转,多用于返回指针的状况(指针指向的值不可扭转)。 7)字符串字面量存储于只读存储区,必须应用const char*来援用字符串字面量(这样在编译期会将批改只读存储区字面量的谬误报进去);如果只是应用了char *,此时编译的过,然而运行时就会遇到段谬误。 8)volatile通知编译器每次都必须去内存中去变量值;volatile多用于多线程环境中(变量可能在其余线程或其余中央被扭转) const和volatile1、const1.1 const润饰变量const int const_global_i = 1; // const全局变量,全局生命期int main(){ const static int const_static_i = 2; // static局部变量,全局生命期 const int const_local_i = 3; // 一般局部变量 // const_local_i = 30; // error,在编译期不容许呈现在赋值符号左侧 int* p = NULL; // 规范C语法须要将要应用的变量先申明。 p = (int*)&const_global_i; *p = 10; printf("const_global_i = %d\n", const_global_i); p = (int*)&const_static_i; *p = 20; printf("const_statici = %d\n", const_static_i); p = (int*)&const_local_i; *p = 30; printf("const_local_i = %d\n", const_local_i); return 0;}在bcc编译器下(规范C语言编译器),这段代码可失常编译,测试的后果为三个变量的值均被扭转,阐明,在规范C编译器下,const定义的所有只读变量的值都能够扭转。在gcc编译器下(扩大C编译器),这段代码能够编译的过,然而上面这两处赋值语句,在运行时都会产生段谬误。阐明,在古代C语言编译器下,const定义的只读变量如果是全局生命期(如全局变量、static变量),则会将该变量放到只读存储区,批改了就会段谬误。 ...
【二】零根底上手HAL库之—环境搭建JRE :Java运行环境STM32CubeMx:意法半导体官网图形化配置软件HAL库:软件库包2.1 JRE装置STM32Cubemx是基于Java开发,并须要Java环境能力反对失常运行。 官网地址:Java下载官网地址百度云地址:百度云地址提取码见文章尾公众号,上面是一个Java装置的步骤。 关上网站后点击如下选项 关上刚下载实现的软件,见得如下页面 点击装置后,见得如下页面,点击更改抉择本人想要装置的门路 点击下一步后进行装置,吃点货色,坐等进度条拉满 点击敞开,装置胜利 2.2 Cubemx装置Cubemx就是用于图形化配置stm32的,由意法半导体业余团队开发的一个软件。 官网地址 : 官网地址百度云地址 : 百度云地址提取码见文章尾公众号 点击Get software获取软件 下载实现后,关上安装包如下,点击next: 抉择对应的选项,点击next 点击红框中的抉择,点击next 抉择对应的装置门路后,点击next 点击next进行装置 期待装置实现即可 2.3 装置Hal包 关上刚刚下载完的Cubemx后点击上面按钮: 选取对应型号芯片的包,点击install now 即可 期待装置实现即可 最初祝贺你,入门Hal库的第一步就这样实现啦,上面就是"爽"的时候了,曾经急不可待了吧,来吧,咱们先点灯。
Summary1) 个别工程开发中须要禁用goto语句,不同的编译器对goto语句的错误处理可能不同。 2)在C语言中,如果函数没写返回值,则默认返回值类型是int;如果函数没有写参数列表,则函数承受任意数量的参数。因而,如果函数没有返回值,必须显式申明返回值类型为void;如果函数没有参数,则必须申明参数列表为void。 3)void是一种根底类型,但不是根底数据类型,所以无奈用来定义变量(C语言没规定void是多大内存空间的别名)。 4)对于规范C语言标准,不容许sizeof(void)这种写法,如bcc编译器;对于扩大C语言,可能容许sizeof(void)这种写法,如gcc编译器,输入为1。 5)void*指针的次要作用是:用于接管任意类型的指针。在C语言中,void* 和 type*能够互相转换;在C++中,void*能够作为左值接管任意类型的指针,作为右值时,必须进行强制类型转换。 goto和void1、gotogoto语句带有很浓重的汇编语言个性,可能跳转到指定的语句,如同汇编语言的跳转指令。在工程实际中,个别都禁用“goto”。goto会毁坏程序的构造,带来意想不到的谬误。 // 代码示例:以下代码输入什么?int main(){goto End; int i = 0;End: printf("i = %d\n", i); return 0;}gcc编译器:编译谬误error,提醒i变量的初始化被跳过了。vs编译器:编译正告warning,提醒i未初始化,然而编出了可执行程序。运行的后果是随机值,意料之外的谬误!2、void2.1 void润饰返回值和参数在C语言中: 如果函数没有返回值,那么应该将其申明为void如果函数没有参数,应该申明其参数为void留神, C语言中: 如果函数没写返回值,默认的返回值类型是int如果函数没有申明参数列表,默认承受任意多参数 f(){}int main(){ int i = f(1, 2, 3); // ok return 0;}2.2 是否存在void类型的变量?论断:不存在void类型的变量 void是一种根底类型,但不是根底数据类型;C语言中没有定义void到底是多大内存的别名,所以也无奈定义对应的变量。 int main(){ void var; // error void arr[5]; // error return 0;}小贴士: ANSI C:规范C语言标准,如bcc编译器扩大C:在ANSI C的根底上进行了扩大,如gcc编译器 // void类型有大小么?int main(){ prinft("%d\n", sizeof(void)); return 0;}gcc编译器,Demo能够编过,输入为1bcc编译器,编译谬误,提醒void是不容许的类型。2.3 void类型的指针在C语言中,对指针类型的查看不那么严格,所以void类型的指针能够和任意数据类型的指针进行互相转换。 // test.cint main(){ int* p = nullptr; void* pv = p; // ok char* pc = pv; // ok return 0;}在C++语言中,对类型进行了加强,共事也兼容了C语言中的写法。void能够作为左值承受任意类型的指针,然而,void作为右值时,就必须进行强制类型转换。 ...
旧文章,遗记了发到 segmentfault: 原文本文记录一次排查工作,顺便提供对于 C++ RVO 的一句话总结。 根本问题问题引发在我的 message-queue 开发过程中,有这么样的代码: inline std::optional<T> pop_front() { lock l(_m); _cv.wait(l, [this] { return _abort || !_data.empty(); }); if (_abort) return {}; // std::nullopt; auto r = std::move(_data.back()); _data.pop_back(); return r;}它的用意足够简略,就是从 std::deque _data 中弹出一个队尾元素。只是因为队列可能为空,所以有一个阻塞式的条件变量来期待队列中有有效值(前三行)。 依照直觉,我牵强附会地写完了这段代码。 随即我编写了一个测试片段: void test_mq() { hicc::pool::threaded_message_queue<hicc::debug::X> xmq; hicc::debug::X x1("aa"); { xmq.emplace_back(std::move(x1)); hicc_debug(" xmq.emplace_back(std::move(x1)) DONE. AND, xmq.pop_front() ..."); std::optional<hicc::debug::X> vv = xmq.pop_front(); hicc_debug("vv (%p): '%s'", (void *) &vv, vv.value().c_str()); } hicc_debug("x1 (%p): '%s'", (void *) &x1, x1.c_str());}hicc::debug::X 是一个专门用来调试 RVO,In-place construction,Copy Elision 等等个性的工具类,它平平无奇,只不过是在若干地位埋点冰打印 stdout 文字而已,这能够让咱们直观察看到哪些行为实际上产生了。 ...
旧文章,遗记了发在segmentfault:原文谬误示范push_back 这么写是错的: template<class T> class threaded_message_queue { public: using lock = std::unique_lock<std::mutex>; void push_back(T t) { { lock l(_m); _data.push_back(std::move(t)); } _cv.notify_one(); } }};//入参 T t 导致了调用者在这里会产生一次长期对象 TMP 的复制,稍后在函数退出点处 TMP 还会被隐式析构。所以这个写法不是良构。 至于函数体中的 std::move(t) 也就是聊胜于无了,它并不会让 t 少掉 TMP 的复制,仅仅只是少掉了 t 到 _data 的一次复制而已。 正确工作做模板类开发时,常常会遇到 push_back 的这种场景。 正确的 push_back 应该蕴含左值复制和右值挪动两种语义,一般来说像是这样子: template<class T> class threaded_message_queue { public: using lock = std::unique_lock<std::mutex>; void emplace_back(T &&t) { { lock l(_m); _data.template emplace_back(std::move(t)); } _cv.notify_one(); } void push_back(T const &t) { { lock l(_m); _data.push_back(t); } _cv.notify_one(); } }};留神右值加上挪动语义才是一对搭配。T t 和挪动语义在一起只是一种错觉。 ...
【一】零根底上手HAL库之—为什么要抉择HAL库寄存器:(Snippets)ST规范库:(Standard Peripheral Libraries)LL库:(Low Layer Libraries)HAL库:(Hardware Abstraction Layer Libraries)1.1 寄存器操作寄存器,不同于其余三种库的操作形式,实用于同C51中的开发模式,间接操作低层的寄存器,自身只对寄存器的地址进行了封装映射关系。因为在C51这样的单片机中,只有大量的寄存器须要操作,应用比较简单。但在stm32寄存器极多,须要记忆十分难,每次进行开发都须要看开发手册,开发周期增大,所以个别很少人应用,当然谋求效率另说。但不得不说在寄存器操作中的位带操作是真的挺好用的,但须要留神不同的芯片的IO的映射关系的区别(能够参考晚点原子的sys.h文件)。 实用于谋求高效率的人群,但LL库能够齐全代替寄存器,效率并驾齐驱。 1.2 ST规范库 官网首先推出的一套由低层寄存器对STM32芯片封装而来的一个库,包含规范器件外设的器件驱动文件。基本上用C语言实现,仅仅应用了大量的汇编。库思维是面向过程的,面向过程的了解就是一个函数的执行仅仅实现一件事件。库自身的运作也更靠近于寄存器,仅仅是在寄存器上的一层封装操作。 实用于初学stm32的人群,能够让学者通过库理解局部的低层寄存器。 1.3 LL库LL 库的一大特点就是奇妙的使用 C 语言的动态、内联函数来间接操作寄存器,操作硬件低层的库。LL库是配合CubeMx软件开发时,更靠近与硬件的一个库,能够独自应用LL库开发,也能够配合HAL库进行开发。 实用于谋求效率,同时须要放慢开发周期的人群。 1.4 HAL库 HAL库的一大特点就是面向对象,置信很多人对面向对象曾经再相熟不过了,操作形象层的库。通过几层的封装,HAL库将每个外设封装为一个对象,应用CubeMx软件配置后,只须要操作对象句柄就能够应用对应的外设,操作非常简单。当然谋求效率能够配合LL库进行开发。 实用于不执着与低层代码实现,而在业务逻辑代码上有更高要求的人群,不须要理解太多的硬件,开发简略,但效率有所落后。 1.5 规范库与Cube LL,Cube HAL,寄存器的效率比照图 1.6 总结两个方面,效率和开发周期。须要进行取舍越形象的库开发周期越短,但效率往往越低。但对应当代社会的需要,倒退十分迅速,设施改新换代十分之快,所以往往须要在短周期内进行开发,谋求先机,分得市场红利,这也是官网为什么力推HAL库而停更ST规范库的起因之一,对于我集体学过了规范库上手HAL之后,就一个字 “爽”。至于为什么爽,一起来看看吧~当他人还在配置低层驱动的时候,你曾经能够开始你的业务逻辑代码的编写了,就是这么爽!!!!
C语言是一门通用计算机编程语言,利用宽泛。C语言的设计指标是提供一种能以繁难的形式编译、解决低级存储器、产生大量的机器码以及不须要任何运行环境反对便能运行的编程语言。 只管C语言提供了许多低级解决的性能,但依然放弃着良好跨平台的个性,以一个规范规格写出的C语言程序可在许多电脑平台上进行编译,甚至蕴含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。 20世纪80年代,为了防止各开发厂商用的C语言语法产生差别,由美国国家标准局为C语言订定了一套残缺的国际标准语法,称为ANSI C,作为C语言最后的规范。 C语言嵌入式零碎编程注意事项不同于个别模式的软件编程,嵌入式零碎编程建设在特定的硬件平台上,势必要求其编程语言具备较强的硬件间接操作能力。无疑,汇编语言具备这样的特质。然而,归因于汇编语言开发过程的复杂性,它并不是嵌入式零碎开发的个别抉择。而与之相比,C语言--一种“高级的低级”语言,则成为嵌入式零碎开发的最佳抉择。笔者在嵌入式零碎我的项目的开发过程中,一次又一次感触到C语言的精妙,沉醉于C语言给嵌入式开发带来的便当。 大多数嵌入式零碎的硬件平台。它包含两局部: (1) 以通用处理器为核心的协定解决模块,用于网络控制协议的解决; (2) 以数字信号处理器(DSP)为核心的信号处理模块,用于调制、解调和数/模信号转换。 本文的探讨次要围绕以通用处理器为核心的协定解决模块进行,因为它更多地牵涉到具体的C语言编程技巧。而DSP编程则重点关注具体的数字信号处理算法,次要波及通信畛域的常识,不是本文的探讨重点。 着眼于探讨广泛的嵌入式零碎C编程技巧,零碎的协定解决模块没有抉择特地的CPU,嵌入式零碎学习加意义气呜呜吧久林就易,而是抉择了家喻户晓的CPU芯片--80186,每一位学习过《微机原理》的读者都应该对此芯片有一个根本的意识,且对其指令集比拟相熟。80186的字长是16位,能够寻址到的内存空间为1MB,只有实地址模式。C语言编译生成的指针为32位(双字),高16位为段地址,低16位为段内编译,一段最多64KB。 协定解决模块中的FLASH和RAM简直是每个嵌入式零碎的必备设施,前者用于存储程序,后者则是程序运行时指令及数据的寄存地位。零碎所抉择的FLASH和RAM的位宽都为16位,与CPU统一。 实时钟芯片能够为零碎定时,给出以后的年、月、日及具体工夫(小时、分、秒及毫秒),能够设定其通过一段时间即向CPU提出中断或设定报警工夫到来时向CPU提出中断(相似闹钟性能)。 NVRAM(非易失去性RAM)具备掉电不失落数据的个性,能够用于保留零碎的设置信息,譬如网络协议参数等。在零碎掉电或重新启动后,依然能够读取先前的设置信息。其位宽为8位,比CPU字长小。文章特意抉择一个与CPU字长不统一的存储芯片,为后文中一节的探讨创造条件。 UART则实现CPU并行数据传输与RS-232串行数据传输的转换,它能够在接管到[1~MAX_BUFFER]字节后向CPU提出中断,MAX_BUFFER为UART芯片存储接管到字节的最大缓冲区。 键盘控制器和显示控制器则实现零碎人机界面的管制。 以上提供的是一个较齐备的嵌入式零碎硬件架构,理论的零碎可能蕴含更少的外设。之所以抉择一个齐备的零碎,是为了后文更全面的探讨嵌入式零碎C语言编程技巧的方方面面,所有设施都会成为后文的剖析指标。 嵌入式零碎须要良好的软件开发环境的反对,因为嵌入式零碎的指标机资源受限,不可能在其上建设宏大、简单的开发环境,因此其开发环境和指标运行环境互相拆散。因而,嵌入式应用软件的开发方式个别是,在宿主机(Host)上建设开发环境,进行应用程序编码和穿插编译,而后宿主机同指标机(Target)建设连贯,将应用程序下载到指标机上进行穿插调试,通过调试和优化,最初将应用程序固化到指标机中理论运行。 CAD-UL是实用于x86处理器的嵌入式应用软件开发环境,它运行在Windows操作系统之上,可生成x86处理器的指标代码并通过PC机的COM口(RS-232串口)或以太网口下载到指标机上运行。其驻留于指标机FLASH存储器中的monitor程序能够监控宿主机Windows调试平台上的用户调试指令,获取CPU寄存器的值及指标机存储空间、I/O空间的内容。 后续章节将从软件架构、内存操作、屏幕操作、键盘操作、性能优化等多方面论述C语言嵌入式零碎的编程技巧。软件架构是一个宏观概念,与具体硬件的分割不大;内存操作次要波及零碎中的FLASH、RAM和NVRAM芯片;屏幕操作则波及显示控制器和实时钟;键盘操作次要波及键盘控制器;性能优化则给出一些具体的减小程序工夫、空间耗费的技巧。 在咱们的修炼旅途中将通过25个关口,这些关口主分为两类,一类是技巧型,有很强的适用性;一类则是常识型,在实践上有些意义。 So, let’s go. C语言嵌入式零碎编程注意事项之软件架构篇模块划分的“划”是布局的意思,意指怎么正当的将一个很大的软件划分为一系列性能独立的局部单干实现零碎的需要。 模块划分 模块划分的“划”是布局的意思,意指怎么正当的将一个很大的软件划分为一系列性能独立的局部单干实现零碎的需要。C语言作为一种结构化的程序设计语言,在模块的划分上次要根据性能(依性能进行划分在面向对象设计中成为一个谬误,牛顿定律遇到了相对论),C语言模块化程序设计需了解如下概念: (1) 模块即是一个.c文件和一个.h文件的联合,头文件(.h)中是对于该模块接口的申明; (2) 某模块提供给其它模块调用的内部函数及数据需在.h中文件中冠以extern关键字申明; (3) 模块内的函数和全局变量需在.c文件结尾冠以staTIc关键字申明; (4) 永远不要在.h文件中定义变量!定义变量和申明变量的区别在于定义会产生内存调配的操作,是汇编阶段的概念;而申明则只是通知蕴含该申明的模块在连贯阶段从其它模块寻找内部函数和变量。如: /module1.h/ int a = 5; / 在模块1的.h文件中定义int a / /module1 .c/ include “module1.h”/ 在模块1中蕴含模块1的.h文件 //module2 .c/ #i nclude “module1.h” / 在模块2中蕴含模块1的.h文件 / /module3 .c/ #i nclude “module1.h” / 在模块3中蕴含模块1的.h文件 / 以上程序的后果是在模块1、2、3中都定义了整型变量a,a在不同的模块中对应不同的地址单元,这个世界上从来不须要这样的程序。正确的做法是: /module1.h/ extern int a; / 在模块1的.h文件中申明int a / ...
本文不适宜初学者,你应该曾经对 Factory 模式有所理解,你对于 C++17 的常见个性也不生疏。Factory Pattern回顾下工厂模式,并思考实现一个通用的工厂模板类以达成业务端低代码的指标。 FROM: Refactoring Guru实践Factory 模式是 Creational Patterns 中的一种。 创立型模式所谓的创立型模式,次要蕴含这几种: Abstract factory 形象工厂模式。一组具备同一主题的对象创立工厂被独自封装起来,而多组不同的对象工厂具备对立形象的创立接口,该形象的创立接口即为形象工厂。Builder 构建者模式。目标是为了结构出一个简单对象,有必要将其蕴含的各种属性分门别类地顺次设定,以便令结构过程易于治理。个别采纳链式调用形式,而在属性结构结束之后,一个发令枪(例如 build())将指挥该简单对象被最终结构为实例。Factory method 古典的工厂模式。工厂办法模式。个别有一个动态的 create() 以便创建对象的实例。Prototype 原型模式。通过复制一个已有类型的形式创立新实例,即 clone()Singleton 单例模式。全局只有一个对象实例。以上为 GoF 的经典划分。不过,简直三十年过来了,当初还有更多的创立型模式: 和 Builder 略有不同的 生成器模式(generator pattern)提早初始化模式。Kotlin 中的 lazyinit 关键字是它的一种语言性反对。对象池模式。如果对象的创立相当耗时或者耗资源,那么一次性提前创立一组对象,须要时取用,用完后放回池子里。等等。工厂模式本文中提到工厂模式时,泛指 Factory Method,Factory,Abstract Factory 等等。综合起来看,工厂模式是指借助于一个厂房 Factory 来创立产品 Product 的某种编程范式。其目标是为了让消费者(业务端代码)不去关怀产品怎么制作进去的(简略地通过 Factory.create() 就可能失去),只需间接关怀怎么应用产品就行了。 从另一角度看,工厂模式具备这样的个性:我晓得工厂可能造清污产品,然而肥皂还是香皂就无所谓了,我想有点香味的,工厂就将会造香皂给我,我没有要求的,工厂造给我的可能就是肥皂了。也就是说,接口是那个样子,但工厂将会造出来的肯定合乎这个接口约定,但到底是那个类的实例就不肯定了(通常会由创立参数来决定)。 在编程实际上,工厂模式总是随同着一个产品根类,这是一个接口类,通常蕴含一系列形象办法作为业务端操作接口。对于简单的产品族来说则在该接口类的根底上会持续派生若干品类。 Factory Method最古典的工厂模式是 Factory Method,由 GoF 首次阐述的一种 Pattern。 以 Point 为例, namespace hicc::dp::factory::inner { class Transport { public: virtual ~Transport() {} virtual void deliver() = 0; }; class Trunk : public Transport { float x, y; public: explicit Trunk(double x_, double y_) { x = (float) x_, y = (float) y_; } explicit Trunk(float x_, float y_) { x = x_, y = y_; } ~Trunk() = default; void deliver() override { printf("Trunk::deliver()\n"); } friend std::ostream &operator<<(std::ostream &os, const Trunk &o) { return os << "x: " << o.x << " y: " << o.y; } static std::unique_ptr<Trunk> create(float x_, float y_) { return std::make_unique<Trunk>(x_, y_); } }; class Ship : public Transport { float x, y; public: explicit Ship(double x_, double y_) { x = (float) x_, y = (float) y_; } explicit Ship(float x_, float y_) { x = x_, y = y_; } ~Ship() = default; void deliver() override { printf("Ship::deliver()\n"); } friend std::ostream &operator<<(std::ostream &os, const Ship &o) { return os << "x: " << o.x << " y: " << o.y; } static std::unique_ptr<Ship> create(float r_, float theta_) { return std::make_unique<Ship>(r_ * cos(theta_), r_ * sin(theta_)); } };} // namespace hicc::dp::factory::innervoid test_factory_inner() { using namespace hicc::dp::factory::inner; auto p1 = create_transport<Trunk>(3.1, 4.2); std::cout << *p1.get() << '\n'; p1->deliver(); auto p2 = create_transport<Ship>(3.1, 4.2); std::cout << *p2.get() << '\n'; p2->deliver(); auto p3 = Ship::create(3.1, 4.2); std::cout << *p3.get() << '\n'; p3->deliver();}依照古典的表述,工厂办法模式倡议应用非凡的工厂办法代替对于对象构造函数的间接调用 (即应用 new运算符)。 不必放心, 对象仍将通过 new运算符创立, 只是该运算符改在工厂办法中调用罢了。 工厂办法返回的对象通常被称作 “产品”。 ...
前言:大家好,我是周杰伦 这里整合了一下之前本人学习软件手工脱壳的一些笔记和脱文,心愿能给新学软件逆向和脱壳的童鞋们一点帮忙。 1 一些概念1.1 加壳加壳的全称应该是可执行程序资源压缩,是爱护文件的罕用伎俩。加壳过的程序能够间接运行,然而不能查看源代码。要通过脱壳才能够查看源代码。 加壳是利用非凡的算法,对EXE、DLL文件里的资源进行压缩、加密。相似WINZIP 的成果,只不过这个压缩之后的文件,能够独立运行,解压过程齐全荫蔽,都在内存中实现。它们附加在原程序上通过Windows加载器载入内存后,先于原始程序执行,失去控制权,执行过程中对原始程序进行解密、还原,还原实现后再把控制权交还给原始程序,执行原来的代码局部。加上外壳后,原始程序代码在磁盘文件中个别是以加密后的模式存在的,只在执行时在内存中还原,这样就能够比拟无效地避免破解者对程序文件的非法批改,同时也能够避免程序被动态反编译。 壳的类型通常分为压缩壳和加密壳两类。压缩壳的特点是减小软件体积大小,加密爱护不是重点。加密壳品种比拟多,不同的壳侧重点不同,一些壳单纯爱护程序,另一些壳提供额定的性能,如提供注册机制、应用次数、工夫限度等。 1.2 OEPOEP:(Original Entry Point),程序的入口点。软件加壳个别暗藏了程序实在的OEP(或者用了假的OEP), 咱们须要寻找程序真正的OEP,才能够实现脱壳。 个别加壳程序在应用Ollydbg等动静调试工具时,会停在壳的预处理块。即处在对于程序原始代码块的解压或解密操作之前,在运行完程序自脱壳模块后,会停留在程序加壳之前的OEP地位,此时是dump程序的最佳时期。脱壳时在实在OEP处下int3断点,就能够捕捉到程序代码段完全恢复的状态。因而,寻找加壳程序的正确OEP,也成了手动脱壳时的第一要务。 1.3 IATIAT:(Import Address Table),导入地址表。因为导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL中。当PE文件被装入内存的时候,Windows装载器才将DLL 装入,并将调用导入函数的指令和函数理论所处的地址分割起来(动静连贯),这操作就须要导入表实现。其中导入地址表就批示函数理论地址。少数加壳软件在运行时会重建导入地址表,因而获取加壳程序正确的导入地址表也是手动脱壳操作中的一个关键问题。 2 一些脱壳办法2.1单步跟踪法单步跟踪法的原理就是通过Ollydbg的单步(F8)、单步进入(F7)和运行到(F4)性能,残缺走过程序的自脱壳过程,跳过一些循环复原代码的片段,并用单步进入确保程序不会略过OEP。这样能够在软件主动脱壳模块运行结束后,达到OEP,并dump程序。 2.2 ESP定律法ESP定律法是脱壳的利器,是利用频率最高的脱壳办法之一。 ESP定律的原理在于程序中堆栈均衡的正当利用。因为在程序自解密或者自解压过程中,不少壳会先将以后寄存器内容压栈,如应用pushad,在解压完结后,会将之前的寄存器值出栈,如应用popad。因而在寄存器出栈时,往往程序代码被主动复原,此时硬件断点触发。而后在程序以后地位,只须要少许单步跟踪,就很容易达到正确的OEP地位。 2.3内存镜像法(二次断点法)内存镜像法是在加壳程序被加载时,通过OD的ALT+M快捷键,进入到程序虚拟内存区段。而后通过加两次内存一次性断点,达到程序正确OEP的地位。 内存镜像法的原理在于对于程序资源段和代码段下断点,个别程序自解压或者自解密时,会首先拜访资源段获取所需资源,而后在主动脱壳实现后,转回程序代码段。这时候下内存一次性断点,程序就会停在OEP处。 2.4一步达到OEP所谓的一步达到OEP的脱壳办法,是依据所脱壳的特色,寻找其间隔OEP最近的一处汇编指令,而后下int3断点,在程序走到OEP的时候dump程序。如一些压缩壳往往popad指令间隔OEP或者Magic Jump特地近,因而应用Ollydbg的搜寻性能,能够搜寻壳的特色汇编代码,达到一步断点达到OEP的成果。 2.5最初一次异样法最初一次异样法的原理是,程序在自解压或自解密过程中,可能会触发无数次的异样。如果能定位到最初一次程序异样的地位,可能就会很靠近主动脱壳实现地位。当初最初一次异样法脱壳能够利用Ollydbg的异样计数器插件,先记录异样数目,而后从新载入,主动停在最初一次异样处。 2.6 模仿跟踪法模仿跟踪法的原理就是应用Ollydbg下条件断点,SFX相当于是一个自解压段,在自解压段完结时(eip的值转到代码段时),曾经间隔OEP很近,然而这种跟踪办法会比拟耗时。 2.7 “SFX”法“SFX”法利用了Ollydbg自带的OEP寻找性能,能够抉择间接让程序停在OD找到的OEP处,此时自解压曾经实现,能够间接dump程序。 3一些脱壳实际上面给出整顿的应用以上办法,本人尝试手动脱这几种罕用壳的脱壳笔记。 3.1UPX脱壳笔记首先进行侦壳: 首先把程序扔到OllyIce外面能够看到: 而后这里尝试应用ESP定理:即在ESP第一次扭转时,对ESP的地址设置硬件字拜访断点,这样能够在代码被UPX算法还原之后,跳转到程序的失常入口处。 而后F5运行,并没有间接到跳转到程序入口处的大跳地位,然而能够看到UPX的大跳就在眼前: 所以被还原后的程序入口点就是0x00445151(通过单步往下走,F4略过往回走的循环语句,也能够看到这个大跳的地位。)接下来走到大跳地位,跳到失常程序入口处: 而后去掉硬件断点,并应用LoadPE的dump性能dump目标程序: 先修改映像大小,而后再抉择残缺脱壳,这样能够失去第一步dump的程序,而后再应用ImportREC修复dump程序的OEP,OEP的信息通过OD自带的dump性能查问或者间接填45151: 将正确的入口地址填入ImportREC中,而后主动搜寻IAT信息: 而后点击获取输出表失去修改IAT之后的程序函数输出表,而后再点击显示有效函数,欢快地发现没有有效函数,那么就能够间接修复转存文件了。 抉择刚刚第一步dump下来的转储文件进行修复,修复实现之后脱壳实现: 这里对于压缩壳UPX,间接应用了ESP定律,能够很不便找到OEP并dump程序。 4.2 tElock脱壳笔记这里脱的是一个tElock的壳: 1、先应用最简略的最初一次异样法:首先把程序扔到OllyIce外面设置OD调试选项中的异样选项, 仅保留内存非法拜访异样,而后应用异样计数器插件,在应用前要清空断点设置: 等到程序失常运行后,从新加载程序,再抉择第二步,停在最初一次异样之前: 而后用Alt+M转到内存窗口,对主程序code段下内存断点,SHIFT+F9执行: 这样程序就中断在了正确的OEP处,能够抉择从模块中删除剖析以显示失常剖析的汇编代码。而后应用LoadPE dump程序,并修改程序映像大小。然而在应用ImportREC v1.6F Fix版,输出正确的OEP,获取函数输出表信息时,会发现有效的指针。应用办法一修复后,再应用办法三能够齐全修复 ...
导读:“域名劫持是互联网攻打的一种形式,通过攻打域名解析服务器(DNS),或伪造域名解析服务器(DNS)的办法,把指标网站域名解析到谬误的 IP 地址从而实现用户无法访问指标网站的目标 , 或者蓄意 / 歹意要求用户拜访指定 IP 地址(网站)的目标 。 ”(以上内容引自 「 域名劫持 」百度百科 ) 。网易云信 IM SDK 作为一款 ToB 产品,撑持着各种三方业务的发展。面对各种简单的网络环境,DNS 劫持与 DNS 净化时有发生,那么在咱们提供服务的过程中应该如何防止此类事变的产生呢? 文|郝魁 网易云信资深 C++ 开发工程师 技术是一把双刃剑,在大侠手中定国安邦,在鼠辈手中祸国殃民。 “域名劫持”尽管带了“劫持”二字,但在此环境中实属中性词汇。例如对于一些非法网站的拜访,能够通过 DNS 服务把相应的域名解析到不可拜访的 IP 地址,以阻止对于该非法网站的拜访并给予正告等。在网易云信即时通信产品运维过程中,已经产生过服务域名“netease.im”被居心叵测的人或组织歹意劫持的事件,导致接入网易云信 IM SDK 的利用无奈失常登录,给客户以及客户的用户造成影响。为了弄清楚这种事变是怎么产生的,咱们来剖析一下网易云信 IM SDK 的登录过程:从流程上来看,在“更新 LBS”的节点上,如果产生 DNS 劫持,在拜访网易云信 LBS 服务时有可能超时或者拿到了谬误的应答,导致 IM SDK 无奈获取失常的 Link 服务器地址及端口,如何防止此类事变的产生呢?本文将围绕网易云信端侧服务高可用技术计划以及高可用组件实现计划进行具体分享。 一、如何预防 DNS 劫持 通常对于域名被劫持后,咱们能够采纳以下几种形式:上述几种形式,都是在产生劫持后采取的计划,无论是从服务提供侧还是服务应用侧来说都不够灵便,为了解决这些问题,提前预防事变的产生,咱们次要采纳以下两种计划:其中接入了 HttpDNS 服务的计划升高了所有场景下域名被劫持的危险。 1、LocalDNS域名劫持 域名劫持始终是困扰许多开发者的问题之一,其体现为域名 A 应该返回的 DNS 解析后果 IP1 被歹意替换为了 IP2,导致 A 的拜访失败或拜访了一个不平安的站点,常见域名劫持形式有以下几种: 黑客入侵宽带路由器,篡改终端用户 LocalDNS,并指向伪造 LocalDNS,通过管制 LocalDNS 的逻辑返回谬误的 IP 信息进行域名劫持。 ...
类型零碎设计师 之前想聊聊规范库和类型零碎(C# 类型零碎),但当初想想,不如聊聊类型零碎设计师(类型零碎设计师是怎么炼成的) 或者这才是经验过很大的考量之后,设计进去的类型零碎的样子 参考https://www.tutorialsteacher.... +对 C# type 和 .NET type 的由来的把握(C#, C# type, .NET type, C# 规范库, mscorlib.dll)(C# 类库 其实是来自于他 ‘面对的问题’ 是怎么的(他关注的问题就是桌面软件开发,他可能预知到了为 .NET 平台开发软件会遇到什么问题 所以才把 C# 设计得这么好))https://www.javatpoint.com/csharp-data-typeshttps://www.c-sharpcorner.com/UploadFile/dacca2/difference-between-data-type-in-C-Sharp-and-data-type-in-net/https://docs.microsoft.com/en-us/dotnet/api/system.eventhandler-1参考 Immutable Collections 是 .NET Framework 的根底外围类库之一https://www.cnblogs.com/xishu... System.Collections.Concurrent Classes ( vs Java JUC )https://docs.microsoft.com/en... EventHandler<TEventArgs> Delegatehttps://docs.microsoft.com/en... 语言反对、 app 程序员、SDK 的关系如果以程序软件的模式失去一个小成绩,那么须要有 operation blueprint 和 business logic blueprint 。开发者必然是 app 程序员,而在两个层面上都要用到编程语言提供反对。 如果一个语言有幸提供反对给一个弱小的 operation architecture 那么它肯定是一个好语言如果一个语言提供反对给一个还不错的 LOB developer 用的 framework architecture 那么它不肯定是一个好语言(但肯定是有 framework developer 的功绩,封装师的功绩: 比方对于 web app C/S 架构, 封装师不仅封装出很根底的形象模型接口 还提供了现成的可令人调用的函数, 参考 WSGI 接口 和 Django framework / Rack 接口 和 WEBRick 服务器 和 ror 框架 12122) 4 5 )参考https://www.zhihu.com/questio...封装师给出的 Laravel 提供了大量助手函数长得像PHP内置一样,在框架中外围中大量应用这些自定义全局辅助办法,这是尽量复用和优雅的后果评估:二者解决问题的层面不同,二者关注的问题不同;封装师生产类型零碎设计师给出的类型零碎https://segmentfault.com/a/11...类型零碎设计师 ...
1.问题阐明返回一个长期对象的援用是不平安的,因为当来到作用域,这个长期对象会马上析构,所以函数返回的总是悬挂援用(空悬援用),应用这个援用是一个未定义行为,会导致程序解体。2.问题剖析看上面这段代码: const int&retRef(){ return 1180;} const int&k = retRef();std::cout<<k<<std::endl;此时打印k,就是一个未定义行为,因为,1180的援用所指对象曾经被销毁了(援用其实只是常量指针)。这个很容易辨认。然而,这里实际上隐含一个只能指针的坑。上面这段代码其实也是不平安的: const std::shared_ptr<int>& retRef(){ return nullptr;}智能指针也是一个对象,返回nullptr,编译器会结构一个长期对象,并用拷贝构造函数吧null复制进去,此时,会产生和下面那段代码一样的问题。如果此时,调用这个函数,其实也是未定义行为。
一、什么是Http ClientHttp协定,是全互联网独特的语言,而Http Client,能够说是咱们须要从互联网世界获取数据的最根本办法,它实质上是一个URL到一个网页的转换过程。而有了根本的Http客户端性能,再搭配上咱们想要的规定和策略,上至内容检索下至数据分析都能够实现了。 继上一次介绍用Workflow能够10行C++代码实现一个高性能Http服务器,明天持续给大家用C++实现一个高性能的Http客户端也同样很简略! // [http_client.cc]#include "stdio.h"#include "workflow/HttpMessage.h"#include "workflow/WFTaskFactory.h"int main (int argc, char *argv[]){ const char *url = "https://github.com/sogou/workflow"; WFHttpTask *task = WFTaskFactory::create_http_task (url, 2, 3, [](WFHttpTask * task) { fprintf(stderr, "%s %s %s\r\n", task->get_resp()->get_http_version(), task->get_resp()->get_status_code(), task->get_resp()->get_reason_phrase()); }); task->start(); getchar(); // press "Enter" to end. return 0;}只有装置好了Workflow,以上代码即能够通过以下命令编译出一个简略的http_client: g++ -o http_client http_client.cc --std=c++11 -lworkflow -lssl -lcrypto -lpthread依据Http协定,咱们执行这个可执行程序 ./http_client,就会失去以下内容: HTTP/1.1 200 OK同理,咱们还能够通过其余api来取得返回的其余Http header和Http body,所有内容都在这个 WFHttpTask 中。而因为Workflow是个异步调度框架,因而这个工作收回之后,不会阻塞以后线程,外加外部自带的连贯复用,从根本上保障了咱们的Http Client的高性能。 接下来给大家具体解说一下原理~ 二、申请的过程1. 创立Http工作上述demo能够看到,申请是通过发动一个Workflow的Http异步工作来实现的,创立工作的接口如下: WFHttpTask *create_http_task(const std::string& url, int redirect_max, int retry_max, http_callback_t callback);第一个参数就是咱们要申请的URL。对应的,在一开始的示例中,咱们的重定向次数redirect_max是2次,而重试次数retry_max是3次。第四个参数是一个回调函数,示例中咱们用了一个lambda,因为Workflow的工作都是异步的,因而咱们处理结果这件事件是被动告诉咱们的,后果回来就会调起这个回调函数,格局如下: ...
1.问题自身:在语法上,C++容许在析构中抛出异样,但异样解决,自身就是为了解决结构失败的状况。当一个对象结构失败而抛出异样时,咱们该当利用析构函数,清理有效对象,并回收其资源,这是使程序强壮的重要伎俩。2.问题剖析:从1可知,构造函数显然是能够抛出异样的。进一步剖析,在析构函数中抛出异样,会呈现哪些状况:(1)对象失常完结,在析构时,触发析构函数中的异样,打印异样信息,退出。在这种状况下,抛出异样并没有什么问题,析构函数的异样会被里面捕捉;当然,如果里面的程序没有持续开释残余的资源,可能会造成内存泄露。(2)对象来到作用域之前,抛出异样,此时会调用析构函数,析构函数再抛出异样,此时之前的异样就不能被捕捉了,而且会造成程序crash。3.问题总结:那么,是否就不能够在析构函数中应用异样?答案显然是否,在某些状况下,咱们不可避免的要在析构函数中应用异样,使程序仍然强壮的要害的关键在于,不要让异样逃离析构函数。能够用try catch吞掉异样。当然,更好的实际是,就不要在析构函数中应用异样。
源码合集 鸣谢qtcn飞腾青云一去二三里雨田哥问题反馈邮箱:1508539502@qq.com滑动窗口具体阐明 背景切换 背景图放弃 网格窗口具体阐明 动静曲线具体阐明 多彩仪表盘具体阐明 通用仪表盘具体阐明 水波进度条具体阐明 圆弧进度条具体阐明 滚动横幅 Banner具体阐明 音讯弹窗对话框具体阐明 繁难对话框具体阐明 IconHelper 字体图标辅助类具体阐明 公共辅助类具体阐明
仓库 桌面背景切换 桌面背景放弃 个性反对小部件追加反对获取小部件的数量反对获取以后小部件的索引和小局部地址反对查问给定的小部件索引反对设置首页小部件反对小部件移除反对背景图片是否可滑动切换 !!!#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QWidget>#include <QScrollArea>#include <QHBoxLayout>#include <QScroller>#include <QTimer>#include <QPropertyAnimation>/* 滑动窗口 * 1. 反对小部件追加 * 2. 反对获取小部件的数量 * 3. 反对获取以后小部件的索引和小局部地址 * 4. 反对查问给定的小部件索引 * 5. 反对设置首页小部件 * 6. 反对小部件移除 * 7. 反对背景图片是否可滑动切换 !!! * SliderWidget(QWidget *parent = nullptr, const QString &picture = ""); * 当 picture 传入图片地址时, 背景图片将始终放弃不可切换 */class SliderWidget : public QWidget{ Q_OBJECTpublic: SliderWidget(QWidget *parent = nullptr, const QString &picture = ""); ~SliderWidget(); int addWidget(QWidget *page); int count() const; int currentIndex() const; QWidget *currentWidget() const; int indexOf(QWidget *widget) const;public slots: void setCurrentIndex(int index); void setCurrentWidget(QWidget *widget); void removeWidget(int index); void removeWidget(QWidget *widget);protected: void resizeEvent(QResizeEvent *ev) override;protected slots: void onScrollerStateChanged(QScroller::State state); void onSliderTimerTimeout();signals: void currentChanged(int index);private: void initUi(); void initCtrl(); void updateIndicator(int index);private: QWidget *m_pMainWidget; QScrollArea *m_pScrollArea; QHBoxLayout *m_pMainLayout; QWidget *m_pIndicatorWidget; QHBoxLayout *m_pIndicatorLayout; QPropertyAnimation *m_pAnimation; QTimer *m_pSlidingTimer; bool m_sliderFlag = false; int m_sliderPressedValue = 0; int m_sliderReleaseValue = 0; int m_curPageIndex = -1; int m_nextPageIndex = -1; QString m_background;};#endif // MAINWINDOW_H
1.问题形容: 在做一个分布式存储的服务器。服务器在某些异样敞开的场景下,会在析构时,报错:pure virtual method called, terminate called without an active exception。服务器析构时,会调用上面这行代码:`tx->Commit();` // std::shared_ptr<SeGhBacken> tx;其申明为:基类:class SeGhDB {public: virtual int Commit();}申明:class SeGhBacken : public SeGhDB { int Commit() override;}之后服务器会段谬误,并报错:pure virtual method called, terminate called without an active exception2.问题剖析: 段谬误,根本都是内存问题。个别是指针被置空,或是指针所指对象曾经生效,此处也不例外。这个谬误的起因为:在运行期,子类对象被以前析构,或者基本未被结构进去。调用这个函数实现的时候,编译器就调用到了基类的虚函数,如果基类的虚函数未实现,则会报上述谬误。3.问题解决 对象提前被析构或未被结构,通常是因为多线程环境下,线程调度引起的。遇到上述问题,应该首先查看线程内资源的结构和回收情况。题主遇到的这个问题,正是由此导致。在本我的项目线程池实现中,遇到退出信号,会摈弃后续工作,导致对象没有被正确结构,从而造成了这个问题,在调整了线程池参数后,问题失去解决。4.总结: 此问题个别解决方案:(1)应用new和delete治理对象内存,本人手动管制对象生命周期;(2)线程内实现对象全生命周期治理,在一个线程内实现对象的结构、调用和析构;(3)对于同一个线程内的对象,要恪守谁申请,谁开释的内存治理标准;(4)禁止多级继承。
线程间同步形式 引言互斥锁 探索底层,实现一个锁 测试并加锁(TAS)比拟并替换(CAS)另一个问题,过多的自旋?回到互斥锁信号量 有名信号量无名信号量总结条件变量 什么是条件变量?相干函数 1. 初始化2. 期待条件3. 告诉条件用法与思考实际——读写者锁文章已收录至我的仓库:Java学习笔记与收费书籍分享 线程间同步形式引言不同线程间对临界区资源的拜访可能会引起常见的并发问题,咱们心愿线程原子式的执行一系列指令,但因为单处理器上的中断,咱们必须想一些其余方法以同步各线程,本文就来介绍一些线程间的同步形式。 互斥锁互斥锁(又名互斥量),强调的是资源的拜访互斥:互斥锁是用在多线程多任务互斥的,当一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其余的线程才开始能够利用这个资源。 int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);留神了解trylock函数,这与一般的lock不一样,一般的lock函数在资源被锁住时会被梗塞,直到锁被开释。 trylock函数是非阻塞调用模式,也就是说如果互斥量没被锁住,trylock函数将把互斥量加锁,并取得对共享资源的拜访权限; 如果互斥量被锁住了,trylock函数将不会阻塞期待而间接返回EBUSY,示意共享资源处于忙状态,这样就能够防止死锁或饿死等一些极其状况产生。 探索底层,实现一个锁实现一个锁必须须要硬件的反对,因为咱们必须要保障锁也是并发平安的,这就须要硬件反对以保障锁外部是原子实现的。 很容易想到保护一个全局变量flag,当该变量为0时,容许线程加锁,并设置flag为1;否则,线程必须挂起期待,直到flag为0. typedef struct lock_t { int flag;}lock_t;void init(lock_t &mutex) { mutex->flag = 0;}void lock(lock_t &mutex) { while (mutex->flag == 1) {;} //自旋期待变量为0才可进入 mutex->flag = 1;}void unlock(lock_t &mutex) { mutex->flag = 0;}这是基于软件的初步实现,初始化变量为0,线程自旋期待变量为0才可进入,这看上去仿佛并没有什么故障,然而认真思考,这是有问题的: 当线程恰好通过while断定时陷入中断,此时并未设置flag为1,另一个线程闯入,此时flag依然为0,通过while断定进入临界区,此时中断,回到原线程,原线程继续执行,也进入临界区,这就造成了同步问题。 在while循环中,仅仅设置mutex->flag == 1是不够的,只管他是一个原语,咱们必须有更多的代码,同时,当咱们引入更多代码时,咱们必须保障这些代码也是原子的,这就意味着咱们须要硬件的反对。 咱们思考下面代码为什么会失败?起因是当退出while循环时,在这一时刻flag依然为0,这就给了其余线程抢入临界区的机会。 解决办法也很直观 —— 在退出while时,借助硬件反对保障flag被设置为1。 测试并加锁(TAS)咱们编写如下函数: int TestAndSet(int *old_ptr, int new) { int old = *old_ptr; *old_ptr = new; return old;}同时从新设置while循环: ...
Summary1)do...while语句先执行后判断,循环体至多执行一次;while语句先判断后执行,循环体可能不执行;for语句先判断后执行,相比while更简洁(因为for的一行里包含了循环变量初始化、条件判断、批改循环变量三个因素)。 2)break和continue的区别: break示意终止循环的执行continue示意终止本次循环,进入下次循环执行循环语句循环语句的根本工作形式: 通过条件表达式判断是否执行循环体条件表达式遵循if语句表达式的准则1、do ... while 2、while 3、for 4、do... while示例应用do while(0);配合break语句,实现对动静申请内存的开释。 int func(int n){ int i = 0; int ret = 0; int* p = (int*)malloc(sizeof(int) * n); do { if(NULL == p) break; if(n < 5) break; if(n > 100) break; for(i = 0; i<n; i++) { p[i] = i; printf("%d\n", p[i]); } ret = 1; } while(0); free(p); return ret;}本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。
宏定义中应用可变参数C99 减少了宏定义中应用可变参数的反对。用法为在定义中通过'...'定义可变参数,前面通过__VA_ARGS__援用。如上面定义DGB宏,在log中主动减少'DEBUG'。 #define DBG(format, ...) printf("DEBUG: " #format "\n", ##__VA_ARGS__)宏应用例 DBG("%d - %s", a,b);问题如下不指定参数应用,则会编译失败 DBG("hahaha");: error: expected expression before ‘)’ token#define DBG(format, ...) printf("DEBUG: " #format "\n", __VA_ARGS__) ^解决办法 #define DBG(format, ...) printf("DEBUG: " #format "\n", ##__VA_ARGS__) ^^参考 https://stackoverflow.com/que...http://gcc.gnu.org/onlinedocs...
1. 门路 应用 std::filesystem::path 类来示意文件门路. #include <iostream>#include <filesystem>namespace fs = std::filesystem;int main(){ fs::path p1("E:\\"); fs::path p2(p1 / "Program Files (x86)" / "Tesseract-OCR"); std::cout << p2; // 判断门路是否存在 if (fs::exists(p2)) { std::cout << " exists.\n"; } else { std::cout << " does not exists.\n"; }}"E:\\Program Files (x86)\\Tesseract-OCR" exists.获取、切换当前工作门路: #include <iostream>#include <filesystem>namespace fs = std::filesystem;int main(){ // 打印当前工作门路 std::cout << fs::current_path() << '\n'; // 切换当前工作门路 fs::current_path(fs::path("C:\\")); std::cout << fs::current_path() << '\n';}"E:\\repos\\data-structure\\data-structure""C:\\"2. 文件信息 ...
首先阐明两点,一是集体是业余爱好桌面开发(单纯以找工作去学客户端开发,这种行为不太举荐哦),二是WPF的款式比Winform真的要好看很多(因为自定义的很自在)。 根本除了3D之类的图像和相干动画成果没有涵盖进去 其余的罕用的根本都涵盖了在外面。 外面次要分类为根本控件、布局控件、视图控件、条目控件、其余UI(弄 的理论我的项目可用的界面),其实分类并不是很精准,只是便于辨别而已。 局部截图: 自定义的播放器: ps: 这播放根本的性能都实现了,没有清空列表的性能,页面缩放的自适应根本没问题。 源码:https://gitee.com/ten-ken/Ken_WPF_UI 欢送关注我的公众号:程序员ken,程序之路,让咱们一起摸索,共同进步。
1. Boost.Asio 线程池 下载:https://sourceforge.net/proje... VS 中应用:我的项目 - 属性 - VC目录 - 蕴含目录,增加 YourPath\asio-1.18.2\include 官网文档:https://www.boost.org/doc/lib... #include <iostream>#include <atomic>#include <asio.hpp>using namespace std::literals;static std::atomic_uint32_t count = 0;int main(){ // 两个线程 asio::thread_pool pool(2); auto work = []() { std::this_thread::sleep_for(1ns); count++; }; int n = 1000; for (int i = 0; i < n; i++) { // 提交工作 asio::post(pool, work); } // 期待所有线程执行实现 pool.join(); std::cout << "count = " << count << '\n';}count = 1000其余操作: ...
1. 简介 局部特化(partial specialization)容许为给定类别的模板实参自定义类模板和变量模板. 2. 语法 template<parameter-list> class ClassName<argument-list> declaration其中,argument-list 能够蕴含具体的类型(对应类型参数)、具体的值(对应非类型参数),也能够蕴含在 <parameter-list> 中指定的模板形参. 如, // primary templatetemplate<class T1, class T2, int I>class A {};// #1: partial specialization where T2 is a pointer to T1template<class T, int I>class A<T, T*, I> {};// #2: partial specialization where T1 is a pointertemplate<class T, class T2, int I>class A<T*, T2, I> {};// #3: partial specialization where T1 is int, I is 5, and T2 is a pointertemplate<class T>class A<int, T*, 5> {}; // #4: partial specialization where T2 is a pointertemplate<class X, class T, int I>class A<X, T*, I> {};具体例子:移除援用 ...
1. 简介 齐全特化(full specialization)也叫显式特化(explict specialization). 它容许为给定的模板实参自定义模板代码. 2. 语法 template <> declaration必须实例化所有的模板形参. 如, #include <iostream>template <typename T>struct IsVoid{ static constexpr bool value = false;};template <>struct IsVoid<void>{ static constexpr bool value = true;};int main(){ std::cout << std::boolalpha << IsVoid<int>::value << '\n'; // false std::cout << std::boolalpha << IsVoid<void>::value << '\n'; // true}3. 什么时候不须要加上 template <>? (1)如果你正在显式特化一个模板,则应该加上 template <>. template<class T> class Array { /*...*/ };// primary templatetemplate<class T> void sort(Array<T>& v);// specialization for T = inttemplate<> void sort(Array<int>&);template<typename T>struct A { struct B {} template<class U> struct C { };};template <>struct A<int> { void f(int);};(2)如果你正在为一个已显式特化的类模板定义成员,则不须要加上 template <>. ...
1. 简介 模板参数包是承受零个或多个模板参数(非类型、类型或模板)的模板参数。函数参数包是承受零个或多个函数参数的函数参数。 2. 语法 (1) 非类型模板参数包: Type... Args此处 Type 为具体的类型,如 int. (2) 类型模板参数包: typename... Types(3) 模板模板参数包:(即模板形参是另一个模板) template <parameter-list> typename... Types(4) 函数参数包: Types... Args(5) 参数包扩大: pattern...将 pattern 扩大为以逗号分隔的列表(蕴含0或多个模式). 其中,pattern 必须蕴含至多一个参数包. 在类模板中,模板参数包需是最初一个模板形参. 而在函数模板中则不用(指的是在模板形参列表中的地位,而不是函数形参列表),只有可能从函数实参列表推断出参数包即可. template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end template<typename... Ts, typename U, typename=void>void valid(U, Ts...); // OK: can deduce U// void valid(Ts..., U); // Can't be used: Ts... is a non-deduced context in this position从上例中能够看出,即便是在函数模板中,参数包仍须要是函数形参列表的最初一个参数. 简略起见,尽量将参数包放在最初. ...
1. std::forawrd std::forward<T>(arg) 能够实现完满转发,即如果 arg 是一个右值援用,则转发之后后果仍是右值援用;反之,如果 arg 是一个左值援用,则转发之后后果仍是左值援用. #include <iostream>struct BigObject{ char data[1 << 10];};void g(BigObject& o){ std::cout << "lvalue reference\n";}void g(BigObject&& o){ std::cout << "rvalue reference\n";}template <typename T>void f(T&& arg){ g(std::forward<T>(arg));}int main(){ BigObject o; f(o); f(BigObject());}lvalue referencervalue reference2. 为什么须要完满转发? 在函数模板编程中,常有一种场景是应用模板参数去调用另一个函数(如,上例中 f 去调用 g),这时候如果只提供值传递版本会显得效率太低。函数的参数个别会尽可能地设为援用类型,以防止对象拷贝带来的昂扬开销.为了使一个函数既能够承受左值,又能够承受右值,C++11 之前的解决方案是将参数类型设为 const Type&. 但这并不是很不便,如限度了参数是常量.如果函数 g 既提供了左值援用版本和右值援用版本,则最好的状况是函数 f 能够依据参数类型去调用相应版本的 g. 而完满转发正能够满足此要求.3. 援用折叠规定 右值援用和右值援用叠加将失去右值援用;右值援用和左值援用叠加将失去左值援用;左值援用和左值援用叠加将失去左值援用.template <typename T>using TR = T&; // v 的类型TR<int> v; // int&TR<int>& v; // int&TR<int>&& v; // int&template <typename T>using TRR = T&&; // v 的类型TRR<int> v; // int&&TRR<int>& v; // int&TRR<int>&& v; // int&&4. 完满转发的原理 ...
简介 本文基于 Boost.Asio 编写一个 TCP echo 程序. 且应用协程来解决异步逻辑. Asio 下载:https://sourceforge.net/proje... VS 中应用:我的项目 - 属性 - VC目录 - 蕴含目录,增加 YourPath\asio-1.18.2\include 官网文档:https://www.boost.org/doc/lib... 1. 头文件及全局变量 #include <iostream>#include <thread>#include <mutex>#include <chrono>#include <string>#include <asio.hpp>#include <asio/ts/buffer.hpp>#include <asio/ts/internet.hpp>using asio::ip::tcp;static std::mutex coutLock;// 简略起见,此处回显固定的音讯static std::string Message("hello, asio!");static size_t MsgLength = Message.size();2. TcpServer // 异步class TcpServer{public: TcpServer(asio::io_context& context, uint16_t port) : m_context(context), m_acceptor(context, tcp::endpoint(tcp::v4(), port)) { waitForConnection(); }private: asio::awaitable<void> waitForConnection() { while (true) { tcp::socket socket(m_context); co_await m_acceptor.async_accept(socket, asio::use_awaitable); coutLock.lock(); std::cout << "New Connection From: " << socket.remote_endpoint() << '\n'; coutLock.unlock(); // 创立协程,解决新到的连贯 asio::co_spawn(socket.get_executor(), handleConnection(std::move(socket)), asio::detached); } } static asio::awaitable<void> handleConnection(tcp::socket&& socket) { std::vector<uint8_t> buffer(MsgLength); try { while (true) { std::size_t n = co_await asio::async_read(socket, asio::buffer(buffer), asio::use_awaitable); co_await asio::async_write(socket, asio::buffer(buffer, n), asio::use_awaitable); } } catch (const std::exception& e) { std::lock_guard<std::mutex> lock(coutLock); std::cerr << "Handle Connection Error: " << e.what() << '\n'; } }private: asio::io_context& m_context; tcp::acceptor m_acceptor;};此时不必再传入回调函数,而是将回调函数替换为 asio::use_awaitable,而后再 co_await async_xxx(...); 即可. ...
协程就是一个可挂起可复原执行的函数. C++ 中的协程是无栈式的:当协程被挂起时,它将返回到调用者,且复原执行所须要的相干数据并不保留在栈上. 协程的益处是: 容许以串行的形式编写异步执行的代码,而无需显式应用回调;能够实现惰性计算(如,生成器).1. 协程 一个函数只有蕴含如下之一便是协程: 应用 co_await 运算符来挂起执行; size_t n = co_await socket.async_read_some(buffer(data));应用 co_yield 关键字来挂起执行,并返回一个值; co_yield n++;应用 co_return 关键字来完结执行,并可选地返回一个值. co_return;co_return 7;2. co_await co_await 运算符的操作数必须是 Awaitable. 一个对象如果有如下3个成员函数,则它就是 Awaitable: await_ready:协程开始时会调用此函数,如果返回 true,示意你想得到的后果曾经失去了,协程不须要执行了. 所以大部分状况这个函数的实现是要返回 false.await_suspend:执行 co_await Awaitable 时便会执行此函数,它会挂起协程. 该函数会传入一个 coroutine_handle 类型的参数,这是一个由编译器生成的变量. 在此函数中调用 handle.resume() 就能够复原协程.await_resume:复原协程运行时便会调用此函数. 这个函数的返回值就是 co_await 运算符的返回值.3. promise_type 设协程的返回值类型为 Task,则 Task 必须蕴含一个外部类 promise_type,且 promise_type 须要蕴含如下成员函数: struct promise_type { /* 结构协程的返回值 */ Task get_return_object() { return Task{}; } /* 在执行协程体之前调用 */ std::suspend_never initial_suspend() { return std::suspend_never{}; } /* 在执行完协程体之后调用 */ std::suspend_never final_suspend() noexcept { return std::suspend_never{}; } /* 执行 co_return; 时调用 只需定义 return_void 或 return_value 其中之一便可 */ void return_void() {} /* 执行 co_return value; 时调用 */ void return_value(T value) { // ... } /* 执行 co_yield value; 时执行 如果不须要用到 co_yield,则无需定义 */ auto yield_value(T value) { // ... return std::suspend_always{}; } /* 异样解决 */ void unhandled_exception() {}};4. 例子1:应用协程来解决异步逻辑 ...
束缚指定了模板实参须要满足的要求,而 concept 则是束缚的命名汇合. 1. concept template <template-parameter-list>concept concept-name = constraint-expression; 尔后 concept-name 能够取代 typename 来申明模板形参以增加束缚.也能够将 concept-name<template-parameter-lis> 作为束缚表达式的一部分. 2. 束缚 束缚能够是一个原子束缚,或两个束缚的逻辑与运算(&&),或两个束缚的逻辑或运算(||). 原子束缚只蕴含一个表达式,且该表达式的值的类型须要为 bool(不能通过类型转换). 如果表达式的值为 true,则满足束缚,否则不满足束缚. template <typename T>requires std::is_arithmetic<T>::valueT add(T x, T y){ return x + y;}int main(){ int i = add(1, 2); double d = add(1.2, 3.4); // error // std::string s(add(std::string("111"), std::string("222")));}此处应用 requires 从句(区别于 requires 表达式)来要求 T 必须满足 std::is_arithmetic<T>::value 为 true. 等价于, template <typename T>concept Arithmetic = std::is_arithmetic<T>::value;template <Arithmetic T>T add(T x, T y){ return x + y;}束缚的逻辑运算: ...
1、背景在视觉我的项目开发过程中碰到了图像显示和ROI矩形框或其余框体的显示的需要,最早我在开发过程中间接将Halcon的显示窗口间接贴在Qt的控件上,这样就省去了图像转换后再绘图的操作(Halcon具备独特的图像格式HObject),然而Halcon没有图层的概念,只有create_drawing_object_circle这些算子能够应用,但这些在图像实时刷新的时候比拟耗时且也没有图层能够操作(Win环境实时成果还行,Linux下较难实现实时成果),采纳Qpixmap显示在UI端,并应用QGraphicsItem来实现自定义的图形显示需要,成果比应用Halcon窗口显示要好很多,本篇就如何实现自定义的QGraphicsItem开发实现各种图形的显示进行开展。 2、成果展现目前依据需要,给出了如下图所示的图形的自定义成果,能够依据须要创立不同形态的图形框: 3、自定义创立同心圆3.1 同心圆的创立首先在创立的同心圆结构类里,有中心点,两个圆半径,以及两个圆上的Edge点(用于拖动扭转圆大小),其类的定义如下 // 同心圆class BConcentricCircle : public BCircle{public: BConcentricCircle(qreal x, qreal y, qreal radius1, qreal radius2, ItemType type); enum { Type = 22}; int type() const { return Type; } void updateOtherRadius(); void setAnotherEdge(QPointF p);protected: virtual QRectF boundingRect() const override; virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;public: QPointF m_another_edge; qreal m_another_radius;};同心圆的策略是在圆的根底上再画一个圆,所以在其构造函数中要先去定义同心圆的几个点-圆心、圆上边缘点。 BConcentricCircle::BConcentricCircle(qreal x, qreal y, qreal radius1, qreal radius2, ItemType type) : BCircle(x, y, radius1, type), m_another_edge(x+radius2*sqrt(2)/2, y+radius2*sqrt(2)/2){ BPointItem *point = new BPointItem(this, m_another_edge, BPointItem::Special); point->setParentItem(this); m_pointList.append(point); m_pointList.setRandColor(); updateOtherRadius();}由构造函数可知,同心圆是由一个圆和另一个圆组成,其蕴含BCircle(x, y, radius1, type),再以圆心和m_another_edge(x+radius2sqrt(2)/2, y+radius2sqrt(2)/2)去画另一个圆。其余局部实现如下: ...
自在存储区和堆placement-new、new operator、operator new 区别demo1:operator new/operator delete应用new 与 malloc自在存储区和堆参考:https://stackoverflow.com/que...参考:https://www.quora.com/What-is...参考:https://www.cnblogs.com/qg-wh...参考:https://blog.csdn.net/nie1994...两种动态内存区域。 heapfree store C++的内存布局堆(malloc/free)、栈、自在存储区(new delete)、全局/动态存储区、常量存储区free store 和 heap的区别(语言背景角度)malloc在堆上调配的内存块,应用 free 开释内存,而 new 所申请的内存则是在自在存储区上,应用 delete 来开释 精确来说free store 对应的是 operator newfree store 和 headp是不同内存区域吗(操作系统角度)多角度探讨异同(编译器上)大部分C++编译器默认应用堆来实现自在存储。也就是说这个时候说它是在堆上还是在自在存储区上都对。(语言背景)堆是C或操作系统概念。自在存储区是C++形象进去的逻辑概念。 C++提出该概念更加强调了这两种内存自在调配不应该被互操作,且C++使得内存自在调配更加灵便(placement-new)。C++中应用 malloc 和 free 在技术上是可能的(偶然有用),但这种做法应该防止或至多是孤立的。(程序角度) free store:对象的生命周期 能够小于 调配存储空间的工夫(即free store能够在不立刻初始化的状况下分配内存,并且能够再不立刻开释内存的状况下销毁对象)heap:尽管默认的全局 new 和 delete (注:特指::operator new)可能由特定编译器依据 malloc 和 free 实现,但堆与闲暇存储不同,并且在一个区域(heap or free store)中调配的内存不能在另一个区域中平安地开释。 从堆(malloc进去的)中的内存能够用placement-new结构和显式析构,这样的话对于free store对象生存期的阐明再这里也实用。placement-new、new operator、operator new参考:https://blog.51cto.com/yiluoh...*参考:https://www.cnblogs.com/luxia...区别new operator/delete operator就是new和delete操作符,而operator new/operator delete是函数。(注:不是很精确,然而很帮忙了解)new operator(操作符) 作用:调配足够的空间(调用 operator new),并且调用对象的构造函数执行过程: 调用operator new分配内存调用构造函数生成类对象返回相应指针重载:不能够被重载new operator与delete operator的行为是不可能也不应该被扭转,这是C++规范作出的承诺operator new(函数) 作用:分配内存,但不执行结构重载:能够重载。 ...
从0搭建在线聊天室,只需4步!聊天室不同于单聊和群聊,是一类集成了多种 IM 性能一体的大规模实时音讯散发零碎。在跨入新世纪的2000年,聊天室作为新型的即时通讯场景迅速在年轻人群体中炽热起来, “网易聊天室”“碧海银沙” 引领了过后语音聊天的热潮。 时至今日,聊天室的势头也不减当年,宽泛使用于在线 KTV 、连麦开黑、主播 PK 、在线秀场等场景,还具备文本、表情、点赞、撒花等互动形式,架起沟通桥梁的同时,玩法也更加多变。 本期云信小课堂就教大家如何疾速集成聊天室,并实现进出聊天室、简略的音讯收发、权限治理等性能。 视频解说https://www.bilibili.com/vide... 集成聊天室-根底篇Step1 初始化并进入聊天室进入聊天室能够有两种形式:以独立模式进入聊天室和非独立模式进入聊天室。 独立模式是指 在IM处于未登录的状况下,进入聊天室的形式,针对只须要聊天室性能的业务场景。请引入NIM_Web_Chatroom_*.js,并通过 Chatroom.getInstance({...}) 来初始化聊天室实例。非独立模式是指 先实现IM登录,再进入聊天室的形式,针对须要IM和聊天室性能的业务场景。请引入 NIM_Web_SDK_*.js,并请通过 SDK.NIM.getInstance({...}) 和SDK.Chatroom.getInstance({...})来别离初始化 IM 和 聊天室的实例这里为了不便演示,抉择独立模式: Step2 收发音讯进入聊天室胜利后能力发送/接管音讯 Step3 权限治理聊天室成员治理和聊天室信息查问 Step4 来到聊天室来到或者切换聊天室 集成聊天室-Demo篇Step1 下载DemoDemo链接:https://github.com/netease-ki...,下载到本地,解压后通过IDE关上,下载期间,率先获取APP_KEY; 如何获取APP_KEY? 如果曾经是网易云信开发者,能够间接从网易云信的管制后盾获取APP_KEY; 如果是第一次体验,则须要返回网易云信官网(http://163.lu/f04GM3)注册云信账号-创立利用-点击App_Key治理即可获取相干信息。 Step2 运行Demo在NIM_Web_Demo-master\webdemo\imNew\js\config.js 外面输出对应环境的appkey,而后在demo根目录下执行npm install 和node app命令 Step3 体验Demo性能关上http://127.0.0.1:8182/webdemo...地址体验demo性能;(倡议先登录账号) demo中实现收发音讯、禁言、拉黑等相干性能的代码在NIM_Web_Demo-master\webdemo\imNew\chatroom\src\js\link.js和room.js外面,大家能够参考API文档自行补充更多功能 总结以上就是网易云信聊天室解决方案 Demo Web 端接入流程的具体讲解,依照如上步骤就能够轻松实现聊天室的搭建。 任何零碎的搭建都不是欲速不达的,尤其是对于娱乐社交产品而言,自研搭建聊天室意味着较高的人力老本、工夫老本和资金老本。而交融通信云服务专家网易云信所提供的聊天室解决方案,可实现疾速上线,1天即可实现集成工作,轻松应答亿级日活的高并发场景。网易云信也会持续打磨底层技术能力、为开发者提供更多简略集成,疾速接入的音视频和即时通讯解决方案。
背景 最近在做一个需要,就是在Job中捕获异样,而后通过邮件或者音讯的形式推送给指定人员,在需要实现的过程中遇到的一个注入问题,觉得很有意思,特此记录。 如果您看了感觉或者曾经有更好的方法,烦请通知我一下,咱们能够独特探讨,如果有中央不对,也请不吝斧正。 遇到的问题 因为不同性能的Job很多,每一个Job中都要实现对产生异样的音讯发送,现有的Job是这样的 为了实现这个需要,也为了当前更好的保护,我筹备用事件委托的模式去实现,将对异样音讯发送的业务逻辑拆出,保障职责繁多,思路如下: 1. 在Job和IJob两头再加一层IExceptionJobHandler接口作为束缚 2. 接口中定义一个发送异样音讯的事件PushException 3. 而后每一个Job在异样时去触发这个事件,内部只须要对立订阅一个事件即可。 到目前这一步是可行的,尽管不是很完满的设计,然而还是比拟清晰的,然而另外一个问题来了,我如何通过Autofac注入这个Job呢,原来间接继承自Ijob当初加了一层。 问题剖析 这个问题花了我整整大概4个小时的工夫去钻研依赖注入的形式还在网上搜了良久,没什么停顿,而后从新梳理了一下框架依赖注入的代码,发现是走错路了,注入Job应用的是Autofac.Extras.Quartz来实现的,这下晓得方向了。 var builder = new ContainerBuilder(); builder.RegisterModule(new QuartzAutofacFactoryModule { ConfigurationProvider = c => schedulerConfig }); 这个QuartzAutofacFactoryModule外面肯定有我想晓得的货色,就在Github上下载了Autofac.Extras.Quartz的源码,关上一看,果然发现了它的真面目 这外面就是对Job依赖注入的,然而没有发现对Job的注册和构建,留神箭头标记的,它有一个AutofacJobFactory类,接管一个ILifetimeScope,而它的作用就是用来构建Job为作用域周期失效的服务实例。 解决的方法 1.下面以及剖析出构建Job服务实例的,是一个叫AutofacJobFactory的工厂,返回一个IJob,那如何把IExceptionJobHandler注入进去,让咱们Job实例领有这个事件呢? 2.因为咱们的IExceptionJobHandler曾经继承了IJob,也就是说这里间接能够把Ijob换成IExceptionJobHandler,那该如何实现呢?继承 3.咱们察看到在AutofacJobFactory构建服务的办法是一个虚办法,所以利用继承咱们新建一个ExtendAutofacJobFactory来重写它将Ijob换成IExceptionJobHandlerpublic class ExtendAutofacJobFactory : AutofacJobFactory { protected override IJob ResolveJobInstance(ILifetimeScope nestedScope, IJobDetail jobDetail) { //验证Job是否派生自IExceptionJobHandler if (typeof(IExceptionJobHandler).IsAssignableFrom(jobDetail.JobType)) { IExceptionJobHandler instance = null; instance = (IExceptionJobHandler)CreateIJob(nestedScope, jobDetail); //注册事件对立解决入口 instance.PushException += pushExceptionMessageManager.Value.ExceptionJobHandler return instance; } else { return base.ResolveJobInstance(nestedScope, jobDetail); } } } 4.咱们须要把重写的结构工厂放入QuartzAutofacFactoryModule,仍然抉择继承的形式去重写QuartzAutofacFactoryModule,而后在加载服务时将服务结构工厂换成咱们本人的ExtendAutofacJobFactory ...
背景开始 故事的开始因为在工作中遇到一个需要去解决加载数据十分慢的问题,要害程序是曾经在生产环境运行了相当长一段时间的,按情理来说是不会有问题,至多从线上运行的稳定性来说。 个人观点 在这里仅作为开发小结记录一下,谁写的和为什么这样写的问题,不在此次记录总结范畴之内,只针对代码做出剖析,如果您看了感觉或者曾经有更好的方法,烦请通知我一下,咱们能够独特探讨,如果有中央不对,也请不吝斧正。 为什么要记录下来? 做任何事件都要有产出,日常开发也一样,除了交付工作,还应有本人的总结,以利于本人后续复盘,产出不在于多少,几行文字、一个表格或者画一幅图。产出也不在于100%能搞懂开发中的每一个问题,把看不懂的总结进去,也是产出。 后续解决的问题或没解决的都在这里总结记录,此前解决过的也会陆续从新总结 找到起因 通过一番调试定位到代码之后,找到了问题次要分2类: 1.反复求值 这个很好了解,就是下面曾经获得雷同操作后果,或者能够获得雷同操作后果存入变量,然而此处并没有这个做而是每一次都执行须要求值的表达式去失去后果,大大的升高了执行效率和进步了资源占用 2.循环嵌套 鉴于波及公司代码,不不便贴出,为了更加活泼的形容具体问题,上面是我依据问题形象出的代码Demo和形容如下: 1).循环500次,对应的list汇合中有50W条数据 2).依据Lambda找到ProductId为213000的数据,而后存入result汇合 依据下面的示例代码能够发现,在循环中,又应用了循环,暂且不思考ForEach是否是多线程或者它和foreach 以及for的执行效率,为什么说又应用了循环呢?通过排查发现导致问题的外围的起因就是外部的Lambda表达式求值,为什么求值慢呢? 特意查看了源码并且查看相干参考资料,因为Lambda就是循环实现的,只不过退出了迭代器和状态机这样一些的提早机制,即便这样它仍然会影响效率。 如果从数据结构的角度形象来剖析,此处工夫复杂度就是O(500*50w) 随着次数增大,执行工夫就是越来越大的增长。3.验证执行工夫 验证执行工夫为:1195毫秒,如果在外层再加一层循环呢? 汇合操作皆可Lambda? 鉴于曾经找到了问题所在,下一步就要着手解决的办法了,第一个问题好解决,扭转一下求值写法就好了。 次要是第二个问题的解决,在解决问题之前失去了一个很重要的的论断 那就是"Lambda并不实用所有中央,反而有时会让你的程序更蹩脚" 看了下面的例子是不是深有体会。 解决方案如何优化lambda(循环)? 咱们要思考问题的实质是在这里对list怎么查找比程序循环快,在数据结构中程序表查找分为三类: 1.程序查找 2.二分查找 3.索引查找 如何抉择对应查找优化,这里的重点就是要将工夫复杂度从O(n)或者O(10x500x50w)转为O(1),毫无疑问在这抉择索引查找,因为依据索引下标拜访元素的工夫复杂度为O(1),那索引查找必然优于程序查找的,接下来如何变为索引查找呢? 咱们不禁会问:"在这里恰好是索引下标跟ProductId对上了能力找到,那如果是乱的呢"? 那就须要变通一下,就是变为字典汇合,依据键找到值 ...
Summary1) 和0值、常量值(立刻数)进行比拟时,须要把立刻数放在比拟符号的左侧,如if(0 == i),避免出现if(i = 0)这样的书写谬误。 2)float型变量因为是不准确的表示法,所以在和0值比拟时不能间接用==,必须定义一个精度。如 #define EOSILON 0.00000001float f = 0.0;if( (-EPSILON <= f) && (f <= EPSILON) ) { // ...}else { //...}3)switch分支语句中,case语句中的值只能是整型或字符型(字符型实质也是一种整型);肯定留神每种case完结的break。 4)if次要实用于按片比拟的状况,简单逻辑;switch次要实用于单值、多分支的状况,离散值。 分支语句1、if ... else ...if语句用于依据条件抉择执行语句else不能独立存在并且总是与他最近的if相匹配else语句后能够连贯其余if语句if语句中零值比拟的留神点: bool型的变量应该间接呈现于条件中进行比拟变量和0值比拟时,0值应该呈现在比拟符号右边float型变量不能间接和0进行比拟,应定义精度2、switch ... case ...switch语句对应单个条件,多个分值的情景case语句分支必须要有break,否则会导致分支重叠default语句有必要加上,以解决非凡状况留神: case语句中的值只能是整型或字符型失常状况放在后面,异常情况放在前面default语句只能用于解决真正的默认状况本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。
回溯算法(back tracking)实际上一个相似枚举的搜寻尝试过程,次要是在搜寻尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的门路。 能够了解为深度优先算法。通过枚举各种类型,依据条件限度,失去符合条件的后果。 个别用于要求出所有可能的后果。例如排列组合等等。回溯算法框架: result = []def backtrack(门路, 抉择列表): if 满足完结条件: result.add(门路) return for 抉择 in 抉择列表: 做抉择 backtrack(门路, 抉择列表) 撤销抉择以全排列为例: 给定一个不含反复数字的数组 nums ,返回其 所有可能的全排列 。你能够 按任意程序 返回答案。int **ret = NULL;int ret_num = 0;int *array = NULL;int array_num = 0;int *used = NULL;void backtrack(int *nums, int numsSize, int index){ if(index == numsSize) { int *temp = malloc(sizeof(int) * numsSize); memcpy(temp, array, sizeof(int) * numsSize); ret[ret_num] = temp; ret_num++; } else { for(int i =0; i < numsSize; i++) { if(used[i] == 1) continue; array[array_num] = nums[i]; array_num++; used[i] = 1; backtrack(nums, numsSize, array_num);//array num 其实就是遍历的深度。 used[i] = 0; array_num--; } }}int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){ if(numsSize <= 0) { *returnSize = 0; return ret; } ret_num = array_num = 0; int total_num = 1; for(int i = 1; i <= numsSize; i++) total_num *= i; used = malloc(sizeof(int) * numsSize); memset(used, 0, sizeof(int) * numsSize); ret = malloc(sizeof(int*) * total_num); array = malloc(sizeof(int) * numsSize); backtrack(nums, numsSize, 0); *returnColumnSizes = malloc(sizeof(int) * total_num); for(int i = 0; i < total_num; i++) (*returnColumnSizes)[i] = numsSize; *returnSize = total_num; return ret;}此处应用一个数组来示意数字是否曾经应用。每次抉择只抉择没有应用的数字。重点是每次backtracking完当前,要复原现场,复原到backtracking的上一步,保障对下次抉择没有影响。 ...
主计算机操作系统:win10虚拟机操作系统:win10 一.创立虚拟机参考:Windows 10 上的 Hyper-V 二.创立虚构网络参考:创立虚构替换网络本次采纳形式一:应用 Hyper-V 管理器创立虚构交换机 关上 Hyper-V 管理器。 执行此操作疾速的办法是按 Windows 按钮或密钥,而后键入“Hyper-V 管理器”。在左窗格中抉择服务器,或在右窗格中单击“连贯到服务器…”。在 Hyper-V 管理器中,从右侧的“操作”菜单中选择虚构交换机管理器... 。在“虚构交换机”局部下,抉择“新建虚构网络交换机”。在“你要创立哪种类型的虚构交换机?”下,抉择“内部”。抉择“创立虚构交换机” 按钮。在“虚构交换机属性”下,为新交换机命名,如“内部 VM 交换机”。在“连贯类型”下,确保已抉择“内部网络”。抉择要与新虚构交换机配对的物理网卡。 这是以物理形式连贯到网络的网卡。抉择“利用” 来创立虚构交换机。 此时,你很可能看到以下音讯。 单击“是”持续。抉择“确定” 敞开虚构交换机管理器窗口。三.禁用平安启动关上 Hyper-v 管理器并抉择创立的虚拟机点击设置。抉择 " 平安 设置"。勾销选中 " 启用平安启动 " 复选框。抉择“确定”以保留设置。四.启动虚拟机确保主计算机和虚拟机能够相互ping通如果不通临时敞开防火墙 五.在虚拟机上启用内核模式调试1.在指标计算机上,以管理员身份关上“命令提示符”窗口。 输出此命令以启用调试(注:hostip是主计算机ip)。 bcdedit /debug onbcdedit /dbgsettings net hostip:192.168.102.113 port:50005bcdedit /set testsigning on六.通过windbg工具连贯虚拟机1.关上命令窗口进入到windbg.exe目录而后输出(windbg.exe -k net:port=50005,key=1ndik7jy60dms.zh9m958erplb.1bkbxwiwz3klu.1gx6qh5e4z61k)key为上一步虚拟机返回的key。或者双击windbg.exe关上后点击file抉择kernel Debug.输出port和key也是一样的。 七.重启虚拟机重启虚拟机后会看到窗口会输入日志,等呈现Debuggee is running...阐明曾经连贯好。 下一篇介绍如何通过VisualStudio2019连贯虚拟机进行双机调试。
leetcode 3. 无反复最长字串题目形容给定一个字符串 s ,请你找出其中不含有反复字符的 最长子串 的长度。 思路用mp记录字符上次呈现的地位,用last记录以后起始地位,遇到反复的字符计算ans,并更新last。留神:这里因为有last限定了字符串的起始地位,因而每次判断时如果mp[s[i]]在last之前,就不必更新。 class Solution {public: int lengthOfLongestSubstring(string s) { vector<int> mp(129, -1); int len = s.length(), ans = 0, last = 0, i = 0; for(int i=0; i<len; ++i) { if(mp[s[i]] != -1 && mp[s[i]] >= last) { ans = max(ans, i-last); last = mp[s[i]] + 1; } mp[s[i]] = i; } ans = max(ans, len-last); return ans; }};leetcode 10. 正则表达式匹配题目形容给你一个字符串 s 和一个字符法则 p,请你来实现一个反对 '.' 和 '*' 的正则表达式匹配。 '.' 匹配任意单个字符'*' 匹配零个或多个后面的那一个元素所谓匹配,是要涵盖 整个 字符串 s的,而不是局部字符串。 ...
1.strlen函数,通过字符串的地址计算其长度,遇到/0进行计算. strlen函数的三种实现办法: #include<stdio.h>#include<assert.h>//1.计数器办法int my_strlen1(char* p){ int count = 0; assert(p != NULL); while (*p != '\0') { p++; count++; } return count;}//2递归办法int my_strlen2(char* p){ assert(p!= NULL); if (*p =='\0') { return 0; } else { return 1 + my_strlen2(p + 1); }}//指针相减法int my_strlen3(char* p){ assert(p!= NULL); char* ret = p; while (*p!='\0') { p++; } return p - ret; }int main(){ char arr[] = "abcdefgh"; int ret = my_strlen1(arr); printf("%d\n", ret);}2.strcpy函数 ...
C++20 最大的个性是什么? 最大的个性是迄今为止没有哪一款编译器齐全实现了所有个性。 有人认为 C++20 是 C++11 以来最大的一次改变,甚至比 C++11 还要大。本文仅介绍 C++20 四大个性当中的 Module 局部,分为三局部: 探索 C++ 编译链接模型的由来以及利弊介绍 C++20 Module 机制的应用姿态总结 Module 背地的机制、利弊、以及各大编译器的反对状况C++ 是兼容 C 的,岂但兼容了 C 的语法,也兼容了 C 的编译链接模型。1973年初,C 语言根本定型:有了预处理、反对构造体;编译模型也根本定型为:预处理、编译、汇编、链接四个步骤并沿用至今;1973年,K&R 二人应用 C 语言重写了 Unix 内核。 为何要有预处理?为何要有头文件?在 C 诞生的年代,用来跑 C 编译器的计算机 PDP-11 的硬件配置是这样的:内存:64 KiB 硬盘:512 KiB。编译器无奈把较大的源码文件放入狭小的内存,故过后 C 编译器的设计指标是可能反对模块化编译,行将源码分成多个源码文件、挨个编译,以生成多个指标文件,最初整合(链接)成一个可执行文件。 C 编译器别离编译多个源码文件的过程,实际上是一个 One pass compile 的过程,即:从头到尾扫描一遍源码、边扫描边生成指标文件、过眼即忘(以源码文件为单位)、前面的代码不会影响编译器后面的决策,该个性导致了 C 语言的以下特色: 构造体必须先定义再应用,否则无奈晓得成员的类型以及偏移,就无奈生成指标代码。局部变量先定义再应用,否则无奈晓得变量的类型以及在栈中的地位,且为了不便编译器治理栈空间,局部变量必须定义在语句块的开始处。内部变量只须要晓得类型、名字(二者合起来便是申明)即可应用(生成指标代码),内部变量的理论地址由连接器填写。内部函数只需晓得函数名、返回值、参数类型列表(函数申明)即可生成调用函数的指标代码,函数的理论地址由连接器填写。头文件和预处理恰好满足了上述要求,头文件只需用大量的代码,申明好函数原型、构造体等信息,编译时将头文件开展到实现文件中,编译器即可完满执行 One pass comlile 过程了。 至此,咱们看到的都是头文件的必要性和好处,当然,头文件也有很多负面影响: 低效:头文件的本职工作是提供前置申明,而提供前置申明的形式采纳了文本拷贝,文本拷贝过程不带有语法分析,会一股脑将须要的、不须要的申明全副拷贝到源文件中。传递性:最底层的头文件中宏、变量等实体的可见性,能够通过两头头文件“透传”给最上层的头文件,这种透传会带来很多麻烦。升高编译速度:退出 a.h 被三个模块蕴含,则 a 会被开展三次、编译三次。程序相干:程序的行为受头文件的蕴含顺影响,也受是否蕴含某一个头文件影响,在 C++ 中尤为重大(重载)。不确定性:同一个头文件在不同的源文件中可能体现出不同的行为,导致这些不同的起因,可能源自源文件(比方该源文件蕴含的其余头文件、该源文件中定义的宏等),也可能源自编译选项。C++20 中退出了 Module,咱们先看 Module 的根本应用姿态,最初再总结 Module 比 头文件的劣势。 ...
Summary1)auto是C语言中局部变量的默认属性;auto表明将被润饰的变量存储于栈上(局部变量的存储地位) 2)register将申明的局部变量存储于寄存器中;register是一种申请,不肯定胜利;不能用取地址符&去取register变量的地址 3)static关键字次要体现两个方面的意义: 指明变量的动态属性(生命期、存储地位:动态局变存储区从栈上变到全局数据区)限定变量的作用域(动态全局变量、动态函数:作用域只在以后文件中,原本是程序作用域)4)extern关键字次要有两个方面的用处: extern通知编译器,我申明的变量和函数都存在的,在其余中央定义了,你只管释怀用。(定义指的是:给变量分配内存空间,给函数确定的函数体)extern "C" { // Demo here }:命令编译器,用C的形式编译。常见用法:C++须要用到C的库,都是二进制sdk,源码是不可能改的。如果用C++的形式编不过,就用上面形式应用 extern "C"{#include "xx.h" // C库的头文件}C语言中的各种属性关键字C语言中的变量能够有本人的属性在定义变量的时候能够加上属性关键字“属性”关键字指明变量的特有意义 语法:property type var_name;auto char i;register int j;static long k;extern double m;1、autoauto是C语言中局部变量的默认属性;auto表明将被润饰的变量存储于栈上;编译器默认所有的局部变量都是auto的; void f(){ int i; // 局部变量默认属性是auto auto int j; // 显示申明属性为auto}2、registerregister关键字指明将局部变量存储于寄存器中;register只是申请寄存器变量,但不肯定申请胜利;register变量的值必须是CPU寄存器能够承受的值;不能用&运算符获取register变量的地址; register int g_v; // errorint main(){ register char var; printf("%08x", &var); // error}问题1:为什么不能将全局变量存储为寄存器变量?答:因为寄存器的访问速度比内存的速度要快的多,所以在C语言中为了高效,把一些须要拜访高效的变量存储为寄存器变量。全局变量的生命期是程序生命期,如果存储为register变量,就会始终占用该寄存器,这是不容许的。因为CPU的寄存器是无限的,因而register只是一种申请,不肯定胜利。 问题2:为什么不能取寄存器的地址?答:因为取地址符&只能用来取内存的地址。 3、staticstatic关键字指明变量的“动态”属性 static润饰的局部变量存储在程序动态区static关键字同时具备“作用域限定符”的意义 static润饰的全局变量作用域只是申明的文件中static润饰的函数作用域只是申明的文件中int g_v; // 全局变量,程序的任意中央都能够拜访static int g_vs; // 动态全局变量,只能在以后文件中拜访(作用域只是以后文件)int main(){ int var; // 局部变量,在栈上调配空间 static int svar; // 动态局部变量,全局数据区调配空间}4、 externextern用于申明“内部”定义的变量和函数 ...
起源:https://note.guoqianfan.com/2...Range-范畴范畴运算符范畴运算符:..,它会生成一个Range对象。语法:{startIndex}..{endIndex}:蕴含startIndex,不蕴含endIndex限度:0 <= startIndex <= endIndex <= arr.Length,其余均会报错! startIndex等于endIndex时,取不出元素,会生成一个空数组。 留神: 默认状况下,..的后面是0,前面是 arr.Length(不含)。..等价于0..arr.Length。所以..的后面和前面均能够为空。所以..也能够独自应用,代表整个索引范畴(0..arr.Length):arr[..]是arr的残缺拷贝。深拷贝还是浅拷贝须要看元素是值类型还是援用类型。值类型复制值,能够认为是深拷贝;援用类型复制援用,就是浅拷贝。 RangeRange用来从汇合中取出 指定索引范畴 的元素来生成新的汇合。 创立Range:Range range = 2..4; var arr = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };Range range = 2..4;int[] slice = arr[range]; // 或 arr[2..4];foreach (var number in slice){// [3, 4] Console.WriteLine(number);}arr[2..4]示意把arr这个序列,从索引为2的元素始终到索引为4(不含4)的元素提取进去组成新的序列。所以后果就是3,4。 Index-索引Index用来指定索引。该索引能够用来从汇合中取出指定索引处的元素,是单个元素。 乍一看,Index与int类型的索引没啥区别。嗯。。的确是这样,因为Index要与^操作符联合起来能力施展更大的作用。对了,还有Range。。 开端运算符^是开端运算符(Hat运算符),它会生成一个Index对象,用来从开端开始往前取数据。 与正向取数据时索引从0开始不同,^取数据时是从1开始的,代表倒数第一个元素。 arr[^1]等于arr[arr.Length-1],最初一个元素arr[^0]等于arr[arr.Length],常与Range组合应用如果应用arr[^0]的话就会抛出IndexOutOfRangeException,arr[^0]和arr[arr.Length]是一个意思。 这的确有点容易让人混同,但其实其它语言也差不多是这样设计的,例如-1这个索引示意最初一个元素。 组合应用 Range 和 Index残缺拷贝数组的3种形式int[] arr = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int[] arr01 = arr[0..arr.Length];int[] arr02 = arr[0..^0];int[] arr03 = arr[..];留神: ...
写在后面 首先申明这不是分享框架如何应用的教程,也不是作为一个传教者讲授的教程,这只是作为一个一直学习的开发者学习的一个记录,然而这个记录我把他分享进去,如果您看到了,或者有不同的见解,请您提出来咱们进行敌对的探讨,而不是毫无根据的鞭挞。 咱们开始吧 因为做.Net开发工作也曾经好几年了,经验过一些技术的变迁,刚接触的是webform,到前面的mvc以及当初比拟风行的webapi,从开始的一把梭,再到近年来微服务思维的遍及,前后端拆散的劣势从而显现出来,随着工夫往后推移,或者还会出更多优良的框架,然而作为开发人员我感觉除了框架的应用之外,很有必要的去理解一下框架运行的原理和机制,哪怕很少的一部分。 在此总结了下我集体认为须要去学习的观点,仅仅只代表我本人,在此我并没有把.NetFrameWork和.NetCore分为三六九等,在我心中它们都是十分优良的框架,只是侧重点不同而已,如果有不失当或者基本不正确的论证援用,那只能阐明我写总结的程度有余,并不代表我对它们任何一个带有敌意和歧视,大家可能学习过.NetCore,他的特点是什么?以及吸引到你的中央是什么,是纯正为了谋求时尚?还是跨平台?又或者是它的可配置,高性能? 各种中间件的扩大,跟搭积木一样,依照你本人的想法来搭建一个应用程序,是不是更加体现了它的魅力所在,咱们比照.Net framework 他的毛病就显著很多了,你要的不要的都放框架里,你要执行或者不执行的它都给你执行,简称"大杂烩" ,而后益处是开发者只须要关怀业务,因为你不须要操心技术问题,更不须要理解框架帮你做了什么,你只须要写好你负责的那个模块的增删改查。 这尽管在某种程度上进步了生产能力,然而作为程序员的角度来说就很危险了,所以这就是为什么后面说要学习它的机制的起因,你能够不必,然而要理解,因为一个程序员想要晋升本人的技术水平,则必须学习基础知识,在此处的“基础知识”指的是针对现有框架的机制和原理以及框架代码中优良的设计思维以及编码,这对集体技术晋升有很大的帮忙。至于学到什么水平,取决于每个人本人的认知
FPNN 1.1.2 已于7月26日下午正式公布。 比照 0.9.3 版本,次要的改变如下: FPNN UDP 模块正式公开FPNN UDP 提供牢靠UDP链接,开发者可应用UDP牢靠连贯在弱网环境下作为 TCP 链接的代替FPNN DUP 牢靠连贯反对在同一链接中混合发送牢靠数据和非牢靠数据TCP减少更加及时的保活性能,以及更加及时的链接活性检测MultipleURLEngine 接口全面更新,能够在调用接口时间接指定 HTTP 头数据,不必再做独立的内部保护全面反对 GCC/G++ 8 & GCC/G++ 9减少对 CentOS 8 和 Ubuntu Server 20 的反对全面改良文档,重写应用向导,并减少 API 接口文档FPNN 1.0.0 和 1.1.0 两个版本,因为过后文档暂未更新实现,所以没有对外公布。此次1.1.2版本公布,对应文档曾经全副实现更新。后续 云上曲率 RTM 及相干业务将逐渐从1.1.1版本更新至1.1.2版本。 其余开发语言的SDK,将随后陆续更新,并进新公布。 最初,1.1.0版本及1.1.2版本相干性能数据摘要如下: 数据摘要索引 压力测试 TCP 同一局域网TCP 洲际传输:德国法兰克福到美国西部俄勒冈(直线间隔超过8,000公里)UDP 同一局域网UDP 洲际传输:德国法兰克福到美国西部俄勒冈(直线间隔超过8,000公里)海量链接 TCP 同一局域网UDP 同一局域网TCP & UDP 比照测试 新加坡到德国法兰克福(直线间隔超过10,000公里)压力测试 TCP 同一局域网 机型虚构 CPU内存(GB)链接数量QPS均匀响应工夫(usec)AWS m5.xlarge416100049,708335AWS m5.xlarge416130227,91912,854AWS m5.xlarge4161,500148,95910,403AWS m5.xlarge4162,00099,552356AWS m5.xlarge4163,000149,61527,456TCP 洲际传输:德国法兰克福到美国西部俄勒冈(直线间隔超过8,000公里) 机型虚构 CPU内存(GB)链接数量QPS均匀响应工夫(usec)ping/2 (msec)AWS m5.xlarge4161017,921147,825137AWS m5.xlarge416190338,601151,772139AWS m5.xlarge416700339,240183,541136 ~ 137AWS m5.xlarge4163,200312,073256,980139 ~ 141AWS m5.xlarge4166,000299,175346,927136 ~ 137UDP 同一局域网 ...
Summary0)工程中须要防止隐式转换,强转有时无奈防止,但肯定要时刻留神操作的数据的类型,对操作的数据的类型要非常清晰,对转换的后果也要非常清晰。 1)C语言中有强制类型转换和隐式类型转换 2)强制类型转换的语法为:(Type)var; 强制类型转换的后果为: 指标类型可能包容目标值:后果不变指标类型不能包容目标值:后果将产生截断(截断只会保留低地址的字节)(对于整型值,和第2节中总结的整型值溢出运算后果统一)浮点数到整型数的强转会间接舍去小数位不是所有的强转都能胜利,如果无奈胜利强转,编译器会报错3)隐式类型转换指的是:编译器被动进行的转换。规范C编译器的类型查看是比拟宽松的,因而隐式类型转换可能带来意外的谬误(如高类型向低类型转换会产生截断)。 4)隐式类型转换的产生点: 算术运算式中,低类型转换为高类型(char和short进行算术运算时,全都会先转换为int)赋值时,表达式的值转换为右边变量的类型函数调用时,实参转换为形参的类型函数返回值,return表达式转换为返回值类型 C语言中的强制类型转换强制类型转换 long l = 50;int i = (int)l; // 强制类型转换,long --> int隐式类型转换 short s = 800;int i = s; // 隐式类型转换,short --> int // no error, no warning1、强制类型转换强制类型转换的语法: (Type)var_name;(Type)value;强制类型转换的后果: 指标类型可能包容目标值:后果不变指标类型不能包容目标值:后果将产生截断(截断只会保留低地址的字节)留神:并不是所有的强制类型转换都能胜利,如果不能强制类型转换时,编译器会报错 short s = 256; // 256的16进制示意:0x 0 1 0 0char c = (char)s; // 产生截断,只保留低地址的字节, // 即c的值为:0x00, c = 0示例详解: struct TS{ int i; int j;};struct TS ts;int main(){ short s = 0x1122; char c = (char)s; // short类型到char类型的转换,有溢出,产生截断,只保留低地址的字节 int i = (int)s; // short类型到int类型的转换,不会溢出,无风险 int j = (int)3.1415; // 浮点数到整形的转换,间接舍去小数局部 unsigned int p = (unsigned int)&ts; long l = (long)ts; // error,从构造体类型到long类型无奈强转 ts = (struct TS)l; // error printf("s = 0x%x\n", s); // 0x1122 printf("c = 0x%x\n", c); // 0x22 printf("i = 0x%x\n", i); // 0x1122 printf("j = 0x%x\n", j); // 0x3 printf("p = 0x%x\n", p); // 0x804a01c printf("&ts = %x\n", &ts);// 0x804a01c return 0;}2、隐式类型转换隐式类型转换指的是:编译器被动进行的类型转换 ...
Summary1)浮点数在内存中的存储形式: 类型符号位指数尾数float1位(第31位)8位(第23-30位)23位(第0-22位)double1位(第63位)11位(第52-62位)52位(第0-51位)2)float与double类型的数据在计算机外部的表示法是雷同的,然而因为所占存储空间大小的不同,其别离可能示意的数值范畴和精度不同。 3)浮点数的转换如:浮点数-8.25的二进制转换,float类型:1. 符号位:1(正数)2. 绝对值二进制:1000.01(整数局部的指数顺次为0,1,2...;正数局部的指数一次为-1,-2,-3...)3. 迷信计数法:1.00001 * 23,指数为34. 指数+偏移:3 + 127 = 130 <--> 1000 0010(float的指数占8位)5. 尾数:尾数局部为00001,占23位,有余的前面补0:00001 0000 0000 0000 0000 006. 最终:二进制为:1 1000 0010 00001000000000000000000(符号位 + 指数 + 尾数);用16进制示意为:0xc1040000 留神: 对于float类型,指数的偏移为+127;对于double类型,指数的偏移为+1023。尾数局部,有余的位数前面补0。通过如下指针形式,能够取得一段内存中的二进制位 unsigned int* p = (unsigned int*)&val;printf("%08x", *p); // %08x示意以16进制的模式来打印内存里的值4)float能示意的具体数字的个数与int雷同(都占用4个字节,一共32个bit位,所以最多就232种排列组合形式,即最多能示意232个数) 5)float的表示法是不准确的,所以能示意的范畴比int大。因为float的值不准确,所以对于一个float值,间接打印的数据可能有偏差;同时对于浮点数的运算,不能间接和0比拟。 6)因为float的内存表示法比int简单,所以float的运算速度比int慢。 浮点数的机密1、浮点数在内存中的存储形式类型符号位指数尾数float1位(第31位)8位(第23-30位)23位(第0-22位)double1位(第63位)11位(第52-62位)52位(第0-51位)float与double类型的数据在计算机外部的表示法是雷同的,然而因为所占存储空间大小的不同,其别离可能示意的数值范畴和精度不同。 2、浮点数的转换如:浮点数-8.25的二进制转换,float类型:1. 符号位:1(正数)2. 绝对值二进制:1000.01(整数局部的指数顺次为0,1,2...;正数局部的指数一次为-1,-2,-3...)3. 迷信计数法:1.00001 * 23,指数为34. 指数+偏移:3 + 127 = 130 <--> 1000 0010(float的指数占8位)5. 尾数:尾数局部为00001,占23位,有余的前面补0:00001 0000 0000 0000 0000 006. 最终:二进制为:1 1000 0010 00001000000000000000000(符号位 + 指数 + 尾数);用16进制示意为:0xc1040000 ...
Summary0)在进行数据运算、应用变量的时候,肯定要非常分明变量的具体类型! 1)对于整形数据,数据类型的最高位用于标识数据的符号:最高位为1示意正数,最高位为0示意整数。 2)类型溢出时的运算总结: 溢出的值为正的:理论值为:溢出后的值 - 该类型能示意的个数溢出的值为负的:理论值为:溢出后的值 + 该类型能示意的个数3)十进制正数和补码的互相转换:如何计算正数的补码:如十进制整数-7,8位 符号位:1绝对值:7 <--> 0000 0111取反:1111 1000加1:1111 1001(补码)如何从一个补码推算十进制正数:如已知(1111 1001)示意一个十进制正数上述规定反向计算 减1:1111 1000取反:0000 01111绝对值:计算得7符号位:-7非凡的,如1个字节的整数-128,它没有原码和反码,只有补码为1000 0000,即应用(-0)来示意(-128)。但依然能够应用上述规定进行推算二进制补码。 4)C语言中的变量默认为有符号的类型signed;unsigned关键字将变量申明为无符号类型,留神:只有整数类型才能够申明为unsigned 5)当无符号数和有符号数进行数学运算时,有符号数会被转换为有符号数,运算后果为无符号数。 有符号与无符号数分析1、负数和正数数据类型的最高位用于标识数据的符号 最高位为1,标识这个数为正数最高位为0,标识这个书为负数 int sign = 0;char i = -5;short j = 5;int k = -1;sign = (i & 0x80); // i为正数,最高位为1 ==> sign != 0sign = (j & 0x8000); // j为负数,最高位为0 ==> sign == 0sign = (k & 0x80000000);// k为正数,最高位为1 ==> sign != 02、原码和补码2.1 类型溢出的运算在计算机外部用原码示意无符号数 无符号数默认为负数无符号数没有符号位对于固定长度的无符号数 MAX_VALUE + 1 --> MIN_VALUEMIN_VALUE - 1 --> MAX_VALUE类型溢出时的运算总结: ...
Summary1)数据类型的实质是固定内存大小的别名;变量的实质是具体的一段内存的别名。 2)变量隶属于某一种数据类型,变量所在的内存大小取决于其所属的数据类型。 1、什么是数据类型数据类型能够了解为固定内存大小的别名数据类型是创立变量的模子 2、变量的实质变量是一段理论间断存储空间的别名程序中通过变量来申请并命名存储空间通过变量的名字能够应用存储空间 本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。如有错漏之处,恳请斧正。
Summary1)头文件(.h)是源文件(.c)的 接口定义文件。接口指的是可被其余文件拜访的函数或者数据。如果a.c中想应用b.c中定义的函数或变量,就能够通过b.h来应用,b.h中则声明了b.c中的函数和变量。 2)include中(<>)和("")的区别:#include <header.h>,指的是在编译系统文件中查找头文件#include "header.h",指的是先查找以后工程文件夹,再查找编译系统文件夹 3)头文件的一些规定: 头文件中只做函数申明和变量申明(不做具体定义)头文件中能够定义数据类型(typedef,struct,union,enum)一个头文件能够对应于多个源文件(少数状况一对一)不要应用#include蕴含源文件4)再论全局变量: 一般全局变量 源文件中定义的全局变量能够在其余任意源文件中进行应用,如果其余源文件中定义了同名的全局元素,则会产生反复定义谬误。(因为即便没有通过extern申明,不可间接可见,但实质各自的作用域依然是全局的)可应用extern关键字在头文件中申明,并在其余文件中应用(如果不申明,则在其余源文件中不可见)留神,应用extern申明时的类型和变量名都必须统一,且不能够给初始值(否则就会报反复定义谬误)。动态全局变量 static润饰的全局变量只能在以后源文件中应用无奈通过extern关键字申明,也就没有方法在其余文件中应用5)函数申明语句默认自带extern关键字润饰;类型定义必须放在头文件里,仅仅是类型申明无奈创立变量。 6)static函数和static全局变量的行为是统一的。static关键字润饰函数使得函数具备文件作用域;动态函数无奈在其余文件中被调用;函数设计时就须要思考是否在其余文件中应用。 多文件程序设计问题:理论工程开发中,所有的代码都是写在同一个文件中吗?答:很显然不是。理论的工程开发中,通常是不同工程师或不同的团队负责不同的模块,而后各自的模块组合起来实现整个工程。 多文件编译链接: 问题:不同文件之间如何互相拜访各自的程序元素?多文件之间的互相拜访: 每个文件能够定义性能接口(可被其余文件拜访的函数或数据) 源文件:代码实现文件,后缀为.c头文件:源文件的接口定义文件,后缀为.h当须要应用其余文件提供的性能时,蕴含对应的头文件 语法: #include <header.h>,指的是在编译系统文件中查找头文件#include "header.h",指的是先查找以后工程文件夹,再查找编译系统文件夹再论全局变量: 一般全局变量 源文件中定义的全局变量能够在其余任意源文件中进行应用可应用extern关键字在头文件中申明,并在其余文件中应用(如果不申明,则在其余源文件中不可见)动态全局变量 static润饰的全局变量只能在以后源文件中应用无奈通过extern关键字申明,也就没有方法在其余文件中应用本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。如有错漏之处,恳请斧正。
前言应用.NET的同学们可能都会有一个比拟苦楚的体验,就是如果须要写一段测试的代码或者示例代码,也不得不创立一个我的项目去解决,即使是最根底的控制台程序, 应用 dotnet cli,也须要面对如下一段最根底的入口代码。 using System;namespace Demo{ class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } }}全然不像python、js之类,关上编辑器/命令行就能够撸代码一样的不便。 作为支流的开发语言,天然是有特定的解决方案的。 老牌的.NET程序员可能相熟的一个开发工具LINQPad,就是一个不错的.NET代码演练工具,同时也提供了F#和SQL语言反对,设置能够间接链接数据库进行查问,不堪称不弱小。 现在LINQPad 6 也反对了.NET 5,也是一个不错的开发辅助工具。尽管LINQPad也提供了免费版的,然而想要应用NuGet包集成、代码提醒、debug等高级性能的话,还是须要购买受权的,如果经济能力富裕的话,尽量间接购买PREMIUM版本即可,体验最残缺的LINQPad。 除了LINQPad,还有其余很多的抉择,其中一个可能获取C#最原生体验的,就是间接应用C# script。早在 VS 2015 update 1的时候,VS就集成了一个 C# REPL Command-Line Interface(csi.exe),能够在命令行中间接执行C#语句,整体的体验和Python REPL和Node.js REPL差不多。于此同时,推出了一种新的脚本文件格式 csx,这样的话就能够应用C#写脚本文件了。 .NET Core时代以来,有了更多开源工具的抉择,其中的 dotnet script就是一个十分不错的C#脚本工具。 联合VS Code 和 OmniSharp ,应用 dotnet script能够给到相当不错的C#脚本开发体验。当然也是反对NuGet包和调试,而且开源收费,得益于相干性能的跨平台个性,根本反对全平台。 装置dotnet script自身的装置绝对容易。 装置dotnet core 2.1+ sdk ,如果应用 C# 8语言个性,须要装置 dotnet core 3.1+ sdk运行dotnet tool install -g dotnet-script 即可dotnet-script 装置实现之后,间接运行dotnet script即可进入RPEL模式中,间接撸C#代码间接运行。 ...