关于c#:如何使用-ArrayPool

如果不停的 new 数组,可能会造成 GC 的压力,因而在 aspnetcore 中举荐应用 ArrayPool 来重用数组,本文将介绍如何应用 ArrayPool。 应用 ArrayPoolArrayPool 是一个动态类,它提供了一个共享的数组池,能够用来重用数组。它能够用来防止频繁的调配和回收数组,从而缩小 GC 的压力。 ArrayPool 的应用非常简单,只须要调用它的静态方法 Rent 即可。Rent 办法有两个参数,第一个参数是数组的长度,第二个参数是数组的最小长度。如果你不晓得数组的最小长度,能够传递一个默认值,比方 16。上面是一个应用 ArrayPool 的 C# 示例: using System;using System.Buffers;class Program{ static void Main(string[] args) { // 创立一个数组池 var pool = ArrayPool<int>.Shared; // 从池中获取一个长度为 10 的数组 int[] array = pool.Rent(10); try { // 在数组中填充一些数据 for (int i = 0; i < array.Length; i++) { array[i] = i; } // 应用数组中的数据 foreach (int i in array) { Console.WriteLine(i); } } finally { // 将数组偿还到池中 pool.Return(array); } }}在下面的示例中,咱们首先通过调用 ArrayPool<int>.Shared 来获取一个数组池的实例。接下来,咱们通过调用 pool.Rent(10) 办法从池中获取一个长度为 10 的整数数组。在数组中填充数据后,咱们遍历数组并输入其中的元素。最初,咱们通过调用 pool.Return(array) 办法将数组偿还到池中。 ...

February 18, 2023 · 1 min · jiezi

关于c#:写个-NET-程序解决-Windows-版微信-39-收到文件只读的问题

Windows 版微信降级到 3.9 之后,接管到的文件都变成了只读属性,对须要常常批改微信接管文件进行交换的人来说极为不变。尽管从业务性能上来说,须要频繁交换的文档还是用在线协同(比方腾讯文档)比拟好一些,但从技术的角度来看,应该如何解决这个问题呢? 其实很多技术栈都提供了监听系统文件变动的 API,比方 .NET 就在其 System.IO 命名空间下提供了 FileSystemWatcher 类用于监听文件的变动,包含创立文件、删除文件、文件改名/挪动、文件属性变动等。所以能够应用 C# 和 .NET 疾速的写一个程序用来监听指定文件夹下的新建文件,如果新创建的文件属性是只读,则去掉其只读属性。 第一步,找到微信接管文件的目录。在微信的「设置→文件治理」中就能够找到,比方 C:\Users\James\Documents\WeChat Files。而后须要辨认这个目录下的用户目录,个别是微信号。点击微信窗口上本人的头像就能够看到。用户目录下的 FileStorage\File 目录就是咱们的目标目录了。 为了帮忙用户疾速定位到这个目录,在假如用户没有手工扭转微信文件目录的状况下,能够这样来查找 通过 Environment.GetFolderPath() 获取到以后用户的“文档”目录;拼接失去 WeChat Files 目录的门路;遍历 WeChat Files 目录下的子目录,去掉非凡命名的 All Users 和 Applet 等,在剩下的子目录中进一步猜想个别最近拜访过的就是本人的微信账号目录那么整个猜想目录的过程代码示例如下: string? GuessReceivePath() { var docDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); var wxDir = Path.Combine(docDir, "WeChat Files"); if (!Directory.Exists(wxDir)) { return null; } HashSet<string> excludes = new(StringComparer.InvariantCulture) { "All Users", "Applet" }; var userDir = Directory.EnumerateDirectories(wxDir) .Select(it => new FileInfo(it)) // 对每个目录产生 FileInfo 对象(小性能损耗能够疏忽) .ExceptBy(excludes, fi => fi.Name) // 去掉特定名称的目录 .OrderByDescending(it => it.LastAccessTime) // 按最近拜访工夫排序 .FirstOrDefault() // 取最最初拜访的那一个,留神有可能是 null ?.FullName; // 获得残缺门路 if (userDir == null) { return null; } return Path.Combine(userDir, "FileStorage", "File");}当然,猜想的目录不肯定精确,所以写程序的时候最好能提供给用户一个能够手工批改/设置目录的伎俩,当然别忘了目录的有效性查看(查看目录存在)。 ...

February 17, 2023 · 2 min · jiezi

关于c#:2023-年-dotnet-仓库社区年度调查已经开始

筹备好往年对 dotnet 指手画脚了吗,平时在群里我饮泣吞声,明天我必须重拳出击,快来参加吧。 我间接一个参的加.NET 是一个跨平台的开发框架,反对多种语言和利用场景,如 C#、F#、VB.NET、ASP.NET、Blazor、MAUI 等。2022 年是 .NET 的一个重要的年份,因为它将公布 .NET 7,将为开发者提供更稳固和牢靠的开发环境。.NET 7 预计将持续欠缺和优化 .NET 6 的性能和个性,并且引入一些新的技术和概念,让开发者可能更灵便和弱小地解决各种简单的问题。 2023 年是 .NET 的另一个重要的年份,因为它将公布 .NET 8,将为开发者提供更多的翻新和摸索的机会。.NET 8 预计将引入一些新的技术和概念,让开发者可能更疾速和高效地构建各种利用。 中国地区是 .NET 的一个重要的市场和社区,有着泛滥的 .NET 开发者和爱好者。中国地区也有着很多优良的 .NET 相干的我的项目和资源。这些我的项目和资源为中国地区的 .NET 开发者提供了很多学习和交换的机会和平台。 为了更好地理解寰球 .NET 开发者的状况和需要,并且为将来 .NET 的倒退提供参考和倡议,dotnet 官网特地推出了 dotnet 仓库社区生态考察。这是一个针对寰球 .NET 开发者的在线问卷调查,波及到你对于 .NET 的应用状况、满意度、冀望等方面。咱们诚挚邀请你参加到这个考察中来,并且分享给你身边更多应用或感兴趣于 .NET 的人。作为中国地区的一员,咱们更应该积极响应并表白咱们对于 dotnet 社区的反对与激情! 进入该链接,通过第一个 comment 便能够退出考察: Annual dotnet/runtime community survey 2023 1 现已倒闭和今年相似,往年的 dotnet 社区年度考察曾经开始了。你能够对相干的包含 aspnetcore、runtime、orleans 等等 29 的仓库重拳出击,针对问卷内容提出本人的响应。 以下摘录了局部内容(其中的 xxxx 示意你想要反馈的仓库): 总体而言,您如何评估您在 xxxx repo 中的体验?你是否在 xxxx repo 中提出或评论过任何问题、PR 或探讨?您对问题、探讨或 PR 的后果的满意度如何?你是否理解版本库维护者是如何确定问题和 PR 的优先秩序的?你感觉维护者对社区参加设计过程的凋谢水平如何?如果你在本地编译仓库,你是否胜利?你是否尝试过对 xxxx repo 中的代码提交批改(PR)?如果你提交了一个 PR,你是如何决定修复哪个问题的?你对 repo 维护者的响应和参加有多称心?思考到 xxxx repo 中的社区,你如何评估该 repo 中其他人的敌对性和帮忙性?你对版本库维护者如何解决违反行为准则的行为有多称心?与其余开放源码软件我的项目相比,你在 xxxx repo 中感觉有多受欢迎?当然,最初也有一个文本框能够让你提供本人独到的见解。 ...

February 17, 2023 · 1 min · jiezi

关于c++:SkeyeGisMap开发文档二

1、整体架构SkeyeGisMap 由以下几局部组成: cdt 只有一个文件, 来自 mapbox 的三角剖分库 earcut, 其官网链接为: https://github.com/mapbox/earcut core 地图外围, 次要定义了地图形态节点, 地图事件, 地图助手工具。 item 地图的可视化项(即地图容器)。 parser 地图矢量形态解析器(暂只实现了 Esri Shapefile格局解析)以及坐标参考。 style 地图款式加载解析相干, 个别状况下无需关怀。 其构造如图所示: 2、坐标系SkeyeGisMap 中总共有四种坐标系: 1、屏幕坐标系 { Screen Coordinate System } 该坐标系就是字面意思, 所有的坐标对应屏幕像素坐标。 通常事件的原始坐标即是屏幕坐标。 另外, SkeyeGisMap 中的地图事件不间接承受原始的{ Qt Event }, 须要进行一些转换。 2、显示坐标系 { Display Coordinate System } 该坐标系是地图所有可视节点的(顶点)坐标系, 即执行绘制时应用的坐标。 3、世界坐标系(地图坐标系) { World Coordinate System } SkeyeGisMap 应用 EPSG:32650 做为世界坐标的参考系. 次要来自于地图矢量形态解析器解析后产生的坐标, 通常不须要关注. 4、经纬度坐标系 { Lonlat Coordinate System } SkeyeGisMap 应用 EPSG:4326 做为经纬度坐标的参考系. ...

February 16, 2023 · 1 min · jiezi

关于c#:我不想再传递-nameof-了

有的时候抛出一个异样,咱们须要晓得是哪个办法抛出的异样。那么,咱们能够通过传递 nameof 来获取调用者的办法名。然而,感觉很烦,每次都要传递 nameof。那么,有没有更好的办法呢? CallerLineNumberAttribute获取调用者的行号。 using System;using System.Runtime.CompilerServices;public static class Program{ public static void Main() { TraceMessage("Something happened."); } public static void TraceMessage(string message, [CallerLineNumber] int sourceLineNumber = 0) { Console.WriteLine("Line: {0} - {1}", sourceLineNumber, message); }}// The example displays the following output:// Line: 10 - Something happened.CallerFilePathAttribute获取调用者的文件门路。 using System;using System.IO;using System.Runtime.CompilerServices;public static class Program{ public static void Main() { TraceMessage("Something happened."); } public static void TraceMessage(string message, [CallerFilePath] string sourceFilePath = "") { Console.WriteLine("File: {0} - {1}", Path.GetFileName(sourceFilePath), message); }}// The example displays the following output:// File: Program.cs - Something happened.可发帖可群聊的技术交换形式曾经上线,欢送通过链接,退出咱们一起探讨。 https://www.newbe.pro/links/CallerMemberNameAttribute获取调用者的办法名。 ...

February 16, 2023 · 2 min · jiezi

关于c#:转义编码和加密

本义、编码和加密是开发中很常见也很根底的概念。对于初学开发的开发者,可能有时会无奈精确的辨别着几个词。咱们将通过这篇文章来理解一下“本义、编码和加密”这几个词的关联和区别。 本义第一种本义场景绝大多数的开发者都已经在本人学习第一个编程语言时,就遇到了这个概念。以经典的C语言中字符串中的字符本义为例。 如果在一个字符串中存在一个",那么就须要在"前增加\才可能失常的示意,比方上面这样。 char* universal_law = "月老板说:\"世界上本也不存在'银弹'。一套框架解决不了所有问题。\""之所以须要这样,是因为对于字符串来说,"自身就是示意一个字符串的起止符号。如果不进行本义,那么编译器将无奈正确的辨认其中的"哪些是分隔符,哪些是字符串外部的"。 所以,第一种须要本义的场景就是:如果不进行本义就可能与语法规定的某些内容产生混同,所以这些内容都被设计为须要本义。 基于这种场景,能够在很多的编程语言和概念中找到这种场景的体现: java String honor = "月老板-\"赛博坦首席技术官\"";对"进行本义 C# var proverbs = "月老板:\"这里不要写死,下次需要必改\"";对"进行本义 XML <nb>月老板的衬衫价格&gt;99磅6便士</nb>&gt;是对>的本义,>是XML的边界符 正则表达式 \d+\\\.\d+\.示意一个.,因为在正则表达式中.示意匹配除\n和\r之外的任何单个字符。 \\示意一个\,转义字符的本义示意。 可发帖可群聊的技术交换形式曾经上线,欢送通过链接,退出咱们一起探讨。 https://www.newbe.pro/links/第二种本义场景当然,另外还有一种场景,同样还是以C语言为例,看一下上面这个例子: char* hammurabi_no1 = "月落大佬:\"业务复杂度不会因为零碎设计变动而缩小,\r\n它只是从一个中央转移到了另外的中央。\""其中的\r和\n也是一种本义场景的应用。他们别离示意一个回车符和换行符。之所以要本义,是因为失常状况下,这样的字符是不可见的,对于这种字符,不过不采纳本义的模式进行表白,那么会比拟艰难,因为语言设计者设计了这种本义的形式来表白不容易表白的字符。 因而,能够总结出第二种须要本义的场景:本义能够使得表白内容的形式更加容易,更加容易了解,所以设计了这类本义规定。 基于这种场景,也能够在很多编程语言和概念中找到对应的体现: C# var colorOfYueluoShirt = 0xFFFFFF;0xFFFFFF示意一个十六进制数,对应的十进制数是16777215。0xFFFFFF的表达形式更容易浏览。 HTML <nb>月老板的衬衫价格&gt;966&yen;</nb>&yen;是对¥的本义,因为在期初的HTML中,只能用ASCII表中的字符进行表白,所以过后设计了这种形式。 除了在IT畛域,在其余畛域其实也存在相似第二场景的利用。例如在中国的航空畛域,对于数字的念法有非凡的解决:7读作拐,0读作洞,1读作幺,2读作两。通过这样的“本义”解决,能够防止误听而造成的困扰。 本义的总结总结来说,本义规定的设计,次要解决了两种场景下对代码的表白问题: 如果不进行本义就可能与语法规定的某些内容产生混同,所以这些内容都被设计为须要本义。本义能够使得表白内容的形式更加容易,更加容易了解,所以设计了这类本义规定。值得一提的是,很多名称中蕴含有escape或者unescape的函数或者办法都表明了它们与本义无关。 编码编码也是一个十分常见的概念。比方常常会听到UTF8编码、GBK编码、Base64编码、URL编码、HTML编码、摩斯电码等等一些和编码无关的概念。 生活化地了解编码在理解编码之前,首先通过一个生活化的例子来理解一下“什么是信息,什么是信息的载体”。 全世界,对于“我爱你”这样一句话的表达方式千差万别。口头表白,书面表白,肢体表白,普通话表白,英语表白,音乐表白,绘画表白。甚至有生之年咱们能够脑电波表白。但不管表达方式是如何的,其中蕴含的信息能够是统一的。都是为了传播“我爱你”这样的一个外围价值。 在以上这段表述中,能够将“我爱你”这样的概念了解为“信息”。而各种表达方式了解为这个信息的各种载体。 那么,回到编程的世界中来。计算机中的信息次要的载体是以电磁信号的物理载体存在于计算机世界中。那么如果要将事实世界简单的内容都依附这种载体来表白,就须要进行转化,咱们能够将这种转化了解为编码。联合前文生活化的例子,应用普通话来表白“我爱你”这个信息,就能够了解为应用普通话来编码这个信息。 因而,编码,其能够了解为,采纳一种新的载体来示意前一个载体所表白的信息。 能够套用相似这样一个公式来了解:XX编码,将A编码为B,以实现通过B进行存储或传输传输的目标。 技术相干的编码那么,采纳这样的概念,咱们来了解一下以往见到的各种技术概念: 文本文件编码,将“文本数据”编码为“二进制数据”,以实现通过“二进制数据”进行存储或者传输的目标 文本文件在计算机中,最终的载体是二进制文件的模式存在。早起,因为计算机诞生在美国,文本内容也只蕴含有英文内容。因而过后只有应用ASCII进行编码就能够了。然而起初随着计算机的遍及,须要表白的信息越来越多了。因而诞生了Unicode、GB2312等等编码模式。但不论如何,这些编码其实都是对文本信息的编码模式。 Base64编码,将“二进制数据”编码为“64个可打印字符的组合”,以实现通过“可打印字符的模式”进行存储或者传输的目标 在Web场景中,在有些中央限度了数据传输的形式。例如,在URL,只能传递文本。因而,如果想要传输一组二进制数据。那么能够选用Base64编码,将二进制数据编码为可打印的字符串。这样能力实现URL上二进制数据的传输。 URL编码,将“非数字字母字符”编码为“十六进制转义序列”,以实现通过“十六进制转义序列”进行传输的目标 如果须要在URL中传递中文作为参数,或者须要在URL中传递空格、&、?、=等等特殊符号。这个时候就须要进行URL编码。例如月老板会被编码为%E6%9C%88%E8%80%81%E6%9D%BF。编码的目标HTTP协定的外在要求,通过这种模式,能够浏览器表单数据的打包。 总的来说,通过编码,能够转化信息表白的载体。这样就能够利用新载体带来的益处。这里也有一些生活化的例子: 摩斯电码,将“文本数据”编码为“点横组成的电信号”,以实现通过“电报”进行传输的目标。 例如: -·-- ··- · ·-·· ··- --- ·· ··· - ···· · -- --- ··· - ··-· ·- -- --- ··- ··· -·· ·- ·-·· ·- ---社会主义外围价值观编码,将“文本数据”编码为“社会主义外围价值观组成的字符”,以实现通过“社会主义外围价值观”进行传输的目标。 ...

February 15, 2023 · 1 min · jiezi

关于c++:OneFlow-源码阅读-12从-Tensor-看-CPython-的对象创建过程

春节前后拜读了许啸宇的《TorchDynamo初探:Python ByteCode的动静批改》,随后简略总结了一下 TorchDynamo 的执行过程。这期间对 CPython 也多了一些理解。记得之前看到过,oneflow Tensor 是通过 CPython API 导出给 Python 环境的,过后很多细节都不理解,就着这个热乎劲,从新看了一下 Tensor 类型注册相干的内容,顺便相熟一下 CPython 创建对象的过程。本文中波及 CPython 相干的内容能够参考 Python behind the scenes。 1 Python 中的类型Python 中一切都是对象。对象的数据类型自身也是对象,比方 float、list 这些,都是对象。这些类型对象中,有一个比拟非凡,就是 type,它是元类型(metatype),即类型的类型。以下断言是成立的: assert(float.__class__ is type)assert(type.__class__ is type)这些类型对象在 CPyhton 中的定义如下,它们的 C 类型都是 PyTypeObject,也都能够被视为 PyObject。 object: PyBaseObject_Type(定义)type: PyType_Type(定义)float: PyFloat_Type(定义)list: PyList_Type(定义)PyTypeObject 的很多字段(slot)都是函数指针。这些 slot 的不同取值,决定了不同类型的行为。slotdefs 数组定义了 magic method 的 slot。 2 Tensor 类型的注册one::Tensor 类型注册的代码在 tensor.cpp 中。这里定义了三个 PyTypeObject 对象: TensorMetaclass_Type: metatypePyTensorObject_Type: oneflow.TensorPyParameterObject_Type: oneflow.Parameter,是 oneflow.Tensor 的子类型。MakeTensorMetaclass 函数负责初始化 TensorMetaclass_Type 对象。上面这行代码创建对象实体: ...

February 14, 2023 · 5 min · jiezi

关于c++:TorchDynamo-的执行过程

1 执行流程1.1 初始化设置全局变量 extra_index。这是 PEP 523 设置 PyCodeObject.co_extra 须要的。初始化 TSS Key。这是 PEP 539 须要的。Tss 的 key 是固定的,这里定义的这个 key 只用于存储 callback,tss 是每个线程公有的(Each thread has a distinct mapping of the key to a void* value)。 Dynamo callback 都是一样的?1.2 设置 eval frame 以代替默认的 eval frame 函数set_eval_frame_py 输出参数中的 args[0] 应该就是 Python 传入的 callback。 在 Python _TorchDynamoContext 中调用这个函数,Python 的办法名是 set_eval_frame。 1.2.1 函数的次要作用将传入的 callback 保留到 thread specific storage如果需要的话,设置 eval_frame 为 custom_eval_frame_shim返回之前保留在 tss 的、旧的 callback1.2.2 set_eval_frame_py 执行流程set_eval_frameincrement_working_threads,如果需要的话enable_eval_frame_shimtstate->interp->eval_frame = &custom_eval_frame_shim1.3 _TorchDynamoContext: 用于设置转换字节码的 callback 函数这是 torch._dynamo.optimize(...) 对应的 context manager。callback 是 Python 代码。 ...

February 14, 2023 · 2 min · jiezi

关于c#:dotnet-8-preview-1-即将发布

.Net 8 preview 1 行将到来,让咱们来提前看看都要公布什么吧。 .Net 8 preview 1 行将到来.NET 8 的第一个预览版将在几周内公布,微软的 David Ortinau 在斯德哥尔摩举办的一场在线技术流动中说。这个音讯是在.NET Frontend Day 的一个.NET MAUI 的演示中走漏的,这个流动能够在 YouTube 上观看。.NET 8 将在 2023 年 11 月 10 日左右的.NET Conf 2023 流动中公布,.NET GitHub 仓库显示.NET 8 的开发工作曾经实现了 44%。.NET MAUI 和 Blazor 是明天.NET Frontend Day 的重点,GitHub 仓库显示有很多与它们相干的问题,例如“MAUI Android 的 ImageSharp 性能很差”、“Blazor WebAssembly 在低内存的挪动设施上解体”等。Steve Sanderson 在一月份的 YouTube 视频中展现了一个名为 Blazor United 的新我的项目,它是一个原型我的项目,之后会被挪动到了 GitHub 流程中。 可发帖可群聊的技术交换形式曾经上线,欢送通过链接,退出咱们一起探讨。 https://www.newbe.pro/links/将会带来什么?目前已知的包含: BCL: Utility methods for working with randomness. - BCL: 解决随机性的实用办法。CLR AppModel team: NativeAOT size improvements. - CLR AppModel 团队: NativeAOT 大小优化。System.Text.Json Improvements: Missing member handling, Source generator support for required and init properties, Interface hierarchy support, Snake Case and Kebab Case, Add JsonSerializer.MakeReadOnly() and IsReadOnly APIs. - System.Text.Json 改良: 缺失成员解决,源代码生成器反对必须和初始化属性,接口档次反对,蛇形命名和烤串命名,增加 JsonSerializer.MakeReadOnly()和 IsReadOnly APIs。Mono: .NET Hot Reload supports adding instance fields, properties and events - Mono: .NET Hot Reload 反对增加实例字段,属性和事件。WebAssembly: experimental “Webcil” a new container format for .NET assemblies - WebAssembly: 实验性的"Webcil",一种用于.NET 程序集的新容器格局。Mono: Debugging .NET WebAssembly App supports loading symbols from symbol server as configured in Visual Studio - Mono: 调试.NET WebAssembly 应用程序反对从符号服务器加载符号,依照 Visual Studio 的配置。General SIMD improvements - 通用 SIMD 改良。Fundamental PGO improvements - 根本 PGO 改良。Loop Optimizations - 循环优化。JIT Throughput Improvements - JIT 吞吐量改良。.NET Libraries - System.Numerics and System.Runtime.Intrinsics - .NET 库 - System.Numerics 和 System.Runtime.Intrinsics。说点我看得懂的其实很多我也看不懂,找点我看得懂的。 ...

February 14, 2023 · 2 min · jiezi

关于c#:一个容器但是一整个k8s集群

你可能须要一个疾速启动和销毁的 k8s 集群;你可能在资源受限的环境中运行 k8s 集群;你可能是一个齐全的初学者,感觉搭建残缺的 k8s 套件太难。那么这篇短文可能能够帮到你。 各种丐版 k8s 集群你可能见过各种丐版的 k8s 集群部署计划,比方:K3S、K3d、Kind、MicroK8S、Minikube、Docker Desktop。而明天要写的是其中之一:K3d。 为什么抉择 k3d 呢,因为笔者在一个十分非凡的环境中应用 k8s: 这是一个 x86 的 openwrt 软路由零碎,曾经内置了 docker。除了 k3d,其余的计划都因为各种起因而失败了。当然一般的 PC 以上计划都是能够的。而 k3d 简直也是最简略的。笔者打算在这个软路由上装置本人平时要用到的各种中间件,比方 nexus oss、jenkins 等等 。思考到这个环境可能须要做备份和重建,因而须要思考一个疾速启动和销毁的 k8s 集群。后续在加上 argo-cd 等技术,能够实现一个残缺的 k8s 集群的备份和复原。应用 k3d 之前的筹备你须要一个 docker 环境。(必要)开始装置 k3d办法 1,你能够抉择应用官网提供的脚本进行装置: wget -q -O - https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash办法 2,你也能够间接下载二进制文件,而后退出到 PATH 即可:https://github.com/k3d-io/k3d... 如果从 github 下载对你的网络来说是一种艰难,你能够抉择 k3d1 或者 FastGithub2 创立一个 k3d.yml 文件k3d.yml 是用户在创立 k3d 集群时应用的配置文件。这是一个范例的配置文件: apiVersion: k3d.io/v1alpha4kind: Simplemetadata: name: k3s-defaultservers: 1 # same as `--servers 1`agents: 2 # same as `--agents 2`image: docker.io/rancher/k3s:v1.25.6-k3s1kubeAPI: # same as `--api-port myhost.my.domain:6445` (where the name would resolve to 127.0.0.1) host: '127.0.0.1' # important for the `server` setting in the kubeconfig # hostIP: "192.168.1.200" # where the Kubernetes API will be listening on hostPort: '6445' # where the Kubernetes API listening port will be mapped to on your host systemports: - port: 80:80 # same as `--port '8080:80@loadbalancer'` nodeFilters: - loadbalanceroptions: k3d: # k3d runtime settings wait: true # wait for cluster to be usable before returining; same as `--wait` (default: true) timeout: '60s' # wait timeout before aborting; same as `--timeout 60s` disableLoadbalancer: false # same as `--no-lb` disableImageVolume: false # same as `--no-image-volume` disableRollback: false # same as `--no-Rollback` loadbalancer: configOverrides: - settings.workerConnections=2048 k3s: # options passed on to K3s itself extraArgs: # additional arguments passed to the `k3s server|agent` command; same as `--k3s-arg` - arg: '--tls-san=127.0.0.1 --tls-san=ks.newbe.io' nodeFilters: - server:* kubeconfig: updateDefaultKubeconfig: true # add new cluster to your default Kubeconfig; same as `--kubeconfig-update-default` (default: true) switchCurrentContext: true # also set current-context to the new cluster's context; same as `--kubeconfig-switch-context` (default: true)registries: # define how registries should be created or used config: | # define contents of the `registries.yaml` file (or reference a file); same as `--registry-config /path/to/config.yaml` mirrors: "docker.io": endpoint: - "https://mirror.ccs.tencentyun.com"创立一个 k3d 集群有了配置文件,当初就能够创立一个 k3d 集群了: ...

February 13, 2023 · 3 min · jiezi

关于c#:C-如何部分加载超大解决方案中的部分项目

在有的特有的我的项目环境下,团队会将所有的我的项目应用同一个解决方案进行治理。这种形式方面了治理,然而却会导致解决方案变得十分宏大,导致加载工夫过长。那么,如何局部加载解决方案中的局部我的项目呢?就让咱们来借用微软退出的slngen 工具来体验一下局部加载解决方案中的局部我的项目吧。 slngen 从根我的项目生成长期解决方案SlnGen 是一个 Visual Studio 解决方案文件生成器。Visual Studio 解决方案对于大型项目树来说通常不能很好地扩大。SlnGen 读取一个给定我的项目的我的项目援用,按需创立一个 Visual Studio 解决方案。例如,你能够针对一个单元测试我的项目运行 SlnGen,并出现一个蕴含单元测试我的项目及其所有我的项目援用的 Visual Studio 解决方案。你也能够针对一个有根的文件夹中的遍历我的项目运行 SlnGen,关上一个蕴含你的我的项目树的那个视图的 Visual Studio 解决方案。 装置 slngendotnet tool install --global Microsoft.VisualStudio.SlnGen.Tool --add-source https://api.nuget.org/v3/index.json --ignore-failed-sources运行以上命令,你就能够在全局装置 slngen 工具了。而后,你就能够在任何中央应用 slngen 命令了。 slngen --help最近咱们正在组织全新的技术交换形式,欢送点击链接莅临指导 https://www.newbe.pro/links/为所有的我的项目引入 Microsoft.VisualStudio.SlnGen在你的我的项目树中,你须要为所有的我的项目引入 Microsoft.VisualStudio.SlnGen 包。能够通过 Directory.Build.props 来轻松实现。 <ItemGroup> <PackageReference Include="Microsoft.VisualStudio.SlnGen" Version="9.5.2" /></ItemGroup>筹备一个长期的测试项目为了不便演示,咱们创立三个我的项目,别离是 slngen-demo、slngen-demo-a、slngen-demo-b。 其中,slngen-demo-a 和 slngen-demo-b 我的项目都援用了 slngen-demo 我的项目。 mkdir slngen-democd slngen-demodotnet new classlib -o slngen-demodotnet new console -o slngen-demo-adotnet new console -o slngen-demo-bcd slngen-demo-adotnet add reference ../slngen-demo/slngen-demo.csprojcd ../slngen-demo-bdotnet add reference ../slngen-demo/slngen-demo.csproj文件夹构造大抵如下: ...

February 10, 2023 · 1 min · jiezi

关于c++:C右值引用与移动语义

一文看懂C++右值援用和挪动语义目录背景什么是右值援用为什么须要右值援用挪动结构move的原理move的利用场景右值援用注意事项总结背景C++11引入了右值援用,它也是C++11最重要的新个性之一。起因在于它解决了C++的一大历史遗留问题,即打消了很多场景下的不必要的额定开销。即便你的代码中并不间接应用右值援用,也能够通过规范库,间接地从这一个性中收益。为了更好地了解该个性带来的优化,以及帮忙咱们实现更高效的程序,咱们有必要理解一下无关右值援用的意义。 什么是右值援用右值 在引入右值的概念前,咱们无妨先看看左值。一句话加以概括:左值就是等号右边的值;同理,右值也就是等号左边的值。举个例子:int a = 2; 这里的a是等号右边,能够通过取址符&来获取地址,所以是一个左值。而5在等号左边,无奈通过取址符&来获取地址,所以只一个右值。 右值援用 左值援用是对于左值的援用或者叫别名。同样地,右值援用也就是对于右值的援用。语法也很简略,就是在左值援用的语法之上在多加一个&,写成类型 &&右值援用名 = 右值;的模式即可,比方: int &&a = 5;a = 6;string s1 = "hello";string &&s2 = s1 + s1;s2 += s1;上述简略例子,展现了右值援用的根本用法。不过通常状况下,右值援用更多的是被用于解决函数参数。比方: struct Student { Student(Student &&s);};为什么要应用右值援用在C++11之前,很多C++程序里存在大量的长期对象,又称无名对象。次要呈现在如下场景: 函数的返回值用户自定义类型通过一些计算后产生的长期对象值传递的形参先说函数的返回值,最常见的类型就是某些返回用户自定义类型的时候,如果没有将其复制,就会产生长期对象,比方: Student func1(); // 返回一个Student对象...func1(); // 调用了func1创立了一个Student对象,然而没有应用,于是编译器创立了一个长期对象来进行存储而后是某些计算操作后产生的长期对象,比方: Complex result = c1 + c2 + c3; // 编译器先计算c1 + c2的后果,并产生一个长期对象temp来存储后果,而后计算temp + c3的后果,而后将后果复制给result还有值传递的形式的形参,例如: void func(Student s); // 值传递...Student stu;func(stu); // 这里相当于是做了一次复制操作 Student s(stu);而且这些长期对象随着生命周期的完结,编译器还会调用一次析构函数。随着这些操作次数的减少,或者当长期变量是个很大的类型时,这无疑会极大进步程序的开销,从而升高程序的效率。 C++11之后,随着右值援用的呈现,能够无效的解决这些问题。通过move和挪动结构,挪动赋值运算符函数来取得长期对象的所有权,从而防止拷贝带来的额定开销,进步程序效率 挪动结构咱们都晓得,因为C++11之前,如果没有手动申明,编译器会给一个用于自定义类型(包含class和struct)主动生成的4个函数,别离是构造函数,拷贝构造函数,赋值运算符重载函数和析构函数。尽管通过传援用的形式,能够防止对象的复制。然而还是没法防止上述的长期对象的复制。而挪动语义胜利的解决的这个问题。 在C++11之后,编译器主动生成的函数中又新增了2个,它们就是挪动结构和挪动赋值运算符重载函数,通过它们,咱们能够很好地实现对用户自定义类型的挪动操作。而挪动的实质就是获取长期对象的所有权,而不是通过复制的形式来取得。间接看代码: class Foo {public: Foo(Foo &&rhs) : ptr_(rhs.ptr_) { delete rhs.ptr_; } Foo &operator(Foo &&rhs) { if (*this != rhs) { ptr_ = rhs.ptr_; delete rhs.ptr_; } return *this; } private: int *ptr_;};Foo类重载了挪动构造函数和挪动赋值运算重载函数,使得Foo取得了挪动的能力,当咱们在面对产生长期的对象的时候,编译器就会依据传入的参数是左值还是右值来抉择调用拷贝还是挪动。如果是右值,就调用挪动结构或挪动赋值运算符函数。当Foo是一个很大的对象时候,就会极大的升高开销,进步程序效率。 ...

February 9, 2023 · 1 min · jiezi

关于c++:Leetcode专题233数字1的个数

力扣链接:https://leetcode.cn/problems/...解题思路: 找法则从个位数开始,依照以后位将数字分成右边high局部,左边low局部,以后位的数字分为三种类型:(1)0: res = high * digit;(2)1: res = high * digit + low + 1;(3)2/.../9: res = (high + 1) * digit;class Solution{ public: int countDigitOne(int n) { // 参数判断 if (n < 1) { return 0; } // 从个位数开始 long digit = 1; int high = n / 10, low = 0, cur = n % 10; int res = 0; while(cur != 0 || high != 0) { if (cur == 0) { res += high * digit; } else if (cur == 1) { res += high * digit + low + 1; } else { res += (high + 1) * digit; } low += cur * digit; high = high / 10; cur = high % 10; digit *= 10; } return res; }};

February 9, 2023 · 1 min · jiezi

关于c#:在-AspNet-Core-中什么是认证和授权

认证(Authentication) 和 受权(Authorization)在 Asp.Net core 充当了两个不同的职责。有的老伙计在了解的时候还存在误会。本文咱们将会通过一些简略的例子来阐明这两个概念。 认证(Authentication)辨认你是谁,受权(Authorization)决定你能做什么退出 A 用户当初通过浏览器想要拜访时总的网站,这个时候咱们须要晓得他是谁,也就是认证。如果他是一个普通用户,那么他只能拜访一些公开的页面,如果他是管理员,那么他能够拜访一些管理员的页面。这个时候咱们须要晓得他能做什么,也就是受权。 因而,认证是指辨认用户的身份,而受权是指决定用户能做什么。 特地阐明,辨认你是谁的意思是,你可能被辨认为一个普通用户,也可能被辨认为一个管理员,也可能被辨认为一个游客(匿名用户)。 脱离 Asp.Net Core 认证还有另外一层意思咱们常见的 OAuth2.0 认证、OpenID Connect 认证,账号密码认证,二维码认证等等,这些认证其实是用户与零碎交互而产生凭据的过程。这些凭据能够是一个 token,也能够是一个 cookie,也能够是一个 session。这些凭据都是用来辨认用户身份的。 为了区别这种状况,咱们将前者在本文中称为“登录形式”,后者称为“认证形式”。 而在 Asp.Net Core 中,认证是指申请中的凭据如何被转换为一个 Principal 或者 Identity 对象。所以咱们会见到 Claims-based authentication,也就是基于申明的认证。 所以实际上整个过程,能够了解为:用户通过登录形式登录,如果登录胜利,那么零碎会产生一个凭据,这个凭据回绝与采纳的认证形式无关,而是与 Asp.Net Core 中的认证形式无关。 举一些例子: 用户通过基于账号密码的 OAuth2.0 认证登录,那么零碎会产生一个 JWT token, 而后咱们应用 JWT bearer 认证形式,将这个 token 作为凭据,而后 Asp.Net Core 会将这个 token 转换为一个 Principal 或者 Identity 对象。用户通过手机扫码的形式登录,那么零碎会产生一个 session,而后咱们应用 cookie 认证形式,将这个 session 作为凭据保留在 Cookie中,而后 Asp.Net Core 会将这个 Cookie 转换为一个 Principal 或者 Identity 对象。但其实我也能够这样:用户通过基于账号密码的 OAuth2.0 认证登录,那么零碎会产生一个 JWT token, 而后咱们应用 cookie 认证形式,将这个 token 作为凭据保留在 Cookie中,而后 Asp.Net Core 会将这个 token 转换为一个 Principal 或者 Identity 对象。一些状况那么联合以上状况,咱们来甄别一些词语的意思: ...

February 8, 2023 · 1 min · jiezi

关于c:collect2exe-fatal-error-CreateProcess

明天解决了一个问题,就是:collect2.exe: fatal error: CreateProcess: No such file or directory。 其实实质上就是:在Windows上,传递给CreateProcess()的字符串(包含所有参数)的最大长度为32768个字符,看起来最终的链接器命令超过了这个限度。 咱们当初看看链接参数: aarch64-none-elf-gcc -o oneos.elf -mcpu=cortex-a55 -Lexternal -nostartfiles -Wl,--gc-sections,-Map=oneos.map,-cref,-u,_start -z max-page-size=4096 -T board/linker_scripts/link.lds @.link_param.tmp -LD:\xiaoneng\oneos-qiyun\drivers\link -lc -lm这么一看总的字符串长度,挺短的不是?其实不是这样的,因为有大量的须要链接的*.o文件门路存在于link_param.tmp。因而,咱们计算字符串长度的时候须要把它给开展,这一下就大了哟!!!很容易就超出了32767个字符!!! 晓得起因了,解决起来就有方向了,有以下方向: 躲避:让字符不要超过最大长度,把太多的.o文件间接归类为.a的库全副代替:间接替换工具链,让它反对更大长度局部代替:应用反对超出最大长度为32767个字符,这里能够间接代替collect2.exe这里改的形式就是应用了第三种计划,简略高效。 当然,还有各种有意思的改发,比方让代码的目录放在盘符根目录,其实也是躲避的一种,因为这样无效减小了*.o的门路,变相的减小了传递给CreateProcess()的字符串,也算是曲线救国了。。。

February 7, 2023 · 1 min · jiezi

关于c#:如何在-C-项目中链接一个文件夹下的所有文件

在 C# 我的项目中通过链接形式引入文件能够让咱们在我的项目中应用这些文件中的代码。常见的比方链接 AssemblyInfo.cs 文件,这样咱们就能够在我的项目中应用这个文件中的版本号等信息。然而如果咱们想要链接一个文件夹下的所有文件,该怎么做呢?明天咱们就来看看如何在 C# 我的项目中链接一个文件夹下的所有文件。 编辑我的项目文件引入单个文件在我的项目文件中,咱们能够通过 Compile 标签来引入单个文件。比方咱们想要引入 AssemblyInfo.cs 文件,咱们能够这样做: <Project> <ItemGroup> <Compile Include="../Shared/AssemblyInfo.cs"> <Link>Properties/AssemblyInfo.cs</Link> </Compile> </ItemGroup></Project>这样咱们就能够在我的项目中应用 AssemblyInfo.cs 文件中的代码了。 编辑我的项目文件引入文件夹下的所有文件那如果想要引入多个文件,咱们能够应用通配符来引入文件夹下的所有文件。比方咱们想要引入 Shared 文件夹下的所有文件,咱们能够这样做: <Project> <ItemGroup> <Compile Include="..\Shared\**\*.cs"> <Link>Properties/%(Filename)%(Extension)</Link> </Compile> </ItemGroup></Project>这样咱们就能够在我的项目中应用 Shared 文件夹下的所有文件中的代码了。 不过这样会使得所有的文件在我的项目中都会显示在 Properties 文件夹下,这样会让我的项目文件看起来很乱。咱们能够通过批改 Link 标签来批改文件在我的项目中的显示地位。比方咱们想要把 Shared 文件夹下的所有文件都显示在我的项目的根目录下,咱们能够这样做: <Project> <ItemGroup> <Compile Include="..\Shared\**\*.cs"> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link> </Compile> </ItemGroup></Project>别忘了应用 Directory.Build.props 文件下面的办法都是在我的项目文件中引入文件的,然而如果咱们有很多我的项目,那么咱们就须要在每个我的项目文件中都引入这些文件。这样会让咱们的我的项目文件变得很乱。咱们能够通过应用 Directory.Build.props 文件来解决这个问题。咱们能够在解决文件夹下创立一个 Directory.Build.props 文件,而后在这个文件中引入文件夹下的所有文件。比方咱们想要引入 Shared 文件夹下的所有文件,咱们能够这样做: <Project> <ItemGroup> <Compile Include="..\Shared\**\*.cs"> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link> </Compile> </ItemGroup></Project>总结通过下面的办法,咱们能够在 C# 我的项目中引入文件夹下的所有文件。这样咱们就能够在我的项目中应用这些文件中的代码了。 参考资料Linking files in a project1Directory.Build.props2感谢您的浏览,如果您感觉本文有用,请点赞、关注和转发;更多精彩内容请关注我的博客 https://www.newbe.pro和https:...。 本文作者: newbe36524本文链接: https://www.newbe.pro/Others/0x017-csharp-how-to-link-all-the-files-in-a-given-folder/版权申明: 本博客所有文章除特地申明外,均采纳 BY-NC-SA 许可协定。转载请注明出处! https://learn.microsoft.com/e... ↩ https://learn.microsoft.com/v... ↩

February 7, 2023 · 1 min · jiezi

关于c:CC常见函数编译报错警告二

1、地址问题问题形容:scanf语句输出数据时,变量前没有加 '&'语句编写: #include<stdio.h>int main(){。 int x; scanf("%d,x"); printf("%d\n",x); return 0;}编译正告:warning clyeo: local variable "x" used without h having been initjalized 解决办法:在变量X前加上地址符 '&'。输出:2 运行后果 2、参数问题:问题形容1putchar函数没有字符变量或者字符常量参数语句编写: #include<stdio.h>int main(){ char ch; ch=getchar(); putchar(); return 0;}编译正告:warning c4003: not enough actual parameters for macro 'putchar' 解决办法:在putchar函数形参中退出要输入的字符变量或者字符变量。输出:X 运行后果 问题形容2getchar函数中退出了参数。语句编写: #include<stdio.h>int main(){ char ch; getchar(ch); putchar(ch); return 0;}编译正告:warning C4602: too many actual parameters for macro 'getchan' 解决办法:去除getchar函数形参中字符变量或者字符常量。输出:X 运行后果 3、书写问题问题形容:printf函数少写一个字母f语句编写: #include<stdio.h>int main(){ int x; scanf("%d",&x); print("%d\n",x); return 0;}编译正告:warning C4013: "print"undefined; assuning extern returning int ...

February 2, 2023 · 1 min · jiezi

关于c:CCgetchar函数与putchar函数的用法

1、getchar函数函数格局:getchar(); 函数性能:从规范输出设施输出一个字符。语句编写: #include<stdio.h>int main(){ char ch; ch=getchar(); printf("ch=%c\n",ch); return 0;}输出:Y 运行后果: 留神阐明:(1)该函数没有参数,函数的返回值是从输出设施失去的字符。(2)从键盘上输出数据通过回车键完结,送入缓冲区。该函数从缓冲区中读入一个字符赋给字符变量。(3) 该函数也能够接管回车符。回车符也是一个字符语句编写: #include<stdio.h>int main(){ char ch1,ch2; ch1=getchar(); ch2=getchar(); printf("ch1=%c,ch2=%c\n",ch1,ch2); return 0;}输出:X 空格 运行后果: 2、putchar函数函数格局:putchar(ch);ch能够是一个字符型常量、变量或者是一个不大于255的整型常量或者变量,也能够是一个转义字符。 函数性能:向规范输出设备输入一个字符。语句编写: #include<stdio.h>int main(){ char ch='Y'; putchar(ch); return 0;}运行后果: 留神阐明:(1)输入字符型变量。(2)输入字符型或者整型变量。(3)输入转义字符。

February 1, 2023 · 1 min · jiezi

关于c:C-语言初学者必备开发工具DevCpp-图文安装教程

前言C 语言是一门功能强大的专业化编程语言,深受业余程序员和业余编程爱好者的青睐,同时 C 语言也是当今最风行的嵌入式开发语言。大多数嵌入式我的项目的开发都是用 C 语言来编写的。 既然 C 语言这么厉害,那学习 C 语言应该用什么软件呢? 举荐应用 Dev-Cpp在这我举荐初学者应用 Dev-Cpp,对这方面有肯定理解的敌人可能会说,咱们明明有更多更好的抉择,为什么要用这个小软件呀,它从 2016 年就进行更新了。 是的没错,一个从 2016 年就进行更新的软件,为什么能在当今泛滥功能强大的 C 语言 IDE 中成为首选呢? 那是因为它收费、安装简单、不必建工程、而且操作也简略,对于初学者来说,这些是要害中的要害,而不是说装置一个风行且功能强大的 IDE,鼓捣半天到最初可能开发环境都没配置胜利,就给劝退了。 初学阶段还是简简单单的比拟好,能够等学的差不多了,再去尝试功能强大的 IDE,那时你会有新的播种。 简略介绍 Dev-Cpp上面咱们来简略介绍一下这款软件。 Dev-Cpp 是一个 Windows 环境下的一个适宜于初学者应用的轻量级 C/C++ 集成开发环境(IDE)。它是一款自由软件,恪守 GPL 许可协定散发源代码。它汇合了 MinGW 中的 GCC 编译器、GDB 调试器和 AStyle 格局整顿器等泛滥自由软件。 Dev-Cpp 应用 MinGW/GCC 编译器,遵循 C/C++ 规范。开发环境包含多页面窗口、工程编辑器以及调试器等,在工程编辑器中汇合了编辑器、编译器、连贯程序和执行程序,提供高亮度语法显示的,以缩小编辑谬误,还有欠缺的调试性能,可能适宜初学者与编程高手的不同需要,是学习 C 语言和 C++ 的首选开发工具。 看到这里是不是认定就是它啦,那你真的很有眼光耶。既然认定是它啦,上面跟着我来把它装置到你的电脑上吧。 下载安装包对于 Dev-Cpp 安装包的获取,我提供了两种路径供读者敌人们自由选择,别离是百度云和 SourceForge,下载实现后,记得回来看装置教程呦。 百度云我已将本篇所应用的安装包打包上传至百度云,扫描下方二维码关注「main工作室」,后盾回复【<font color=red>0004</font>】即可收费获取分享链接。 SourceForgeSourceForge 是寰球最大的凋谢源代码软件开发平台和仓库。网站建设的主旨是为开源软件提供一个存储、合作和公布的平台,领有大量十分优良的开源软件。 SourceForge 官网下载链接 >> 点击跳转在该网页点击“ Download ”,期待几秒便会弹出下载提醒,下载实现后,依据装置教程进行软件的装置即可。 ...

February 1, 2023 · 1 min · jiezi

关于c++:C中static关键字的作用

static是什么在最开始C中引入了static关键字能够用于润饰变量和函数,起初因为C++引入了class的概念,当初static能够润饰的对象分为以下5种: 成员变量,成员函数,一般函数,局部变量, 全局变量 static的作用润饰成员变量static润饰成员变量之后,该变量会属于该类,而不是某一个该类的对象。举个例子,Student类种有一个count的变量,在应用static关键字润饰之后,所有Student的对象共用这1个count。 调用形式会产生扭转,无奈通过 对象名 + . 变量名来调用,而是须要通过类名 + 作用域(::) + 变量名来调用,举个例子 Studnet s1;cout << s1.count << '\n'; // 会编译正告 Clang-Tidy: Static member accessed through instance 通过实例调用动态成员变量cout << Studnet::count << '\n'; // ok润饰成员函数和成员变量一样,应用static润饰的成员函数的生命周期和应用形式都产生了变动 通过static润饰的函数,如果拜访非static成员变量,编译器会间接报错 润饰一般函数函数的作用域会发声变动,被static润饰的一般函数只能在本文件内能够见,同一个程序的其余文件将无奈调用该函数。能够在肯定水平上解决命名抵触的问题,不过C++提供了namespace,所以个别不用于润饰一般函数。 润饰全局变量和润饰一般函数一样,被static润饰的全局变量的可见性会发生变化,其余文件将无奈调用该全局变量,其余和一般全局变量没有区别 润饰局部变量static润饰的局部变量被初始化一次之后,每次函数调用都持续应用之前的值,而不是从新进行初始化操作 如何应用static成员变量通过在成员变量后面加上关键字static即可 class Studnet {private: static int count;};// static润饰的成员变量只能在类外初始化int Student::count = 0;// C++17之后能够通过inline的形式在类内初始化,例如class Studnet2 {private: static inline int count = 0;};成员函数class Studnet {public: static int init(int number1, int number2) { age = number1; // 编译报错 Invalid use of member 'age' in static member function count = number2; // ok }private: static inline int count = 0; int age = 18;};一般函数static int add(int a, int b) { return a + b;}全局变量static int count = 2;局部变量void print() { static int a = 0; ++a; cout << a << endl;}底层原理之所以被static润饰的变量或者函数的生命周期会超过摆布其所在的作用域的实质是因为它在内存中的存储地位产生了变动 ...

February 1, 2023 · 1 min · jiezi

关于c:CCscanf函数的使用负载printf函数格式说明

1、函数格局:scanf(格局管制字符串,变量地址列表) 函数性能:通过规范输出设施(键盘、写字板等),依照格局管制字符串中的格局要求为变量地址列表中的变量输出数据。假如变量a和b的值任意输出,如何将a和b的值进行替换后输入?语句编写: #include<stdio.h>int main(){ int a,b,t; scanf("%d%d",&a,&b); t=a; a=b; b=t; printf("a=%d,b=%d\n",a,b); return 0;}运行后果: 留神阐明(1)格局管制字资串中多个格局转换说明符之间没有逗号,输出数据时,通常应用空格键或者回车键来分隔数据;格局转换说明符之间有逗号,输出数据时,肯定要用逗号来分隔数据。(2)格局转换说明符个数和类型必须与变量地址列表 --对应。 (3)*(克制字符):示意本输出项只是读入,但不赋给相应变量。 格局管制字符串:格局转换说明符性能形容%d输出一个十进制整数%f输出一个单精度实数%lf输出一个双精度实数%c输出一个字符%s输出一个字符串%o输出一个八进制整数%x输出一个十六进制整数%*示意本输出项只是读入,但不赋给相应变量变量地址列表串:变量地址列表由我的项目组成,两个输出我的项目之间用逗号宰割,输出我的项目个别由地址符&和变量名组成,即:&变量名。 1、printf函数函数格局printf(格局管制字符串,输入列表) 函数性能:讲输入列表中的各个表达式的值依照格局管制字符串中对应的格局输入到规范输出设备(显示屏) 格局管制字符串:格局转换说明符性能形容%d输入一个十进制整数%f输入一个单精度实数%lf输入一个单精度实数%e或%E按指数格局输入一个实数%c输入一个字符%s输入一个字符串%o输入一个八进制整数%x输入一个十六进制整数输入列表:输入列表有输入列表组成,两个输入项之间用逗号分隔,输入项能够是个别的表达式,也能够是简略变量,即:变量名或者表达式。 主见阐明:(1)格局转换说明符个数和类型必须与输入列表--对应。(2)格局管制字符串中能够有转义字符和一般字符。转义字符依据具体作用实现操作,一般字符原样输入。(3) 修饰符m(正整数):指定输入项所占的宽度,当指定宽度小于理论宽度时按理论宽度输入,当指定宽度大于理论宽度时在后面用空格补足。(4)修饰符.n(正整数):指定输入的实型数据的小数位数(四舍五入),零碎默认小数位数为6。(5)修饰符0(数字):指定数字前的空格用0填补。(6)修饰符-;指定输入项的对齐形式,示意左对齐。

January 31, 2023 · 1 min · jiezi

关于c++:C1-学习C的意义

学习C++的劣势 古代软件产品架构图 古代软件产品的个性零碎个别由多种语言写成零碎架构指标是拥抱用户需要文化零碎领有较好的移植性零碎可能不便部署与更新 小结

January 30, 2023 · 1 min · jiezi

关于c:CC算法定义及特征

1、算法定义:现实生活中解决问题时,个别都要定制一个针对具体问题的步骤和办法,以此为据去实现目标。将为了解决问题所定制的步骤,办法称为算法(Algorithm)。 计算上面分段函数 算法形容:(1)输出x的值;(2)判断x是否大于0,若大于0,则y为2x-1而后转第5步;否则进行第3步;(3)判断x是否等于0,若等于0。则y为0,而后转第5步;否则进行第4步:(4)y为3x+1; 算法特色:(1)有穷性:算法中所蕴含的步骤必须是无限的,不能无穷无止,应该在一个人所能承受的正当时间段内产生后果;(2)确定性:算法中的每一步所要实现的指标必须是明确无误的,不能有二义性;(3)有效性:算法中的每一步如果被执行了,就必须被无效地执行。例如,有一步是计算X除以Y的后果,如果Y为非0值,可无效执行,但如果Y为0值,则无奈失去无效执(4)有零或多个输出:依据算法的不同,有的在实现过程中须要输出一些原始数据,而有些算法可能不须要输出原始数据;(5)有一个或多个输入:设计算法的最终目标是为了解决问题,为此,每个算法至多应该要有一个输入后果,来反馈问题的最终后果。 2、流程图罕用的符号 流程图的个别示意形式:(1)示意相应操作的框。(2)带箭头的流程线。(3)框内外必须要文字说明。用流程图示意上面的分段函数: 3、程序设计程序构造流程图:执行过程:先执行A,在执行B(左图所示)求x的绝对值,请画出该算法的流程图(右图所示) 抉择构造流程图执行过程:先判断条件,如果条件成立,执行A,否则,执行B。(上图所示) 循环构造流程图:执行过程:先判断条件,如果条件成立,执行A,再循环判断条件,否则,跳出循环。(上图所示)

January 30, 2023 · 1 min · jiezi

关于c:CC常见赋值编译报错警告如何处理

问题形容1将默认为双精度的实型常量赋值给单精度变量。语句编写: #include<stdio.h>#define pl 3.14int main(){ float r,area; r=1.5; area=pl*r*r; printf("area=%f\n",area); return 0;}编译正告️werning C4244:"=’:co onversion from 'double / to "float但不影响程序运行 解决办法:如果将float批改为double,将%f批改为%lf后,就没有正告了。运行后果: 问题形容2两个定义的时候,同时赋初值。语句编写: #include<stdio.h>int main(){ int x=y=2,z; z=x+y; printf("x+y=%d\n",z); return 0;}编译谬误erlor c2065: 'y' : undeclared identifier解决办法:变量x和y独自定义独自赋初值,将int x=y=2,z批改为int x=2,y=2,z;运行后果: 问题形容3:将带双引号的字符串赋值给了字符变量语句编写: #include<stdio.h>int main(){ char ch; ch="A"; printf("%c的ASCll的值为:%d\n",ch,ch); return 0;}编译正告️ warning c4847: "=" : "char " diff Fers in levels of indirection from "char [2]解决办法:将字符串"A"批改为字符'A'。运行后果: 问题形容4:实型数据参加了%(求余)运算语句编写: #include<stdio.h>int main(){ printf("%d\n",3%2.0); return 0;}编译谬误:error C2297: '%' : illegal, right operand has type "const double "解决办法:只有整数能力加入%(求余)运算,将2.0批改为2。运行后果: ...

January 29, 2023 · 1 min · jiezi

关于c:开源C语言库Melon多线程治理

问题形容不知你是否有过相似如下的需要: 有一些性能,它们足够繁多,但又须要后盾继续运行,以容器实现感觉太重了,以过程实现又太琐碎了,以线程实现能够承受然而又不好治理。 这类程序诸如:数据采集程序、可观测性程序、中间件、代理等等。 这一需要乍看之下倒是有点相似supervisor在做的事件,每个性能一个繁多后盾过程。诚然过程是一个抉择,然而理论应用中则会面临是大量的可执行程序和因人而异的开发格调。 当然,抉择多线程还有另一个重要起因,这里先卖个关子,咱们往下看。 解决方案因而,笔者将介绍一个开源C语言库——Melon,它实现了一套多线程框架。在这套框架之下,每一个线程是一个独立的功能模块,并且能够承受来自主线程的治理。 对于 Melon 库,这是一个开源的 C 语言库,它具备:开箱即用、无第三方依赖、装置部署简略、中英文文档齐全等劣势。 Github repo 对于上述的问题,咱们能够应用这一框架来解决。除此之外,Melon还反对了另一个性能,这也是抉择多线程的起因之一,谜底将在示例中揭晓。 示例在Melon的多线程框架中,有两种形式能够启动不同的线程模块,上面的示例将以动态创建和杀掉线程的形式进行演示。 #include <stdio.h>#include <errno.h>#include <unistd.h>#include "mln_core.h"#include "mln_log.h"#include "mln_thread.h"#include "mln_trace.h"int sw = 0; //开关switch缩写char name[] = "hello";static void thread_create(mln_event_t *ev);static int hello_entrance(int argc, char *argv[]){ printf("%s\n", __FUNCTION__); while (1) { mln_trace("s", "Hello"); usleep(10); } return 0;}static void timer_handler(mln_event_t *ev, void *data){ if (!sw) { mln_string_t alias = mln_string("hello"); mln_thread_kill(&alias); mln_event_timer_set(ev, 1000, NULL, timer_handler); } else { thread_create(ev); } sw = !sw;}static void thread_create(mln_event_t *ev){ char **argv = (char **)calloc(3, sizeof(char *)); if (argv != NULL) { argv[0] = name; argv[1] = NULL; argv[2] = NULL; mln_thread_create(ev, "hello", THREAD_DEFAULT, hello_entrance, 1, argv); mln_event_timer_set(ev, 1000, NULL, timer_handler); }}int main(int argc, char *argv[]){ struct mln_core_attr cattr; cattr.argc = argc; cattr.argv = argv; cattr.global_init = NULL; cattr.main_thread = thread_create; cattr.worker_process = NULL; cattr.master_process = NULL; if (mln_core_init(&cattr) < 0) { fprintf(stderr, "Melon init failed.\n"); return -1; } return 0;}能够看到,main函数中只初始化了Melon库。而多线程框架也正是在库初始化时启动的。 ...

January 29, 2023 · 2 min · jiezi

关于c:CC算术运算符与表达式基本赋值逗号运算符全

1.分类:(1)加法 “+”,或正值运算符,如2+9=11,+6。(2)减法 “_”,或负值运算符,如9-5=4,-5。(3)乘法“”,如48=32。(4)除法“/”,如7/2=3,整除后果为整数,舍去小数,只取商。(5)求模 “%”,或称求余运算符,要求两侧均为整数,如9%2=1 2.优先级:() 〉 *、/、%、 〉 +,– 自增自减运算符1.作用:自增运算使单个变量的值增1,自减运算使单个变量的值减1。2、运算规定:(1)前置运算:运算符放在变量之前:++a、--a,先使变量的值增;(或减)1,而后再以变动后的值参加其它运算,即先增减、后运算。 (2)后置运算:运算符放在变量之后:a++、a--,变量先参加其它运算,而后再使变量的值增(或减)1,即先运算、后增减。举例: #include<stdio.h>int main(){ int i=3,j; j=i++; printf("i=%d,j=%d\n",i++,j); j=++i; printf("i=%d,j=%d\n",++i,j); j=--i; printf("i=%d,j=%d\n",i--,j); j=i--; printf("i=%d,j=%d\n",--i,j); return 0;}运行后果: 留神阐明:(1)++和--只能用于变量,而不能用于常量或表达式。如:(i+j)++或5--是不非法的。(2)++和--的联合方向是“自右至左”。如:i=4,则-i--相当于-(i--)后果为-4,而i的值为3。(3)运算符的组合准则是自左而右。如:a+++b等价于(a++)+b,而不是a+(++b)。(4)++和--罕用于循环语句中,使循环变量加(或减)1,指针变量中,使指针上移(或下移)一个地位。 定义:算术表达式是用算术运算符和括号将运算对象(也称操作数)连接起来的、合乎C语法规定的式子,其中运算对象能够是常量、变量、函数等。例如:a*b/c-1.5+’a’是一个非法的算术表达式。 留神阐明:1)与数学表达式的书写模式的区别:C语言算术表达式的乘号()不能省略。例如:b2-4ac,应该写成bb-4ac。 C语言表达式中只能呈现字符集容许的字符。例如:r2应该写成PIrr。C语言算术表达式不容许有分子分母的模式。例如:(a+b)/(c+d)不等于a+b/c+d。 C语言算术表达式只应用圆括号扭转运算的优先程序(不能用{}[])。(2)各运算符的“优先级”和“联合性”:在表达式求值时,按运算符的优先级高下秩序执行,如:a-bc等价于a-(bc)。如果优先级别雷同,则按规定的“联合方向”,如表达式:a-b+c联合性为“自左向右”,所以等价于(a-b)+c。C对于简单表达式为了清晰起见能够加圆括号“()”强制规定计算程序。 2、赋值运算符与表达式复合赋值运算符+=,–=,*=,/=,%=,<<=,>>=,&=,”=,丨= 个别模式:变量 复合赋值符=表达式 ← → 变量=变量 运算符 表达式例如:a+5=5 等价于 a=a+5x=y+7 等价于 x=x(y+7)r%=p 等价于 r=r%p 定义:由赋值运算符组成的表达式称为赋值表达式。个别模式:变量=表达式例如:x=5 赋值表达式x=5的值为 为5,x的值也为5。 x=1%2+(y=5) 赋值表达式的值为6, x的值也为6,y的值为5。 a=(b=6)或a=b=6 赋值表达式的值为6, a、b的值均为6。 a+=a(a=5) 相当于a=5+55,赋值 表达式的值为30,a的值最终也是30。 性能:赋值表达式的性能是计算表达式的值在赋予右边的变量。赋值语句举例语句编写: #include<stdio.h>int main(){ int a; //定义整型变量 a=34.567; //赋值 printf("a=%d\n",a); //输入后果 return 0;}运行后果: 留神阐明:(1)赋值表达式加上一个分号则可形成赋值语句,即:变量=表达式:(2)赋值语句不是表达式,表达式能够用在其它语句或表达式中,而语句只能作为一个独自的语句应用。(3)C语言规定:能够在定义变量的同时给变量赋值,也叫给变量初始化。例如:int x=5;(4) 赋值运算时,当赋值运算符两边数据类型不同时,零碎主动进行类型转换,转换准则是:先将赋值号左边表达式类型转换为右边变量的类型,而后赋值。 ...

January 28, 2023 · 1 min · jiezi

关于c:常见的数据类型转换

1、什么叫做主动类型转换?规定:(1)若参加运算量的类型不同,则先转换成同 类型,而后进行运算。 (2)转换按数据长度减少的方向进行,以保障精度不升高。如int 型和lbng 型运算时,先把int量转成long型后再进行运算。(3)所有的浮点运算都是以双精度进行的,即便仅含float单精度量运算的表达式,也要先转换成double 型,再作运算。(4)char型和short型参加运算时,必须先转换换成int型。 举个栗子假如已指定i为整型变量,f为float型变量,d为double型变量,e为long型变量,表达式为:10+'a'4i*f-d/e,则表达式的运行秩序?运行程序:第一步运行: i*f第二步运行: d/e第三步运行: 10+’a’第四步运行: 10+’a’+i*f第五步运行: 10+’a’+i*f–d/e语句编写: #include<stdio.h>int main(){ int i=2; float f=1.5; double d=4.0; long e=2; printf("i*f=%f,i转化为float类型参加运算",i*f); printf("d/e=%lf,e转化为double类型参加运算",d/e); printf("10+'a'=%d,'a'转化为int类型参加运算",10+'a'); printf("10+'a'+i*f=%f,int转化为float类型",10+'a'+i*f); printf("10+'a'+i*f-d/e=%lf.float转化为double类型",10+'a'+i*f-d/e); return 0;}运行后果: 2、强制类型转换如何应用?个别模式(类型说明符)(表达式)举个栗子:(double)a 将变量a强制转换为double类型(int)(x+y) 将x+y的值强制转换为int类型(float)(5%3)将5%3的值强制转换为float类型(float) x/y 将x强制转换成float类型后,在参加运算 语句编写: #include<stdio.h>int main(){ float f; f=9.8; printf("(int)f=%d,f=%f\n",(int)f,f); return 0;}运行后果: 留神阐明:●表达式应该用括号括起来。●进行强制类型转换时,失去的是一个所需类型的两头变量,原来变量的类型并未产生扭转

January 27, 2023 · 1 min · jiezi

关于c:C语言常量与变量的意义

常量与变量总结:⭐整型常量: 通常的整数。⭐实型常量:实型也叫浮点型,实型常量也叫实数或浮点数,在C语言中,实数只用十进制示意。⭐字符常量:字符常量是用单引号指起来的一个字符。 ⭐转义字符:转义字符以反斜线”\”结尾,后跟一个或几个字符。⭐符号常量:个别模式:#define 标识符 常量⭐整型变量:基本型:类型说明符为 在内存中占4个字节。 短整型:类型说明符为short int或者 isnort 听占字节和取值范畴均与基本型雷同。 长整型:类型说明符为long int或者leng,在内存中占4个字节。无符号型:类型说明符为unsigned。注:无符号型也能够和上述三种类型匹配应用。定义:整型变量的定义个别模式为: 类型说明符 变量1[=值1],变量2[=值2],......;⭐字符变量字符变量的定义个别模式为: 类型说明符 变量1[=值1],变量2[=值2],..... 常量与变量:标识符是对变量名、函数名、标号和其余各种用户定义的对象名。名规定:标识符由字母、数字或者下划线组成,且第一个字符必须是字母或下划线。留神:(1)标识符辨别大小写;(2)标识符的无效长度取决于具体的c编译系统:(3)标识符的书写个别采纳具备肯定理论含意的单词,这样可进步程序的可读性,(4)标识符不能与c语言的关键字同名,也不能与自定义函数或c语言库函数同名 关键字是具备固定名字和特定含意的非凡标识符、也称保留字,不容许将它们另作别用。32个关键字:(1)数据类型定义:vpedef(2)数据类型:char,double,enum,float,int,long, short, struct,union, unsighed,void, signed, volatile, auto, extern, register, static, const(3)运算符:sizeof(4)语句:break,case,continue,default,do, else, for, goto, if, return, switchs.while 间接常量 1.整型常量:(1)十进制整数: 如250、12,每个数字位是0~9,属于非法的;如058、35,含有非 十进制数码,属于不非法的。(2)八进制整数:最高位为0,如十进制的128,用八进制示意为0200,每个数字位是0~7,属于非法的;如256(无前缀0)、02A6(蕴含了非八进制数码A)、 -0256(呈现了负号),属于不非法的。(3)十六进制整数:以0x或0X结尾,如十进制的128,用十六进制示意为0x80或0X80,每个数字位能够是0~9,A~F,其中A代表10,B代表1.….,属于非法的。如3A(无前缀0x)、0x8H(蕴含了数码H),属于不非法的。 2、实型常量:实型也叫浮点型,实型常量也叫实数或浮点数,在C语言中,实数只用十进制示意。 (1)十进制数模式:(必须有小数点)5,如0.123、.123、123.0、0.0都属于非法的。(2Y指数模式: (e或E之前必须有数字,指数必须为整数),如12.3e3、123E2、1.23e4 属于非法的;而 e-5、1.2E-3.5属于不非法的。 3、字符常量:字符常量是用单引号指起来的一个字符。 (1)字符常量只能用单引号括起来,不能用双引号或其它括号。如 ’、'b’'=’、'+’、'?’ 都属于非法的。(2)字符常量只能是单个字符,不能是字符串。(3)字符能够是字符集中任意字符。但数字被定义为字符型之后就不能参加数值坛算如'5'和 5 是不同的。 4、转义字符:转义字符以反斜线”\”结尾,后跟一个或几个字符。 字符模式功 能\n换行\t横向跳格(即跳到下一个输出区)\b退格\r回车\f走纸换页\\反斜杠字符’\’\’单撇号字符\”双撇号字符\a报警,相当于’\007’\ddd1~3位8进制数所代表的字符\xhh1~2位16进制数所代表的字符举个栗子语句编写: #include<stdio.h>int main(){ printf("l love music!\n"); printf("the music is \"D:\\ music\\love.mp3\".\n"); return 0;}运行后果: 符号常量个别模式:#define 标识符 常量问题:已知圆的半径为1.5,求圆的面积语句编写: #include<stdio.h>#define Pl 3.14 //符号常量int main(){ float r,area; //定义变量 r=1.5; //赋值 area=Pl*r*r; //计算语句 printf("area=%f\n",area); //输入后果 return 0;}运行后果: ...

January 26, 2023 · 1 min · jiezi

关于c++:C语言中的4大基本数据类型

四大类型:⭐根本数据类型 整型 int 字符型 char 实型(浮点型):单精度实型 float 双精度实型 double⭐构造类型数组类型构造类型 struct联结类型 union枚举类型 enum⭐指针类型⭐空类型 void 思维导图 根本数据类型:整型 int int类型在内存中占用了4个字节,也就是 32位。int类型是有符号的,因而,32位 并不会全副用来存储数据,应用最高位来 存储符号,最高位是0,提醒数据是正 数,最高位是1,示意数据是正数,应用 其余的31位来存储数据。字符型 charchar类型只占一个字节,取值范畴为-128~ +127(-2^7~2*7-1)。 单精度实型 float float类型占 4 个字节,包含一个符号位、一个 8 位 excess-127 二进制指数和一个 23 位尾数。 尾数示意一个介于 1.0 和 2.0 之间的数。 因为尾数的高程序位始终为 1,因而它不是以数字模式存储的。 此示意模式为 float 类型提供了一个大概在 3.4E–38 和 3.4E+38 之间的范畴。双精度实型 doubledouble类型占8个字节。比特数为64,有效数字16位,示意范畴是-1.79E+ 308~-1.79E+308

January 25, 2023 · 1 min · jiezi

关于c:开源C语言库MelonIO线程模型

本文展现开源C语言库Melon中的I/O线程模型。在Melon中存在三种线程模型,本篇仅对I/O线程模型进行阐明。 对于 Melon 库,这是一个开源的 C 语言库,它具备:开箱即用、无第三方依赖、装置部署简略、中英文文档齐全等劣势。 Github repo 简介首先简略介绍一些什么是I/O线程模型。这一话题将引入图形界面或挪动APP开发进行阐明。 在惯例的波及网络通信的图形界面客户端程序(如微信、QQ等等)中,客户端既要负责界面展现,也要负责网络数据传输。然而网络传输操作实际上是会存在拥塞和丢包等不稳固因素,因而就存在了阻塞通信与非阻塞通信。在图形界面的程序开发中,很多图形接口的应用与非阻塞I/O很难交融于一个线程内。因而,如果一个线程即负责图形渲染,又负责IO通信,则会呈现界面卡顿甚至卡死。 很显然,这样的人机交互界面很不敌对,因而就呈现了渲染与IO拆散,分为了两个线程。即主线程做界面渲染,子线程做IO通信。这也就是I/O线程这一名词的由来。 Melon反对I/O线程模型不仅仅是为了图形界面开发的须要,也是为了兼顾一些中间件的开发须要。上面咱们来一起看一下其应用。 应用咱们先给出代码,再进行阐明。 #include "mln_iothread.h"#include <string.h>#include <stdio.h>#include <errno.h>static void msg_handler(mln_iothread_t *t, mln_iothread_ep_type_t from, mln_iothread_msg_t *msg){ mln_u32_t type = mln_iothread_msg_type(msg); printf("msg type: %u\n", type);}static void *entry(void *args){ int n; mln_iothread_t *t = (mln_iothread_t *)args; while (1) { n = mln_iothread_recv(t, user_thread); printf("recv %d message(s)\n", n); } return NULL;}int main(void){ int i, rc; mln_iothread_t t; struct mln_iothread_attr tattr; tattr.nthread = 1; tattr.entry = (mln_iothread_entry_t)entry; tattr.args = &t; tattr.handler = (mln_iothread_msg_process_t)msg_handler; if (mln_iothread_init(&t, &tattr) < 0) { fprintf(stderr, "iothread init failed\n"); return -1; } for (i = 0; i < 1000000; ++i) { if ((rc = mln_iothread_send(&t, i, NULL, io_thread, 1)) < 0) { fprintf(stderr, "send failed\n"); return -1; } else if (rc > 0) continue; } sleep(1); mln_iothread_destroy(&t); sleep(3); printf("DONE\n"); return 0;}main中的流程大抵如下: ...

January 24, 2023 · 1 min · jiezi

关于c:开源C语言库MelonCron格式解析

本文介绍开源C语言库Melon的cron格局解析。 对于 Melon 库,这是一个开源的 C 语言库,它具备:开箱即用、无第三方依赖、装置部署简略、中英文文档齐全等劣势。 Github repo 简介cron也就是咱们常说的Crontab中的工夫格局,格局如下: * * * * *分 时 日 月 周例如: 30 21 * * * 示意:每晚的21:30 应用Melon中cron格局解析器会将上述格局解析成一个time_t类型值。应用代码如下: #include "mln_cron.h"#include <stdio.h>int main(void){ char p[] = "* * * * *; mln_string_t s; mln_string_nset(&s, p, sizeof(p)-1); time_t now = time(NULL); time_t next = mln_cron_parse(&s, now); printf("%lu %lu %s\n", (unsigned long)now, (unsigned long)next, ctime(&next)); return 0;}能够看到,本例中,cron格局为* * * * *,含意即是每分钟。 程序流程大抵: 初始化格局字符串获取以后零碎秒值将以后秒值作为参考基数,来计算该cron格局的秒级工夫戳,即下一秒的工夫值输入cron解析器返回的秒值及其格式化字符串内容Melon中cron解析器在mln_cron.h中,应用时须要include进来。 另外需注意,在Melon中,cron临时不反对-示意范畴值。 结语cron格局解析目前能反对的格局是crontab的一个子集,后续随着应用需要可能会再进行扩大。 ...

January 24, 2023 · 1 min · jiezi

关于c:头文件主函数和语句格式常见的4种问题

最常见的4种问题别离是:头文件:没有加头文件studio.h主函数:主函数第一个问题字母大写了。语句格局:(1)语句完结后忘加分号";" (2) 将英文状态下的双引号""写成中文状态下的双引号“” 接下来咱们别离来看这4种具体的谬误问题: 头文件问题形容:没有加头文件studio.h语句编写: //没有加头文件<studio.h>main(){ printf("welcome to c_program!\n"); return 0;}编译正告️warning C4013: 'printf' undefined; assuming extern returning int解决办法:在主函数下面加上代码#include "studio.h"运行后果: 主函数:问题形容:主函数第一个问题字母大写了。语句编写: #include<stdio.h>Main() //主函数第一个问题字母大写了。{ printf("welcome to c_program!\n"); return 0;}编译正告️:error LNK2001: unresolved external symbol _main解决办法:将Main批改为main运行后果 语句格局:问题形容1:语句完结后忘加分号";"语句编写: #include<stdio.h>main(){ printf("welcome to c_program!\n") //语句完结后忘加分号";" return 0;}编译正告️:error C1243: syntax error : missing ';' before ';'解决办法:在语句printf("welcome to c_program!\n")前面加上分号";" 运行后果:问题形容2:将英文状态下的双引号""写成中文状态下的双引号“”语句编写 #include<stdio.h>main(){ printf(“welcome to c_program!\n”); //将英文状态下的双引号""写成中文状态下的双引号“” return 0;}编译正告️error C2018: unknown character '0xa1'error C2018: unknown character '0xa0'解决办法:将中文状态下的双引号“”批改为英文下的双引号 ""运行后果: ...

January 24, 2023 · 1 min · jiezi

关于c:C语言的开发过程与Visual-C60开发环境

本期指标::::info ★C语言的开发过程★Visual C++6.0开发环境:::————————————————————————————————————————————— ★C语言的开发过程(1)编辑源程序:C语言源程序的录入和批改,文件扩展名必须为“.c”。(2)编译源程序:像翻译一样,将已编辑好的源程序翻译成二进制的指标代码,并产生以“.ob”为扩展名的目标程序。(3)连贯目标程序:编译后产生的目标程序和库函数进行连贯能力运行,连贯后,产生以“.exe”为扩展名的可执行程序。(4)运行:可执行程序生成后,就能够在操作系统的反对下运行,输入后果。 ★Visual C++6.0开发环境1、启动Visual C++6.0(1)抉择 "开始" 菜单 "程序" 项或者抉择桌面快捷方(2)关上VC6.0,在菜单栏中抉择“文件 -> 新建”,或者 Ctrl+N,弹出上面的对话框:(3)切换到“工程”选项卡,抉择“Win32 Console Application”,填写工程名称和门路,点击“确定”,会弹出一个对话框询问类型,这里抉择“一个空工程”,如下图所示:(4)点击“实现”按钮实现工程的创立。 2、新建C源文件(1)在菜单栏中抉择“文件 -> 新建”,或者 Ctrl+N,弹出上面的对话框:(2)切换到“文件”选项卡,抉择“C++ Source File”,填写文件名,点击确定实现。该步骤是向方才创立的工程增加源文件。 3、编写C语言代码(1)在工作空间中能够看到方才创立的工程和源文件,如下图所示:(2)双击 hello.c,进入编辑界面,输出上节中的代码。 4、编译并运行代码(1)你能够在“组建”菜单中找到编译、组建和运行的性能,如下图所示:(2)更加简略的办法是应用快捷方式,如下图所示:(3)保留编写好的源代码,点击运行按钮或 Ctrl+F5,如果程序正确,能够看到运行后果,如下图所示: 留神:编译生成的 .exe 文件在工程目录下的Debug文件夹内。以下面的工程为例,门路为 E:\cDemo,关上看到有一个Debug文件夹,进入能够看到 cDemo.exe。在Debug目录中还会看到一个名为 hello.obj 的文件。.obj是VC/VS生成的指标文件,相似于C-Free下的.o文件。 工程文件阐明:进入工程目录 E:\cDemo,除了 hello.c,还会看到很多其余文件,它们是VC6.0创立的,用来反对以后工程,不属于C语言的范畴,你能够疏忽它们。 如果您感兴趣,咱们也提供了简略的阐明:(1) .dsp文件:DeveloperStudio Project,工程文件(文本格式),用来保留以后工程的信息,例如编译参数、蕴含的源文件等,不倡议手动编辑。当须要关上一个工程时,关上该文件即可。(2) .dsw文件:DeveloperStudio Workspace,工作区文件,和DSP相似。(3) .opt文件:IDE的Option文件,保留了与以后工程无关的开发环境的配置,例如工具条地位、关上的文件、光标地位等。(4) .plg文件:日志文件(HTML文件),保留了程序的编译信息,例如谬误和正告等。一个工程能够蕴含多个源文件和资源文件(图片、视频等),但只能生成一个二进制文件,例如可执行程序.exe、动态链接库.dll、动态链接库.lib等。工程类型决定了不同的配置信息,也决定了生成不同的二进制文件。一个工作区能够蕴含多个工程,可能批量生成多个二进制文件。咱们装置的较大的程序,装置目录中个别蕴含多个 EXE 和 DLL。对于这样的程序,能够先创立一个工作区,再创立多个工程,这样就能一次性生成所需的多个二进制文件。 往期文章:上期文章C发展史的特点与常见的C语言程序下棋文章:待更新

January 23, 2023 · 1 min · jiezi

关于c:还没学C就哭着说难真的比找女朋友还难吗菜鸟级C教程

C语言设计—菜鸟明轩 一、C语言概述★简述C发展史及特点★常见的高级菜鸟C语言程序1.简述C发展史及特点发展史:1、1963年,剑桥人学将ALGOL 60语言倒退成为CPL语言。2、1967年,朝侨大学的Martin Richards 对CPL语言进行了简化,产生了BCPL语言。3、1970年,美国贝尔实验室的Ken Thompson将BCPL中的精髓提炼进去,并为它起了一个乏味的名字“B语言”。4、1973年,美国贝尔实验室的Dennis M.Ritchie在B语言的根底上最终设计出了一种新的语言,即C语言。5、1977年,Dennis M.Ritchie 发表了不依赖于机器零碎的《可移植的C语言编译程序》。6、1978年,Brian W.Kemighian和Dennis M.Ritchie出版The C Programming Language7、1989年,ANSIC规范被采纳。 C语言的特点:1、C请言具备构造通言的特点,程水之间得的期实药实的其事。2、C语言的主着构造成分是法数。3、运简得丰盛。4、数瞩类型丰盛。5、比拟靠近进件。6、道法眼制少和程序设计自作度大。7、生成日标代码品质高、程序执行改车高。8、可移植性好,基本上不能批改能的用子各种型号的十算机和各种操作系统。 2.常见的高级菜鸟C语言程序例码1输入"Hello would!"代码语句:运行后果:代码阐明: #include <studio.h> //编译预处理命令是在程序编译之前要解决的内容,称为编译预处理命令。void main() //函数首部 示意无返回值 也就是尾部不须要增加return0;{ ptintf("Hello would!");} //函数体 简略来说是程序中定义一个函数性能的所有代码组成的整体。注意事项:(1)每个C程序必须有,且只能有一个主函数(main函数)(2)一个函数是有两个局部组成,函数首部及函数体(3)函数体由"{ }"括起来的(4)函数调用语句,printf函数是输入函数,是把内容输入到显示器去显示的(5)双引号的内容依照原样输入,然而"\n"是转义字符,代表的是换行例码2:有两个数,求它们的和,并输入代码语句:运行后果:代码阐明: #include "studio.h" // .h为头文件 双引号快起来及尖括号也行main(){int x,y,sum; //定义三个变量 x=3,y=7; //变量赋值 sum=x+y; //计算和 printf("sum=%d\n",sum); return 0; //return 0;代表程序失常退出}注意事项:(1) #include称为文件蕴含命令,扩大名为.h的文件称为头文件。(2)//表明为行正文局部, /..../表明为局部正文只有在符号两头的内容都被正文。都示意这句话的意思及代表着什么性能(3)每一个阐明、每一条语句都必须以分号结尾。(4)一行内能够书写一条或多条语句,一条语句也能够分多行书写。 对于符号报错提醒案例1呈现这种报错就是应用了中文的符号 在C编程语言中只能辨认英文符号 切记不要应用中文符号及结尾的时候肯定要加上完结分号 不然一样会报错 C程序由五个局部组成1.预处理局部2.变量阐明局部3.函数原型申明局部4.主函数局部5.自定义函数局部//尽管说有五个局部组成 但并不谁说这五个局部必须要有的// 必须要有的:预处理局部 // #include <studio.h>主函数局部 // 有且仅有一个的 不是必须的:变量阐明局部 // 向计算机申请空间函数原型申明局部 //先申明自定义函数局部 //在自定义去写 注意事项(1)并非所有的C语言源和事都必须蕴含上述的5个局部。(2)每个C语言源程序都必须有且只能有一个主函数 。(3)每个C 语言源程序能够有零个或多个自定义的非主函数,只是它的名称不能是main。(4)每个C语言源程序的语句必须用分号“;”完结,必须是英文状态下的。(5)当C语言源程序由多个函数组成时,主函数能够定义在任何地位,但程序总是从主函数开始执行,且在主函数中完结执行。 ——17K.【微语】新年和平常不一样,欲望也不肯定要在过年期盼。唯有一些特地的人,他们用心通知你,我又陪伴了你一年。 ...

January 22, 2023 · 1 min · jiezi

关于c:开源C语言库Melon斐波那契堆

本篇介绍开源C语言库Melon的斐波那契堆的应用。对于 Melon 库,这是一个开源的 C 语言库,它具备:开箱即用、无第三方依赖、装置部署简略、中英文文档齐全等劣势。 Github repo 简介对于斐波那契堆,感兴趣的敌人能够参考《算法导论》或者是各类解说博客。 本篇介绍的是斐波那契最小堆,但对于判断条件和初始化属性进行调整后,也可实现最大堆。 数据结构各类操作工夫复杂度: 创立堆:O(1)插入:O(1)取最小值:O(1)将最小值从堆中移除:O(logN)合并堆:O(1)将堆结点key值减小:O(1)移除某个堆结点:O(logN)由此,咱们能够看到斐波那契堆非常适合频繁插入和删除以及获得极值(最小值)结点操作。这样的操作第一个能想到的场景就是实现对超时定时器的治理。 应用咱们先给出示例代码: #include <stdio.h>#include <stdlib.h>#include "mln_core.h" #include "mln_log.h"#include "mln_fheap.h" static int cmp_handler(const void *key1, const void *key2) { return *(int *)key1 < *(int *)key2? 0: 1; } static void copy_handler(void *old_key, void *new_key) { *(int *)old_key = *(int *)new_key; } int main(int argc, char *argv[]) { int i = 10, min = 0; mln_fheap_t *fh; mln_fheap_node_t *fn; struct mln_fheap_attr fattr; struct mln_core_attr cattr; cattr.argc = argc; cattr.argv = argv; cattr.global_init = NULL; cattr.master_process = NULL; cattr.worker_process = NULL; if (mln_core_init(&cattr) < 0) { fprintf(stderr, "init failed\n"); return -1; } fattr.pool = NULL; fattr.pool_alloc = NULL; fattr.pool_free = NULL; fattr.cmp = cmp_handler; fattr.copy = copy_handler; fattr.key_free = NULL; fattr.min_val = &min; fattr.min_val_size = sizeof(min); fh = mln_fheap_new(&fattr); if (fh == NULL) { mln_log(error, "fheap init failed.\n"); return -1; } fn = mln_fheap_node_new(fh, &i); if (fn == NULL) { mln_log(error, "fheap node init failed.\n"); return -1; } mln_fheap_insert(fh, fn); fn = mln_fheap_minimum(fh); mln_log(debug, "%d\n", *((int *)mln_fheap_node_key(fn))); mln_fheap_free(fh); return 0;}main函数中的流程大抵如下: ...

January 19, 2023 · 1 min · jiezi

关于c:开源C语言库双向链表

本篇次要介绍开源C语言库Melon的双向链表应用,对开源C库感兴趣的读者能够拜访:Github repo。 链表简介先简略介绍一下什么是双向链表。能够参考下图: 简略来说,链表是将一个一个的结点,通过指针连接起来。而双向链表则是每一个结点不仅记录了指向下一结点的指针,也记录了指向前一结点的指针。 Melon中的双向链表属于上图中带有尾部结点的双向链表。 双向链表的劣势:结点的插入和删除操作的工夫复杂度为O(1),所以应答频繁插入和删除的场景,是非常适合的。 双向链表应用咱们先定义一个自定义构造体类型: typedef struct test_s { int val;} test_t;后续的介绍中,咱们要做的就是应用Melon的链表组件对这个构造进行革新,将其构建成一个双向链表。 在Melon中,双向链表有两种实现。两种实现进行比照也各有其特点,因而读者在理解各自特点后,可依据本人须要进行抉择应用。 第一种实现咱们间接上代码,而后再进行阐明: #include <stdio.h>#include <stdlib.h>#include "mln_defs.h"typedef struct test_s { int val; struct test_s *prev; struct test_s *next;} test_t;MLN_CHAIN_FUNC_DECLARE(test, test_t, static inline void, );MLN_CHAIN_FUNC_DEFINE(test, test_t, static inline void, prev, next);int main(void){ int i; test_t *head = NULL, *tail = NULL, *t; for (i = 0; i < 10; ++i) { t = (test_t *)malloc(sizeof(test_t)); if (t == NULL) { fprintf(stderr, "malloc failed.\n"); return -1; } t->val = i; t->prev = t->next = NULL; test_chain_add(&head, &tail, t); } for (t = head; t != NULL; t = t->next) { printf("%d\n", t->val); } return 0;}这段代码中,main函数中的内容很简略,利用一个for循环,malloc10个test_t构造,而后将其val填充数值。随后利用test_chain_add函数将这些结点连成一个链表。而后利用for循环遍历结点并打印val的值。 ...

January 18, 2023 · 1 min · jiezi

关于c++:说说-Spring-定时任务如何大规模企业级运用

Spring 定时工作简介Cloud Native定时工作是业务利用开发中十分普遍存在的场景(如:每分钟扫描超时领取的订单,每小时清理一次数据库历史数据,每天统计前一天的数据并生成报表等等), 解决方案很多 ,Spring 框架提供了一种通过注解来配置定时工作的解决方案,接入十分的简略,仅需如下两步: 在启动类上增加注解@EnableScheduling @SpringBootApplication@EnableScheduling // 增加定时工作启动注解public class SpringSchedulerApplication { public static void main(String[] args) { SpringApplication.run(SpringSchedulerApplication.class, args);}}复制代码 开发定时工作 Bean 并配置相应的定时注解@Scheduled @Componentpublic class SpringScheduledProcessor { /** * 通过Cron表达式指定频率或指定工夫 */@Scheduled(cron = "0/5 * * * * ?")public void doSomethingByCron() { System.out.println("do something");}/** * 固定执行间隔时间 */@Scheduled(fixedDelay = 2000)public void doSomethingByFixedDelay() { System.out.println("do something");}/** * 固定执行触发频率 */@Scheduled(fixedRate = 2000)public void doSomethingByFixedRate() { System.out.println("do something");} }复制代码Spring 定时工作原理Cloud Native运行原理Spring 定时工作外围逻辑次要在 spring-context 中的 scheduling 包中,其次要构造包含: ...

January 11, 2023 · 2 min · jiezi

关于c++:重载的奥义之函数重载

一、根本定义    重载,顾名思义从字面上了解就是反复装载,打一个不失当的比如,你能够用一个篮子装蔬菜,也能够装水果或者其它,应用的是同一个篮子,然而能够用篮子反复装载的货色不一样。 函数重载是C++多态(动态多态)的特色体现,它能够容许重复使用同一个函数名(篮子)的函数,然而函数的参数列表(篮子装的货色)是能够不一样的。这样就能够利用函数的重载功能设计一系列性能相近,然而性能细节不一样的函数接口。 二、利用举例      以同一个函数printData为例: #include <iostream>using namespace std; void printData(const char *str, int num){ //函数体;} void printData(const char *str){ //函数体;} void printData(double data, int num){ //函数体;} void printData(int data, int num){ //函数体;} void printData(long data, char num){ //函数体;} class Test{ public: void MyPrint(int num) {cout << "class int: " << num << endl;} void MyPrint(float num) {cout << "class float: " << num << endl;} void MyPrint(char num) {cout << "class char: " << num << endl;}}; int main(void){ printData("hello", 5); // (const char *str, int num) printData("hello"); // (const char *str) printData(1993.0, 97); printData(1993, 98); printData(1993L, 99); Test test1; test1.MyPrint(2); // class int: 2 test1.MyPrint(2.0f); // class float: 2.0 浮点型必须要显式类型,否则编译器不晓得该转换为int还是float。 test1.MyPrint("hello"); // class char: hello return 0;} 应用重载函数时,须要在函数调用中应用与对应的重载函数匹配的函数参数类型。 ...

January 8, 2023 · 1 min · jiezi

关于c#:匿名方法

C# 匿名办法咱们曾经提到过,委托是用于援用与其具备雷同标签的办法。换句话说,您能够应用委托对象调用可由委托援用的办法。 匿名办法(Anonymous methods) 提供了一种传递代码块作为委托参数的技术。匿名办法是没有名称只有主体的办法。 在匿名办法中您不须要指定返回类型,它是从办法主体内的 return 语句推断的。 大题目编写匿名办法的语法匿名办法是通过应用 delegate 关键字创立委托实例来申明的。例如: delegate void NumberChanger(int n); ... NumberChanger nc = delegate(int x) { Console.WriteLine("Anonymous Method: {0}", x); };代码块 Console.WriteLine("Anonymous Method: {0}", x); 是匿名办法的主体。 委托能够通过匿名办法调用,也能够通过命名办法调用,即,通过向委托对象传递办法参数。 留神: 匿名办法的主体前面须要一个 ;。 例如: nc(10);实例上面的实例演示了匿名办法的概念: 实例using System;delegate void NumberChanger(int n);namespace DelegateAppl{ class TestDelegate { static int num = 10; public static void AddNum(int p) { num += p; Console.WriteLine("Named Method: {0}", num); } public static void MultNum(int q) { num *= q; Console.WriteLine("Named Method: {0}", num); } static void Main(string[] args) { // 应用匿名办法创立委托实例 NumberChanger nc = delegate(int x) { Console.WriteLine("Anonymous Method: {0}", x); }; // 应用匿名办法调用委托 nc(10); // 应用命名办法实例化委托 nc = new NumberChanger(AddNum); // 应用命名办法调用委托 nc(5); // 应用另一个命名办法实例化委托 nc = new NumberChanger(MultNum); // 应用命名办法调用委托 nc(2); Console.ReadKey(); } }}当下面的代码被编译和执行时,它会产生下列后果: ...

January 5, 2023 · 1 min · jiezi

关于c++:一种将函数模板定义和声明分开的方法

 在 C++ 中为了操作简洁引入了函数模板。所谓的函数模板实际上是建设一个通用函数,其函数类型或形参类型不具体指定,用一个虚构的类型来表白,这个通用函数就称为函数模板。 1、通用的写法 函数模板不是一个具体的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数性能框架的形容,当它具体执行时,将依据传递的理论参数决定其性能。为了容易应用,个别通用的写法都是在头文件中间接定义函数模板,定义的同时也是申明该函数,供应其它文件蕴含调用。 //------fun.h或fun.hpp------//#ifndef _FUN_H_#define _FUN_H_using namespace std;template<typename T> void fun(int b, T c, T d) //定义函数模板 { ......}#endif 对编译器而言,定义函数模板的时候,编译器并不会对它进行编译,因为它没有一个实体可用,编译器只看到了申明,只有模板被实例化后(用在特定的类型上),编译器才会依据具体的类型对模板进行编译。因而当在别的文件中调用该函数模板时,依据传递的理论参数决定其性能,这样编译器就能够在编译期间看到模板函数的定义并实现模板的实例化,如果在编译的时候,找不到模板函数的定义,就先不在这一次编译中实例化该模板函数。 2、问题的引出 然而头文件中定义和应用函数模板时,碰到了一个这样的场景,即在函数模板中应用到了全局变量: //------fun.h或fun.hpp------//#ifndef _FUN_H_#define _FUN_H_using namespace std;int a; //定义全局变量template<typename T> void fun(int b, T c, T d) //定义函数模板 { ...... a = b;}#endif 因而碰到其它多个文件须要应用该函数模板时,都须要各自蕴含该函数模板的头文件,编译时就会呈现“全局变量反复定义”的谬误。 尝试依照一般函数定义和申明离开的思路将函数模板的定义和申明离开: 源文件: //------fun.cpp------// //错误做法using namespace std;int a; //定义全局变量template<typename T> void fun(int b, T c, T d) //定义函数模板{ ...... a = b;} 头文件: ...

January 2, 2023 · 1 min · jiezi

关于c#:反射Reflection

反射(Reflection)反射指程序能够拜访、检测和批改它自身状态或行为的一种能力。 程序集蕴含模块,而模块蕴含类型,类型又蕴含成员。反射则提供了封装程序集、模块和类型的对象。 您能够应用反射动静地创立类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。而后,能够调用类型的办法或拜访其字段和属性。 优缺点长处:1、反射进步了程序的灵活性和扩展性。2、升高耦合性,进步自适应能力。3、它容许程序创立和管制任何类的对象,无需提前硬编码指标类。 毛病:1、性能问题:应用反射基本上是一种解释操作,用于字段和办法接入时要远慢于间接代码。因而反射机制次要利用在对灵活性和拓展性要求很高的零碎框架上,一般程序不倡议应用。2、应用反射会含糊程序外部逻辑;程序员心愿在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因此会带来保护的问题,反射代码比相应的间接代码更简单。反射(Reflection)的用处反射(Reflection)有下列用处: 它容许在运行时查看个性(attribute)信息。它容许审查汇合中的各种类型,以及实例化这些类型。它容许提早绑定的办法和属性(property)。它容许在运行时创立新类型,而后应用这些类型执行一些工作。查看元数据咱们曾经在下面的章节中提到过,应用反射(Reflection)能够查看个性(attribute)信息。 System.Reflection 类的 MemberInfo 对象须要被初始化,用于发现与类相干的个性(attribute)。为了做到这点,您能够定义指标类的一个对象,如下: System.Reflection.MemberInfo info = typeof(MyClass);上面的程序演示了这点: 实例using System;[AttributeUsage(AttributeTargets.All)]public class HelpAttribute : System.Attribute{ public readonly string Url; public string Topic // Topic 是一个命名(named)参数 { get { return topic; } set { topic = value; } } public HelpAttribute(string url) // url 是一个定位(positional)参数 { this.Url = url; } private string topic;}[HelpAttribute("Information on the class MyClass")]class MyClass{}namespace AttributeAppl{ class Program { static void Main(string[] args) { System.Reflection.MemberInfo info = typeof(MyClass); object[] attributes = info.GetCustomAttributes(true); for (int i = 0; i < attributes.Length; i++) { System.Console.WriteLine(attributes[i]); } Console.ReadKey(); } }}当下面的代码被编译和执行时,它会显示附加到类 MyClass 上的自定义个性: ...

December 31, 2022 · 3 min · jiezi

关于c:CentOS下安装gcc报错

CentOS下装置gcc报错cannot find a valid baseurl for repo: base/7/x86_64办法一:网络切换成 桥接 模式。办法二:1.进入 /etc/sysconfig/network-scripts 目录 cd /etc/sysconfig/network-scriptsls2.vim 关上文件 ifcfg-ens33 vi ifcfg-ens333.批改 ONBOOT=no 为 ONBOOT=yes4.重启网络:service network restart5.重新安装 gcc yum install gcc转发https://blog.nowcoder.net/n/5...

December 27, 2022 · 1 min · jiezi

关于c:Y-分钟速成-raylib

源代码下载: learnraylib-cn.c raylib 是一个跨平台、易用的图形库,围绕OpenGL 1.1、2.1、3.3和OpenGL ES 2.0构建。尽管它是用C语言编写的,却有超过50种不同语言的绑定。本教程将应用C语言。更确切地说,是C99。 #include <raylib.h>int main(void){ const int screenWidth = 800; const int screenHeight = 450; // 在初始化raylib之前,能够设置标记位 SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); // raylib并不要求咱们存储任何实例构造 // 目前raylib一次只能解决一个窗口 InitWindow(screenWidth, screenHeight, "MyWindow"); // 设置咱们的游戏以每秒60帧的速度运行 SetTargetFPS(60); // 设置一个敞开窗口的键。 //能够是0,示意没有键 SetExitKey(KEY_DELETE); // raylib定义了两种类型的相机。Camera3D和Camera2D // Camera是Camera3D的一个类型化定义 Camera camera = { .position = {0.0f, 0.0f, 0.0f}, .target = {0.0f, 0.0f, 1.0f}, .up = {0.0f, 1.0f, 0.0f}, .fovy = 70.0f, .type = CAMERA_PERSPECTIVE }; // raylib反对加载各种不同的文件格式的模型、动画、图像和声音。 Model myModel = LoadModel("my_model.obj"); Font someFont = LoadFont("some_font.ttf"); // 创立一个100x100的渲染纹理 RenderTexture renderTexture = LoadRenderTexture(100, 100); // WindowShouldClose办法检查用户是否正在敞开窗口。 // 可能用的是快捷方式、窗口管制或之前设置的敞开窗口键 while (!WindowShouldClose()) { // BeginDrawing办法要在任何绘图操作之前被调用。 BeginDrawing(); { // 为背景设定某种色彩 ClearBackground(BLACK); if (IsKeyDown(KEY_SPACE)) DrawCircle(400, 400, 30, GREEN); // 简略地绘制文本 DrawText("Congrats! You created your first window!", 190, // x 200, // y 20, // 字体大小 LIGHTGRAY ); // 大多数函数都有几个版本 // 通常后缀为Ex, Pro, V // 或者是Rec、Wires(仅实用于3D)、Lines(仅实用于2D)。 DrawTextEx(someFont, "Text in another font", (Vector2) {10, 10}, 20, // 字体大小 2, // 间距 LIGHTGRAY); // 绘制3D时须要,有2D的等价办法 BeginMode3D(camera); { DrawCube((Vector3) {0.0f, 0.0f, 3.0f}, 1.0f, 1.0f, 1.0f, RED); // 绘图时的红色色调将放弃原来的色彩 DrawModel(myModel, (Vector3) {0.0f, 0.0f, 3.0f}, 1.0f, // 缩放 WHITE); } // 完结3D模式,这样就能够再次一般绘图 EndMode3D(); // 开始在渲染纹理上绘图 BeginTextureMode(renderTexture); { // 它的行为与方才调用的`BeginDrawing()`办法雷同 ClearBackground(RAYWHITE); BeginMode3D(camera); { DrawGrid(10, // Slices 1.0f // 间距 ); } EndMode3D(); } EndTextureMode(); // 渲染有Texture2D字段的纹理 DrawTexture(renderTexture.texture, 40, 378, BLUE); } EndDrawing(); } // 卸载已载入的对象 UnloadFont(someFont); UnloadModel(myModel); // 敞开窗口和OpenGL上下文 CloseWindow(); return 0;}延长浏览raylib有一些不错的例子如果你不喜爱C语言你也能够看看raylib的其余语言绑定 ...

December 20, 2022 · 2 min · jiezi

关于c++:万字避坑指南C的缺陷与思考下

  导读 | 在万字避坑指南!C++的缺点与思考(上)一文中,微信后盾开发工程师胡博豪,分享了C++的倒退历史、右值援用与挪动语义、类型说明符等内容,深受宽广开发者青睐!此篇,咱们邀请作者持续总结其在C++开发过程中对一些奇怪、简单的语法的了解和思考,分享C++开发的避坑指南。 static 我在后面章节吐槽了const这个命名,也吐槽了“右值援用”这个命名。那么static就是笔者下一个要重点吐槽的命名了。static这个词自身没有什么问题,其次要的槽点就在于“一词多用”,也就是说,这个词在不同场景下示意的是齐全不同的含意。(作者可能是出于节俭关键词的目标吧,明明是不同的含意,却没有用不同的关键词)。 第一,在局部变量前的static,限定的是变量的生命周期。 第二,在全局变量/函数前的static,限定的是变量/函数的作用域。 第三,在成员变量前的static,限定的是成员变量的生命周期。 第四在成员函数前的static,限定的是成员函数的调用方(或暗藏参数)。 下面是static关键字的4种不同含意,接下来我会逐个解释。 1)动态局部变量当用static润饰局部变量时,static示意其生命周期: void f() { static int count = 0; count++;}上述例子中,count是一个局部变量,既然曾经是“局部变量”了,那么它的作用域很显著,就是f函数外部,而这里的static示意的是其生命周期。一般的全局变量在其所在函数(或代码块)完结时会被开释,而用static润饰的则不会,咱们将其称为“动态局部变量”。动态局部变量会在首次执行到定义语句时初始化,在主函数执行完结后开释,在程序执行过程中遇到定义(和初始化)语句时会被疏忽。 void f() { static int count = 0; count++; std::cout << count << std::endl;}int main(int argc, const char *argv[]) { f(); // 第一次执行时count被定义,并且初始化为0,执行后count值为1,并且不会开释 f(); // 第二次执行时因为count曾经存在,因而初始化语句会忽视,执行后count值为2,并且不会开释 f(); // 同上,执行后count值为3,不会开释} // 主函数执行完结后会开释f中的count例如下面例程的输入后果会是: 1232)外部全局变量/函数当static润饰全局变量或函数时,用于限定其作用域为“以后文件内”。同理,因为曾经是“全局”变量了,生命周期肯定是合乎全局的,也就是“主函数执行前结构,主函数执行完结后开释”。至于全局函数就不用说了,函数都是全局生命周期的。因而,这时候的static不会再对生命周期有影响,而是限定了其作用域。与之对应的是extern。用extern润饰的全局变量/函数作用于整个程序内,换句话说,就是能够跨文件。 // a1.ccint g_val = 4; // 定义全局变量// a2.ccextern int g_val; // 申明全局变量void Demo() { std::cout << g_val << std::endl; // 应用了在另一个文件中定义的全局变量}而用static润饰的全局变量/函数则只能在以后文件中应用,不同文件间的static全局变量/函数能够同名,并且相互独立。 ...

December 20, 2022 · 17 min · jiezi

关于c++:百度工程师带你探秘C内存管理ptmalloc篇

作者 | daydreamer 前篇《探秘C++内存治理(实践篇)》次要介绍了Linux C++程序内存治理的实践根底,本文作为系列文章《探秘C++内存治理》的第二篇,将会探讨经典内存管理器ptmalloc如何治理C++程序的内存。借助分析ptmalloc解决问题的着重点和设计实现老本的衡量,更具体的出现c++内存治理面临的问题和工程落地中的巧思。 一、概述ptmalloc是开源GNU C Library(glibc)默认的内存管理器,以后大部分Linux服务端程序应用的是ptmalloc提供的malloc/free系列函数,而它在性能上远差于Meta的jemalloc和Google的tcmalloc。服务端程序调用ptmalloc提供的malloc/free函数申请和开释内存,ptmalloc提供对内存的集中管理,以尽可能达到: 用户申请和开释内存更加高效,防止多线程申请内存并发和加锁寻求与操作系统交互过程中内存占用和malloc/free性能耗费的平衡点,升高内存碎片化,不频繁调用零碎调用函数简略概括ptmalloc的内存管理策略: 事后向操作系统申请并持有一块内存供用户malloc,同时治理已应用和闲暇的内存用户执行free,会将回收的内存治理起来,并执行管理策略决定是否交还给操作系统接下来,将从ptmalloc数据结构、内存调配及优缺点介绍最经典的c++内存管理器的实现和应用(以32位机为例)。 二、内存治理2.1 数据结构为了解决多线程锁抢夺问题,将内存调配辨别为主调配区(main\_area)和非主调配区(no\_main\_area)。同时,为了便于管理内存,对预申请的内存采纳边界标记法划分成很多块(chunk);ptmalloc内存分配器中,malloc\_chunk是根本组织单元,用于治理不同类型的chunk,性能和大小相近的chunk串联成链表,被称为一个bin。 main\_arena与non\_main\_arena主调配区和非主调配区造成一个环形链表进行治理, 每一个调配区利用互斥锁实现线程对该调配区的拜访互斥。每个过程只有一个主调配区,但容许有多个非主调配区,且非主调配区的数量只减少不缩小。主调配区能够拜访过程的heap区域和mmap映射区域,即主调配区能够应用sbrk()和mmap()分配内存;非主调配区只能应用mmap()分配内存。 对于不同arena的管理策略大抵如下: 分配内存查看该线程的公有变量中是否曾经存在一个调配区并对其进行加锁操作,如果加锁胜利,则应用该调配区分配内存;如果未找到该分区或加锁失败,遍历环形链表中获取一个未加锁的调配区如果整个环形链表中没有未加锁的调配区,开拓一个新的调配区,将其退出循环链表并加锁,应用该调配区满足以后线程的内存调配开释内存先获取待开释内存块所在的调配区的锁,如果有其余线程正在应用该调配区,期待其余线程开释该调配区互斥锁后,再开释内存主调配区和非主调配区的构造如下: 其中fastbinsY和bins是对理论内存块的治理和操作构造: fastbinsY: 用以保留fast binsbins[NBINS * 2 - 2]: unsorted bin(1个,bin[1])、small bins(62 个,bin[2]~bin[63])、large bins(63 个,bin[64]~bin[126])的汇合,一共有 126 个表项(NBINS = 128),bin[0] 和 bin[127] 没有被应用malloc\_chunk与binsptmalloc对立治理heap和mmap映射区域中闲暇的chunk,当用户进行调配申请时,会先试图在闲暇的chunk中查找和宰割,从而防止频繁的零碎调用,升高内存调配的开销。为了更好的治理和查找闲暇chunk,在预调配的空间的前后增加了必要的管制信息,内存治理构造malloc\_chunk的成员及作用如下: mchunk_prev_size: 前一个闲暇chunk的大小mchunk_size: 以后chunk的大小必要的属性标记位:前一个chunk在应用中(P = 1)以后chunk是mmap映射区域调配(M = 1)或是heap区域调配(M = 0)以后chunk属于非主调配区(A = 0)或非主调配区(A = 1)fd和bk: chunk块闲暇时存在,用于将闲暇chunk块退出到闲暇chunk块链表中对立治理基于chunk的大小和应用办法,划分出以下几种bins: fast bins fast bins仅保留很小的堆,采纳单链表串联,增删chunk都产生在链表的头部,进一步提高小内存的调配效率。fast bins记录着大小以8字节递增的bin链表,个别不会和其余堆块合并。 unsorted bin small bins和large bins的缓冲区,用于放慢调配的速度,chunk大小无尺寸限度,用户开释的堆块,会先进入unsorted bin。调配堆块时,会优先查看unsorted bin链表中是否存在适合的堆块,并进行切割并返回。 small bins ...

December 19, 2022 · 1 min · jiezi

关于c++:HIFIVE-音加加提供曲库评分修音功能的-K-歌-SDKiOS-版本

性能阐明:KXKTVSDK 整合了歌词展现、演唱评分、音色音量调节等简单的 K 歌模块,实现了性能组件化,升高了 K 歌性能开发的门槛。SDK 反对:1> 逐字歌词、逐行歌词、动态歌词2> 反对演唱评分3> 反对调节伴奏音量、伴奏升降 Key、麦克风音量、美声音量(仅佩戴耳机时可用)4> 反对 EQ 调节5> 反对耳机返听开关(仅佩戴耳机时可用)利用场景: 开发环境:macOS 版本 10.10 及以上版本。XCode 9.0 或以上版本。(本文 XCode 的界面形容以 XCode 13.0 为例)iOS 9.0 或以上版本的设施。模拟器临时不反对本我的项目,所以请应用真机。无效的 accessKeyId 受权码。 集成 KXKTVSDK依照以下步骤将 KXKTVSDK 集成到我的项目中。1、 下载KXKTVSDK并解压。2、 将 SDK 包中 .framework 文件复制到您的我的项目中。3、 抉择我的项目的 Targets->Build Phases->Link Binary With Libraries,增加以下依赖库:√ AVFoundation.framework√ Accelerate.framework√ CoreMedia.framework√ AudioToolBox.framework√ libz.dylib 或者 libz.tbd√ libc++.dylib 或者 √libc++.tbd√KexuanKTVSDK.framework 增加实现(如下图):4、 抉择我的项目的 Targets->Build Phases->Copy Bundle Resources,将 KexuanKTVSDK.framework 增加到 Bundle Resources 依赖。增加实现(如下图):5、抉择我的项目的 Targets->Build Settings 搜寻“alway”,将 Always Embed Swift Standard Libraries 的配置更改为 Yes(如下图)6、抉择我的项目的 Targets->Info,新增 NSMicrophoneUsageDescription 麦克风权限申请提醒(如下图)以上 6 步实现后,KXKTVSDK 就集成在我的项目中了。接下来咱们要在我的项目中应用 KXKTVSDK 提供的外围 API 实现 K 歌性能,API 调用时序见下图: ...

December 15, 2022 · 1 min · jiezi

关于c#:使用ILSpy反编译C代码

应用ILSpy反编译C#代码 1.点击dll,保留,保留为.csproj文件2.应用visual studio关上.csproj我的项目3.生成即可 碰到的问题:1.把AssemblyInfo.css外面的如下所示删除 [assembly: TargetFramework(".NETFramework,Version=v4.5", FrameworkDisplayName = ".NET Framework 4.5")]2.无奈调试 通过调试->窗口->模块查看 发现该模块被设置为了用户代码:否,符号状态:已跳过加载符号 解决办法:把AssemblyInfo.css里的批改为:而后从新生成即可

November 30, 2022 · 1 min · jiezi

关于c:CC-关于结构体变量传参时遇到的问题

论述主函数中的构造体变量作为非主函数的参数时,若只是将构造体变量名传与非主函数, 那就意味着仅是将其值传递给了非主函数外部,相当于在非主函数外部复制粘贴了一个新的构造体(此处称之为X),这个新构造体X与主函数中的构造体(此处称之为Y)是互相独立的,但它们二者的初始成员值都雷同. struct SSS{ ...};void NonMain(SSS X){ // X & Y的初始成员值都雷同, 地址却相异 ...}int mian(){ SSS Y; NonMain(Y); return 0;}根据上述代码,如果在NonMain函数中扭转了构造体变量外部的值,当NonMain函数运行完结后,程序返回主函数时,主函数中原有的构造体变量外部不会有丝毫变动.这是因为构造体变量名自身不是地址, 非主函数内所操作的构造体变量的地址,并非主函数中的构造体变量的地址. 因而,正确的构造体变量传参加非主函数时,须要先取出构造体变量的地址当作指针,再将指针传与非主函数. 代码示例#include <string>#include <cstring>#include <iostream>#include <queue>using namespace std;typedef struct TimesRecord{ int year; int month; int day;} times;void initializationError(TimesRecord date_record){ date_record.year = 2015; date_record.month = 5; date_record.day = 18; cout << "initializationError.&date_record: " << &date_record << endl;}void alterError(TimesRecord date_record, int newYear, int newMonth, int newDay){ date_record.year = newYear; date_record.month = newMonth; date_record.day = newDay; cout << "alterError.&date_record: " << &date_record << endl;}/* ----------------------------------------------------------------------------- */void initializationCorrect(TimesRecord &date_record){ date_record.year = 2015; date_record.month = 5; date_record.day = 18; cout << "initializationCorrect.&date_record: " << &date_record << endl;}void alterCorrect(TimesRecord &date_record, int newYear, int newMonth, int newDay){ date_record.year = newYear; date_record.month = newMonth; date_record.day = newDay; cout << "alterCorrect.&date_record: " << &date_record << endl;}int main(int argc, char const *argv[]){ TimesRecord t; int newYear = 2006; int newMonth = 9; int newDay = 22; initializationError(t); cout << "Error initialization: "; cout << t.year << "-" << t.month << "-" << t.day << endl; cout << "main.&t: " << &t << endl; cout << endl; alterError(t, newYear, newMonth, newDay); cout << "Error Alter: "; cout << t.year << "-" << t.month << "-" << t.day << endl; cout << "main.&t: " << &t << endl; cout << endl; initializationCorrect(t); cout << "Correct initialization: "; cout << t.year << "-" << t.month << "-" << t.day << endl; cout << "main.&t: " << &t << endl; cout << endl; alterCorrect(t, newYear, newMonth, newDay); cout << "Correct Alter: "; cout << t.year << "-" << t.month << "-" << t.day << endl; cout << "main.&t: " << &t << endl; return 0;} ...

November 29, 2022 · 2 min · jiezi

关于c++:DeepRoute-Lab-C-内存管理介绍

文章概述内存治理对于每种开发语言来说都是一个非常重要的话题;即便像Java这种领有“简单”垃圾收集器的语言,也会面临GC带来的各种困扰。C++程序设计中的很多bug也是因为内存治理不善导致的,而且难以发现和排除;如何有条理地治理内存,对于C++开发尤为重要。 “他山之石可以攻玉”,在钻研如何做好C++内存治理之前,咱们也能够看下其余语言是怎么做内存治理的,有什么模式或者模型能为咱们所借鉴,可能更好地帮忙咱们了解和做好C++的内存治理。 01不同语言的内存管理机制介绍C/C++、Java、Objective-C/Swift 和Golang 是几种应用宽泛的语言,内存管理机制也绝对典型。 1.1 C/C++C  也常被称作“可移植的汇编”,诞生之初次要是解决汇编语言的移植问题,在嵌入式和操作系统等绝对底层的开发畛域利用宽泛;对于简单的业务问题,因为它没有面向对象的能力(不好形象业务逻辑),显得难以应酬。 C++ 是一种很弱的面向对象的语言(模版和Interface简直是一种互相违反的思维)。为了兼容简直所有的C个性,背上了比拟重的历史包袱。在C++11之后,这种景象有了比拟大程度的改善,各种新的语言个性能够让C++开发者开发出更优雅、强壮的代码。 C语言的内存治理是典型的手动管理机制,通过malloc申请,free开释。C++语言除了手动治理之外,还领有弱的“垃圾回收机制”(智能指针的反对)。 C/C++中常见的内存治理问题有:a. 数组拜访越界(Java语言可抛出ArrayIndexOutOfBoundsException)b. 内存拜访超过生命周期‣ 栈弹出之后,仍旧进行拜访(函数返回外部栈地址)‣ 堆内存开释,仍旧进行拜访c. 内存泄露 (没有开释不再应用的内存)d. 悬空指针导致的问题‣ 指针指向内存开释之后,指针没有复位(设置为nullptr)‣ 应用没有复位(不为null)的有效内存(已开释或者未申请的内存)e. C++独有的问题‣ 非预期内的拷贝结构函数调用带来的适度复制(性能问题)‣ 不合理的复制、拷贝构造函数的实现,导致的意外数据共享(没有设置为nocopyable) 1.2 JavaJava 是一种面向对象的古代语言,有着丰盛的语言个性和开发生态。Java语言是为了实现下一代智能家电的通用零碎而设计的。在借鉴C++语言的根底上,又摒弃了C++的一些简单个性(可能升高软件开发品质)。比方:a. 不容许多继承b. 更纯正的interfacec. 所有皆对象(根底类型除外)d. non-static办法默认反对多态e. 等等 不想“有心栽花花不开,无心插柳柳成荫”。Java 在家电市场毫无起色,却因为优异的网络编程反对能力、平台无关性、垃圾回收等能力,加上恰逢互联网时代的到来,而后在企业级市场上大放异彩。 Java 因为有虚拟机的反对(先编译成字节码,由虚拟机解释成不同平台的“语言”),能够做到“一次编译,到处运行”。Java 目前在后盾开发、大数据以及App开发畛域(Kotlin也是类Java语言)有着十分宽泛的利用。 Java 的内存治理依靠于JVM的垃圾回收器(Garbage Collections)一般而言,垃圾回收的步骤包含两步:a. 找到可被回收的对象;b. 进行内存回收和整顿JVM(HotSpot)的GC也是如此。 (一)可收回对象判断 JVM GC基于可达剖析,来查找可回收对象;能够防止援用计数计划的循环援用问题。    (图1 基于根的可达对象剖析) (二)可回收策略和算法简直所有的垃圾回收器,都存在STW问题,高效回收以及升高对业务代码执行的影响是一件很难的事件。为了尽可能地优化性能,GC采纳 分代收集 和 标记-革除/标记-革除-整顿/标记-复制 进行内存回收。‣ 分代收集 (新生代和老年代)不难理解,新申请的内存比拟大的概率能够在不久后删除;如果一个内存存在比拟久了,那么接下来被回收的概率就会比拟低;新生代的回收会比拟轻量和高效,老年代的GC绝对会比拟重。 G1之前根本都是下图这种典型的分代内存模型。(图2 典型的内存分代模型) G1依然保留了新生代和老年代的概念,然而新生代和老年代的内存区域不再固定,都是一系列的动静汇合。(图3 G1的内存分代模型) ‣ 标记-革除/标记-革除-整顿/标记-复制标记-革除 办法绝对简略、高效,然而会存在内存碎片;标记-革除-整顿 能够解决内存碎片的问题,然而会减少GC的持续时间(益处大于害处);标记-复制 办法相似于ping-pang机制,须要有两片内存区域;在内存清理阶段,会将存活对象对立放到一个区域,而开释另外一个区域;和整顿办法一样,也不会产生内存碎片,并且复制和标记能够同时进行,然而须要更多的内存区域。 JVM有多种垃圾收集器可供选择,须要依据业务需要(低提早or高吞吐)进行衡量,CMS和G1应用绝对较多。 a. CMS用于老年代的垃圾回收,谋求最短进展;b. G1老年代和新生代都能够应用,并且绝对高效;c. Java11 推出的Z Garbage Collector(ZGC)有着不错的性能,目前根本能够投入生产。(https://docs.oracle.com/en/ja...) 1.3 Objective-C/SwiftObjective-C 是基于C语言倒退出的面向对象的开发语言(Objective);Objective-C的语法绝对繁琐、不够便捷,所以苹果在2014推出了Swift,领有脚本语言般的表现力。 Objective-C 的内存治理基于简略的援用计数,能够分为两类: ‣ MRR:Manual Retain-Release(图4 Objective-C MRR机制) ‣ ARC:Automatic Reference CountingARC底层还是MRR,只是由编译器在失当的地位帮咱们插入retain和release。是否开启ARC反对和编译器版本以及编译器选项无关。 1.4 GolangGolang 也是具备垃圾回收的一种语言,次要利用在后端开发畛域;回收策略也是基于可达对象剖析和标记-革除-整顿/复制算法。和Java的比对,能够参考以下链接:https://blog.mooncascade.com/... 02援用模型对对象生命周期的影响不同的援用类型对对象的生命周期影响不一样,从语义上可分为三类:‣ 强援用(Strong reference)强援用对象,不能够被回收如果是基于援用计数,援用计数会被影响‣ 软援用(Soft reference)非必要不回收,比方JVM在OOM之前会尝试对Soft reference对象进行回收- 如果基于援用计数,会进化为弱援用‣ 弱援用(Weak reference)  不影响对象生命期- 如果基于援用计数,不会影响援用计数 03C++的内存治理计划准则:尽量应用智能指针,不要放心智能指针带来性能损耗。 3.1 手动治理内存在某些场景下,C++须要手动治理内存;咱们能够应用一些技巧来更平安地应用和治理内存。a. 防止悬空指针(点击查看大图) b. 基于Allocator 策略进行内存调配通过Allocator能够扭转stl容器的内存分配机制,比方为vector在栈上分配内存;或者应用内存池进行内存治理;(点击查看大图) ...

November 29, 2022 · 1 min · jiezi

关于c++:结构体笔记结构体嵌套自引用结构体指针

构造体笔记(构造体嵌套、自援用,构造体指针)构造体(struct)1、基本概念 构造体-----将不同类型的数据成员组织到对立的名字之下,实用于对关系严密,逻辑相干、具备雷同或不同类型的数据进行解决 2、构造体定义格局定义构造 为了定义构造,您必须应用 struct 语句。struct 语句定义了一个蕴含多个成员的新的数据类型,struct 语句的格局如下: struct 标签名 { 类型 变量名; 类型 变量名; ······ } 构造变量 (构造体名字); struct tag { member_list member_list member_list ... } variable_list ; tag 是构造体标签。 member-list 是规范的变量定义,比方 int i; 或者 float f,或者其余无效的变量定义。 variable-list 构造变量,定义在构造的开端,最初一个分号之前,您能够指定一个或多个构造变量。上面是申明student构造的形式: struct student{ char name[50]; //名字 int id; //学号} student1; (申明构造体类型仅仅是申明了一个类型,零碎并不为之分配内存,就如同零碎不会为类型 int 分配内存一样。只有当应用这个类型定义了变量时,零碎才会为变量分配内存。所以在申明构造体类型的时候,不能够对外面的变量进行初始化。) 定义了一个tag为student的构造体和一个构造变量student1,如果省略变量名(student1),就变成了对构造的申明,上述构造体申明也可离开写 struct student{ char name[50]; //名字 int id; //学号} ;struct student student1; 与下面成果雷同,可了解为struct student相似于int,而咱们用的是student1相似于变量,如果省略构造名,则称之为无名构造,这种状况经常呈现在函数外部,或者说你只须要student1这一个变量, ...

November 25, 2022 · 3 min · jiezi

关于c++:Y-分钟速成-C

源代码下载: learncpp-cn.cpp C++是一种零碎编程语言。用它的发明者, Bjarne Stroustrup的话来说,C++的设计指标是: 成为“更好的C语言”反对数据的形象与封装反对面向对象编程反对泛型编程C++提供了对硬件的严密管制(正如C语言一样), 可能编译为机器语言,由处理器间接执行。 与此同时,它也提供了泛型、异样和类等高层性能。 尽管C++的语法可能比某些呈现较晚的语言更简单,它依然失去了人们的青睞—— 性能与速度的均衡使C++成为了目前利用最宽泛的零碎编程语言之一。 ////////////////// 与C语言的比拟////////////////// C++_简直_是C语言的一个超集,它与C语言的根本语法有许多相同之处,// 例如变量和函数的申明,原生数据类型等等。// 和C语言一样,在C++中,你的程序会从main()开始执行,// 该函数的返回值该当为int型,这个返回值会作为程序的退出状态值。// 不过,大多数的编译器(gcc,clang等)也承受 void main() 的函数原型。// (参见 http://en.wikipedia.org/wiki/Exit_status 来获取更多信息)int main(int argc, char** argv){ // 和C语言一样,命令行参数通过argc和argv传递。 // argc代表命令行参数的数量, // 而argv是一个蕴含“C语言格调字符串”(char *)的数组, // 其中每个字符串代表一个命令行参数的内容, // 首个命令行参数是调用该程序时所应用的名称。 // 如果你不关怀命令行参数的值,argc和argv能够被疏忽。 // 此时,你能够用int main()作为函数原型。 // 退出状态值为0时,示意程序执行胜利 return 0;}// 然而,C++和C语言也有一些区别:// 在C++中,字符字面量的大小是一个字节。sizeof('c') == 1// 在C语言中,字符字面量的大小与int雷同。sizeof('c') == sizeof(10)// C++的函数原型与函数定义是严格匹配的void func(); // 这个函数不能承受任何参数// 而在C语言中void func(); // 这个函数能承受任意数量的参数// 在C++中,用nullptr代替C语言中的NULLint* ip = nullptr;// C++也能够应用C语言的规范头文件,// 然而须要加上前缀“c”并去掉开端的“.h”。#include <cstdio>int main(){ printf("Hello, world!\n"); return 0;}///////////// 函数重载///////////// C++反对函数重载,你能够定义一组名称雷同而参数不同的函数。void print(char const* myString){ printf("String %s\n", myString);}void print(int myInt){ printf("My int is %d", myInt);}int main(){ print("Hello"); // 解析为 void print(const char*) print(15); // 解析为 void print(int)}///////////////////// 函数参数的默认值///////////////////// 你能够为函数的参数指定默认值,// 它们将会在调用者没有提供相应参数时被应用。void doSomethingWithInts(int a = 1, int b = 4){ // 对两个参数进行一些操作}int main(){ doSomethingWithInts(); // a = 1, b = 4 doSomethingWithInts(20); // a = 20, b = 4 doSomethingWithInts(20, 5); // a = 20, b = 5}// 默认参数必须放在所有的惯例参数之后。void invalidDeclaration(int a = 1, int b) // 这是谬误的!{}///////////// 命名空间///////////// 命名空间为变量、函数和其余申明提供了拆散的的作用域。// 命名空间能够嵌套应用。namespace First { namespace Nested { void foo() { printf("This is First::Nested::foo\n"); } } // 完结嵌套的命名空间Nested} // 完结命名空间Firstnamespace Second { void foo() { printf("This is Second::foo\n") }}void foo(){ printf("This is global foo\n");}int main(){ // 如果没有特地指定,就从“Second”中获得所需的内容。 using namespace Second; foo(); // 显示“This is Second::foo” First::Nested::foo(); // 显示“This is First::Nested::foo” ::foo(); // 显示“This is global foo”}////////////// 输出/输入////////////// C++应用“流”来输入输出。<<是流的插入运算符,>>是流提取运算符。// cin、cout、和cerr别离代表// stdin(规范输出)、stdout(规范输入)和stderr(规范谬误)。#include <iostream> // 引入蕴含输出/输入流的头文件using namespace std; // 输入输出流在std命名空间(也就是规范库)中。int main(){ int myInt; // 在规范输入(终端/显示器)中显示 cout << "Enter your favorite number:\n"; // 从规范输出(键盘)取得一个值 cin >> myInt; // cout也提供了格式化性能 cout << "Your favorite number is " << myInt << "\n"; // 显示“Your favorite number is <myInt>” cerr << "Used for error messages";}/////////// 字符串/////////// C++中的字符串是对象,它们有很多成员函数#include <string>using namespace std; // 字符串也在std命名空间(规范库)中。string myString = "Hello";string myOtherString = " World";// + 能够用于连贯字符串。cout << myString + myOtherString; // "Hello World"cout << myString + " You"; // "Hello You"// C++中的字符串是可变的,具备“值语义”。myString.append(" Dog");cout << myString; // "Hello Dog"/////////////// 援用/////////////// 除了反对C语言中的指针类型以外,C++还提供了_援用_。// 援用是一种非凡的指针类型,一旦被定义就不能从新赋值,并且不能被设置为空值。// 应用援用时的语法与原变量雷同:// 也就是说,对援用类型进行解援用时,不须要应用*;// 赋值时也不须要用&来取地址。using namespace std;string foo = "I am foo";string bar = "I am bar";string& fooRef = foo; // 建设了一个对foo的援用。fooRef += ". Hi!"; // 通过援用来批改foo的值cout << fooRef; // "I am foo. Hi!"// 这句话的并不会扭转fooRef的指向,其成果与“foo = bar”雷同。// 也就是说,在执行这条语句之后,foo == "I am bar"。fooRef = bar;const string& barRef = bar; // 建设指向bar的常量援用。// 和C语言中一样,(指针和援用)申明为常量时,对应的值不能被批改。barRef += ". Hi!"; // 这是谬误的,不能批改一个常量援用的值。///////////////////// 类与面向对象编程///////////////////// 无关类的第一个示例#include <iostream>// 申明一个类。// 类通常在头文件(.h或.hpp)中申明。class Dog { // 成员变量和成员函数默认状况下是公有(private)的。 std::string name; int weight;// 在这个标签之后,所有申明都是私有(public)的,// 直到从新指定“private:”(公有继承)或“protected:”(爱护继承)为止public: // 默认的结构器 Dog(); // 这里是成员函数申明的一个例子。 // 能够留神到,咱们在此处应用了std::string,而不是using namespace std // 语句using namespace绝不该当呈现在头文件当中。 void setName(const std::string& dogsName); void setWeight(int dogsWeight); // 如果一个函数不对对象的状态进行批改, // 该当在申明中加上const。 // 这样,你就能够对一个以常量形式援用的对象执行该操作。 // 同时能够留神到,当父类的成员函数须要被子类重写时, // 父类中的函数必须被显式申明为_虚函数(virtual)_。 // 思考到性能方面的因素,函数默认状况下不会被申明为虚函数。 virtual void print() const; // 函数也能够在class body外部定义。 // 这样定义的函数会主动成为内联函数。 void bark() const { std::cout << name << " barks!\n" } // 除了结构器以外,C++还提供了析构器。 // 当一个对象被删除或者脱离其定义域时,它的析构函数会被调用。 // 这使得RAII这样的弱小范式(参见下文)成为可能。 // 为了衍生出子类来,基类的析构函数必须定义为虚函数。 virtual ~Dog();}; // 在类的定义之后,要加一个分号// 类的成员函数通常在.cpp文件中实现。void Dog::Dog(){ std::cout << "A dog has been constructed\n";}// 对象(例如字符串)该当以援用的模式传递,// 对于不须要批改的对象,最好应用常量援用。void Dog::setName(const std::string& dogsName){ name = dogsName;}void Dog::setWeight(int dogsWeight){ weight = dogsWeight;}// 虚函数的virtual关键字只须要在申明时应用,不须要在定义时反复void Dog::print() const{ std::cout << "Dog is " << name << " and weighs " << weight << "kg\n";}void Dog::~Dog(){ std::cout << "Goodbye " << name << "\n";}int main() { Dog myDog; // 此时显示“A dog has been constructed” myDog.setName("Barkley"); myDog.setWeight(10); myDog.print(); // 显示“Dog is Barkley and weighs 10 kg” return 0;} // 显示“Goodbye Barkley”// 继承:// 这个类继承了Dog类中的私有(public)和爱护(protected)对象class OwnedDog : public Dog { void setOwner(const std::string& dogsOwner) // 重写OwnedDogs类的print办法。 // 如果你不相熟子类多态的话,能够参考这个页面中的概述: // http://zh.wikipedia.org/wiki/%E5%AD%90%E7%B1%BB%E5%9E%8B // override关键字是可选的,它确保你所重写的是基类中的办法。 void print() const override;private: std::string owner;};// 与此同时,在对应的.cpp文件里:void OwnedDog::setOwner(const std::string& dogsOwner){ owner = dogsOwner;}void OwnedDog::print() const{ Dog::print(); // 调用基类Dog中的print办法 // "Dog is <name> and weights <weight>" std::cout << "Dog is owned by " << owner << "\n"; // "Dog is owned by <owner>"}/////////////////////// 初始化与运算符重载/////////////////////// 在C++中,通过定义一些非凡名称的函数,// 你能够重载+、-、*、/等运算符的行为。// 当运算符被应用时,这些非凡函数会被调用,从而实现运算符重载。#include <iostream>using namespace std;class Point {public: // 能够以这样的形式为成员变量设置默认值。 double x = 0; double y = 0; // 定义一个默认的结构器。 // 除了将Point初始化为(0, 0)以外,这个函数什么都不做。 Point() { }; // 上面应用的语法称为初始化列表, // 这是初始化类中成员变量的正确形式。 Point (double a, double b) : x(a), y(b) { /* 除了初始化成员变量外,什么都不做 */ } // 重载 + 运算符 Point operator+(const Point& rhs) const; // 重载 += 运算符 Point& operator+=(const Point& rhs); // 减少 - 和 -= 运算符也是有意义的,但这里不再赘述。};Point Point::operator+(const Point& rhs) const{ // 创立一个新的点, // 其横纵坐标别离为这个点与另一点在对应方向上的坐标之和。 return Point(x + rhs.x, y + rhs.y);}Point& Point::operator+=(const Point& rhs){ x += rhs.x; y += rhs.y; return *this;}int main () { Point up (0,1); Point right (1,0); // 这里应用了Point类型的运算符“+” // 调用up(Point类型)的“+”办法,并以right作为函数的参数 Point result = up + right; // 显示“Result is upright (1,1)” cout << "Result is upright (" << result.x << ',' << result.y << ")\n"; return 0;}///////////// 异样解决///////////// 规范库中提供了一些根本的异样类型// (参见http://en.cppreference.com/w/cpp/error/exception)// 然而,其余任何类型也能够作为一个异样被拋出#include <exception>// 在_try_代码块中拋出的异样能够被随后的_catch_捕捉。try { // 不要用 _new_关键字在堆上为异样调配空间。 throw std::exception("A problem occurred");}// 如果拋出的异样是一个对象,能够用常量援用来捕捉它catch (const std::exception& ex){ std::cout << ex.what();// 捕捉尚未被_catch_解决的所有谬误} catch (...){ std::cout << "Unknown exception caught"; throw; // 从新拋出异样}///////// RAII///////// RAII指的是“资源获取就是初始化”(Resource Allocation Is Initialization),// 它被视作C++中最弱小的编程范式之一。// 简略说来,它指的是,用构造函数来获取一个对象的资源,// 相应的,借助析构函数来开释对象的资源。// 为了了解这一范式的用途,让咱们思考某个函数应用文件句柄时的状况:void doSomethingWithAFile(const char* filename){ // 首先,让咱们假如所有都会顺利进行。 FILE* fh = fopen(filename, "r"); // 以只读模式关上文件 doSomethingWithTheFile(fh); doSomethingElseWithIt(fh); fclose(fh); // 敞开文件句柄}// 可怜的是,随着错误处理机制的引入,事件会变得复杂。// 假如fopen函数有可能执行失败,// 而doSomethingWithTheFile和doSomethingElseWithIt会在失败时返回错误代码。// (尽管异样是C++中处理错误的举荐形式,// 然而某些程序员,尤其是有C语言背景的,并不认可异样捕捉机制的作用)。// 当初,咱们必须查看每个函数调用是否胜利执行,并在问题产生的时候敞开文件句柄。bool doSomethingWithAFile(const char* filename){ FILE* fh = fopen(filename, "r"); // 以只读模式关上文件 if (fh == nullptr) // 当执行失败是,返回的指针是nullptr return false; // 向调用者汇报谬误 // 假如每个函数会在执行失败时返回false if (!doSomethingWithTheFile(fh)) { fclose(fh); // 敞开文件句柄,防止造成内存透露。 return false; // 反馈谬误 } if (!doSomethingElseWithIt(fh)) { fclose(fh); // 敞开文件句柄 return false; // 反馈谬误 } fclose(fh); // 敞开文件句柄 return true; // 批示函数已胜利执行}// C语言的程序员通常会借助goto语句简化下面的代码:bool doSomethingWithAFile(const char* filename){ FILE* fh = fopen(filename, "r"); if (fh == nullptr) return false; if (!doSomethingWithTheFile(fh)) goto failure; if (!doSomethingElseWithIt(fh)) goto failure; fclose(fh); // 敞开文件 return true; // 执行胜利failure: fclose(fh); return false; // 反馈谬误}// 如果用异样捕捉机制来批示谬误的话,// 代码会变得清晰一些,然而依然有优化的余地。void doSomethingWithAFile(const char* filename){ FILE* fh = fopen(filename, "r"); // 以只读模式关上文件 if (fh == nullptr) throw std::exception("Could not open the file."); try { doSomethingWithTheFile(fh); doSomethingElseWithIt(fh); } catch (...) { fclose(fh); // 保障出错的时候文件被正确敞开 throw; // 之后,从新抛出这个异样 } fclose(fh); // 敞开文件 // 所有工作顺利完成}// 相比之下,应用C++中的文件流类(fstream)时,// fstream会利用本人的析构器来敞开文件句柄。// 只有来到了某一对象的定义域,它的析构函数就会被主动调用。void doSomethingWithAFile(const std::string& filename){ // ifstream是输出文件流(input file stream)的简称 std::ifstream fh(filename); // 关上一个文件 // 对文件进行一些操作 doSomethingWithTheFile(fh); doSomethingElseWithIt(fh);} // 文件曾经被析构器主动敞开// 与下面几种形式相比,这种形式有着_显著_的劣势:// 1. 无论产生了什么状况,资源(此例当中是文件句柄)都会被正确敞开。// 只有你正确应用了析构器,就_不会_因为遗记敞开句柄,造成资源的透露。// 2. 能够留神到,通过这种形式写进去的代码非常简洁。// 析构器会在后盾敞开文件句柄,不再须要你来操心这些琐事。// 3. 这种形式的代码具备异样安全性。// 无论在函数中的何处拋出异样,都不会妨碍对文件资源的开释。// 纯粹的C++代码该当把RAII的应用扩大到各种类型的资源上,包含:// - 用unique_ptr和shared_ptr治理的内存// - 各种数据容器,例如规范库中的链表、向量(容量主动扩大的数组)、散列表等;// 当它们脱离作用域时,析构器会主动开释其中贮存的内容。// - 用lock_guard和unique_lock实现的互斥扩大浏览:CPP Reference 提供了最新的语法参考。能够在 CPlusPlus 找到一些补充材料。能够在 [TheChernoProject - C ++](https://www.youtube.com/playl...)上找到涵盖语言根底和设置编码环境的教程。有倡议?或者发现什么谬误?在Github上开一个issue,或者发动pull request! ...

November 25, 2022 · 5 min · jiezi

关于c:防御式编程之断言assert的使用

 进攻式编程的重点就是须要进攻一些程序未曾意料的谬误,这是一种进步软件品质的辅助性办法,断言assert就用于进攻式编程,编写代码时,咱们总是会做出一些假如,断言就是用于在代码中捕获这些假如。应用断言是为了验证预期的后果——当程序执行到断言的地位时,对应的断言应该为真;若断言不为真时,程序会终止执行,并给出错误信息。能够在任何时候启用和禁用断言验证,因而能够在程序调试时启用断言而在程序公布时禁用断言。同样,程序投入运行后,最终用户在遇到问题时能够从新启用断言。 1、原型函数 在大部分编译器下,assert() 是一个宏;在多数的编译器下,assert() 就是一个函数。咱们不须要关怀这些差别,能够只把 assert()当作函数应用即可。即: void assert(int expression); 在程序运行时它会计算括号内的表达式,如果 expression为非0阐明其值为真,assert()不执行任何动作,程序继续执行前面的语句;如果 expression为0阐明其值为假,assert()将会报告谬误,并终止程序的执行,值得理解的是,程序终止是调用abort()函数,这个函数性能就是终止程序执行,间接从调用的中央跳出,abort()函数也是规范库函数,在<stdlib.h>中定义。因而assert()用来判断程序中是否呈现了显著非法的逻辑,如果呈现了就终止程序免得导致严重后果,同时也便于查找谬误。 2、具体释义 assert() 在c规范库中的<assert.h>中被定义。上面就看下在assert.h中的定义: #ifdef NDEBUG#define assert(e) ((void)0)#else#define assert(e) ((void) ((e) ? ((void)0) : __assert (#e, __FILE__, __LINE__)))#endif 能够看到在定义了NDEBUG时,assert()有效,只有在未定义NDEBUG时,assert()才实现具体的函数性能。NDEBUG是“No Debug”的意思,也即“非调试”。程序个别分为Debug版本和Release版本,Debug版本是程序员在测试代码期间应用的编译版本,Release版本是将程序提供给用户时应用的公布版本,一般来说断言assert()是仅在Debug版本起作用的宏。在公布版本时,咱们不应该再依赖assert()宏,因为程序一旦出错,assert()会抛出一段用户看不懂的提示信息,并毫无预警地终止程序执行,这样会重大影响软件的用户体验,所以在公布模式下应该让assert()生效,另外在程序中频繁的调用assert()会影响程序的性能,减少额定的开销。因而能够在<assert.h>中定义NDEBUG宏,将assert()性能敞开。 #define NDEBUG //定义NDEBUG #ifdef NDEBUG#define assert(e) ((void)0)#else#define assert(e) ((void) ((e) ? ((void)0) : __assert (#e, __FILE__, __LINE__)))#endif定义NDBUG时: 当定义了NDEBUG之后,assert()执行的具体函数就变成了 ((void)0),这示意啥也不干了,宏外面这样用的目标是避免该宏被用作右值,因为void类型不能用作右值。所以当在头文件中定义了NDEBUG之后,assert()的检测性能就主动生效了。 未定义NDBUG时: 能够看到assert()执行实际上是通过三目运算符来判断表达式e的虚实,执行相应的解决。当表达式e为真时,执行(void)0,即什么也不执行,程序持续运行;当表达式e为假时,那么它会打印进去assert的内容、以后的文件名、以后行号,接着终止程序执行。 3、用法举例 在未定义NDBUG时,assert()性能失效的状况下,来看一个简略的assert()应用的例子: #include <stdio.h>#include <assert.h>void main(){ int i = 8; assert(i > 0); printf("i = %d\n", i); i = -8; assert(i > 0); printf("i = %d\n", i);} 能够看出在程序中应用assert(i > 0)来判断;当 i > 0 时,assert的判断表达式为真,assert不失效;当 i < 0 时,assert的判断表达式为假,assert失效。 ...

November 23, 2022 · 2 min · jiezi

关于c++:小凯15天快速讲完c语言简单学习第十二课

0. 温习0.1 虚继承当咱们应用多继承的时候,子类的多个父类有同名成员,拜访的时候,会呈现二义性class CA{ public: int m_a;};class CB{ public:int m_a;};class CTest:public CA,public CB{};int main(){CTest obj;obj.m_a = 10;return 0;}解决形式1:能够应用类的作用域拜访具体某一个父类的成员解决形式2:给CA和CB形象出一个独特的父类,而后应用虚继承 class CBase{public: int m_a;};class CA:virtual public CBase{public: //int m_a;};class CB:virtual public CBase{public: //int m_a;};class CTest :public CA, public CB{};int main(){ CTest obj; obj.m_a = 20; obj.CA::m_a = 10; obj.CB::m_a = 20; return 0;}如果类B虚继承自类A,此时类A就是虚基类 0.2 虚函数与多态当咱们应用父类指针指向子类对象,调用虚函数,优先调用子类的虚函数。子类如果没有实现这个虚函数,就调用父类的。虚函数是多态机制,属于动静联编。virtual void fun() =0 ; 这个叫做纯虚函数。只是提供了接口,没有提供具体实现,实现由子类来实现。抽象类:蕴含纯虚函数的类,也叫做抽象类。如果子类没有实现父类中的纯虚函数,子类也是抽象类。抽象类的特点:不能定义对象 class Tuxing{public: virtual int ClacArea() = 0;};class Sanjiaoxing:public Tuxing{public: int ClacArea() { return m_nBottom * m_nHeight / 2; }private: int m_nBottom; int m_nHeight;};class CZhengfangxing :public Tuxing{ int ClacArea() { //.. return m_nLenth * m_nLenth; }private: int m_nLenth;};class CCircle:public Tuxing{};int main(){ CCircle obj; CZhengfangxing obj2; return 0;} ...

November 19, 2022 · 5 min · jiezi

关于c:关于arm的backtrace

应用场景:嵌入式跑死问题追溯提供办法:打印函数的调用关系backtrace原理: 1. main函数运行,main函数调用func1, func1调用func2...2. 当函数调用产生时,会将arm的寄存器PC/LR/SP/FP顺次压栈,造成栈帧,本次的配角是FP寄存器3. main函数调用func1, 会将main函数的栈帧起始地址放入func1的FP寄存器,即func1的FP寄存器指向栈中用于寄存main函数栈的起始地位,即main函数PC指针的前一个地位。如下图所示,留神图中的箭头 接下来咱们看下如下代码: #include <stdio.h>#include <stdint.h>#include <string.h>//-------------------- backtrace --------------------------extern void backtrace(int fp);extern int div(int a, int b);extern int main(int argc, char **argv);#define TOSTRING(x) #x#define READ_REGISTER(var) __asm volatile("mov %[" TOSTRING(var) "], " TOSTRING(var) "\n\t" : [var] "=r" (var))typedef uint32_t volatile *volatile vmemptr;#define VMEM(x) (*(vmemptr)(x))void backtrace(int fp){ printf("\n-------- bt ------\n"); if (fp == NULL) { printf("fp is NULL\n"); return; } printf("bt fp: %p\n", fp); printf("bt fp - 4: 0x%08x\n", (void *) ((*(int *)(fp - 4)))); backtrace((void *) ((*(int *)(fp - 4))));}int div(int a, int b){ int c = ++a; int d = ++b; int e = a * b / c; printf("----- backtrace ----- %s\n", __func__); int fp = 0; // 2 methods of getting fp value READ_REGISTER(fp); printf("fp: %p\n", fp); printf("frame div: %p\n", __builtin_frame_address (0)); backtrace(fp); printf("\n"); return e;}int multi(int a, int b){ int c = ++a; int d = ++b; int e = a * b * div(c, d); printf("frame multi: %p\n", __builtin_frame_address (0)); return e;}int minus(int a, int b){ int c = ++a; int d = ++b; int e = a - b - multi(c, d); printf("frame minus: %p\n", __builtin_frame_address (0)); return e;} int add(int a, int b){ int c = ++a; int d = ++b; int e = a + b + minus(c, d); printf("frame add: %p\n", __builtin_frame_address (0)); return e;}int main(int argc, char **argv){ int a = 5; int b = 4; int c = add(a, b); printf("frame main: %p\n", __builtin_frame_address (0)); // compare the address of variables with frame pointer printf("func main:\t a: %p, b: %p, c: %p\n", &a, &b, &c); printf("func main:\t argc: %p, argv: %p\n", &argc, argv); printf("\n"); return 0;}运行办法: ...

November 19, 2022 · 2 min · jiezi

关于c++:万字好文从无栈协程到C异步框架

导语 | 本文咱们将尝试对整个 C++的协程做深入浅出的分析,不便大家的了解。再联合下层的封装,最终给出一个 C++异步框架理论业务应用的一种状态,不便大家更好的在理论我的项目中利用无栈协程。 浅谈协程 在开始开展协程前,咱们先来看一下一些非 C++语言中的协程实现。 (一)其余语言中的协程实现很多语言外面,协程是作为 "一类公民" 间接退出到语言个性中的, 比方: Dart1.9示例代码Future<int> getPage(t) async { var c = new http.Client(); try { var r = await c.get('http://xxx'); print(r); return r.length(); } finally { await c.close(); }}Python示例代码async def abinary(n): if n <= 0: return 1 l = await abinary(n-1) r = await abinary(n-1) return l + 1 + rC#示例代码aysnc Task<string> WaitAsync(){ await Task.Delay(10000); return "Finished";}小结泛滥语言都实现了本人的协程机制, 通过下面的例子, 咱们也能看到, 相干的机制使函数的执行特殊化了, 变成了能够屡次中断和重入的构造. 那么如果 C++要反对这种机制, 会是一个什么状况呢? 接下来咱们将先从最根本的原理逐渐开展相干的探讨。 ...

November 9, 2022 · 18 min · jiezi

关于c:小凯15天快速讲完c语言简单学习第七课

前言看到这篇博客的同学们,到明天为止,咱们的c语言高级局部解说就完结了(可能有的同学好奇我的题目不是写的15天么,这才七天,哈哈,因为咱们接下来就要开始进入c++的世界了,算是c语言的进阶,我明天整顿公布的已经自学的笔记绝对有些简单,波及指针高级运算,明天的内容不求把握,只求简略了解就好,即便没懂,也没关系啦,楼主纯手动码字不易,还望珍惜。欢送关注,多和我交换。 0. 温习0.1 构造体是一种复合数据类型,能够将多个不同类型得变量给捏在一起。个别用于代表某一个整体得信息。比方:学生信息有学生姓名,年龄,学号.... 贪吃蛇的 速度 血量 长度.....语法: struct 类型名{ 字段1类型 字段1名字; //字段也叫做成员 字段2类型 字段2名字; .....;};struct 类型名 变量名 = {初始值};C语言中定义构造体变量的时候,须要加上struct.c++不须要C语言的程序员为了不写这个struct,有了一种类型定义的写法typedef struct _类型名{ 字段1类型 字段1名字; //字段也叫做成员 字段2类型 字段2名字; .....;}类型名,*P类型名;typedef struct _STUDENT{ //}STUDENT,*PSTUDENT;应用构造体的时候,依照成员自身的类型去应用。 0.2 联合体和构造体语法是类型的,区别在于联结的所有成员是共享内存的。比拟适宜用在 成员互斥的状况下(一个无效,其余的就都是有效的) 0.3 类型定义typedef int INT; //INT 就是int的别名 0.4 堆空间申请:malloc开释:free设置内存中的值:memset拷贝内存:memcpy一些概念:悬空指针:开释之后,没有被置为nullptr的指针野指针:没有初始化的指针悬空指针和野指针都指向有效区域。正在运行的程序,有5个内存区域:静态数据区:全局变量,static局部变量所在的区域常量区:字符串常量所在的区域代码区:代码所在的区域栈区:局部变量和函数的参数都在栈区堆区:malloc 申请的空间,是堆区的。作用域:变量起作用的一个范畴生存期:变量存在的一个期间局部变量,参数:进入函数,无效,此时在栈区创立进去。来到函数,生效,此时主动销毁。动态局部变量:进入函数之前就被创立。然而在函数里面是不能应用的。来到函数,也不会被销毁。堆区:申请就存在,开释就销毁 1. 指针进阶1.1 指针的算术运算1.1.1 指针+(-) 整数#include <stdio.h>int main(){ int a = 100; int* p = NULL; p = (int*)100; //1. 一个地址实质来说也是一个数字 //然而地址个别都是由&失去的,或者malloc函数返回的 //如果咱们轻易写个地址,这个地址通常都是不能拜访 //2. 做加法运算 //a+n 一个整型做加法运算,为了失去一个算术后果 //p+n 指针做加法运算,是为了失去偏移为n的元素的地位。 //有了这个地位,就能够通过*失去偏移为n的地位的数据 printf("%d\n", a ); printf("%d\n", a+2); printf("%d\n", p); printf("%d\n", p + 2); //3. 这个个性有什么用呢??? int arr[10] = { 4,5,20,40,10,6,7,8,9,10 }; p = arr;//能赋值,阐明类型类似 for (int i = 0; i < 10; i++) { printf("%x ", p + i); printf("%d \n", *(p + i)); } //4. double* char* short* 构造体* typedef struct _TEST { int a; double b; int c; }TEST,*PTEST; double* p2 = (double*)100; char* p3 = (char*)100; short* p4 = (short*)100; //上面两种写法等价的 PTEST p5 = (PTEST)100; TEST* p6 = (TEST * )100; printf("%d\n", p2); printf("%d\n", p3); printf("%d\n", p4); printf("%d\n", p5); printf("%d\n", p6); printf("%d\n", p2+1);//108 printf("%d\n", p3+1);//101 printf("%d\n", p4+1);//102 printf("%d\n", p5+1);//124 printf("%d\n", p6+1);//124 return 0;}1.1.2 指针- 指针(大略理解即可)要求:两个指针的类型必须是统一的。失去的后果是两个地址之间可能存储下多少个此类型 ...

November 8, 2022 · 6 min · jiezi

关于c:也谈野指针

 一、引子        咱们都晓得对指针( Pointer)的操作,实际上是对计算机内存地址的操作,通过拜访内存地址实现间接拜访该地址中保留的数据。其实就是CPU的寻址形式中的间接寻址。简略概括失常应用指针时的3个步骤为: 定义指针变量绑定指针即给指针变量赋值解援用即间接拜访指标变量 通过一个简略的例子来看这3个步骤的实现: int a = 5;//定义指针变量pint *p;//绑定指针,就是给指针变量赋值,指向另一个变量a(指针的用处就是指向别的变量)p = &a;//将6放入p所指向的那个变量的空间中,这里就是a的空间*p = 6;        能够看出,在定义指针变量p时,未初始化p,这个时候的p为随机值,此时解援用p是没有意义的,内存随机值的空间是否无效咱们也不得而知。         绑定指针就是将变量a的地址赋值给指针变量p,此时p就有了意义,明确了内存中拜访的具体空间地位,p是指向变量a的空间的,变量a是有具体内容的,因而指针必须给它赋值能力解援用它。         给指针变量p赋值实际上是在变量a前加一个“&”符号,这个符号是取地址符,&a就是指变量a的地址,编译器在给每个变量调配出内存空间,并将a与这块的内存空间地址绑定。这个地址只有编译器晓得,而程序员并不知道编译器随机给这段空间调配什么随机地址值。程序员要获取或操作这个地址时,就须要应用取地址符。         由上述剖析看来,给p赋予了变量a地址的值是一个非法的,在内存中明确的地址值,这个值是受控的,同时通过拜访指针间接拜访该地址中保留的数据也是受控的,p就是一个失常的指针。         相同,如果指针指向了内存中不可用的区域,或者是指针的值是非法的随机值也就是非正常内存地址,那么这个指针就是不受控的,同时通过拜访指针间接拜访该地址中保留的数据也是不受控的,同时是不可知的,此时这个指针就是野指针(Wild Pointer)。 二、须要明确的一点        野指针不同于空指针,所谓空指针,是给指针变量赋NULL值,即: int *p = NULL;        所谓NULL值在C/C++中定义为: #ifdef __cplusplus         // 定义这个符号示意以后是C++环境中#define NULL 0             // 在C++中NULL为0#else#define NULL (void *) 0    // 在C中的NULL是强制类型转换为void *的0#endif        能够看出,给p赋值NULL值也就是让p指向空地址。在不同的零碎中,NULL并不象征等于0,也有零碎会应用地址0,而将NULL定义为其余值,所以不要把NULL和0等同起来。你能够将NULL艰深了解为是空值,也就是指向一个不被应用的地址,在大多数零碎中,都将0作为不被应用的地址,因而就有了这样的定义,C或者C++编译器保障这个空值不会是任何对象的地址。         void *示意的是“无类型指针”,能够指向任何数据类型,在这里void指针与空指针NULL区别:NULL阐明指针不指向任何数据,是“空的”;而void指针实实在在地指向一块内存,只是不晓得这块内存中是什么类型的数据。         空指针的值是受控的,但并不是有意义的,咱们是将指针指向了0地址,这个0地址就是作为内存中的一个非凡地址,因而空指针是一个对任何指针类型都非法的指针,但并不是正当的指针,指针变量具备空指针值,示意它处于闲置状态,没有指向任何有意义的内容。咱们须要在让空指针真正指向了一块有意义的内存后,咱们能力对它取内容。即: int a = 5;int *p = NULL;p = &a;        NULL指针并没有危害,能够应用if语句来判断是否为NULL。 三、一些典型的error        咱们要晓得单纯的从语言层面无奈判断一个指针所保留的地址是否是非法的,等到程序运行起来,配合硬件的内存理论地址,能力发现指针指向的地址是否是你想要让它指向的正当空间地址。在日常编码过程中有一些导致野指针或者内存溢出的谬误编码方式: 1、指针变量未初始化        任何指针在被创立的时候,不会主动变成NULL指针,因而指针的值是一个随机值。这时候去解援用就是去拜访这个地址不确定的变量,所以后果是不可知的。 void main() { char* p;    *p = 6;  //谬误}2、应用了悬垂指针        在C或者C++中应用malloc或者new申请内存应用后,指针曾经free或者delete了,没有置为NULL,此时的指针是一个悬垂指针。         free和delete只是把指针所指的内存给开释掉,并不会扭转相干的指针的值。这个指针理论依然指向内存中雷同地位即其地址依然不变,甚至该地位依然能够被读写,只不过这时候该内存区域齐全不可控即该地址对应的内存是垃圾,悬垂指针会让人误以为是个非法的指针。 void main() {    char* p = (char *) malloc(10); strcpy(p, “abc”);    free(p);  //p所指的内存被开释,然而p所指的地址依然不变    strcpy(p, “def”); // 谬误}3、返回栈内存指针或援用        在函数外部定义的部分指针变量或者部分援用变量不能作为函数的返回值,因为该局部变量的作用域范畴在函数外部,该函数在被调用时,因为部分指针变量或者援用曾经被销毁,因而调用时该内存区域的内容曾经产生了变动,再操作该内存区域就没有具体的意义。 char* fun1(){ char* p = "hello"; return p;}char* fun2(){ char a = 6; return &a;}void main(){ char* p1 = fun1(); //谬误 char* p2 = fun2(); //谬误}4、指针反复开释void fun(char* p, char len){ for(char i = 0; i < len; i++) { p[i] = i; } free(p);}void main(){ char * p1 = (char *)malloc(6 * sizeof(char)); fun(p1, 6); free(p1); //反复开释指针导致谬误 }5、数组越界        应用的数组长度超过了定义的数组长度。 ...

November 8, 2022 · 1 min · jiezi

关于c#:ASPNET连接数据库SQL-Server的操作

1.创立好我的项目后在咱们的Web.config外面连贯SQL Server数据库 2.写入代码<appSettings> <add key="conStr" value="Data Source=localhost;Initial Catalog=s;Integrated Security=True;" /> </appSettings>conStr是字段名字,前面连贯须要,localhost是本地地址的意思,s是数据库中的表名。 3.选中shujuku练习右击,抉择增加,而后在抉择Web窗体 4.创立好的Web窗体如下窗体中的代码:就增加了一个显示控件cs文件代码如下: using System;using System.Configuration;using System.Data;using System.Data.SqlClient;namespace shujuiku练习{ public partial class WebForm1 : System.Web.UI.Page { private static string constr;//这里就是咱们定义数据库的字段constr private static SqlConnection conn;//创立数据库连贯字段 protected void Page_Load(object sender, EventArgs e) { constr = ConfigurationManager.AppSettings["conStr"].ToString(); conn = new SqlConnection(constr);//创立数据库连贯实例化对象 DataSet ds = new DataSet();// 实例化DataSet类型的对象ds SqlDataAdapter s = new SqlDataAdapter("select * from SysUser;", conn);//实例化DataAdapter对象 s.Fill(ds);//填充ds GridView1.DataSource=ds;//将该数据显示在GridView1控件上 GridView1.DataBind(); } }}5.网页效果图和数据库的表:

November 7, 2022 · 1 min · jiezi

关于c++:万字避坑指南C的缺陷与思考上

导语 | 本文次要总结了自己在C++开发过程中对一些奇怪、简单的语法的了解和思考,同时作为C++开发的避坑指南。 前言 C++是一门古老的语言,但依然在不间断更新中,一直援用新个性。但与此同时C++又甩不掉微小的历史包袱,并且C++的设计初衷和理念造成了C++异样简单,还呈现了很多不合理的“缺点”。本文次要有3个目标: 总结一些C++艰涩难懂的语法景象,解释其背地起因,作为防踩坑之用。和一些其余的编程语言进行比拟,列举它们的优劣。发表一些我本人作为C++程序员的认识和感触。 来自C语言的历史包袱C++有一个很大的历史包袱,就是C语言。C语言诞生工夫很早,并且它是为了编写OS而诞生的,语法更加底层。有人说,C并不是针对程序员敌对的语言,而是针对编译期敌对的语言。有些场景在C语言自身可能并没有什么不合理,但放到C++当中会“爆炸”,或者说,会迅速变成一种“缺点”,让人异样费解。 C++在演变过程中始终在排汇其余语言的劣势,一直提供新的语法、工具来进行优化。但为了兼容性(不仅仅是语法的兼容,还有一些设计理念的兼容),还是会留下很多坑。 (一)数组 数组自身其实没有什么问题,这种语法也十分罕用,次要是示意间断一组雷同的数据形成的汇合。但数组类型在待遇上却和其余类型(比如说构造体)十分不一样。 数组的复制咱们晓得,构造体类型是能够很轻松的复制的,比如说: struct St { int m1; double m2;};void demo() { St st1; St st2 = st1; // OK St st3; st1 = st3; // OK}但数组却并不能够,比方: int arr1[5];int arr2[5] = arr1; // ERR明明这里arr2和arr1同为int[5]类型,然而并不反对复制。照理说,数组该当比构造体更加适宜复制场景,因为需要是很明确的,就是元素按位复制。 数组类型传参因为数组不能够复制,导致了数组同样不反对传参,因而咱们只能采纳“首地址+长度”的形式来传递数组: void f1(int *arr, size_t size) {}void demo() { int arr[5]; f1(arr, 5);}而为了不便程序员进行这种形式的传参,C又做了额定的2件事: 提供一种隐式类型转换,反对将数组类型转换为首元素指针类型(比如说这里arr是int[5]类型,传参时主动转换为int*类型)函数参数的语法糖,如果在函数参数写数组类型,那么会主动转换成元素指针类型,比如说上面这几种写法都齐全等价:void f(int *arr);void f(int arr[]);void f(int arr[5]);void f(int arr[100]);所以这里非常容易误导人的就在这个语法糖中,无论中括号里写多少,或者不写,这个值都是会被疏忽的,要想晓得数组的边界,你就必须要通过额定的参数来传递。 但通过参数传递这是一种软束缚,你无奈保障调用者传的就是数组元素个数,这里的危害详见前面“指针偏移”的章节。 剖析和思考之所以C的数组会呈现这种奇怪景象,我猜想,作者思考的是数组的理论应用场景,是常常会进行切段截取的,也就是说,一个数组类型并不总是齐全整体应用,咱们可能更多时候用的是其中的一段。举个简略的例子,如果数组是整体复制、传递的话,做数组排序递归的时候会不会很难堪?首先,排序函数的参数难以书写,因为要指定数组个数,咱们总不能针对于1,2,3,4,5,6,...元素个数的数组都别离写一个排序函数吧?其次,如果取子数组就会复制出一个新数组的话,也就不能对原数组进行排序了。 所以综合思考,罗唆这里就不反对复制,强制程序员应用指针+长度这种形式来操作数组,反而更加合乎数组的理论应用场景。 ...

November 7, 2022 · 11 min · jiezi

关于c:小凯15天快速讲完c语言简单学习第六课

温习0.1 定义函数返回值类型 函数名称(形式参数类型1 形式参数名称1,形式参数类型2 形式参数名称2......){ //函数内的语句 return 返回值;}//获取两个整数中的较大值 int GetMax(int a,int b){ if(a>b) { return a; } else { return b; }}// 调用函数 int main(){ int nMax = 0; nMax = GetMax(10+5,20); return 0;}形参:定义函数的时候,规定要传入的参数的类型实参:调用函数的时候,传递的具体数据形参的扭转,不会影响实参的值。 和宏的比拟函数是先把参数的值给运算进去,而后传递给形参。 即使是最简略的函数,调用也会产生额定的耗费。宏是一个预处理,是编译之前进行的一个替换,不宜编写的简单。什么时候应用函数,什么时候应用宏呢???当代码比拟简短,且大量调用的时候,应用宏代码比较复杂,调用不频繁的时候,应用函数。 #include <stdio.h>#define MUL(a,b) a*bint GetMul(int m, int n){ return m * n;}int main(){ int n1 = 0; int n2 = 0; n1 = MUL(5+1,10);//5+1*10 printf("%d", n1); n2 = GetMul(5+1,10); printf("%d", n2); return 0;}0.2 全局变量和局部变量全局变量:定义在函数内部的变量,能够被所有的函数所共享。局部变量:定义在函数外部的变量,只在定义它的花括号内应用。static: ...

November 7, 2022 · 4 min · jiezi

关于c:小凯15天快速讲完c语言简单学习第五课

0. 温习0.1 循环while(表达式){}do{}while(表达式);for(初始化循环变量;判断条件;循环变量的变动){}个别比拟明确循环次数的时候,应用for不太明确循环次数的时候,个别应用while两个管制语句:break: 跳出循环,只能跳出以后循环(只能跳出一层循环)continue: 间接开始下一轮循环 表达式的虚实问题:a>b a>b&&b>c 相似于这样的关系表达式或者逻辑表达式,后果只有两个:true false值就是1和0反过来说 0是假,非零是真 int n =0;while(n<=100){ printf("%d",n); n++;}n =0;while(101-n){ printf("%d",n); n++;}0.2 预处理命令0.2.1 #include用于蕴含头文件的 <>用于编译器自带的 “”用于本人写的头文件 0.2.2 #define无参宏 0.3 二维数组能够看成是多个1维数组 #include <stdio.h>int main(){ //二维数组的定义和初始化 //如果方括号内的元素不够,前面就都初始化为0 int arrTest1[3][4] = { 1,2,3,4,5,6,7 }; // 1 2 3 4 // 5 6 7 0 // 0 0 0 0 int arrTest2[3][4] = { {1,2},{3,4},{5,6} }; // 1 2 0 0 // 3 4 0 0 // 5 6 0 0 int arrTest3[][5] = { 1,2,3,4,5,6,7,8,9,10,11 }; //等价于int arrTest[3][5] = {1,2,3,4,5,6,7,8,9,10,11}; //1 2 3 4 5 //6 7 8 9 10 //11 0 0 0 0 return 0;}什么时候会应用二维数组:在一组数据中,还要再次分组的时候,须要应用二维数组。如果有一个二维数组,如何遍历这个二维数组 ...

November 5, 2022 · 3 min · jiezi

关于c:小凯15天快速讲完c语言简单学习第四课

这节课笔记的排版有点问题,感觉是markdown语法有问题,当然不排除思否社区的排版问题,哈哈哈,已反馈官网啦,大家对付看。0. 温习0.1 运算符1.赋值留神的点:// 定义变量的时候,给的值 这时叫做初始化。int a = 0; int b = 10;int c = 0;// 定义完变量,再去给值,这个就叫赋值a = 100; //正确的100 = a; //谬误的a = b = c = 500;复合赋值运算a+=b; //相当于 a = a+b;2.算术运算符有几个:+ - * / % ++ --须要留神的:除法运算:两个整数相除,后果就是一个整数取模运算:参加运算的两个数,必须是整数 取模运算常常被用于判断A是不是B的倍数自增自减:自增自减的同时参加了其余运算,前置自增是先自增,再参加其余运算,后置,反过来。 3. 取地址 获取一个变量的地址,常常用于scanf_s 输出数据留神:数组的名字,就是数组的起始地址4.sizeof 求得一个数据类型或者一个曾经定义好的变量的大小 int a= 200; sizeof(int) 就是4 sizeof(a) 也是4 int arr[8] = {0}; sizeof(arr); 这个就是32 5.逗号 他是优先级最低的运算符6.关系运算符 > >= < <= == != 他们是用于比拟大小关系的,后果只能是 true 或者falsetrue和false 是bool类型。从数值上看 true 就是1 false 就是07.逻辑运算符 ...

November 4, 2022 · 4 min · jiezi

关于c:C语言内存分区堆栈全局静态存储区自由存储区代码区与可执行程序的三段Text段Date段Bss段

一、c语言五大内存分区 栈区(stack):寄存函数形参和局部变量(auto类型),由编译器主动调配和开释 堆区(heap):该区由程序员申请后应用,须要手动开释否则会造成内存透露。如果程序员没有手动开释,那么程序完结时可能由OS回收。 全局/动态存储区:寄存全局变量和动态变量(包含动态全局变量与动态局部变量),初始化的全局变量和动态局部变量放在一块,未初始化的放在另一块 文字常量区:常量在对立运行被创立,常量区的内存是只读的,程序完结后由零碎开释。 程序代码区:存放程序的二进制代码,内存由系统管理  二、可执行程序程序三段-Text段,Date段,Bss段 auto变量:函数的局部变量,如果没有申明为static,函数中定义的局部变量全副为auto类型,auto变量包含未加static申明的局部变量和函数的形参。在函数调用时零碎会给他们调配存储空间,在函数调用完结后会主动开释这些空间。属于动静存储形式。 static变量:用static申明的局部变量在调用完结后不会隐没而保留原来的值。static局部变量定义应用后值会存储下来。所以应用static局部变量定义只须要一次赋值。动态局部变量的作用域仅限于所定义的函数。但函数完结后变量的值会保留。直到整个程序运行完结。全局变量从定义开始作用于整个文件直至程序运行完结。 register寄存器变量:寄存器变量能够进步c语言的执行效率,行将局部变量的值存入CPU的寄存器中。须要留神的是!!!:1.只有动静存储的变量(主动局部变量和形参)才能够作为寄存器变量来存储,部分动态变量不能够定义为寄存器变量。2.计算机的寄存器数目是无限的,所以不能定义任意多个寄存器变量。 extern内部变量:即全局变量的内部表现形式,是在函数内部定义的变量。全局变量的作用域为从定义开始到源文件完结。exten对该变量作内部变量申明,扩大变量作用域。 三、可执行程序内存空间与逻辑地址空间的映射与划分 右边是UNIX零碎的执行文件,左边是过程对应的逻辑地址空间的划分状况首先是栈区(堆栈区stack),堆栈是由编译器主动调配开释,寄存函数的参数和局部变量的值(auto类型),操作形式相似于数据结构中的栈。栈的申请是由零碎主动调配,如在函数外部申请一个局部变量int h,同时判断所申请空间是否小于栈的残余空间,如果小于则为其开拓空间,为程序提供内存,否则将报异样提醒栈溢出。堆(heap),堆个别由程序员调配开释,若程序员不开释,程序完结可能由OS回收。它与数据结构中的堆是两回事,调配形式相似于链表,申请则是程序员本人操作应用malloc或new。申请过程比较复杂,当零碎收到程序的申请时,会遍历记录闲暇内存地址的链表,以求寻找第一个空间大于所申请空间的堆节点,而后将该节点从闲暇节点链表中删除,并将该节点的空间调配给程序,有些状况下,新申请的内存块的首地址记录本次调配的内存块的大小,这样在delete尤其是delete[]时能正确的开释内存空间。下边是全局动态存储区,全局变量与动态变量的存储是放在一块的,初始化的全局变量与动态变量寄存在一块区域,未初始化的全局变量与未初始化的动态变量寄存在相邻的另一块区域。文字常量区,常量字符串就是放在该局部,只读存储区,程序完结后由零碎开释程序代码区,存放程序的二进制代码区。 四、存储类型关键字定义变量与函数作用域与生命周期auto变量:函数的局部变量,如果没有申明为static,函数中定义的局部变量全副为auto类型,auto变量包含未加static申明的局部变量和函数的形参。在函数调用时零碎会给他们调配存储空间,在函数调用完结后会主动开释这些空间。属于动静存储形式。static变量:用static申明的局部变量在调用完结后不会隐没而保留原来的值。static局部变量定义应用后值会存储下来。所以应用static局部变量定义只须要一次赋值。动态局部变量的作用域仅限于所定义的函数。但函数完结后变量的值会保留。直到整个程序运行完结。全局变量从定义开始作用于整个文件直至程序运行完结。register寄存器变量:寄存器变量能够进步c语言的执行效率,行将局部变量的值存入CPU的寄存器中。须要留神的是!!!:1.只有动静存储的变量(主动局部变量和形参)才能够作为寄存器变量来存储,部分动态变量不能够定义为寄存器变量。2.计算机的寄存器数目是无限的,所以不能定义任意多个寄存器变量。extern内部变量:即全局变量的内部表现形式,是在函数内部定义的变量。全局变量的作用域为从定义开始到源文件完结。exten对该变量作内部变量申明,扩大变量作用域。 五、堆与栈的区别 1.申请形式 stack:栈;由零碎主动调配,主动开拓空间 heap:由程序员本人申请并指明大小,c中malloc,c++中new。如p1=(char)malloc(10);p2=(char)new(10);但须要留神的是p1,p2本事是在栈中的 2.申请后零碎的响应 栈:只有栈的残余空间大于所申请空间,零碎将为程序提供内存,否则将报异样提醒栈溢出 堆:首先操作系统有一个记录闲暇内存地址的链表,当零碎收到程序的申请时,会遍历该链表,寻找第一个大于所申请空间的堆节点,而后将该节点从闲暇节点链表中删除,并将该节点的空间调配给程序。另外对于大部分零碎,会在这块内存空间中的首地址处记录本次调配的大小,这样代码中的delete语句能力正确的开释本内存空间。另外因为找到的堆节点大小不肯定正好等于申请的大小,零碎会主动的将多余的那局部从新放入闲暇链表中。 3.申请大小的限度 栈:在windows下栈是向低地址扩大的数据结构,是一块间断的内存区域。所以栈的栈顶地址和最大容量是零碎事后设定好的。在windows下栈的大小是2M.因而能从栈取得的空间比拟小。 堆:堆是向高地址扩大的数据结构,是不间断的内存区域。这是是因为零碎用链表来存储闲暇内存地址的,所以是不间断的。而链表的遍历方向是由低地址到高地址。堆得大小受限于计算机系统中无效的虚拟内存大小。相比较而言堆取得的空间比拟灵便,也比拟大。 4.申请效率的比拟 栈:由零碎主动调配,速度较快,但程序员是无法控制的。 堆:由new调配的内存,个别速度比较慢,而且比拟容易产生内存碎片,不过用起来最不便。 5.堆和栈中的存储内容 栈:在函数调用时,第一个进栈的是主函数中的下一条指令(函数调用语句的下一条可执行语句)的地址,而后是函数的各个参数。在大多数c编译器中,参数是由右往左压栈的,而后是函数中的局部变量。动态变量是不入栈的。当函数调用完结后,局部变量先出栈,而后是参数,最初栈顶指针指向最开始存的地址,,也就是主函数的下一条指令,程序由该点继续执行。 堆:个别是在堆的头部用一个字节寄存堆得大小,其余内容本人安顿。 6.存取效率的比拟 1 char str1[]="aaaaaa"; 2 char *str2="cccccc"; 第一行是在运行时刻赋值的,第二行是在编译时就曾经确定的,但在当前的存取过程中,在栈上的数组比指针指向的字符串快。  

November 3, 2022 · 1 min · jiezi

关于c++:10大性能陷阱每个C工程师都要知道

导语 | 每个C++程序员好像都是人形编译器,不止要看懂代码外表的逻辑,甚至要晓得每行代码对应的汇编指令。优化代码也成了C++工程师日常必备,正所谓“一杯茶,一包烟,一段代码,优化一天”。在经验过无数个性能优化的日夜后,笔者也总结了几个中过招的性能陷阱,与你分享~ 本文介绍的性能陷阱次要分为两大类:“有老本形象”和“与编译器作对”。前者是指在应用C++的性能/库时须要留神的隐形老本,后者则是一些C++老手可能会写出不利于编译器优化的代码。另外本文的程序是由根底到进阶,读者能够依据须要间接跳到本人想看的局部。 有老本形象C++“信徒”们经常宣扬C++的“零老本形象(Zero Cost Abstraction)”。然而对于“零老本形象”这个概念存在很多误会。比方有的老手会认为:“应用C++的任何个性都没有老本”。那显然是大错特错的,比方应用模版就会导致编译工夫变慢的编译期老本,而且我花了21天工夫精通C++的工夫老本也是老本啊(狗头)。有些教训的C++程序员会解释为”应用C++的任何个性都没有运行时老本“,这也是对C++最常见的误会。C++的创始人Bjarne Stroustrup是这样解释“零老本形象”的: 你不会为任何你没有应用的个性付出任何老本。对于你应用的个性,你只会付出最小运行时老本。简略来说,就是C++不会背着你偷偷干坏事(比方垃圾回收),而你指定C++干的活,C++会尽量在编译期做,保障在运行期只会做“起码”的工作。连小学生都应该晓得,“起码”并不等于“零”,所以“零老本形象”其实是一种谎话,Google的C++负责人Chandler Carruth就已经在CppCon 2019说过:C++基本不存在”零老本形象“。 (链接:https://www.youtube.com/watch...) 显然,C++的很多个性是有性能老本的,而且,这些老本往往呈现在你“没有写”的代码里,即C++帮你增加的隐形代码。作为C++工程师,咱们就必须理解每个个性会带来的性能损耗,在做代码设计时尽量抉择损耗小的个性。 而下文介绍的很多坑点,C++语言服务器clangd能够帮你实时检测进去并主动修复。 (一)虚函数陈词滥调的性能损耗,这里只介绍一下虚函数调用带来的老本: 会多一次寻址操作,去虚函数表查问函数的地址。可能会毁坏cpu流水线,因为虚函数调用是一次间接调用,须要进行分支预测。妨碍了编译器内联,大部分状况下,虚函数是无奈被内联的(与前两条相比,无奈内联才是虚函数性能损耗的次要起源)。然而在理论生产环境中,可能很多的运行时多态是无奈防止的,毕竟这是OOP的根底个性,因而对于虚函数咱们也只能理解背地的老本而已。某些状况下咱们能够应用编译期多态来代替虚函数,比方CRTP(Curiously Recurring Template Pattern)、Tempated Visitor Pattern、Policy Based Design等等,我的下一篇文章《C++独有的设计模式》中会介绍这些技巧,敬请期待。 (二)隐形的拷贝也是一个陈词滥调的性能损耗,这里次要介绍几个容易被忽略的场景: Member Initialization构造函数class C { public: C(A a, B b): a_(a), b_(b){} private: A a_; B b_;}int main() { A a; B b; C c(a, b);}如果A、B是非平庸的类,会各被复制两次,在传入构造函数时一次,在结构时一次。C的构造函数该当改为: C(A a, B b): a_(std::move(a)), b_(std::move(b)){}For循环std::vector<std::string> vec;for(std::string s: vec){ // ...}这里每个string会被复制一次,改为for(const std::string& s: vec)即可。 Lambda捕捉A a;auto f = [a]{};lambda函数在值捕捉时会将被捕捉的对象拷贝一次,能够依据需要思考应用援用捕捉auto f= [&a]{};或者用std::move捕捉初始化auto f= [a = std::move(a)]{};(仅限C++14当前)。 ...

November 3, 2022 · 3 min · jiezi

关于c:c语言简单学习入门第三课

1. 运算符和表达式1.1 赋值右边的 = 左边的;赋值的含意 是 将左边的值,存储到右边中。右边必须是一个 变量。左边能够是变量,也能够是常量,也能够是一个算式,最终是一个值。int a = 0;int b = 100;a = b;a = 200;a = b+200;复合赋值:比方: a+=b; 等价于 a = a+b; 1.2 算术运算符根本的:+ - * / %自增自减:++ --次要留神的重点:/ 两个整数相除 只能失去整数局部 1.2.1 除法/ 两个整数相除 只能失去整数局部 有浮点数参加的除法,能力失去小数局部 1.2.2 取模% 获取余数 10%3 失去的余数是 1 取模(取余)参加运算的数据只能是 整数 10.8%3 这个语法是谬误的 1.2.3 自增自减前置还是后置的问题 #include <stdio.h>#include <stdlib.h>int main(){ //根本应用 int nNum1 = 10; nNum1++; printf("%d\n", nNum1); int nNum2 = 10; ++nNum2; printf("%d\n", nNum2); //前置和后置有什么区别呢??? //区别在于,自增的同时还要参加其余运算, //前置 是 先自增,再进行其余运算 //后置 是 先参加其余运算,而后再自增 //根本应用 int nNum3 = 10; printf("%d\n", nNum3++); printf("%d\n", nNum3); int nNum4 = 10; printf("%d\n", ++nNum4); printf("%d\n", nNum4); //上面这个会输入什么 int nNum5 = 10; printf("%d\n", nNum5--); printf("%d\n", --nNum5); printf("%d\n", nNum5); return 0;}1.3 取地址运算符获取变量的地址比方一个int变量是 4个字节 每个字节都有本人的地址,取地址符获取到的是 第1个字节的地址。 ...

November 2, 2022 · 1 min · jiezi

关于c:c语言简单学习入门第二课

温习上一课0.1 如何将一个十进制数转为二进制数?整数局部:逆序取余小数局部:正向取整例题:20.28整数局部:20/2 10 010/2 5 05/2 2 12/2 1 01/2 0 1小数局部:0.28*2 0.56 00.56*2 1.12 10.12*2.....后果就是:10100.01 0.2 如何将一个二进制数转为十进制数?将各个位所代表的权值 加到一起5 4 3 2 1 0 -1 -2 -31 1 0 1 1 0. 1 0 1 32 + 16+0+4+2+0 + 0.5 +0 + 0.125后果:54.625 0.3 如何在二进制和十六进制之间转换?外围逻辑:4个二进制位 代表1个十六进制位 比方:二进制转十六进制0001 1010 1001. 1101 1000 1 A 9 D 8十六进制转二进制3 D 40011 1101 0100 0.4 原码,反码,补码负数:三码合一,或者也能够说负数不辨别这些货色正数:原码 最高位是符号位 (1代表正数) 其余的位依照负数去写就能够 比方 -5 1 000 0101反码 符号位不变 其余的位按位取反 1 111 1010补码 反码+1 1 111 1011 ...

November 1, 2022 · 3 min · jiezi

关于c:鸡兔同笼问题

这是我小学四年级做过的第一道数学大题,也是我大学自学c语言时候的第一道题。我对此十分思念。题目:鸡兔同笼,共35只头,94只脚,问鸡兔各多少? 从数学上来看题目很简略,然而从代码角度绝对艰难一些。 这里我参考了小学的解题思路,假如法 假如鸡有x只,那么兔有35-x只。鸡的脚有2x只,兔的脚有4*(35-x)只。 #define CRT_SECURE_NO_WARNINGS#include <stdio.h>#include<stdlib.h>int main(void){ int chicken; int rabbit; int head; //鸡的头有35个 int foot; scanf("%d %d", &chicken, &rabbit); printf("鸡兔同笼数据如下:\n"); 4 * (35 - chicken) +2* chicken == 94; rabbit = 94 - chicken; printf("%d ,%d", chicken, rabbit); system("pause"); return 0;}

October 31, 2022 · 1 min · jiezi

关于c++:算法分析之快速排序算法c实现

算法剖析之疾速排序算法,c++实现疾速排序算法剖析如图所示,数组开始是无序的,从两端开始比拟,找到两头值,而后以两头值为中心点,进行分段 第一次划分而后再从每一段中反复此操作,直到只剩下分段长度为1完结,即i>=j则完结 实现// # 疾速排序,将一个数组,找到一个两头值,而后以这个两头值为核心,分段// # 而后再从每一段中反复此操作,直到只剩下分段长度为1完结,即i>=j则完结 // 1. 定义一个分段函数,参数别离是arr(待排序数组),begin(终点),end(起点)// 2. 初始化函数值 i = begin; j = end;// 3. 从右往左找,i不变,j--;如果arr[i] > arr[j],替换arr[i]和arr[j]的值,而后替换挪动地位,j不变,i--;// 4. 重复操作,直到i >= j;完结算法并返回i#include<iostream>using namespace std;// 排序算法,arr数组,begin终点下标,end起点下标 int sortArr(int arr[], int begin, int end) { int i = begin, j = end; while(i < j) { while(i < j && arr[i] <= arr[j]) j--; if (i < j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } while(i < j && arr[i] <= arr[j]) i++; if (i < j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } return i;}// 分段办法,arr数组,begin终点下标,end起点下标 void quilckSort(int arr[], int begin, int end) { if (begin < end) { int middle = sortArr(arr, begin, end); quilckSort(arr, begin, middle - 1); quilckSort(arr, middle + 1, end); }}int main() { int arr[] = {0, 9, 8, 6, 1, 5, 4, 12, 15, 3, -2}; int len = sizeof(arr) / 4; quilckSort(arr, 0, len - 1); for (int i = 0; i < len; i++) { cout<< arr[i] << " "; } return 0;}

October 31, 2022 · 1 min · jiezi

关于c++:动态规划之01背包问题c实现

动静布局之01背包问题,c++实现问题形容01背包问题问题剖析 动静布局法分析:1.划分子问题,2.得出子问题的递推公式,3.填表划分子问题 用数组Vn存储价值和分量关系,行示意物体,列示意分量第0行和第0列设置为0,没有物体的时候价值没0,分量为0的时候物体无论多少价值也为0如果第i个物体分量小于以后总重量j,则取前i-1和第i个物体组合的最优值,否则该物体不能够放进背包,取前i-1个物体的最优价值(例如装入以后物体,则残余分量用来装后面残余物体的最优装法失去以后最优价值,得出max{V(i-1, j), V(i-1, j-wi)+vi } ;不装入以后物体,则以目前分量装后面所有物体的最优法),得出V(i-1, j)。递推公式 不装入背包时:V(i-1, j) j < wi装入背包时:max{V(i-1, j), V(i-1, j-wi)+vi } j >= wiV(i-1, j-wi)+vi 示意用以后值和子问题的解联合,取最优填表 算法实现#include<iostream>using namespace std;struct Node { int value; int weight;}; // 物体,长度 void packageHander(Node arr[], int n, int c) { int V[n][c+1], i; // 第0行和第0列设置为0,没有物体的时候价值没0,分量3为0的时候物体无论多少价值也为0 for (i = 0; i < n; i++) { V[i][0] = 0; } for (i = 0; i <= c; i++) { V[0][i] = 0; } for (i = 1; i < n; i++) { // 分量从1到c for (int j = 1; j <= c; j++) { // 分量小于目前总量 // 如果第i个物体分量小于以后总重量j,则取前i-1和第i个物体组合的最优值,否则该物体不能够放进背包,取前i-1个物体的最优价值 if (arr[i-1].weight <= j) { V[i][j] = max(V[i-1][j], V[i-1][j-arr[i-1].weight]+arr[i-1].value); } else { V[i][j] = V[i-1][j]; } } } for (i = 0; i <= c; i++) { cout<<i<<"\t"; } cout<<endl; for (i = 0; i < n; i++) { for (int j = 0; j <= c; j++) { cout<<V[i][j]<<"\t"; } cout<<endl; } int j = 0; // 如果以后值比前一个值大,阐明被取了,就取以后值,而后减去分量,分量作为列 for ( i=n, j=c; i>0; i--) { if (V[i][j] > V[i-1][j]) { x[i-1] = 1; j = j - arr[i-1].weight; } else { x[i-1] = 0; } } cout<<"选取计划为"; for (i = 0; i<n-1; i++) { cout<<x[i]<<" "; } cout<<endl;}int main() { int n = 6; Node a[n]; a[0].value = 6; a[0].weight = 2; a[1].value = 3; a[1].weight = 2; a[2].value = 5; a[2].weight = 6; a[3].value = 4; a[3].weight = 5; a[4].value = 6; a[4].weight = 4; for (int i = 0; i < n-1; i++) { cout<<"val"<<a[i].value<<"weight"<<a[i].weight<<endl; } packageHander(a, n, 10); return 0;} 后果如下 ...

October 31, 2022 · 2 min · jiezi

关于c#:委托与事件的定义与用法

1.委托1.1定义及应用委托是一种动静调用办法的类型,它与类、接口和数组雷同属于援用型。 注意事项:委托实际上定义了一种办法的模板,只有返回值类型和形参列表与该模板统一,就都可用该委托类型进行实例化。 委托对象实质上代表了办法的援用。在.NET Framework 中,委托具备以下特点。(1)委托相似于 C++函数指针,但与指针不同的是,委托是齐全面向对象的、平安的数据类型。(2)委托容许将办法作为参数进行传递。(3)委托可用于定义回调办法。(4)委托能够把多个办法连贯在一起,这样在触发事件时,可同时启动多个事件处理程序。 委托(delegate)申明的个别模式:[拜访修饰符] delegate 返回值类型 委托名( [参数列表] );如:public delegate int Do( int x);下面的语句申明了一个公开的名为“Do”的委托,该委托返回值类型为整型。例如:public delegate void wt(int x);定义一个没有返回值、带有整型参数的x的一个名叫wt的委托。 具体怎么应用接下来看看代码示例: namespace C练习 { public delegate void wt(int x); class Program { public static void Add(int x) //定义了一个静态方法Add,留神该办法的返回值类型和参数与委托的雷同 { Console.WriteLine(x+x); //输入x+x的值 } public static void Cheng(int x)//同上 { Console.WriteLine(x*x); } static void Main(string[] args) { wt j = Add; //用委托定义j并调用Add办法 j += Cheng; //+代表在j的根底上增加Cheng办法 j -= Cheng;//-代表在j的根底上移除Cheng办法 j(3); } }}那么输入的后果就为:6 ...

October 30, 2022 · 1 min · jiezi

关于c#:C隐藏基类成员base用法

代码如下:class Program { public class Student//定义一个学生类,其中字段有姓名、年龄、学号 { private string Name; private string Age; private int Id; public Student(string name,string age,int id)//创立学生类的构造函数 { Name = name; Age = age; Id = id; } public void sc()//定义一个输入办法sc,别离输入姓名,年龄,学号信息 { Console.WriteLine("姓名:{0}", Name); Console.WriteLine("年龄:{0}", Age); Console.WriteLine("学号:{0}", Id); } } public class e : Student//定义一个新类e来根底学生类,换个说法就是e是学生类的子类 { private string department;//定义两个字段业余和性别 private string sex; public e(string name, string age, int id, string d,string s) : base(name, age, id) { department = d; sex = s; } new public void sc()//应用new修饰符清晰的表明了派生类暗藏了基类,如果不加new,编译会有正告 { base.sc();//调用基类的构造函数 Console.WriteLine("业余:{0}", department); Console.WriteLine("性别:{0}", sex); } } static void Main(string[] args) { e s1 = new e("张三", "22", 12, "软件工程", "男");//实例化派生类对象为s1,写入相应信息。 s1.sc(); }}编译后果如下: ...

October 29, 2022 · 1 min · jiezi

关于c++:C-mutex-类及方法介绍

头文件介绍Mutex 系列类(四种) std::mutex,最根本的 Mutex 类。std::recursive_mutex,递归 Mutex 类。std::time_mutex,定时 Mutex 类。std::recursive_timed_mutex,定时递归 Mutex 类。Lock 类(两种) std::lock_guard,与 Mutex RAII 相干,不便线程对互斥量上锁。std::unique_lock,与 Mutex RAII 相干,不便线程对互斥量上锁,但提供了更好的上锁和解锁管制。其余类型 std::once_flag 标记位,与函数std::call_once 配合应用std::adopt_lock_tstd::defer_lock_tstd::try_to_lock_t函数 std::try_lock,尝试同时对多个互斥量上锁。std::lock,能够同时对多个互斥量上锁。std::scoped_lock RALL,能够同时对多个互斥量上锁并保障解锁。std::call_once,如果多个线程须要同时调用某个函数,call_once 能够保障多个线程对该函数只调用一次。mutexmutex 的全名为 mutual exclusion(互斥体),其 object 用来帮助采取独占且排他的形式管制“对资源(object或多个Object的组合)的并发拜访”。 构造函数,std::mutex不容许拷贝结构,也不容许 move 拷贝,最后产生的 mutex 对象是处于 unlocked 状态的。lock(),调用线程将锁住该互斥量。线程调用该函数会产生上面 3 种状况: (1). 如果该互斥量以后没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程始终领有该锁。(2). 如果以后互斥量被其余线程锁住,则以后的调用线程被阻塞住。(3). 如果以后互斥量被以后调用线程锁住,则会产生死锁(deadlock)。unlock(), 解锁,开释对互斥量的所有权。try\_lock(),尝试锁住互斥量,如果互斥量被其余线程占有,则以后线程也不会被阻塞。线程调用该函数也会呈现上面 3 种状况, (1). 如果以后互斥量没有被其余线程占有,则该线程锁住互斥量,直到该线程调用 unlock 开释互斥量。(2). 如果以后互斥量被其余线程锁住,则以后调用线程返回 false,而并不会被阻塞掉。(3). 如果以后互斥量被以后调用线程锁住,则会产生死锁(deadlock)。上面给出一个与 std::mutex 的小例子: #include <iostream> // std::cout#include <mutex> // std::mutex#include <thread> // std::threadvolatile int counter(0); // non-atomic counterstd::mutex mtx; // locks access to countervoid attempt_10k_increases() { for (int i = 0; i < 10000; ++i) { if (mtx.try_lock()) { // only increase if currently not locked: ++counter; mtx.unlock(); } }}int main(int argc, const char* argv[]) { std::thread threads[10]; for (int i = 0; i < 10; ++i) threads[i] = std::thread(attempt_10k_increases); for (auto& th : threads) th.join(); std::cout << counter << " successful increases of the counter.\n"; return 0;}std::recursive\_mutex 介绍std::recursive_mutex 与 std::mutex 一样,也是一种能够被上锁的对象,然而和 std::mutex 不同的是,std::recursive_mutex 容许同一个线程对互斥量屡次上锁(即递归上锁),来取得对互斥量对象的多层所有权,std::recursive_mutex 开释互斥量时须要调用与该锁档次深度雷同次数的 unlock(),可了解为 lock() 次数和 unlock() 次数雷同,除此之外,std::recursive_mutex 的个性和 std::mutex 大致相同。 ...

October 28, 2022 · 6 min · jiezi

关于c#:C什么是面向对象类

面向对象本质就是对事实世界的对象进行建模操作。 1.首先什么是对象1.其实随处可见的事物就是一个对象,对象是事物存在的实体,比方人类,计算机等等。其实咱们也能够了解为对象=行为+字段。 2.什么是类类就是一组具备雷同属性和行为的对象的形象,也能够了解为类是一组具备雷同属性和行为的对象的汇合,为该类的所有对象提供对立的形象形容。类的申明个别模式如下: [拜访修饰符] class 类名 [:基类] { 类的成员; }其中,拜访修饰符用来限度类的作用范畴或拜访级别。常见拜访修饰符有:public、private、protected等。如下图例子所示:这里我定义了一个学生类,其中字段包含学号、姓名,一个性别的属性,还有一个办法。那么有人就会纳闷什么是字段、属性和办法? 2.1字段字段示意类的成员变量,字段的值代表某个对象的数据状态。不同的对象,数据状态不同,意味着各字段的值也不同。申明字段的办法与定义一般变量的办法雷同,其个别格局如下:[拜访修饰符] 数据类型 字段名;其中,拜访修饰符用来管制字段的拜访级别,可省略。就如下面的图所示: public string name; 2.2属性字段和常量形容了类的数据,当这些数据容许外界拜访时,能够应用拜访修饰符public,不容许外界拜访时,能够应用private或protected等。但当咱们心愿某些字段只读拜访(如上例中的PI);或者只写访问;或者可读可写访问时,须要应用属性。public、private等拜访修饰符管制成员能不能被拜访;属性管制以何种形式拜访(只读、只写或可读写)。类的属性定义个别模式如下: [拜访修饰符] 数据类型 属性名{ get { //获取属性的代码,用return 返回值 } set { //设置属性的代码,用value赋值 }}拜访修饰符管制成员能不能被拜访;属性管制以何种形式拜访(只读、只写或可读写)。 2.3办法申明办法的个别模式如下: [拜访修饰符] 返回值类型 办法名 ([参数列表]) { 语句; …… [return 返回值;] }如上图所示,我定义了一个办法run(),目标输入跑步这条信息。 综上所述,一个类就定义好了,如果感觉还不错点个赞呦!

October 28, 2022 · 1 min · jiezi

关于c#:openKylin出品-COSCon22-开源操作系统论坛快来看看吧

业界具备宽泛影响力的开源年度盛会2022第七届中国开源年会(COSCon22)将于10月29日-30日由开源社举办。往年,咱们的主题是: Open the World !咱们喜爱 Open 胜过 Close咱们喜爱 Connection 胜过 Broken咱们喜爱 Public Good 胜过 Bad News咱们心愿能以开源的技术、开源的形式、开源的力量,为改善这个世界做出一点奉献。线上共设有1个主论坛和16个分论坛,其中【开源操作系统论坛】由openKylin技术负责人余杰和西安邮电大学传授陈莉君联结出品。目前,残缺论坛议程已出,快来一起看看吧~01论坛介绍开源曾经被写入国家十四五布局,目前国内外开源操作系统倒退迅速,操作系统开源社区建设热火朝天。本次论坛拟邀请国内外开源操作系统的开发者和爱好者独特分享与探讨开源操作系统的研制停顿、痛点痒点和将来布局,一起畅想开源操作系统的美妙今天。直播观看地址:http://segmentfault.com/area/...本次大会的线上互动群(COSCon'22 @开源人团圆)也如约而至。退出社群,理解大会一手信息,精彩周边拿不停!出品丨COSCon'22组委会编辑丨王梦玉设计丨苏子馨 王梦玉 朱亿钦openKylin(凋谢麒麟)社区旨在以“共创”为外围,在开源、被迫、平等、合作的根底上,通过开源、凋谢的形式与企业构建合作伙伴生态体系,独特打造桌面操作系统顶级社区,推动Linux开源技术及其软硬件生态凋敝倒退。社区首批理事成员单位包含麒麟软件、普华根底软件、中科方德、麒麟信安、凝思软件、一铭软件、中兴新支点、元心科技、中国电科32所、技德零碎、北京麟卓、先进操作系统翻新核心等13家产业同仁和行业机构。

October 26, 2022 · 1 min · jiezi

关于c:记录使用dlclose后so无法卸载问题

记录应用dlclose后so无奈卸载问题问题形容有一个相似插件的性能,应用dlopen形式加载一个so,降级so的时候,先dlclose,而后再dlopen加载。这样能够做到更换so的时候不必重新启动程序。原本所有运行的比拟好,但有一个so比拟奇怪,降级so后,某些函数无奈应用新的so外面的实现,还是旧的so中的实现。 问题定位在gdb中应用info sharedlibrary命令查看加载的so,再应用lsof查看如下: (gdb) info sharedlibrary 0x00007fff2132a2c0 0x00007fff213bc5f8 Yes /xxxx/xxx/libxxx.so[email protected]:~# lsof |grep libxxx.songinx 3391245 root mem REG 253,0 8604544 5250054 /xxx/xxx/libxxx.so从这些信息中能够看到,目前正在应用的so的inode是5250054,而是用stat查看当初so的inode信息,发现inode是5377891,如下所示: [email protected]:~# stat /xxx/xxx/libxxx.so File: /apisec/modules/component/sensitive_data/libs/libdi_rechk.so Size: 8604544 Blocks: 16808 IO Block: 4096 regular fileDevice: fd00h/64768d Inode: 5377891 Links: 1Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)Access: 2022-10-21 17:01:57.404283135 +0800Modify: 2022-10-21 15:48:29.000000000 +0800Change: 2022-10-21 17:00:23.423822609 +0800两者inode不一样,阐明应用的不是同一个so文件,因为旧的so曾经被删除。但程序中曾经dlclose并从新dlopen了,于是从网上查找材料,有些材料中都说查看下是否有NODELET标记。于是应用readelf命令查看下: [email protected]:~# readelf -d libxxx.so |grep NODELETE 0x000000006ffffffb (FLAGS_1) Flags: NODELETEso中有此标识,则动静加载程序已被告知不要卸载库,所以调用dlclose后,不会从过程中卸载此so。 手动测试本人写一个test.c的测试例子,代码如下: ...

October 24, 2022 · 1 min · jiezi

关于c:深入剖析Sgementation-fault原理

深刻分析Sgementation fault原理前言咱们在日常的编程当中,咱们很容易遇到的一个程序解体的谬误就是segmentation fault,在本篇文章当中将次要剖析段谬误产生的起因! Sgementation fault产生的起因产生Sgementation fault的间接起因是,程序收到一个来自内核的SIGSEGV信号,如果是你的程序导致的内核给过程发送这个信号的话,那么就是你的程序正在读或者写一个没有调配的页面或者你没有读或者写的权限。这个信号的起源有两个: 程序的非法拜访,本身程序的指令导致的Sgementation fault。另外一种是由别的程序间接发送SIGSEGV信号给这个过程。在类Linux零碎中,内核给过程发送的信号为SIGGEV,信号对应数字为11,在Linux当中信号对应的数字状况大抵如下所示: 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR111) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+338) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+843) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+1348) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-1253) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-758) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-263) SIGRTMAX-1 64) SIGRTMAX当一个程序产生 segmentation fault 的时候,这个程序的退出码 exitcode 等于 139! ...

October 20, 2022 · 2 min · jiezi

关于c++:2022升级百度大牛带你结合实践重学C网潘货区

download:2022降级百度大牛带你联合实际重学C++网潘货区数据库什么时候分表? 子数据库和子表要解决的是现有海量数据拜访的性能瓶颈,以及一直减少的数据量的架构前瞻。数据库划分和表划分的要害指标是否是数据量。咱们以fire100.top网站的资源表t_resource为例。零碎运行初期,每天只上传几十个资源。此时应用单数据库单表的形式足以撑持零碎的存储,数据量小简直没有任何数据库性能瓶颈。然而有一天,一个神秘的流量进来了,零碎产生的资源数据量忽然减少到10万甚至上百万。此时资源表数据量达到数千万,查问响应变得迟缓,数据库的性能瓶颈逐步浮现。以MySQL数据库为例,单个表的数据量达到亿级。当通过增加索引和SQL调优等传统优化策略取得的性能晋升依然很小时,能够思考划分数据库和表。既然MySQL在存储海量数据时会呈现性能瓶颈,是否能够思考用其余计划代替?比方高性能非关系数据库MongoDB?能够,但这取决于存储的数据类型!目前互联网上大多数公司的外围数据简直都存储在关系数据库(MySQL、Oracle等。),因为它们领有堪比NoSQL的稳定性和可靠性,产品的成熟生态系统,以及其余存储工具不具备的外围交易个性。不过还是能够思考用MongoDB来评论和赞美这些非核心数据。如何划分数据库和表 子数据库和子表的外围是将数据分片并绝对平均地路由到不同的数据库和表中,分片后疾速定位数据并整合搜寻后果。 图书馆和桌子能够从垂直(纵向)和程度(横向)两个纬度来划分。咱们以经典的订单业务为例,看看如何拆分。 垂直决裂1.垂直存储一般来说,垂直数据库是依照业务和性能的维度划分的,不同的业务数据放在不同的数据库中,外围概念库是专用的。依据业务类型,将数据拆分到多个数据库中,订单、领取、会员、积分等表放在相应的订单数据库、领取数据库、会员数据库、积分数据库中。不同服务禁止跨数据库直连,获取的对方业务数据全副通过API接口交互,这也是微服务拆分的重要依据。 垂直的数据库划分很大水平上取决于业务的划分,然而有时候业务之间的划分并不是那么清晰,比方电商中订单数据的拆分,其余很多业务都是依赖订单数据的,有时候界线划分的并不好。垂直子数据库将一个数据库的压力扩散到多个数据库,进步了局部数据库的性能。然而没有解决单个表数据量大带来的性能问题,须要前面的子表来解决。2.垂直子表对业务中字段较多的大表进行竖表拆分。个别将宽业务表中的独立字段或不常用字段拆分成独自的数据表,这是一种将大表拆分成小表的模式。例如,一个t_order表有几十个字段,其中订单金额的相干字段是常常计算的。为了不影响订单表t_order的性能,能够将order amount的相干字段离开,保护一个独自的t_order_price_expansion表,使每个表只存储原表的一部分字段,这些字段由订单号order_no关联,而后将拆分后的表路由到不同的库。 数据库是以行为为单位将数据加载到内存中的,所以拆分后的外围表大部分是拜访频率高的字段,字段的长度也较短,这样能够将更多的数据加载到内存中,缩小磁盘IO,进步索引查问的命中率,进一步提高数据库性能。程度决裂数据库和表垂直拆散后,依然会存在单个数据库和表中数据过多的问题。当咱们的利用无奈进行细粒度的垂直划分时,单个数据库读写和存储的性能依然存在瓶颈。这时候就须要配合数据库和表的横向拆散。1、图书馆的程度横向数据库拆分是将同一个表依照肯定的规定拆分成不同的数据库,每个数据库能够位于不同的服务器上,从而实现横向扩大,是进步数据库性能的罕用办法。 例如,db_orde_1和db_order_2的数据库中有雷同的t_order表。当咱们拜访一个订单时,咱们能够通过对订单的订单号取模来指定该订单应该在哪个数据库中操作:订单号mod 2(数据库实例的数量)。这种计划往往能够解决单库存和性能瓶颈的问题,然而因为同一个表散布在不同的数据库中,数据拜访须要额定的路由工作,因而零碎的复杂度也有所提高。2.程度表在同一个数据库中,一个数据量很大的表依照肯定的规定被分成若干个构造雷同的表,每个表只存储原表的一部分数据。例如,一个t_order订单表有900万个数据,t_order_1、t_order_2和t_order_3三个表被程度拆分。每个表有300万个数据,以此类推。 程度表尽管拆分了表,然而子表都在同一个数据库实例中,只是解决了单个表数据过多的问题,并没有把拆分的表扩散到不同的机器上,依然在抢夺CPU、内存、网络IO等。同一个物理机器。为了进一步提高性能,须要将拆分后的表扩散到不同的数据库中,以达到分布式的成果。 存在哪个数据库的表。在将来,会有一个问题。一个表将呈现在多个数据库中。到底应该寄存在哪个数据库的哪个表呢?咱们在下面曾经屡次提到某些规定。实际上,这个规定是一个路由算法,它决定了一个数据应该存储在哪个数据库的哪个表中。常见的有取模算法、范畴限度算法、范畴+取模算法和预约义算法。1.模块化算法关键字段模块化(从散列后果中取余数hash(XXX) mod N),其中N是数据库实例或子表的数量)是最常见的路由办法。以t_order表为例,先将数据库从0到N-1编号,而后对t_order表中的order number字段取模hash (order_no) mod n失去余数I,I=0存储第一个库,i=1存储第二个库,i=2存储第三个库,以此类推。 雷同程序的数据将落在雷同的数据库和表中。查问时应用同样的规定,以t_order订单号作为查问条件,能够疾速定位数据。劣势实现简略,数据分布绝对平均,申请不容易命中一个数据库。劣势模块化算法对集群的扩大反对不是很敌对。集群中有n个数据库real hash (user _ id) mod n。当某台机器停机时,本应达到数据库的申请不能被解决,而后被抛弃的实例将被踢出集群。此时,机器号缩小算法扭转hash(user_id) mod N-1,雷同的用户数据落在不同的数据库中。本机复原后,以user_id为条件的用户数据查问会少一些。2.范畴限度算法范畴限度算法由一些范畴字段宰割,如工夫或ID区域。用户表t_user分为三个表:t_user_1、t_user_2和t_user_3。而后将user_id范畴从1到1000 W的用户数据放入t_user_1,1000到2000 W放入t_user_2,2000到3000 W放入t,日期范畴也是如此。劣势单表数据量可控。横向扩大很简略,减少节点即可,不须要迁徙其余碎片化数据。劣势因为间断切片可能存在数据热点,比方按工夫域切片时,如果某段时间(如双11)订单急剧减少,存储11月数据的表可能会频繁读写,而存储在其余切片表中的历史数据很少被查问,导致数据歪斜,数据库压力散布不平均。 3.范畴+模数算法为了防止热数据的问题,咱们能够优化下限算法。这次咱们先通过range算法定义每个数据库的用户表t_user只存储1000w的数据,第一个db_order_1库存的userId从1到1000 W,第二个数据库是10002000w,第三个数据库是20003000w,以此类推。 在每个数据库中,用户表t_user被拆分成t_user_1、t_user_2、t_user_3等。,userd模路由到相应的表。无效防止了数据分布不平均的问题,数据库的横向扩大也很简略。间接增加实例不须要迁徙历史数据。4.地理位置碎片化地理位置碎片化其实是一个更大的范畴,是以城市或者区域来划分的。比方华东和华北的数据放在不同的碎片化数据库和表中。5.预约义算法预约义算法是事后明确晓得子数据库和子表的数量,它能够间接将某一类数据路由到指定的数据库或表,甚至在查问的时候。子数据库和子表的问题不难发现,与拆分前的单数据库、单表相比,当初零碎的数据存储架构曾经变得非常复杂。看几个有代表性的问题,比方:分页、排序和跨节点联结查问分页、排序、联查,这些开发中频繁应用的看似一般的操作,在数据库和表划分后却是令人头疼的事件。查问扩散在不同库中的表的数据,而后将所有后果汇总合并提供给用户。例如,咱们须要查问11月和12月的订单数据。如果两个月的数据扩散在不同的数据库实例中,咱们须要查问两个数据库相干的数据。合并、排序和分页数据的过程很简单。

October 19, 2022 · 1 min · jiezi

关于c++:c中string的stoi函数

在学习c++的stoi函数时,遇到第二个参数不明确如何正确应用 int stoi (const string& str, size_t* idx = 0, int base = 10)后查找材料如下:所以第二个参数的应用分两种状况: 状况1:用0或者nullptr,示意不应用该参数(比拟常见)状况2:搁置一个size_t类型的指针,str调用stoi函数后,该指针指向str中第一个不为数字的下标对状况2举个栗子: #include<iostream>#include<string>using namespace std;int main() { string s = "1345s3544"; size_t n ; std::size_t* pos=&n; int m = stoi(s,pos,10); cout << *pos;}最终输入后果为4

October 19, 2022 · 1 min · jiezi

关于c#:NET候选版本2发布

本文作者为Jon Douglas、Jeremy Likness 和 Angelos Petropoulos 明天,咱们发表推出 .NET 7 Release Candidate 2。这是 .NET 7 的最终候选版本(RC),并在生产环境中失去反对。 您能够下载实用于 Windows、macOS 和 Linux 的 .NET 7 Release Candidate 2。  安装程序和二进制文件容器图像Linux 软件包发行阐明已知的问题GitHub 问题跟踪器.NET 7 Release Candidate 2 已通过 Visual Studio 17.4 Preview 3 测试。如果您想在 Visual Studio 系列产品中试用 .NET 7,咱们建议您应用预览通道构建。如果您应用的是 macOS,咱们倡议应用最新的 Visual Studio 2022 for Mac 预览版。 不要遗记 .NET Conf 2022。在 2022 年 11 月 8 日至 10 日与咱们一起庆贺 .NET 7 的公布! 在本博客中,咱们将重点介绍 .NET 7 的外围主题,并为您提供深刻理解细节的资源。 ...

October 18, 2022 · 3 min · jiezi

关于c++:linux-epoll用法记录

include <sys/epoll.h>/* 创立一个epoll的句柄,size用来通知内核须要监听的数目一共有多大。当创立好epoll句柄后,它就是会占用一个fd值,所以在应用完epoll后,必须调用close()敞开,否则可能导致fd被耗尽。*/int epoll_create(int size); /epoll的事件注册函数/int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); /期待事件的到来,如果检测到事件,就将所有就绪的事件从内核事件表中复制到它的第二个参数events指向的数组/int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); struct epoll_event ev, *events;for(;;) { nfds = epoll_wait(kdpfd, events, maxevents, -1);for(n = 0; n < nfds; ++n) { if(events[n].data.fd == listener) { client = accept(listener, (struct sockaddr *) &local, &addrlen); if(client < 0){ perror("accept"); continue; } setnonblocking(client); ev.events = EPOLLIN | EPOLLET; ev.data.fd = client; if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) { fprintf(stderr, "epoll set insertion error: fd=%d0, client); return -1; } } else do_use_fd(events[n].data.fd);}} ...

October 16, 2022 · 1 min · jiezi

关于c#:80c-报错-SystemSecuritySecurityException-未找到源无法启动计算机上的服务

利用场景: 利用c#创立windows服务,c#程序默认没有管理员的权限。 服务装置尽管胜利, 但在启动时:提醒: 无奈启动计算机“.”上的服务 看日志: System.Security.SecurityException 未找到源。 配置下以管理员身份运行。 配置过程: (1)在要编译的我的项目上右击抉择【属性】而后抉择【安全性】再勾选上【启用ClickOnce平安设置】,此时会在解决方案资源管理器的Properties中生成app.manifest文件。 (2)双击关上生成的app.manifest文件,找到如下代码: <requestedExecutionLevel level="asInvoker" uiAccess="false" /> 将其批改为: <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />(3)而后在【安全性】中去掉对【启用ClickOnce平安设置】的勾选,全副保留后从新编译就能够了。 参考起源: https://blog.csdn.net/songyi160/article/details/49926433

October 15, 2022 · 1 min · jiezi

关于c++:OneFlow源码阅读8eager模式下的SBP-Signature推导

oneflow 的 global tensor 有两个必要属性: placement:决定了 tensor 数据分布在哪些设施上。sbp:决定了 tensor 数据在这些设施上的散布形式。例如: split:将切分后的不同局部放到不同设施;同时指定切分的 axis。broadcast:将数据复制到各个设施。如果参加运算的 tensor 的 sbp 不一样,后果 tensor 的 sbp 是什么呢?例如上面的代码: # export MASTER_ADDR=127.0.0.1 MASTER_PORT=17789 WORLD_SIZE=2 RANK=0 LOCAL_RANK=0# export MASTER_ADDR=127.0.0.1 MASTER_PORT=17789 WORLD_SIZE=2 RANK=1 LOCAL_RANK=1import oneflow as flowP0 = flow.placement("cpu", ranks=[0, 1])t1 = flow.Tensor([[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]], placement=P0, sbp=flow.sbp.split(0))# t1 = flow.Tensor([[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]], placement=P0, sbp=flow.sbp.broadcast)t2 = flow.Tensor([[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]], placement=P0, sbp=flow.sbp.split(1))t3 = t1 + t2# oneflow.placement(type="cpu", ranks=[0, 1])print(t3.placement)# (oneflow.sbp.split(dim=0),)print(t3.sbp)t1和t2是散布在雷同设施上的两个 tensor。t1.sbp是S(0),在行上切分;t2.sbp是S(1),在列上切分。计算结果t3的 sbp 不须要用户手动指定,零碎能够主动推导出t3.sbp是S(0)。这个过程中的一个外围步骤,就是 SBP Signature 的推导。 ...

October 14, 2022 · 4 min · jiezi

关于c#:79C-在创建窗口句柄之前不能在控件上调用-Invoke-或-BeginInvoke错误的解决办法

会呈现这个谬误的可能起因为:窗体敞开了,但还有线程没执行完,且该线程有在操作窗体的内容: if (_lik启动工作.IsHandleCreated) { _lik启动工作.Invoke(new Action(() => { if (rs.Exists == true) { _lik启动工作.Enabled = true; if (rs.status == ServiceControllerStatus.Running) { _lik启动工作.Text = "进行工作(已启动)"; } else { _lik启动工作.Text = "启动工作(未启动)"; } _lik装置与卸载服务.Text = "卸载服务"; } else { _lik启动工作.Text = "启动工作(服务不存在)"; _lik启动工作.Enabled = false; _lik装置与卸载服务.Text = "装置服务"; } })); }若没有加:_lik启动工作.IsHandleCreated 这个if判断,间接执行,就会报错。 _lik启动工作 是窗体的link控件。

October 14, 2022 · 1 min · jiezi

关于c#:78c-各种路径AppDomainCurrentDomainBaseDirectory等

控制台程序: static void Main(string[] args) { // 获取程序的基目录。 string p1 = System.AppDomain.CurrentDomain.BaseDirectory; Console.WriteLine("p1=" + p1); // 获取模块的残缺门路,蕴含文件名 string p2 = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; Console.WriteLine("p2=" + p2); // 获取和设置当前目录(该过程从中启动的目录)的齐全限定目录。 string p3 = System.Environment.CurrentDirectory; Console.WriteLine("p3=" + p3); // 获取应用程序的当前工作目录,留神工作目录是能够扭转的,而不限定在程序所在目录。 string p4 = System.IO.Directory.GetCurrentDirectory(); Console.WriteLine("p4=" + p4); // 获取和设置包含该应用程序的目录的名称。 string p5 = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase; Console.WriteLine("p5=" + p5); // 获取启动了应用程序的可执行文件的门路。 /*string p6 = System.Windows.Forms.Application.StartupPath; Console.WriteLine("p6=" + p6); // 获取启动了应用程序的可执行文件的门路及文件名 string p7 = System.Windows.Forms.Application.ExecutablePath; Console.WriteLine("p7=" + p7);*/ Console.ReadLine(); }后果为: ...

October 14, 2022 · 1 min · jiezi

关于c++:cprimer转载函数指针练习65457

原文地址:https://blog.csdn.net/qq_5027... #include<iostream>#include<vector>#define NDEBUGusing namespace std;//练习6.54-6.57 定义函数int fun1(int a, int b) { return a + b;}int fun2(int a, int b) { return a - b;}int fun3(int a, int b) { return a * b;}int fun4(int a,int b){ return a / b;}void compute(int a, int b, int p(int,int)) {//函数指针作形参,函数类型会主动转换成指向函数的指针,也可写成void compute(int a, int b, int (*p)(int,int)) cout << p(a, b) << endl;}int main() { typedef int (*func)(int a, int b); vector<func> v = {&fun1,fun2,fun3,fun4};//函数名作为一个值时,&是选加 for (auto s : v) { compute(10, 2, s); } return 0;}

October 13, 2022 · 1 min · jiezi

关于c++:ffplay调试环境搭建

前言ffplay是基于FFmpeg的最简略的官网播放器。麻雀虽小,五脏俱全,虽说ffplay简略,然而各种播放器应有的性能一一俱全,说它简略或者仅仅是因为它只有一个点c文件而已吧。 想要开发一个优良的播放器,参考是必不可少的,毕竟control c和control v是程序员天生的本能。以前遇到播放的问题向人求教的时,常常听到的一句话就是去看看ffplay是怎么解决的就晓得怎么过解决了呀,可见ffplay在播放畛域的位置不个别。。。 而且想要学好FFmpeg,在理解了FFmpeg的基本知识和相干API之后,再联合我的项目实战是十分有必要的。然而光是跑马观花式的看看ffplay的源码可能播种甚微,要想深刻去理解ffplay外部的大抵细节,集成源码断点调试是必可少的。后续笔者将用几篇文章介绍ffplay的一些次要的性能点,然而想要理解ffplay单靠几篇文章必定是不够的,笔者更加心愿的是这几篇博客能起到抛砖引玉的作用,疏导大家更加深刻地学习理解。。。 所谓兵马未动,粮草先行,明天咱们就先搭建好ffplay的调试环境,不便大家后续的学习调试。 环境搭建笔者所应用的环境是:Mac零碎和Clion开发工具。笔者集成的FFmpeg的版本是应用Homebrew装置的ffmpeg5.0 1、编译集成FFmpeg库 ffplay的调试环境搭建是建设在笔者之前的文章 《FFmpeg连载1-开发环境搭建》 之上,首先须要集成FFmpeg的相干库,这一步大家参考笔者之前的文章即可。 2、新建Clion工程 新建好CLion工程后(留神是C工程,不是C++工程),咱们就须要下载FFmpeg的源码了,留神这个源码的版本最好和后面集成FFmpeg的版本好一样,即便不一样版本呢之间也不要相差太远,不然可能会因为版本的起因导致一些性能或头文件对应不上。 3、生成config.h头文件 下载好FFmpeg的源码后,咱们须要进入到源码目录执行一下./configure命令行,这一步的目标是生成config.h头文件。而后找到文件config.h并拷贝到咱们新建CLion工程中去。 4、拷贝fftools目录下的相干文件 咱们将源码目录fftools下的三个文件拷贝进去咱们的CLion工程,这三个文件别离是cmdutils.c、cmdutils.h、 ffplay.c。 5、拷贝va_copy.h头文件 将源码目录compat下的头文件va_copy.h也拷贝进去咱们新建的CLion目录,到这里咱们所需的源文件是算是筹备实现了。 6、批改CMakeLists.txt 批改CMakeLists.txt如下,次要相干库的门路要替换成本人的FFmpeg库和SDL库的实在门路。 cmake_minimum_required(VERSION 3.21)# 这里是工程名称 C工程project(FFplay_debug C)set(CMAKE_C_STANDARD 99)cmake_minimum_required(VERSION 3.17)# FFmpeg的装置目录,能够通过命令"brew info ffmpeg"获取# 须要替换成本人的FFMpeg装置目录set(FFMPEG_DIR /opt/homebrew/Cellar/ffmpeg/5.0)# SDL2的装置目录,能够通过命令"brew info sdl2"获取# 须要替换成本人的SDL2装置目录set(SDL2_DIR /opt/homebrew/Cellar/sdl2/2.0.20)# 头文件搜寻门路include_directories(${FFMPEG_DIR}/include/)include_directories(${CMAKE_SOURCE_DIR})include_directories(${SDL2_DIR}/include/SDL2/)# 动态链接库或动态链接库的搜寻门路link_directories(${FFMPEG_DIR}/lib/)link_directories(${SDL2_DIR}/lib/)#将指定文件设置在FFmpeg_test_source变量中file(GLOB FFplay_debug_source ${CMAKE_SOURCE_DIR}/*.*)add_executable(FFplay_debug ${FFplay_debug_source} cmdutils.c)#链接库target_link_libraries(FFplay_debug #FFmpeg 库 avcodec avdevice postproc avfilter avformat avutil swresample swscale # SDL2库 SDL2 )7、debug测试 而后就能够欢快地进行断点调试了,找到ffplay.c的main函数,打一个断点,而后debug运行一下。 同理,如果童鞋们想要调试一下ffprobe.c或ffmpeg.c也能够应用同样的形式集成即可。 可能遇到的问题1、SDL2没有装置 ffplay是须要依赖SDL进行画面的展现和声音的播放的。所以集成的前提是须要装置好SDL2。 2、文件"cmdutils.c"找不到头文件"libavutil/libm.h" 笔者集成的时候就遇到了这个问题,解决方案就是间接正文掉即可。 举荐浏览FFmpeg连载1-开发环境搭建 FFmpeg连载2-拆散视频和音频 FFmpeg连载3-视频解码 FFmpeg连载4-音频解码 FFmpeg连载5-音视频编码 FFmpeg连载6-音频重采样 FFmpeg连载8-视频合并以及替换视频背景音乐实战 ...

October 12, 2022 · 1 min · jiezi

关于c++:FFmpeg连载8视频合并以及替换视频背景音乐实战

前言通过后面的实战,咱们实现音视频解封装提取、音视频解码、音视频编码、音频重采样等的性能,明天咱们就联合之前所做的性能,来做一个短视频APP中常见的性能: 1、提取多个mp3文件中的音频,从新编码为合并为aac 2、提取mp4中的视频,从新编码合并为h264 3、h264与aac合并成新的mp4文件因为咱们的目标是以实战为主,为了囊括之前所学的一些知识点,在这个实战中咱们不仅仅须要实现音视频解封装提取、音视频解码、音视频编码、音频重采样这些性能,咱们还须要联合多线程同步等知识点做好生产者消费者队列缓冲管制。还蕴含例如类成员函数作为线程执行函数的应用等知识点。 大抵框架这里要阐明一个常识就是如果音频如果须要合并的话要保障两个音频的采样率、采样格局以及通道数统一,所以须要重采样,为了测试,笔者把音频都重采样为22050hz。 同时视频也一样,如果视频须要合并也须要保障两个视频的分辨率是一样的,这里笔者对立把尺寸转换为720x1280。 笔者文笔不好,常常一句卧槽走天下,间接看图吧。。。 代码实现原本笔者想谋求简略,心愿用一个cpp文件实现的,前面写着写着发现代码量有点多,所以就拆分成了三个cpp文件,上面是代码详情: AudioHandle.cpp /** * 音频解决 * 解码音频,并且重采样为22050,而后编码成aac */#ifndef TARGET_AUDIO_SAMPLE_RATE// 采样率#define TARGET_AUDIO_SAMPLE_RATE 22050#endif#include <iostream>#include <vector>extern "C" {#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libavutil/opt.h>#include <libavutil/channel_layout.h>#include <libavutil/audio_fifo.h>#include <libswresample/swresample.h>};class AudioHandle {public: void handle_audio(std::vector<char *> mp3_paths, std::function<void(const AVCodecContext *, AVPacket *, bool)> callback) { // 音频编码器相干 const AVCodec *avCodec = avcodec_find_encoder(AV_CODEC_ID_AAC); audio_encoder_context = avcodec_alloc_context3(avCodec); audio_encoder_context->sample_rate = TARGET_AUDIO_SAMPLE_RATE; // 默认的aac编码器输出的PCM格局为:AV_SAMPLE_FMT_FLTP audio_encoder_context->sample_fmt = AV_SAMPLE_FMT_FLTP; audio_encoder_context->channel_layout = AV_CH_LAYOUT_STEREO;// audio_encoder_context->bit_rate = 128 * 1024; audio_encoder_context->codec_type = AVMEDIA_TYPE_AUDIO; audio_encoder_context->channels = av_get_channel_layout_nb_channels(audio_encoder_context->channel_layout); audio_encoder_context->profile = FF_PROFILE_AAC_LOW; //ffmpeg默认的aac是不带adts,而fdk_aac默认带adts,这里咱们强制不带 audio_encoder_context->flags = AV_CODEC_FLAG_GLOBAL_HEADER; int ret = avcodec_open2(audio_encoder_context, avCodec, nullptr); if (ret < 0) { std::cout << "音频编码器关上失败" << std::endl; return; } // 初始化audiofifo audiofifo = av_audio_fifo_alloc(audio_encoder_context->sample_fmt, audio_encoder_context->channels, audio_encoder_context->frame_size); AVFormatContext *avFormatContext = nullptr; AVCodecContext *decoder_context = nullptr; AVPacket *avPacket = av_packet_alloc(); AVFrame *avFrame = av_frame_alloc(); std::vector<AVPacket *> pack_vector = std::vector<AVPacket *>(); while (!mp3_paths.empty()) { // 先开释旧的 avcodec_free_context(&decoder_context); avformat_free_context(avFormatContext); const char *mp3 = mp3_paths.at(0); mp3_paths.erase(mp3_paths.cbegin()); avFormatContext = avformat_alloc_context(); ret = avformat_open_input(&avFormatContext, mp3, nullptr, nullptr); if (ret < 0) { std::cout << "音频文件关上失败" << std::endl; break; } int audio_index = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); if (audio_index < 0) { for (int i = 0; i < avFormatContext->nb_streams; ++i) { if (AVMEDIA_TYPE_AUDIO == avFormatContext->streams[i]->codecpar->codec_type) { audio_index = i; std::cout << "找到音频流,audio_index:" << audio_index << std::endl; break; } } if (audio_index < 0) { std::cout << "没有找到音频流" << std::endl; break; } } const AVCodec *avCodec = avcodec_find_decoder(avFormatContext->streams[audio_index]->codecpar->codec_id); decoder_context = avcodec_alloc_context3(avCodec); avcodec_parameters_to_context(decoder_context, avFormatContext->streams[audio_index]->codecpar); ret = avcodec_open2(decoder_context, avCodec, nullptr); if (ret < 0) { std::cout << "音频解码器关上失败" << std::endl; break; } while (true) { ret = av_read_frame(avFormatContext, avPacket); if (ret < 0) { std::cout << "音频包读取结束" << std::endl; break; } if (avPacket->stream_index != audio_index) { av_packet_unref(avPacket); continue; } ret = avcodec_send_packet(decoder_context, avPacket); if (ret < 0) { std::cout << "音频包发送解码失败" << std::endl; break; } while (true) { ret = avcodec_receive_frame(decoder_context, avFrame); if (ret == AVERROR(EAGAIN)) { std::cout << "音频包获取解码帧:EAGAIN" << std::endl; break; } else if (ret < 0) { std::cout << "音频包获取解码帧:fail" << std::endl; break; } else { std::cout << "从新编码音频" << std::endl; // 先进行重采样 resample_audio(avFrame); pack_vector.clear(); encode_audio(pack_vector, out_frame); while (!pack_vector.empty()) { AVPacket *packet = pack_vector.at(0); pack_vector.erase(pack_vector.cbegin()); // 回调 callback(audio_encoder_context, packet, false); } } } av_packet_unref(avPacket); } } avcodec_free_context(&decoder_context); avformat_free_context(avFormatContext); // 回调完结 callback(audio_encoder_context, nullptr, true); }private: // 视频编码器 AVCodecContext *audio_encoder_context = nullptr; AVFrame *encode_frame = nullptr; AVAudioFifo *audiofifo = nullptr; int64_t cur_pts = 0; // 重采样相干 SwrContext *swrContext = nullptr; AVFrame *out_frame = nullptr; int64_t max_dst_nb_samples; void init_out_frame(int64_t dst_nb_samples){ av_frame_free(&out_frame); out_frame = av_frame_alloc(); out_frame->sample_rate = TARGET_AUDIO_SAMPLE_RATE; out_frame->format = AV_SAMPLE_FMT_FLTP; out_frame->channel_layout = AV_CH_LAYOUT_STEREO; out_frame->nb_samples = dst_nb_samples; // 调配buffer av_frame_get_buffer(out_frame,0); av_frame_make_writable(out_frame); } /** * 重采样 * @param avFrame */ void resample_audio(AVFrame *avFrame){ if (nullptr == swrContext) { /** * 以下能够应用 swr_alloc、av_opt_set_channel_layout、av_opt_set_int、av_opt_set_sample_fmt * 等API设置,更加灵便 */ swrContext = swr_alloc_set_opts(nullptr, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_FLTP, TARGET_AUDIO_SAMPLE_RATE, avFrame->channel_layout, static_cast<AVSampleFormat>(avFrame->format), avFrame->sample_rate, 0, nullptr); swr_init(swrContext); } // 进行音频重采样 int src_nb_sample = avFrame->nb_samples; // 为了放弃从采样后 dst_nb_samples / dest_sample = src_nb_sample / src_sample_rate max_dst_nb_samples = av_rescale_rnd(src_nb_sample, TARGET_AUDIO_SAMPLE_RATE, avFrame->sample_rate, AV_ROUND_UP); // 从采样器中会缓存一部分,获取缓存的长度 int64_t delay = swr_get_delay(swrContext, avFrame->sample_rate); // 相当于a*b/c int64_t dst_nb_samples = av_rescale_rnd(delay + avFrame->nb_samples, TARGET_AUDIO_SAMPLE_RATE, avFrame->sample_rate, AV_ROUND_UP); if(nullptr == out_frame){ init_out_frame(dst_nb_samples); } if (dst_nb_samples > max_dst_nb_samples) { // 须要重新分配buffer std::cout << "须要重新分配buffer" << std::endl; init_out_frame(dst_nb_samples); max_dst_nb_samples = dst_nb_samples; } // 重采样 int ret = swr_convert(swrContext, out_frame->data, dst_nb_samples, const_cast<const uint8_t **>(avFrame->data), avFrame->nb_samples); if(ret < 0){ std::cout << "重采样失败" << std::endl; } else{ // 每帧音频数据量的大小 int data_size = av_get_bytes_per_sample(static_cast<AVSampleFormat>(out_frame->format)); // 返回值才是真正的重采样点数 out_frame->nb_samples = ret; std::cout << "重采样胜利:" << ret << "----dst_nb_samples:" << dst_nb_samples << "---data_size:" << data_size << std::endl; } } void encode_audio(std::vector<AVPacket *> &pack_vector, AVFrame *avFrame) { int cache_size = av_audio_fifo_size(audiofifo); std::cout << "cache_size:" << cache_size << std::endl; av_audio_fifo_realloc(audiofifo, cache_size + avFrame->nb_samples); av_audio_fifo_write(audiofifo, reinterpret_cast<void **>(avFrame->data), avFrame->nb_samples); if (nullptr == encode_frame) { encode_frame = av_frame_alloc(); encode_frame->nb_samples = audio_encoder_context->frame_size; encode_frame->sample_rate = audio_encoder_context->sample_rate; encode_frame->channel_layout = audio_encoder_context->channel_layout; encode_frame->channels = audio_encoder_context->channels; encode_frame->format = audio_encoder_context->sample_fmt; av_frame_get_buffer(encode_frame, 0); } av_frame_make_writable(encode_frame); // todo 如果是冲刷最初几帧数据,不够的能够填充静音 av_samples_set_silence while (av_audio_fifo_size(audiofifo) > audio_encoder_context->frame_size) { int ret = av_audio_fifo_read(audiofifo, reinterpret_cast<void **>(encode_frame->data), audio_encoder_context->frame_size); if (ret < 0) { std::cout << "audiofifo 读取数据失败" << std::endl; return; } // 批改pts cur_pts += encode_frame->nb_samples; encode_frame->pts = cur_pts; ret = avcodec_send_frame(audio_encoder_context, encode_frame); if (ret < 0) { std::cout << "发送编码失败" << std::endl; return; } while (true) { AVPacket *out_pack = av_packet_alloc(); ret = avcodec_receive_packet(audio_encoder_context, out_pack); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { std::cout << "avcodec_receive_packet end:" << ret << std::endl; break; } else if (ret < 0) { std::cout << "avcodec_receive_packet fail:" << ret << std::endl; return; } else { pack_vector.push_back(out_pack); } } } }};VideoHandle.cpp ...

October 12, 2022 · 9 min · jiezi

关于c++:FFmpeg连载7mp3转码aac及AVAudioFifo的使用

前言现在以抖音、快手为代表的短视频秀无处不在,比方它们一个很一般的性能就是应用流行音乐替换作为视频的背景音乐。而在视频中音频个别都是以AAC的造成存在,但流行音乐大多以mp3的格局流传,因而须要实现背景音乐替换这个性能,其中的一个步骤就须要实现mp3转aac这样的一个音频转转码的过程。 依照咱们以往的教训,转码的大抵流程应该是这样的: 解封装->提取音频流->解码成PCM->从新编码成AAC流程是这样没错,然而外部的进去细节是怎么的呢?是mp3解码进去后的AVFrame能够通过函数avcodec_send_frame送进aac编码器即可吗? 很显著这是不行的,因为mp3每帧是1152个采样点,而aac每帧是1024个采样点。它们每帧的采样点数不同,所以不能间接通过avcodec_send_frame进行编码。 AVAudioFifo·AVAudioFifo是一个音频缓冲区,是一个先进先出的队列。应用它能够很不便地贮存咱们的音频缓冲数据,例如在mp3转码aac的过程中,因为它们的采样点数不同,咱们就能够把mp3解码进去的pcm数据放入到AVAudioFifo中去,而后每次从AVAudioFifo中获取1024个采样点送进aac编码器,这样的做法让咱们的音频转码变得十分的不便灵便。AVAudioFifo让咱们在采样层面做操作,而不必关怀底层的字节层面;而且它反对多种格局的单次采样,如反对planar或packed的采样格局,反对不同的通道数等等。 AVAudioFifo的API应用也非常简单,次要蕴含调配开释、获取可读写空间长度、写入音频数据、读取音频数据等相干函数: 首先是调配和开释操作: //调配一个AVAudioFifo。//sample_fmt指定采样格局//nb_samples则指定AVAudioFifo的缓冲区大小,能够通过av_audio_fifo_realloc重新分配AVAudioFifo *av_audio_fifo_alloc(enum AVSampleFormat sample_fmt, int channels,int nb_samples); //重新分配缓冲区大小//胜利返回0,失败返回负的谬误值int av_audio_fifo_realloc(AVAudioFifo *af, int nb_samples); //开释AVAudioFifovoid av_audio_fifo_free(AVAudioFifo *af);查问操作: //返回fifo中以后存储的采样数量int av_audio_fifo_size(AVAudioFifo *af); //返回fifo中以后可写的采样数量,即尚未应用的空间数量int av_audio_fifo_space(AVAudioFifo *af); // 以上两个函数的返回值之和等于AVAudioFifo的缓冲区大小读取操作: //将采样写入到AVAudioFifo//胜利则返回理论写入的采样数,如果写入胜利,返回值必然等于nb_samples,失败返回负的谬误值int av_audio_fifo_write(AVAudioFifo *af, void **data, int nb_samples); //peek:读取数据,但读到的数据并不会从fifo中删除int av_audio_fifo_peek(AVAudioFifo *af, void **data, int nb_samples); //从指定的偏移地位peek数据int av_audio_fifo_peek_at(AVAudioFifo *af, void **data, int nb_samples, int offset); //读取数据,读到的数据会从fifo中删除int av_audio_fifo_read(AVAudioFifo *af, void **data, int nb_samples); //从fifo中删除nb_samples个采样int av_audio_fifo_drain(AVAudioFifo *af, int nb_samples); //删除fifo中的所有采样,清空void av_audio_fifo_reset(AVAudioFifo *af);音频转码有了AVAudioFifo,那么咱们音频的转码流程就变成了以下这样子: 解封装 -> 提取音频流 -> 解码成PCM->将PCM数据写入AVAudioFifo -> 每次从AVAudioFifo获取1024个采样点送进aac编码器 -> 从新编码成AAC如果到了最初没有可输出的PCM数据了,然而AVAudioFifo中可读取的采样点数仍然不满足aac的1024个采样点的话,能够通过填充静音的形式补充... ...

October 12, 2022 · 4 min · jiezi

关于c++:FFmpeg连载6音频重采样

明天咱们的实战内容是将音频解码成PCM,并将PCM重采样成特定的采样率,而后输入到本地文件进行播放。 什么是重采样所谓重采样,一句话总结就是扭转音频的三元素,也就是通过重采样扭转音频的采样率、采样格局或者声道数。 例如音频A是采样率48000hz、采样格局为f32le、声道数为1,通过重采样能够将音频A的采样率变更为采样率44100hz、采样格局为s16le、声道数为2等。 为什么须要重采样个别进行重采样有两个起因,一是播放设施须要,二是音频合并、或编码器等须要。 例如有些声音设施只能播放44100hz的采样率、16位采样格局的音频数据,因而如果音频不是这些格局的,就须要进行重采样能力失常播放了。 例如FFmpeg默认的AAC编码器输出的PCM格局为:AV_SAMPLE_FMT_FLTP,如果须要应用FFMpeg默认的AAC编码器则须要进行重采样了。又比有些须要进行混音的业务需要,须要保障PCM三要素雷同能力进行失常混音。 如何进行音频重采样在重采样的过程中咱们要坚守一个准则就是音频通过重采样后它的播放工夫是不变的,如果一个10s的音频通过重采样后变成了15,那必定就是不行的。 影响音频播放时长的因素是每帧的采样数和采样率,上面举一个例子简略介绍下音频播放时长的问题: 如果现有mp3,它的采样率是采样率48000,mp3每帧采样点数是1152,那么每帧mp3的播放时长就是 1152/48000,每一个采样点的播放时长就是1/48000。 如果现有mp3,它的采样率是采样率44100,aac每帧采样点数是1024,那么每帧aac的播放时长就是 1024/44100,每个采样点的播放时长就是1/44100。从下面的例子中咱们能够看出,对于采样率不同的两个音频,不可能1帧mp3转换出1帧aac,它们的比例不是1:1的,对于下面的例子,那么1帧mp3能重采样出多少个aac的采样点呢?以工夫不变为根底,能够有这样的一个公式: 1152 / 48000 = 指标采样点数 / 44100也就是说:指标采样点数 = 1152 * 44100 / 48000这条公式能够用FFmpeg中的函数av_rescale_rnd来实现... 有了计算公式,上面咱们说说FFmpeg重采样的步骤: 1、调配SwrContext并配置音频输入输入参数 这里能够间接应用函数swr_alloc_set_opts实现,也能够应用swr_alloc、av_opt_set_channel_layout、av_opt_set_int、av_opt_set_sample_fmt等组合函数分步实现, 2、初始化SwrContext 调配好SwrContext 后,通过函数swr_init进行重采样上下文初始化。 3、swr_convert重采样 FFmpeg真正进行重采样的函数是swr_convert。它的返回值就是重采样输入的点数。应用FFmpeg进行重采样时外部是有缓存的,而外部缓存了多少个采样点,能够用函数swr_get_delay获取。也就是说调用函数swr_convert时你传递进去的第三个参数示意你心愿输入的采样点数,然而函数swr_convert的返回值才是真正输入的采样点数,这个返回值肯定是小于或等于你心愿输入的采样点数。 上面是残缺代码: #ifndef AUDIO_TARGET_SAMPLE#define AUDIO_TARGET_SAMPLE 48000#endif#include <iostream>extern "C" {#include "libavformat/avformat.h"#include <libswresample/swresample.h>#include <libavcodec/avcodec.h>#include <libavutil/frame.h>#include <libavutil/opt.h>#include <libavutil/channel_layout.h>}class AudioResample {public: // 将PCM数据重采样 void decode_audio_resample(const char *media_path, const char *pcm_path) { avFormatContext = avformat_alloc_context(); int ret = avformat_open_input(&avFormatContext, media_path, nullptr, nullptr); if (ret < 0) { std::cout << "输出关上失败" << std::endl; return; } // 寻找视频流 int audio_index = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); if (audio_index < 0) { std::cout << "没有可用的音频流" << std::endl; return; } // 配置解码相干 const AVCodec *avCodec = avcodec_find_decoder(avFormatContext->streams[audio_index]->codecpar->codec_id); avCodecContext = avcodec_alloc_context3(avCodec); avcodec_parameters_to_context(avCodecContext, avFormatContext->streams[audio_index]->codecpar); ret = avcodec_open2(avCodecContext, avCodec, nullptr); if (ret < 0) { std::cout << "解码器关上失败" << std::endl; return; } // 调配包和帧数据结构 avPacket = av_packet_alloc(); avFrame = av_frame_alloc(); // 关上yuv输入文件 pcm_out = fopen(pcm_path, "wb"); // 读取数据解码 while (true) { ret = av_read_frame(avFormatContext, avPacket); if (ret < 0) { std::cout << "音频包读取结束" << std::endl; break; } else { if (avPacket->stream_index == audio_index) { // 只解决音频包 ret = avcodec_send_packet(avCodecContext, avPacket); if (ret < 0) { std::cout << "发送解码包失败" << std::endl; return; } while (true) { ret = avcodec_receive_frame(avCodecContext, avFrame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { std::cout << "获取解码数据失败" << std::endl; return; } else { std::cout << "重采样解码数据" << std::endl; resample(); } } } } av_packet_unref(avPacket); } } ~AudioResample() { // todo 开释资源 }private: AVFormatContext *avFormatContext = nullptr; AVCodecContext *avCodecContext = nullptr; AVPacket *avPacket = nullptr; AVFrame *avFrame = nullptr; FILE *pcm_out = nullptr; SwrContext *swrContext = nullptr; AVFrame *out_frame = nullptr; int64_t max_dst_nb_samples; /** * 重采样并输入到文件 */ void resample() { if (nullptr == swrContext) { /** * 以下能够应用 swr_alloc、av_opt_set_channel_layout、av_opt_set_int、av_opt_set_sample_fmt * 等API设置,更加灵便 */ swrContext = swr_alloc_set_opts(nullptr, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_FLTP, AUDIO_TARGET_SAMPLE, avFrame->channel_layout, static_cast<AVSampleFormat>(avFrame->format), avFrame->sample_rate, 0, nullptr); swr_init(swrContext); } // 进行音频重采样 int src_nb_sample = avFrame->nb_samples; // 为了放弃从采样后 dst_nb_samples / dest_sample = src_nb_sample / src_sample_rate max_dst_nb_samples = av_rescale_rnd(src_nb_sample, AUDIO_TARGET_SAMPLE, avFrame->sample_rate, AV_ROUND_UP); // 从采样器中会缓存一部分,获取缓存的长度 int64_t delay = swr_get_delay(swrContext, avFrame->sample_rate); int64_t dst_nb_samples = av_rescale_rnd(delay + avFrame->nb_samples, AUDIO_TARGET_SAMPLE, avFrame->sample_rate, AV_ROUND_UP); if(nullptr == out_frame){ init_out_frame(dst_nb_samples); } if (dst_nb_samples > max_dst_nb_samples) { // 须要重新分配buffer std::cout << "须要重新分配buffer" << std::endl; init_out_frame(dst_nb_samples); max_dst_nb_samples = dst_nb_samples; } // 重采样 int ret = swr_convert(swrContext, out_frame->data, dst_nb_samples, const_cast<const uint8_t **>(avFrame->data), avFrame->nb_samples); if(ret < 0){ std::cout << "重采样失败" << std::endl; } else{ // 每帧音频数据量的大小 int data_size = av_get_bytes_per_sample(static_cast<AVSampleFormat>(out_frame->format)); std::cout << "重采样胜利:" << ret << "----dst_nb_samples:" << dst_nb_samples << "---data_size:" << data_size << std::endl; // 交织模式放弃写入 // 留神不要用 i < out_frame->nb_samples, 因为重采样进去的点数不肯定就是out_frame->nb_samples for (int i = 0; i < ret; i++) { for (int ch = 0; ch < out_frame->channels; ch++) { // 须要贮存为pack模式 fwrite(out_frame->data[ch] + data_size * i, 1, data_size, pcm_out); } } } } void init_out_frame(int64_t dst_nb_samples){ av_frame_free(&out_frame); out_frame = av_frame_alloc(); out_frame->sample_rate = AUDIO_TARGET_SAMPLE; out_frame->format = AV_SAMPLE_FMT_FLTP; out_frame->channel_layout = AV_CH_LAYOUT_STEREO; out_frame->nb_samples = dst_nb_samples; // 调配buffer av_frame_get_buffer(out_frame,0); av_frame_make_writable(out_frame); }};应用ffplay播放以下重采样后的PCM文件是否失常,播放命令是: ...

October 12, 2022 · 3 min · jiezi

关于c++:FFmpeg连载5音视频编码

导读在后面的咱们应用FFmpeg进行相干实际,对音视视频进行理解封装、解码等相干操作,明天咱们持续应用FFmpeg进行实际,应用FFmpeg进行音视频编码。 工作一: 在后面《FFmpeg连载4-音频解码》咱们将音频解码成PCM并输入到本地文件,明天咱们就把这个输入到本地的PCM文件进行读取从新编码成AAC音频文件并输入到本地。 工作二: 在《FFmpeg连载3-视频解码》一节中咱们将视频解码成YUV并且输入到本地文件,明天咱们读取这个输入的YUV本地文件进行从新编码成H264视频文件并输入到本地。 H264编码规格简介因为在设置编码器参数时须要用到profile,所以在这里简略介绍下H264的几种profile规格。 1、Baseline Profile 反对I/P帧,只反对无交织(Progressive)和CAVLC个别用于低阶或须要额定容错的利用,比方视频通话、手机视频等即时通信畛域 2、Extended Profile 在Baseline的根底上减少了额定的性能,反对流之间的切换,改良误码性能反对I/P/B/SP/SI帧,只反对无交织(Progressive)和CAVLC适宜于视频流在网络上的传输场合,比方视频点播 3、Main Profile 提供I/P/B帧,反对无交织(Progressive)和交织(Interlaced),反对CAVLC和CABAC用于支流消费类电子产品规格如低解码(相对而言)的MP4、便携的视频播放器、PSP和iPod等。 4、High Profile 最罕用的规格 在Main的根底上减少了8x8外部预测、自定义量化、无损视频编码和更多的YUV格局(如4:4:4) High 4:2:2 Profile(Hi422P) High 4:4:4 Predictive Profile(Hi444PP) High 4:2:2 Intra Profile High 4:4:4 Intra Profile 用于播送及视频碟片存储(蓝光影片),高清电视的利用 YUV视频编码在后面解码的文章中咱们介绍了一组解码的函数avcodec_send_packet和avcodec_receive_frame,同样对于编码也有对应的一组函数,它们是avcodec_send_frame和avcodec_receive_packet,同样一个的调用avcodec_send_frame须要对应多个avcodec_receive_packet的接管。 相干代码及正文如下: VideoEncoder.hclass VideoEncoder {public: void encode_yuv_to_h264(const char *yuv_path,const char *h264_path);};C++实现文件: #include "VideoEncoder.h"#include <iostream>extern "C"{#include "libavcodec/avcodec.h"#include <libavformat/avformat.h>#include <libavutil/avutil.h>#include <libavutil/opt.h>}static FILE *h264_out = nullptr;void encode_video(AVCodecContext* avCodecContext,AVFrame* avFrame,AVPacket* avPacket){ int ret = avcodec_send_frame(avCodecContext,avFrame); if(ret < 0){ std::cout << "yuv发送编码失败" << std::endl; } while (true){ ret = avcodec_receive_packet(avCodecContext,avPacket); if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){ std::cout << "须要输送更多yuv数据" << std::endl; break; } std::cout << "写入文件h264" << std::endl; fwrite(avPacket->data,1,avPacket->size,h264_out); av_packet_unref(avPacket); }}void VideoEncoder::encode_yuv_to_h264(const char *yuv_path, const char *h264_path) { const AVCodec *avCodec = avcodec_find_encoder(AV_CODEC_ID_H264); AVCodecContext *avCodecContext = avcodec_alloc_context3(avCodec); avCodecContext->time_base = {1,25}; // 配置编码器参数 avCodecContext->width = 720; avCodecContext->height = 1280; avCodecContext->bit_rate = 2000000; avCodecContext->profile = FF_PROFILE_H264_MAIN; avCodecContext->gop_size = 10; avCodecContext->time_base = {1,25}; avCodecContext->framerate = {25,1}; // b帧的数量 avCodecContext->max_b_frames = 1; avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P; // 设置H264的编码器参数为提早模式,进步编码品质,然而会造成编码速度降落 av_opt_set(avCodecContext->priv_data,"preset","slow",0); // 关上编码器 int ret = avcodec_open2(avCodecContext,avCodec, nullptr); if(ret < 0){ std::cout << "编码器关上失败:" << strerror(ret) << std::endl; // todo 在析构函数中开释资源 return; } AVPacket *avPacket = av_packet_alloc(); AVFrame *avFrame = av_frame_alloc(); avFrame->width = avCodecContext->width; avFrame->height = avCodecContext->height; avFrame->format = avCodecContext->pix_fmt; // 为frame调配buffer av_frame_get_buffer(avFrame,0); av_frame_make_writable(avFrame); h264_out = fopen(h264_path,"wb"); // 读取yuv数据送入编码器 FILE *input_media = fopen(yuv_path,"r"); if(nullptr == input_media){ std::cout << "输出文件关上失败" << std::endl; return; } int pts = 0; while (!feof(input_media)){ int64_t frame_size = avFrame->width * avFrame->height * 3 / 2; int64_t read_size = 0; // 这里能够自行理解下ffmpeg字节对齐的问题 if(avFrame->width == avFrame->linesize[0]){ std::cout << "不存在padding字节" << std::endl; // 读取y read_size += fread(avFrame->data[0],1,avFrame->width * avFrame->height,input_media); // 读取u read_size += fread(avFrame->data[1],1,avFrame->width * avFrame->height / 4,input_media); // 读取v read_size += fread(avFrame->data[2],1,avFrame->width * avFrame->height / 4,input_media); } else{ std::cout << "存在padding字节" << std::endl; // 须要对YUV重量进行逐行读取 for (int i = 0; i < avFrame->height;i++) { // 读取y read_size += fread(avFrame->data[0] + i * avFrame->linesize[0],1,avFrame->width,input_media); } // 读取u和v for (int i = 0; i < avFrame->height / 2; i++) { read_size += fread(avFrame->data[1] + i * avFrame->linesize[1],1,avFrame->width / 2,input_media); read_size += fread(avFrame->data[2] + i * avFrame->linesize[2],1,avFrame->width / 2,input_media); } } pts += (1000000 / 25); avFrame->pts = pts; if(read_size != frame_size){ std::cout << "读取数据有误:" << std::endl; } encode_video(avCodecContext,avFrame,avPacket); } // 冲刷编码器 encode_video(avCodecContext, nullptr,avPacket); fflush(h264_out);}须要留神的是在读取YUV数据填充AVFrame时须要辨别开释存在字节对齐的问题。 ...

October 12, 2022 · 4 min · jiezi

关于c++:FFmpeg连载4音频解码

导读后面咱们介绍了应用FFmpeg解码视频,明天咱们应用FFmpeg解码音频。咱们的指标将mp4中的音频文件解码成PCM数据,并输入到本地文件,而后应用ffplay播放验证。 音频的解码过程就是将通过压缩后的数据从新还原成原始的PCM声音信号的过程。对于音频解码所用到的API和视频解码是一样的。 PCM基础知识PCM是指未通过压缩的原始声音脉冲信号数据,它次要通过采样率、采样格局(比方每个采样点是8位、16位、32位等)、声道数来形容。 在FFmpeg中有两种示意PCM数据包的模式,别离是planer和packed模式,那么它们有什么区别呢? 其中packed又叫做交织模式,而planer又叫立体模式,所谓交织或立体就是不同声道的声音信号排列贮存的形式,例如对于一个双声道的PCM数据来说,用packed模式示意是这样子的: // 咱们用L示意左声道数据,用R示意右声道数据LRLRLRLRLRLRLRLR而用laner模式示意的话,则是这样子的: // 咱们用L示意左声道数据,用R示意右声道数据LLLLLLLL RRRRRRRR在FFmpeg中,packed模式的格局有: AV_SAMPLE_FMT_U8, ///< unsigned 8 bitsAV_SAMPLE_FMT_S16, ///< signed 16 bitsAV_SAMPLE_FMT_S32, ///< signed 32 bitsAV_SAMPLE_FMT_FLT, ///< floatAV_SAMPLE_FMT_DBL, ///< double它的数据只存在于AVFrame的data[0]中。 而planer模式个别是FFmpeg外部贮存音频所应用的模式,例如通过个别planar模式的前面都有字母P标识,planar模式的格局有: AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planarAV_SAMPLE_FMT_S16P, ///< signed 16 bits, planarAV_SAMPLE_FMT_S32P, ///< signed 32 bits, planarAV_SAMPLE_FMT_FLTP, ///< float, planarAV_SAMPLE_FMT_DBLP, ///< double, planarAV_SAMPLE_FMT_S64, ///< signed 64 bitsAV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar例如对于一帧planar格局的双声道的音频数据,AVFrame中的data[0]示意左声道的数据,data[1]示意的是右声道的数据。 在FFmpeg中咱们能够应用函数av_sample_fmt_is_planar来判断采样格局是planar模式还是packed模式。 须要留神的一点是planar仅仅是FFmpeg外部应用的贮存模式,咱们理论中所应用的音频都是packed模式的,也就是说咱们应用FFmpeg解码出音频PCM数据后,如果须要写入到输入文件,应该将其转为packed模式的输入。 咱们能够应用ffplay播放PCM原始音频数据,命令是: // -ar 示意采样率// -ac 示意音频通道数// -f 示意 pcm 格局,sample_fmts + le(小端)或者 be(大端) f32le示意的是 AV_SAMPLE_FMT_FLTP 的小端模式// sample_fmts能够通过ffplay -sample_fmts来查问// -i 示意输出文件,这里就是 pcm 文件ffplay -ar 44100 -ac 2 -f f32le -i pcm文件门路音频解码间接上代码吧,有正文: ...

October 12, 2022 · 3 min · jiezi

关于c++:FFmpeg连载1环境搭建

前言之前笔者从事安卓开发的时候想要学习音视频实际,脑子外面想的是要是有专门针对安卓开发的FFmpeg教程就好了。缓缓地发现这个想法很不靠谱,因为那时对于音视频的相干教程原本就少,如果还要是针对安卓的就百里挑一了。 起初为了学习FFmpeg,笔者也是断断续续的,从音视频的根底材料开始,从FFmpeg编译到实际,心愿通过本人的学习能够一点一点地揭开FFmpeg的神秘面纱,直到明天笔者只敢说仅是音视频开发大军中的一个入门老手而已,虽说本人摸索挺久,但仍旧难以进阶,我想其中有两个次要的起因,一是短少仙人指路,二是断断续续,不足系统性的学习总结。 学习最重要的是死记硬背,如果你心田想要学习,然而又以没有齐全针对性的教程材料为由,最初你会发现自己还是想的比做的多,还不如不想... 对于FFmpeg这货色,你说它难,其实它也就那么一回事,你说它简略,有数人在编译阶段就被迫放弃了,或者它难的不是它自身,更多学习者一个敢于开始的勇气和坚持不懈的保持吧。 为了让老手能够更疾速地入门FFmpeg,笔者将开始连载对于FFmpeg相干文章,次要波及音视频解封装、音视频解码、音视频编码、音频重采样等相干知识点。 在本系列文章的最初,可能会以一个小小的实际作为收尾,这个实际的内容大体是: 1、输出多个mp3文件,解码成pcm,而后进行重采样,拼接合并编码成aac音频; 2、输出多个mp4文件,提取出视频解码成yuv,而后拼接合并编码成h264; 3、将1中的aac音频和2中的h264视频合并成新的mp4文件。明天咱们次要是先把环境搭建好,毕竟千里之行始于足下。 笔者环境笔者应用的范例环境了Mac零碎,开发工具是CLion。 装置FFmpeg对于引入在PC上引入FFmpeg的话还是比较简单的,能够通过命令行工具进行装置,而后将相干库提取解决即可,又或者能够间接应用源码间接编译相干库都能够。然而如果要想把FFmpeg继承到安卓中去就要应用NDK教程编译了,对于如何应用NDK教程编译FFmpeg童鞋们能够去翻我之前的文章,或者前面有工夫我在整顿一份都行。 鉴于FFmpeg是一个C语言库,天生具备跨平台能力,所以学习的话笔者倡议间接在PC上实际学习即可,如果你都学会了,那移植到其余平台那不是so easy吗。 明天笔者应用的是命令行装置的形式集成FFmpeg,在Mac上咱们能够应用Homebrew包管理工具进行装置,Linux上能够用apt。 1、首先应用Homebrew装置ffmpeg: brew install ffmpeg而后静静期待即可,个别如果失败的话多是网络问题吧,解决形式家喻户晓... 2、装置胜利后咱们用brew info命令查看一下装置到哪里去了,前面引入工程时须要用到,命令是: brew info ffmpeg例如笔者的输入如图: 配置CLion工程新建好CLion工程后,咱们将FFmpeg的库门路和头文件配置一下,配置CMakeLists.txt: cmake_minimum_required(VERSION 3.17)# 留神 FFmpegPro是工程名称,开发这须要依照理论进行替换project(FFmpegPro)set(CMAKE_CXX_STANDARD 11)# FFmpeg的装置目录,能够通过命令"brew info ffmpeg"获取set(FFMPEG_DIR /opt/homebrew/Cellar/ffmpeg/5.0)# 头文件搜寻门路include_directories(${FFMPEG_DIR}/include/)# 动态链接库或动态链接库的搜寻门路link_directories(${FFMPEG_DIR}/lib/)add_executable(FFmpegPro main.cpp)#链接库target_link_libraries(FFmpegPro #FFmpeg 库 avcodec avfilter avformat avutil swresample swscale )简略测试一下配置是否胜利,在main代码中简略调用一些ffmpeg库的API,如果能失常运行则示意配置胜利: #include <iostream>extern "C"{#include "libavcodec/avcodec.h"#include <libavformat/avformat.h>#include "libavutil/avutil.h"}int main(int arg,char **argv) { // 打印ffmpeg的信息 std::cout << "av_version_info:" << av_version_info() << std::endl; std::cout << "av_version_info:" << avcodec_configuration() << std::endl; return 0;}运行如果能失常打印出ffmpeg的版本号即示意环境配置胜利。 ...

October 12, 2022 · 1 min · jiezi

关于c++:c-primer-练习633递归函数使用迭代器踩坑记录

练习6.33:编写一个递归函数,输入vector对象的内容 问题复现如题,笔者在实现练习6.33时,依据题意写出的代码如下 void myPrint(vector<int> v,vector<int>::iterator it) { if (it != v.end()) { cout << *it; myPrint(v, it++); }}int main() { vector<int> v = { 1,2,3,4 }; myPrint(v,v.begin()); return 0;}试图在递归中应用迭代器,一运行发现报错,报错提醒:vector的迭代器不兼容 起因:后百度查阅知,myVector传入的是一般形参,运行时会先将实参v拷贝一份,拷贝之后的不是原来的v的迭代器了,这时再与原来的v.end()作比拟就提醒迭代器不兼容了 解决办法:将形参v改为援用,这时迭代器就都是同一个v下的,不呈现不兼容景象

October 12, 2022 · 1 min · jiezi

关于c#:C-编写的-64位操作系统-MOOS

MOOS ( My Own Operating System ) 是一个应用 .NET Native AOT 技术编译的C# 64位操作系统。我的项目地址:https://github.com/nifanfa/MOOS。 微软MVP实验室研究员 编译对于编译 MOOS 的信息,请浏览编译维基页面:https://github.com/nifanfa/MO... 编译要求VMware Workstation Playerhttps://www.vmware.com/produc...Visual studio 2022https://visualstudio.microsof...QEMUhttps://www.qemu.org/download 或 VMWare ( 留神,VMware 不反对 USB 键鼠模仿。)Windows 10-11 x64或x868GB Ram 特色 微软最有价值专家(MVP) 微软最有价值专家是微软公司授予第三方技术专业人士的一个寰球奖项。29年来,世界各地的技术社区领导者,因其在线上和线下的技术社区中分享专业知识和教训而取得此奖项。 MVP是通过严格筛选的专家团队,他们代表着技术最精湛且最具智慧的人,是对社区投入极大的激情并乐于助人的专家。MVP致力于通过演讲、论坛问答、创立网站、撰写博客、分享视频、开源我的项目、组织会议等形式来帮忙别人,并最大水平地帮忙微软技术社区用户应用 Microsoft 技术。 更多详情请登录官方网站:https://mvp.microsoft.com/zh-cn 点击理解更多的C# 编写的 64位操作系统 - MOOS

October 12, 2022 · 1 min · jiezi

关于c++:技术解读现代化工具链在大规模-C-项目中的运用-龙蜥技术

编者按:C++ 语言与编译器始终都在继续演进,呈现了许多令人振奋的新个性,同时还有许多新个性在孵化阶。除此之外,还有许多小更改以进步运行效率与编程效率。本文整顿自寰球 C++ 及系统软件技术大会上的精彩分享,接下来由作者带咱们理解 C++ 我的项目的实际工作等具体内容,全文整顿如下: 介绍C++ 是一门有着短暂历史并仍然继续沉闷的语言。C++ 最新规范曾经到了 C++23。Clang/LLVM、GCC 与 MSVC 等三大编译器都放弃着十分频繁的更新。除此之外的各个相干生态也都放弃着继续更新与跟进。但遗憾的是,目前看到踊跃更近 C++新规范与 C++新工具链的都次要以国外我的项目为主。国内尽管对 C++ 新规范也十分关注,但大多以爱好者集体为主,不足实在我的项目的跟进与实际。 本文以现代化工具链作为线索,介绍咱们理论工作中的大型 C++ 我的项目中现代化工具链的实际以及后果。 对于 C++ 我的项目,特地是大型的 C++我的项目而言,经常会有以下几个特点(或痛点): 我的项目高度自治 – 自主决定编译器版本、语言规范高度业务导向 – 少关注、不关注编译器和语言规范先发劣势 – 丢失利用新技术、新个性的能力沉疴难起 – 编译器版本、语言规范、库依赖被锁死许多 C++ 我的项目都是高度自治且业务导向的,这导致一个公司外部的 C++ 我的项目的编译器版本和语言规范形形色色,想对立十分艰难。同时因为日常开发次要更关怀业务,工夫一长背上了技术债,再想用新规范与新工具链的老本就更高了。一来二去,编译器、语言规范与库依赖就被锁死了。 同时对于业务来说,切换编译器也会有很多问题与挑战: 修复更严格编译器查看的问题修复不同编译器行为差别的问题修复语言规范、编译器行为扭转的问题 – 欠缺测试二进制依赖、ABI兼容问题 – 全源码编译/服务化性能压测、调优这里的许多问题哪怕对于有许多年教训的 C++工程师而言可能都算是难题,因为这些问题其实实质上是比语言层更低一层的问题,属于工具链级别的问题。所以大家感觉辣手是很失常的,这个时候就须要业余的编译器团队了。 在咱们的工作中,多数编译器造成的程序行为变动问题须要欠缺的测试集,极少数编译器切换造成的问题在产线上裸露进去 – 实质是业务/库代码的 bug,绝大多数问题在构建、运行、压测阶段裸露并失去修复。 这里咱们简略介绍下咱们在理论工作中遇到的案例: 业务1(规模5M) 业务自身10+仓库;三方依赖50+,其中大部分源代码依赖,局部二进制依赖。二进制依赖、ABI兼容问题 – 0.5人月;编译器切换、CI、CD – 1.5人月;性能剖析调优 – 1人月。业务2(规模7M) 二方/三方依赖 30+,二进制依赖。编译器切换革新 – 2 人月;性能压测调优 – 1 人月。业务3(规模3M) 二方/三方依赖 100+,多为二进制依赖。二进制依赖、ABI 兼容问题 – 预估 2 人年。在切换工具链之后,用户们能失去什么呢? ...

October 11, 2022 · 3 min · jiezi

关于c#:71c-创建logtxt文件并写日志

private void WriteLog(string content) { try { string filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log/log_win.txt"); content = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + content; bool append = true; if (System.IO.File.Exists(filePath) == true) { var fileinfo = new System.IO.FileInfo(filePath); if (fileinfo.Length >= 1024 * 1024 * 10) //如果大于等于1M(1024B*1024KB),则重写日志。 { append = false; } } var logFileStream = new System.IO.StreamWriter(filePath, append, System.Text.Encoding.UTF8); logFileStream.WriteLine(content); logFileStream.Flush(); logFileStream.Close(); } catch(Exception e) { } }

October 11, 2022 · 1 min · jiezi

关于c#:70c-定时器-timer

private void GenTimer() { var timer = new System.Timers.Timer(1000*60*10);//10分钟 timer.AutoReset = true;//AutoReset 属性为 true 时,每隔指定工夫循环一次;如果为 false,则只执行一次。 timer.Enabled = true; timer.Elapsed += new System.Timers.ElapsedEventHandler(RefreshLoadedGsmPort); } private void RefreshAllGsmPort(object sender, EventArgs e) { InitAllGsmPort(); Console.WriteLine("RefreshAllGsmPort..."); }

October 11, 2022 · 1 min · jiezi

关于c++:C20协程学习

导语 | 本文推选自腾讯云开发者社区-【技思广益 · 腾讯技术人原创集】专栏。该专栏是腾讯云开发者社区为腾讯技术人与宽泛开发者打造的分享交换窗口。栏目邀约腾讯技术人分享原创的技术积淀,与宽泛开发者互启迪共成长。本文作者是腾讯后盾开发工程师杨良聪。 协程(coroutine)是在执行过程中能够被挂起,在后续能够被复原执行的函数。在C++20中,当一个函数外部呈现了co_await、co_yield、co_return中的任何一个时,这个函数就是一个协程。 C++20协程的一个简略的示例代码: coro_ret<int> number_generator(int begin, int count) {    std::cout << "number_generator invoked." << std::endl;    for (int i=begin; i<count; ++i) {        co_yield i;    }    co_return;}int main(int argc, char* argv[]){    auto g = number_generator(1, 10);    std::cout << "begin to run!" << std::endl;    while(!g.resume()) {        std::cout << "got number:" << g.get() << std::endl;    }    std::cout << "coroutine done, return value:" << g.get() << std::endl;    return 0;}number_generator内呈现了co_yield和co_return所以这不是一个一般的函数,而是一个协程,每当程序执行到第4行co_yield i;时,协程就会挂起,程序的控制权会回到调用者那里,直到调用者调用resume办法,此时会复原到上次协程yield的中央,持续开始执行。 ...

October 8, 2022 · 3 min · jiezi

关于c++:Folly库代码赏析8IO

IOBufIOBuf相似于linux中的sk_buf,是一个网络编程中的常见概念,用一个物理上的非间断空间虚构出逻辑上的间断空间,同时利用援用计数防止拷贝复制。 空间布局如右所示:headRoom + data + tailRoom,因而能够prepend() && append()。 同时通过多个IOBuf chain连接成逻辑上的间断空间,通过unlink开释。 同时兼容了iovec调用,充分利用多个小块读写。 EventBase基于IOBuf和RequestCtx,引入了和libevent相似的事件循环模型。 教训为了P99,肯定要把CPU密集事件挪动到专门的CPU密集操作池中,避免拖慢IO事件。

October 4, 2022 · 1 min · jiezi

关于c++:Folly库代码赏析7Executor

Folly 的余下内容分为 [ ] synchronization[ ] memory[ ] logging[ ] io[ ] gen[ ] fibers[ ] executors[ ] concurrencyclassDiagram Executor Executor <|-- IOExecutor Executor <|-- ThreadPoolExecutor IOExecutor <|-- IOThreadPoolExecutor ThreadPoolExecutor <|-- IOThreadPoolExecutor ThreadPoolExecutor <|-- CPUThreadPoolExecutor Executor <|-- DrivableExecutor Executor <|-- ManualExecutorExecutorKeepAlive是对Executor的平安援用,Executor析构时会join所有KeepAlive对象。 ThreadExecutor对于每个task启动一个线程执行,通过channel告诉工作增加、删除、完结。golang中的select channel 用法。 ThreadPoolExecutorIO/CPUThreadPoolExecutor的基类,提供了统计性能等通用信息。 IOThreadPoolExecutor提供多个ioThread,每个ioThread容许event_base loop。

October 3, 2022 · 1 min · jiezi