关于c:C语言程序设计C程序设计的初步认识

<article class=“article fmt article-content”><h2>简略C语言程序的形成和格局</h2><pre><code class=“C”>/求矩形面积/#include “stdio.h"int main() { double a, b, area; a = 1.2; /将矩形的两条边长别离赋给a和b/ b = 3.6; area = a * b; /计算矩形的面积并贮存到变量area中/ printf(“a=%f,b=%f,area=%f\n”, a, b, area);}</code></pre><ol><li>C语言规定必须用main作为主函数名,每一个可执行的C程序都必须有且只有一个主函数</li><li>C程序中的每一条执行语句都必须用分号完结</li><li><p>正文内容必须放在符号“/”和“/”之间,在正文之间不能够再嵌套“/”和“/”</p><h2>标识符、常量和变量</h2></li></ol><p><strong>标识符</strong></p><ol><li>由字母、数字和下划线组成,并且第一个字符必须为字母或下划线</li><li><p>类别</p><ol><li>关键字</li><li>预约义标识符</li><li>用户标识符</li></ol></li></ol><p><strong>常量</strong></p><ol><li>程序运行中,其值不能被扭转的量</li><li><p>分类</p><ol><li><p>整型常量:只用数字示意,不带小数点</p><ol><li>短整型常量</li><li>长整型常量</li></ol></li><li>实型常量:必须带小数点的数</li><li>字符常量</li><li>字符串常量</li></ol></li></ol><p><strong>符号常量</strong></p><pre><code class=“C”>/计算圆面积/#include “stdio.h”#define PI 3.14159 /定义符号名PI为3.14159/int main() { double r, s; r = 5.0; s = PI * r * r; printf(“s=%f\n”, s);}</code></pre><p><strong>变量</strong></p><ol><li><p>变量指程序运行过程中其值能够扭转的量</p><h2>整型数据</h2></li></ol><h3>整型常量</h3><ol><li>整型常量能够用十进制、八进制和十六进制等模式示意</li><li>八进制数:结尾必须是数字“0”</li><li>十六进制数:应用“0x”或“0X”结尾;十六进制数中的字母a、b、c、d、e、f既能够小写也能够大写</li><li><p>在C程序中,只有十进制数能够是正数,而八进制和十六进制数只能是负数</p><h3>整型变量</h3></li><li><p>基本型的整型变量用类型名关键字int进行定义</p><pre><code class=“C”>int k;int i, j, k;int i = 1, j = 0, k = 2;</code></pre><h3>整型数据的分类</h3></li><li>短整型(short int)</li><li>根本整型(int)</li><li>长整型(long int)</li><li>无符号型(unsigned):无符号整数在数的开端加上字母后缀u或U;若是长整型无符号整型常量,加上后缀lu或LU</li></ol><blockquote>若不指定变量为无符号型,则变量隐含为有符号型(signed)</blockquote><h3>整数在内存中的贮存模式</h3><ol><li>通常把一个字节的最左边一位称为最低位,最右边一位称为最高位。对于一个有符号整数,其中最高位用来寄存整数的符号,称为符号位。若是正整数,最高位搁置0;若是负整数,最高位搁置1</li><li>负整数在内存中以“补码”模式寄存</li></ol><p><strong>例</strong>:取二进制数10000101(十进制数-5)的补码<br/>步骤如下:求原码的反码。把原码除符号位之外的二进制码按位取反,得11111010</p><pre><code> 把所得的反码加1,失去原码的补码。得11111011</code></pre><blockquote><p>把内存中以补码模式寄存的二进制码转化成十进制的负整数</p><ul><li>先对除符号位之外的各位取反</li><li>将所得二进制数转换成十进制数</li><li>对所求得的数再减1</li></ul></blockquote><h2>实型数据</h2><h3>实型常量</h3><ol><li><p>小数模式:必须要有小数点</p><h3>实型变量</h3></li><li><p>单精度型(float)</p><ol><li>定义:<code>float a,b,c;</code></li><li>占4字节的存储单元</li></ol></li><li><p>双精度型(double)</p><ol><li>定义:<code>double x,y,z;</code></li><li>占8字节的存储单元</li></ol><h2>算数表达式</h2><h3>根本的算数运算符</h3></li><li>加(+)</li><li>减(-)</li><li>乘(*)</li><li>除(/)</li><li>求余(%):运算对象只能是整型</li></ol><blockquote><ul><li>这些运算符须要两个运算对象,称为双目运算符</li><li>“+”和“-”也可用作单目运算符,运算符必须呈现在运算数的右边</li><li>如果双面运算符两边运算数的类型统一,则所得后果的类型与运算数的类型统一</li><li>如果双目运算符两边运算数的类型不统一,零碎将主动进行类型转换,使运算符两边的类型达到统一后再进行运算</li><li>在C语言中,所有实型数的运算均以双精度形式进行</li></ul></blockquote><h3>运算符的优先级、联合性和算数表达式</h3><ol><li>算数运算符的优先级</li><li><p>算数运算符和圆括号的联合性</p><ol><li>以上运算符中,只有单目运算符“+”和“-”的联合性是从右到左的,其余运算符的联合性都是从左到右</li></ol></li><li><p>算数表达式</p><ol><li>定义:用算术运算符和一对圆括号将运算符(或称操作数)连接起来的、合乎C语言语法的表达式</li><li>运算对象能够是常量、变量和函数等</li></ol><h3>强制类型转换表达式</h3></li><li><p>格局:<code>(类型名)(表达式)</code></p><ol><li><code>(类型名)</code> 称为强制类型转换运算符</li></ol><h2>赋值表达式</h2><h3>赋值运算符和赋值表达式</h3></li><li>格局:<code>变量名=表达式</code></li><li>赋值运算符的左侧只能是变量,不能是常量或表达式</li><li><p>C语言规定最右边变量中所失去的新值就是赋值表达式的值</p><h3>复合赋值表达式</h3></li><li><p>定义:在赋值运算符之前加上其余运算符</p><h3>赋值运算中的类型转换</h3></li><li>如果赋值运算符两侧的数据类型不统一,在赋值前零碎将主动先把右侧表达式求得的数值按赋值号右边变量的类型进行转换</li><li><p>在C语言的表达式(不包含赋值表达式)中的转换规则</p><ol><li>一个短整型,一个长整型:$短整型 \to 长整型$</li><li>一个是有符号整型,一个是无符号整型:$有符号整型 \to 无符号整型$</li></ol><h2>自加、自减运算符和逗号</h2><h3>自加运算符和自减运算符</h3></li><li>“++”和“–”都是单目运算符,不能给常量或表达式赋值</li><li>既能够前缀模式呈现,也能够后缀模式呈现</li><li>对于变量来说自增或自减1;对于表达式来说,前置先自增后应用变量值,后置先应用变量值再自增</li><li><p>联合方向:从右至左</p><h3>逗号运算符</h3></li><li>联合方向:从左至右</li><li>逗号运算符优先级最低</li></ol></article> ...

March 5, 2024 · 1 min · jiezi

关于c:关系运算符

a+b>c等价于(a+b)>c,a!=b>c等价于a!=(b>c) 依照优先级预处理!=优先级小于>,故等价于a!=(b>c)设a,b,c均为 int 型变量,则执行语句 a=b=3;c=++a||++b;后,b 的值为3 解析:预处理c=((++a)||(++b));++a表达式值为4即为真,逻辑或,右边为真左边不计算,呈现短路景象,所以++b没计算,故b的值为3。根本数据类型:整型,字符型,浮点型整型常量:示意模式有八进制(以0结尾),十进制,十六进制(以0X结尾)短整型:short 2字节,-2^15~2^15无符号短整型:undesigned short 2字节,0~2^16-1联合性:单目运算符,赋值运算符,条件运算符是右联合性字符常量(一般字符和转义字符):用单撇号括起来的一个字符。如’A’,*等。用反斜杠疏导的,具备特定含意的字符,如’\n’,’\367’,’\x8a’。注:C语言字符集中的任何一个字符均可用转义字符来示意。转义字符特色是以”\”作为结尾,前面能够是规定的字母,比方’\n’代表换行,’\’示意反斜线字符,’ 示意逗号字符(,),也能够是1到3位8进制数,或者x加上1到2位十六进制数。

March 4, 2024 · 1 min · jiezi

关于c:Programming-Abstractions-in-C阅读笔记p308p311

《Programming Abstractions in C》学习第76天,p308-p311总结,总计4页。 一、技术总结1.疾速排序伪代码#include <stdbool.h>static int Partition(int array[], int n);/* * Implementation notes: SortIntegerArray * -------------------------------------- * This implementation of SortIntegerArray uses the Quicksort * algorithm, which begins by "partitioning" the array so that * all elements smaller than a designated pivot element appear * to the left of a boundary and all equal or larger values * appear to the right. Sorting the subarrays to the left and * right of boundary ensures that the entire array is sorted. */void SortIntegerArray(int array[], int n) { int boundary; if (n < 2) { return; } boundary = Partition(array, n); SortIntegerArray(array, boundary); SortIntegerArray(array + boundary + 1, n - boundary - 1);}/* * Function: Partition * Usage: boundary = Partition(array, n); * -------------------------------------- * This function rearranges the elements of array relative to * a pivot value, which is taken from array[0]. The partition * function returns a boundary index such that array[i] < pivot * for all i < boundary, array[i] == pivot for i == boundary, * and array[i] >= pivot for all i > boundary. */static int Partition(int array[], int n) { int lh, rh, pivot, temp; pivot = array[0]; lh = 1; rh = n - 1; while (true) { while (lh < rh && array[rh] >= pivot) { rh--; } while (lh < rh && array[lh] < pivot) { lh--; } if (lh == rh) { break; } temp = array[lh]; array[lh] = array[rh]; array[rh] = temp; } if (array[lh] >= pivot) { return 0; } array[0] = array[lh]; array[lh] = pivot; return lh;}2.疾速排序工夫复杂度均匀工夫复杂度:O(NlogN), 最坏工夫复杂度:O(N^2)。 ...

March 2, 2024 · 2 min · jiezi

关于c:Programming-Abstractions-in-C阅读笔记p303p305

《Programming Abstractions in C》学习第74天,p303-p305总结,总计3页。 一、技术总结1.工夫复杂度分类(complexity classes)ClassNotationExampleconstantO(1)Returning the first element in an arraylogarithmicO(logN)Binary search in a sorted arraylinearO(N)Linear search in an arrayNlogNO(NlogN)Merge sortquadraticO(N^2)Selection sortcubicO(^3)Conventional algorithms for matrix multiplicationexponentialO(2^N)Tower of Hanoi当然,这个分类并不是相对的,只是常见的。 二、英语总结1.substantial是什么意思?答:adj. large in size(sizeable)。p305, Even though the selection sort example makes it cleaar that quadratic algorithms have substantial performance problems (重大的性能问题)for large values of N, algorithms whose complexity is O(2^N) are considerably worse。 2.tractable是什么意思?答:tractare("to handle, manage", treat), adj. easily controlled。p305, As a general rule of thumb(依据教训), computer scientists classify problem that can be solved susing algorithms that run in polynomial time as tractable, in the sense that they are amenable to implementation on a computer。 ...

February 26, 2024 · 1 min · jiezi

关于c:Programming-Abstractions-in-C阅读笔记p293p302

《Programming Abstractions in C》学习第73天,p293-p302总结,总计10页。 一、技术总结1.工夫复杂度(1)quadratic time(二次工夫) p293, Algorithms like selection sort that exhibit O(N^2) performance are said to run in quadratic time。 2.线性查找(linear search)p293, Because the for loop in the implementation executes n time, you expect the performance of LinearSearch——as its name implies——to be O(N)。工夫复杂度为O(N)的查找称为线性查找。 3.归并排序(merge sort)书中并为对归并排序做严格的定义。p298, The merge operation, combined with recursive decomposition, gives rise to a new sorting algorithm called merge sort, which you can implement in straightforward way. The basic idea of the algorithm can be outlined as follows: ...

February 25, 2024 · 3 min · jiezi

关于c:带使能控制的锂电池充放电解决方案

一、产品概述TP4594R 是一款集成线性充电治理、同步升压转换、电池电量批示和多种爱护性能的单芯片电源治理 SOC,为锂电池的充放电提供残缺的单芯片电源解决方案。TP4594R 外部集成了线性充电治理模块、同步升压放电治理模块、电量检测与 LED 批示模块、爱护模块。TP4594R内置充电与放电功率 MOS,充电电流为 250mA,最大同步升压输入电流为 500mA。TP4594R 采纳专利的充电电流自适应技术,同时采纳专利的管制形式省去内部的功率设定电阻,降低功耗的同时升高零碎老本。TP4594R 外部集成了温度弥补、过温爱护、过充与过放爱护、输入过压爱护、输入过流爱护、输入短路爱护等多种平安爱护性能以保障芯片和锂离子电池的平安。TP4594R利用电路简略,只需很少的外围元件便能够实现锂电池充放电的残缺计划,极大的节俭了零碎的老本和体积。二、产品特点线性充电,同步升压集成电源门路治理,反对边充边放最大升压输入电流 500mA充电电流 250mA升压输入 EN 使能管制指示灯敞开对应输入电流 5mA使能敞开时待机功耗低至 2.5uA使能无效时待机功耗低至 25uA充电电流自适应技术智能温度控制与过温爱护反对涓流充电以及零电压充电集成多种爱护性能封装模式:ESOP8L三、产品利用蓝牙耳机充电仓锂电池电子设备其余小功率电源治理利用四、利用阐明线性充电TP4594R 充电时工作在线性充电模式。当电池电压低于2.9V 时,芯片工作在涓流充电状态,涓流充电电流为25mA。当电池电压大于 2.9V 时,芯片进入恒流充电状态,恒流充电电流为 250mA。当电池电压达到 4.2V 时,芯片进入恒压充电状态,充电电流开始逐步减小。当充电电流减小至 60mA 时,线性充电过程实现,芯片进入待机状态。TP4594R 具备智能再充电性能,在待机状态中,芯片监控 BAT 电压,当 BAT 电压降落至 4.0V 时,VDD 从新对电池进行充电,开始新的充电循环。边充边放TP4594R 集成了电源门路治理,反对边充边放性能。在充电电源接入和 OUT 端有负载接入的状况下,TP4594R工作在边充边放模式,线性充电的同时 OUT 端提供电源输入。当充电电源移除后,芯片马上进行同步升压模式,OUT 放弃输入状态。为了进步零碎的可靠性,边充边放模式下,TP4594R 也具备输入过流和短路爱护性能。当输入过流或短路产生时,芯片敞开放电门路,此时充电门路不受影响。当负载移除后,放电门路从新关上,边充边放性能复原。EN 使能管制与低功耗智能待机TP4594R 的同步升压输入具备 EN 使能管制性能。当 EN脚的电压高于 1.5V 时,同步升压输入敞开,芯片工作在低功耗待机状态,芯片待机电流为 2.5uA。当 EN 脚电压低于 0.3V 时,同步升压开始工作,OUT 端为输入状态。此时如果 OUT 端没有接入负载,TP4594R 的工作电流可低至 25uA。TP4594R 外部对 EN 脚没有上拉/下拉性能,当 EN 使能管制性能不必时,需把 EN 脚接到 GND来让同步升压电路工作。TP4594R 的线性充电性能不受EN 脚的高低电平管制。LED 敞开/开启对应的输入电流在 EN 脚电压低于 0.3V 时,同步升压工作,OUT 端为输入状态。当 OUT 的输入电流减小到典型值 5mA 并通过16s 延时后,LED 指示灯敞开。LED 指示灯敞开后,OUT的输入电流再增大到典型值 7mA 并通过 16ms 延时后,LED 指示灯从新开启。电池低压爱护启动时,当 BAT 电压大于 3.15V 时,升压电路开始工作,工作过程中如果电池电压低于 3.05V,则 LED1 会以 2HZ频率快闪揭示电量较低,当电池电压低于 2.85V,则放电输入敞开,TP4594R 进入低电流待机模式。智能温度控制TP4594R 外部集成了温度反馈环路,充电或放电时,如果芯片外部的温度升高到 115℃,充电电流或放电电流会智能的随着芯片外部的温度升高而升高,从而减小零碎功耗以克制温升,爱护芯片不被低温损坏,如果芯片温度升高到 150℃时,芯片进行工作,等到芯片温度升高到130℃后再从新复原工作。爱护性能TP4594R 集成了过充爱护、过放爱护、温度保护、输入过压爱护、输入过流爱护和输入短路爱护等多重爱护性能,以保障芯片和锂离子电池的平安。在利用上也能够额定减少一颗 DW01 来对系统进行双重爱护。TP4594R 的输入短路和过流爱护具备自复原性能。在爱护产生后,把输入移除,芯片可主动复原到失常工作状态。元件抉择1、OUT 输入电容抉择品质较好的低 ESR 的贴片电容,否则会影响输入纹波。2、电感 L1 需采纳功率电感且饱和电流满足利用要求,否则因电感饱和可能会导致芯片工作不失常。PCB 设计参考1、IC 上面敷铜接 GND,地线铺开面积要尽量大,其它不重要的线都能够绕开以满足地线须要。2、BAT 电容既要凑近芯片 BAT 脚又要凑近电感;BAT电容的地线尽量接在大面积地线上,不要通过较小的地线再到芯片和大面积地。3、VDD 电容凑近芯片 VDD 脚,其地线尽量接在大面积地线上,不要通过较小的地线再到芯片和大面积地。4、OUT 输入电容尽量凑近芯片,其地线尽量接在大面积地线上,不要通过较小的地线再到芯片和大面积地。5、电感需凑近 BAT 电容,电感和 BAT 电容以及芯片尽量在同一层不要过过孔,电感到 SW 的走线尽量短而粗。工作状态与电量批示LED1~LED4 为充放电状态与电量批示引脚,不同情况时 LED 状态如下:①接入 VDD 时,LED1 到 LED4 会顺次全副点亮,而后再依据电池电量批示充电状态,达到电量的 LED 灯常亮,以后电量的 LED 以 1Hz 频率闪动,充斥电后 LED1~LED4 全亮;②放电时,LED1~LED4 依据电池电压批示以后电量,若电池电压低于 3.05V,LED1 会以 2HZ 的频率快闪提醒电量低,直到电池电压低于 2.85V,进行放电,进入低功耗低压保护模式,须要从新充电至 3.15V 以上才能够再次放电; ...

February 25, 2024 · 1 min · jiezi

关于c:Programming-Abstractions-in-C阅读笔记p283p292

《Programming Abstractions in C》学习第72天,p283-p292总结,总计10页。 一、技术总结1、anylasis of algorithms算法剖析——即判断程序的效率(efficiency)。 2、mathematical induction(数学归纳法)3、Big-O notation(大O标记法)4、constant time(常量工夫)5、linear time(线性工夫)p292, The computational complexity of the part of the Average function is O(N), which is commonly called linear time。 double Average(double array[], int n) { int i; double total; total = 0; for (i = 0; i < n; i++) { total += array[i]; } return total / n;}二、英语总结1、mission是什么意思?答:c. any work that sb believes it is their duty to do(工作,工作)。p285, Your mission is to write a function SortIntegerArray(array, n) that rearranges the elements into ascending order。 ...

February 20, 2024 · 1 min · jiezi

关于c:Kernel-Compilation-Kernel-Module-System-Call-Implementation

Kernel Compilation, Kernel Module, andSystem Call Implementation1 IntroductionThe objective of this assignment is to familiarize yourself with the Linux kernel source code. Specifically,you will:• Compile your own Linux kernel.• Create your own kernel module.• Add a new kernel system call and then test this system call from a user-space program.All the assignment’s steps must be performed using your Debian virtual machine from programmingassignment 1. The notions from the course involved in this assignment are the following:• Linux source code exploration and compilation.• Installing and running a modified kernel, as well as creating and testing a module.• The printk kernel function.• User space / kernel space communication using a system call.2 Finding and Booting Your “Known Good” KernelStart the Debian VM, log in, open a terminal window, and execute the following command to find theexact version of the kernel you are currently running: uname -rWrite down the result somewhere and remember this exact kernel version for the rest of the semester.This is your “known good” kernel which is known to work correctly. Also execute ls -l /boot and youshould see one “vmlinuz” file in the /boot directory that has the exact same version in its name. This isthe file containing your “known good” kernel. Never delete or modify this file in any way.Use Debian’s battery icon menu (upper right corner of the Debian desktop) to restart the virtual machine.As soon as you see the white-on-blue-green menu of the GRUB boot loader, press the down arrow key onthe keyboard to select the second menu entry named “Advanced options for Debian GNU/Linux”. Youhave only 5 seconds to do this! Then press Enter to get a sub-menu. In the sub-menu, select the “knowngood” kernel, based on the version you learned just above (but not the “recovery mode” one, which issimilar to Microsoft Windows’s “safe mode”, which is not what we want here) and press Enter to bootthis ‘known good” kernel. From now on this is what you will do every time you boot or reboot the VM, tomake sure you always know exactly which kernel you are booting! If later you have a problem when testingyour own modified “working” kernel (see below), such as a kernel that panics (crashes) on boot, you willalways be able to boot this “known good” kernel instead to get back to work.3 Compiling Your Own “Working” Kernel1) Before compiling your own “working” kernel, you must first install some tools and the Linux kernelsource code. So, after booting the “known good” kernel (as indicated above), log in, open a terminalwindow, and execute the following command to install all the software required to configure andcompile the Linux kernel source code:sudo apt install build-essential pahole libelf-dev libncurses-dev libssl-dev flex bison(build-essential is a software package that depends on all the development libraries and headerfiles and basic development tools that you need, so installing it guarantees that all those things arepresent on your VM; pahole and libelf-dev are a set of tools and a library to handle the ELFstandard file format for executable files; libncurses-dev is a library to create text-based userinterfaces, which is used by “make menuconfig” below; libssl-dev is a library that providescryptographic functions and secure networking; and flex and bison are a lexer / scanner generatorand parser generator, respectively).2) Install the Linux kernel source code: sudo apt install linux-sourceThis automatically selects the right version of the Linux kernel source code, based on the version ofthe “known good” kernel which is currently executing on the VM. Then execute ls -l /usr/srcand you should see one file named “linux-source-X.Y.tar.xz” (“tar” is the standard file archive formatfor Unix, and “xz” is one among many compressed file formats on Unix; other common ones are “gz”and “bz2”; together “tar” and “xz” give you a compressed file archive, similar in spirit to the “zip”compressed file archive format which is used a lot on Microsoft Windows).3) Execute cd to make sure that you are in your home directory. Then decompress and extract the kernelsource code you just installed: tar -xavf /usr/src/linux-source-X.Y.tar.xz(the -x option means “extract”; -a means auto-compress or auto-decompress, as the case may be; -v means “verbose”, to see the names of the extracted files as the extraction happens; -f is followedby the name of the compresses archive file). After doing this, you should have in your home directorya new directory named “linux-source-X.Y” (use ls to see it).Use du -s -h linux-source-X.Y to see how big the Linux kernel source code is.4) Change the name of this new “linux-source-X.Y” directory: mv linux-source-X.Y pa25) Execute cd pa2 to move into the top directory of your Linux kernel source code. Use ls to have alook at the different directories there (see slides 20-21 in the “Introduction to Linux” lecture notes onCanvas for an explanation of the content of those directories).6) Next we need to create a configuration file for the kernel (see slides 34 and 36 of the “Introduction toLinux” lecture notes). The simplest method is to just copy and then modify the configuration file forthe “known good” kernel which is currently executing on your VM. Again, the command uname -rtells you exactly which kernel version you are currently executing on the VM, so we can use the resultof this command to copy the right kernel configuration file from the /boot directory into the currentdirectory containing your Linux kernel source code: cp /boot/config-$(uname -r) .config7) We can now modify this kernel configuration file using: make menuconfigYou should then get a text-based user interface (see slide 35 in “Introduction to Linux”) which allowsyou to easily modify your kernel configuration file:a) Press Enter to go into the “General setup” sub-menu, press the down arrow twice on yourkeyboard to go down two lines, then Enter to select “Local version - append to kernel release”. Inthe new text window that appears, enter a minus sign - followed by your login name (which isalso your Stevens login name), followed by “-pa2”. For example: -pmeunier-pa2 (replace mylogin name with yours, obviously). Do not add any space before, inside, or after this text. Thispiece of text is going to be added to the version of the Linux kernel you are going to compile, sothat later you can easily recognize which kernel is yours. This will be your “working” kernel. PressEnter to select “OK” and you should be back in the previous sub-menu, with the text you justtyped is now showing between () parentheses in front of the name of the “Local version - appendto kernel release” sub-menu. Press the right arrow on your keyboard to select “Exit” and thenEnter to go back to the previous top-level menu.b) Select Save and press Enter. In the new window, check that the default “.config” file name iscorrect and press Enter to save your new configuration file. Then select “Exit” to quit the“menuconfig” user interface and go back to the usual Unix shell.c) Double check the differences between the original kernel configuration file and the one you justmodified: diff /boot/config-$(uname -r) .configIn the output you should see that the kernel variable CONFIG_LOCALVERSION is now defined tobe the piece of text “-pmeunier-pa2” (except with your own login name) while before it was theempty string (ignore any change for CONFIG_CC_VERSION_TEXT and for SALT, SIG, and KEY stuff,these changes are irrelevant). You are now ready to compile the Linux kernel source code for your“working” kernel!8) Execute nproc to see how many processors your VM is using (this should be the same number youindicated in the settings of the VM in VirtualBox). The more the better.You might also want to go into the settings of your host operating system (Microsoft Windows orApple macOS) and make sure that the power settings of your computer are set for maximumperformance (for example, in Microsoft Windows, click on the battery icon at the right end of thebottom taskbar, then move the slider to “Best performance”); you can undo this setting later againafter you are finished compiling the kernel.Also make sure that your host OS is not set up to automatically suspend or shut down your hostcomputer if you do not move the mouse or whatnot for a while. Again you can undo this later whenyou are finished compiling the kernel.If you are using a laptop computer, also make sure that it is plugged into an electric socket, otherwiseyour host OS might automatically throttle your computer’s performance to save the battery.Finally, click with your right mouse button on the Debian desktop background, select “Settings”, then“Power”, then click on “Automatic suspend” and turn off both “On Battery Power” and “Plugged In”;this will guarantee that Debian does not suspend itself while you are in the middle of compiling. Thenclose the settings window.Now we are ready to compile the whole Linux kernel source code using all the VM’s processors inparallel, to speed up things: make -j $(nproc) allWait an hour or two (or three). You will know the compilation is over when your computer’s fan stopsmaking more noise than usual… If for some reason you need to stop the compilation before it isfinished, just press Ctrl-c on the keyboard. You can then re-start the compilation later using the samecommand again, and it will automatically restart from where it left off.If for some reason the compilation fails, it might be hard to see why it failed because you are compilingdifferent parts of the kernel code in parallel. In that case, you can execute just make all to restartthe compilation from where it failed but this time in sequential mode on a single CPU, which will makeit much easier to see what the problem is. Then contact your nearest Course Assistant.If you look at the output of the make command while it is compiling the code, you will see differentabbreviations: CC is when compiling a C file to get a “something.o” object file (a file containing binaryCPU instructions), LD is when linking (gluing) multiple object files together, AR is when creating alibrary (a “something.a” archive file of object files, which later will be linked with the rest of thekernel), [M] means that this code is part of a dynamically loadable kernel module, not part of themonolithic Linux kernel proper.9) Execute du -s -h . to see how much disk space the current directory containing the whole compiledLinux source code takes (it should be around 19GB).10) When compiling the kernel, all the kernel modules are also compiled at the same time. We now needto install these new kernel modules: sudo make INSTALL_MOD_STRIP=1 modules_installThis must be done as root (the system administrator) hence the use of “sudo” here. TheINSTALL_MOD_STRIP=1 argument given to the make command tells make to strip extra debugginginformation from the modules when installing them, which saves around 2GB of disk space.Once the modules are installed, you can execute ls -l /usr/lib/modules and you should see inthe output a directory with a name that has your login name and “pa2” at the end, which is where themodules were installed. The command du -s -h /usr/lib/modules/* should show you that allthe different directories there (the one for your own modules, as well as the one for the “known good”kernel) are about 400MB in size.11) Now we can install the new “working” kernel itself: sudo make installDo not worry about any “Please install the Linux kernel "header" files matching the current kernel”message in the command’s output; we would need this only if we were to try to use our Debian VMas the host for another VM on top of it!Execute ls -l /boot and you will see that make installed several files there, all of which have yourlogin name and “pa2” at the end of their name: “config-X.Y…-pmeunier-pa2” which is a copy of the“.config” file you used for compiling your kernel (you can check this using the diff command, forexample; see above), “initrd.img-X.Y…-pmeunier-pa2” which is the kernel’s “initial RAM disk” whichthe kernel only uses at boot time (see here for more information if you are curious), “System.map-X.Y…-pmeunier-pa2” which contains a list of the kernel’s symbols (the function names and variablenames inside the kernel) for your kernel (see slide 44 in “Introduction to Linux” lecture notes), andfinally “vmlinuz-X.Y…-pmeunier-pa2” which is your very own “working” kernel!As part of the kernel’s installation, the configuration file for the GRUB bootloader is also automaticallyupdated so that the bootloader now knows about your new kernel.12) Use Debian’s battery icon menu to restart the virtual machine. Again, as soon as you see the white-on-blue-green menu of the GRUB boot loader, press the down arrow key on the keyboard to selectthe second menu entry named “Advanced options for Debian GNU/Linux”. You have only 5 secondsto do this! Then press Enter to get a sub-menu. In the sub-menu, select now your own “working”kernel, which has your login name and “pa2” at the end of its name (but again not the “recoverymode” one) and press Enter to boot your own kernel. Happiness ensues.13) After booting your own “working” kernel, log in, open a terminal window, use Ctrl-+ to increase thefont size of the terminal window, and execute uname -a (which should show a kernel name withyour own login name and “pa2” at the end of it) and then id. Take a screenshot and save it as a picturesomewhere on your host computer, you will need to submit it later on Canvas (more details aboutthis at the end of this document). Make sure the kernel version and your login name (Stevens loginname) are clearly visible inside the terminal window in your screenshot. For example:Congratulations on compiling and booting your first Linux kernel!Now create in your home directory another new directory that you will use for submitting your assignment(again, use your own Stevens login name): cd; mkdir pmeunier-pa2Then copy your “working” kernel configuration file into your submission directory:cp pa2/.config pmeunier-pa2To finish this part of the assignment, create a backup copy of the kernel source code you just compiled,just in case (-r means to do a recursive copy that will automatically copy the directory, its sub-directories,its sub-sub-directories, etc.; -p preserves the file time stamps when copying which the make commanduses to decide whether to compile a file again or not, so this is important when copying compiled codethat has been created using make; this command takes a couple of minutes to complete because you arecopying about 19GB of data): cp -p -r pa2 pa2-backupLater, when doing the rest of this assignment, if you make a big mistake inside your “pa2” directory, suchas accidentally deleting some files, you can always find the original files inside this backup directory. Inthe worst case, if you do not know how to fix the mistake, you can always completely delete your “pa2”directory (cd; rm -rf pa2) and re-create it using the backup copy (cp -p -r pa2-backup pa2).4 Creating a Kernel ModuleIn your submission directory (cd pmeunier-pa2), create a new directory (mkdir module). In that newdirectory (cd module), write a Linux kernel module in a file named “pmeunier.c” (use your own loginname). This module must have a printk() statement that outputs “Hello World from NAME (LOGIN)” inthe kernel log when the module is loaded into the kernel, where NAME is your full legal name and LOGINis your Stevens login name. When the kernel module is unloaded it must print “PID is XYZ and programname is NAME” where XYZ is the PID number and NAME the program name of the current process. (Seeyour “Introduction to Linux” and “Processes in Linux” lecture notes.)Compile your kernel module using the appropriate “Makefile”. (Make sure that you are still running yourown “working” kernel when doing this!)You must provide a single screenshot that shows two Debian terminal windows side by side: one windowmust show the kernel log (with your module’s output visible in it), and the other window must show theoutput of the uname -a command followed by commands to load-unload your module at least twice (sowe can see in the kernel log that the PID printed when unloading the module changes during each unload).Save the screenshot somewhere on your host computer, you will need to submit it later on Canvas (moredetails about this at the end of this document). For example (partly censored):5 Adding a System Call to Your “Working” Kernel and Testing It5.1 Adding The System CallIn the directory containing the compiled source code of your “working” kernel (cd ~/pa2; “~” is a shellspecial character that always represents your home directory) create a new directory (mkdirmy_syscall). In that new directory (cd my_syscall), in a file named “my_syscall.c”, write the C codefor a new system call named “LOGIN_syscall” (replace LOGIN with your Stevens login name) that takes assingle parameter a pointer to a character array containing a string. Make sure you use the proper C macroto define your system call (see the “System Calls” lecture notes). The code of your system call must alwaysreturn a signed long integer (long) as result.Your system call must do the following:1) If the string pointer given as argument is NULL then your system call must immediately return -1.2) If the string length is larger than 32 (where the string length is its total number of characters, includingthe ’\0’ string terminator character), then your system call must immediately return -1.3) Copy the string from user space to kernel space.4) Use printk to print “before: “ followed by the string. Note: whenever you use printk, always makesure that the string you want to print is terminated with a ‘\n’ newline character, otherwise the stringwill not immediately appear in the kernel logs when your code is executed.5) Replace all occurrences of a lowercase vowel letter (a, e, i, o, u, y) in the string with the first letter ofyour login name, in uppercase (for example, given my “pmeunier” login name, the letters a, e, i, o, u,and y are all replaced with the uppercase letter P).6) Use printk to print “after: “ followed by the modified string.7) Copy the modified string from kernel space to user space.8) The system call then returns as result the number of character replacements performed in step 5.In your code, use the functions listed on slide 25 of the “System Calls” lecture notes as much as possible.Make sure your code properly checks for errors when using each of these functions (see the examplesystem call code on slide 21 in those same lecture notes). Do not use a module to implement your systemcall. Also create a “Makefile” in the same directory (see slide 27).Modify other files in the Linux kernel source code as necessary (see slides 26 and 27; use gettimeofdayfrom slide 21 as an example of how to add your own system call to the various Linux files). Give yoursystem call the number 451. Keep a list somewhere of all the files you modify, because later you will needto copy them to your submission directory.Recompile your “working” kernel, the same way you did it in section 3 of this assignment. Note: some ofthe files you need to modify are referenced throughout the kernel’s code, so, when recompiling, the makecommand then needs to recompile most of the kernel code again, which again takes a very long time. Somake sure you modify the files correctly on the first try, so you do not have to recompile the whole kernelover and over. If you only need to change your my_syscall/my_syscall.c file then recompiling the kernelshould take only about 5 minutes since the rest of the kernel’s code will not need to be recompiled againin this case.If the compilation succeeds then reinstall your “working” kernel and its associated modules (do NOT tryto install anything if the compilation failed!) Note that, when installing your modified “working” kernel,the previous version of your “working” kernel will be renamed with an extra “.old” extension at the endof its name. You can just ignore this one.Reboot the virtual machine and make sure that you use the GRUB bootloader to select the correct kernelwhen rebooting (your “working” kernel with the “-pmeunier-pa2” name, that now contains the code foryour own system call).If your “working” kernel panics (crashes) on boot for some reason, reboot with your “known good” kerneland fix the problem in the code of your “working” kernel.After you have booted the correct kernel, execute the following command to double-check that the kernelyou are now running actually knows about your system call (see slide 44 of the “Introduction to Linux”lecture notes; replace “pmeunier” with your own login name): grep pmeunier /proc/kallsymsIf the output of this command is empty then the current kernel does not know about your system call andso you did something wrong somewhere. Re-read this whole section and try again.5.2 Testing The System CallIn your submission directory (cd ~/pmeunier-pa2), write the C code for a user-space test programcalled “syscall.c” that invokes your system call. Since your system call does not have a corresponding Cwrapper function available in the C standard library, you must directly use the “syscall” function of the Cstandard library to call your system call (see slide 18 of the “OS Concepts and Structure” lecture notes foran example). Your code must invoke your system call twice: once for the case where the string size islarger than 32, and once for the case where the string size is less than 32. In both cases, your C programmust print on the screen:1) The string given to the syscall, before the system call happens.2) The return value of the syscall.3) The string after the system call, even if it has not been modified.Note: you can directly use the system call number 451 in your C code.Important note: the strings that you use in your code must be defined as local variables (the same way itis done for example on slide 18 of the “OS Concepts and Structure” lecture notes), not as string constantsthat you directly give to the system call as argument. This is because, when your code executes inside aprocess, all the string constants from your code are stored in a part of the “data” segment of the process’saddress space which is read-only (since string constants are… constant), which in turn means that yourkernel system call would fail when trying to modify those strings. Local variables are stored in the process’sstack, which is read-write and your system call will then be able to modify those strings.You must provide a single screenshot that shows two Debian terminal windows side by side: one windowmust show the kernel log (with your system call’s output visible in it, when the string given as argumentto the system call is short enough), and the other window must show the output of the uname -acommand followed by the execution of your user-space test program. Save the screenshot somewhereon your host computer, you will need to submit it later on Canvas (more details about this below). Forexample:6 What to Submit For This AssignmentOnce both your system call and your user-space test program work, copy the code of the system call, plusany other kernel file you modified, into your submission directory (your user-space test program mustalready be there): cd ~/pa2; cp -p -r my_syscall … any other file you modified … ~/pmeunier-pa2In the same submission directory, create a PDF file named “screenshots.pdf” that contains:1) Your full name.2) The Stevens Honor pledge.3) The three screenshots you created above: one showing that you compiled your own “working” kernel(section 3), one showing how your kernel module works (section 4), and one showing how your systemcall and your user-space test program work (section 5.2). Make sure the screenshots are clearlyreadable.4) Also add a short explanation before each screenshot so the Course Assistants know what you aretrying to show on those screenshots.At this point the submission directory (“pmeunier-pa2”) must contain all the files you have created ormodified during this assignment (including the kernel .config configuration file, which you can only see inthe submission directory by using the ls -a command). Install the zip program on your virtual machine(sudo apt install zip) and then create a ZIP file of your submission directory (the -r option meansto zip all the subdirectories recursively: do not forget it; also use your own student login name, twice,obviously): cd; zip -r pmeunier-pa2.zip pmeunier-pa2Once you have correctly created the file “pmeunier-pa2.zip”, copy it to the host OS using your sharedfolder, double-check its content to make sure it contains everything, then submit it on Canvas.After the deadline for this assignment has passed, you can delete the backup copy of your “working”kernel: rm -rf ~/pa2-backupIt is up to you whether you delete the compiled source code of your “working” kernel or not (rm -rf~/pa2), you will not need it anymore. You can delete it after the deadline for this assignment has passed,if you want to save VM disk space, or delete it only at the end of the semester, or keep it for ever as asouvenir!Done!7 Rubric1) Correct .config file: 5%2) Screenshot showing the corresponding “working” kernel version: 5%3) Code of module/pmeunier.c: 15%4) Code of module/Makefile: 5%5) Screenshot of kernel log with module loading-unloading (twice): 10%6) Code of my_syscall/my_syscall.c: 25%7) Code of my_syscall/Makefile: 5%8) Code of syscall.c: 15%9) Other modified kernel files: 5%10) Screenshot of kernel log and output of syscall.c: 10%Important note: you do not get points for screenshots unless the corresponding files are submitted too.So for example you will not get points for the first screenshot listed above if you do not also provide thecorresponding .config file. Screenshots alone will not get you any points at all. So make sure you doublecheck everything before you submit on Canvas! ...

February 18, 2024 · 20 min · jiezi

关于c:c语言基础

指令#define _CRT_SECURE_NO_WARNINGS //保障scanf函数可能失常运行#include <stdio.h> //函数printf应用#include <string.h> //函数strlen应用关键词getchar() while(1) char short int long long long float double scanfprintf extern const arr[](数组)strlen模板//编写代码的文件#include <stdio.h> //编译预处理指令//main函数是程序的入口//main函数有且只有一个int main() //定义主函数{ // 函数开始的标记 printf("hello world"); //输入所指定的一行信息 //避免程序闪退,咱们要观看运行后果 // getchar(); //获取一个字符,不输出就会始终期待 //或 // while(1); //通过死循环来卡住程序,避免闪退 return 0; //函数执行结束时返回函数值0} //函数完结的标记数据类型char //字符数据类型short //短整型int //整形long //长整形long long //更长的整形float //单精度浮点数double //双精度浮点数数据类型所占内存空间的大小{ printf("%d\n", sizeof(char); //1 printf("%d\n", sizeof(short); //2 printf("%d\n", sizeof(int); //4 printf("%d\n", sizeof(long); //4 printf("%d\n", sizeof(long long); //8 printf("%d\n", sizeof(float); //4 printf("%d\n", sizeof(double); //8 return 0; //完结}计算机中的单位bit(比特位)-->byte(字节)-->kb-->mb-->gb-->tb-->pb1byte = 8bit1kb = 1024byte1mb = 1024kb 1gb = 1024mb1tb = 1024gb1pb = 1024tb 类型的应用{ //类型 变量 = 数据 char = 'w'; int age = 20; return 0; //完结}全局变量和局部变量#include <stdio.h> //预处理指令int num = 10; //全局变量int main() //主函数{ int num = 10; //局部变量 printf("num=%d\n", num); //全局变量名与局部变量名雷同时,部分优先 return 0; //完结}全局变量 - {}内部定义的变量 ...

September 22, 2023 · 3 min · jiezi

关于c:CH395Q学习笔记-一

一、TCP/IP简介TCP/IP协定层网络架构应用层:HTTP/FTP/MQTT/DNS/SMTP..传输层:TCP协定(基于连贯)、UDP协定(非连贯)网络层:IP协定、ARP协定(获取对方主机的MAC地址)、ICMP协定(ping)网际接口层:数据链路层、物理层 TCP/IP协定栈品种 二、CH395芯片CH395Q芯片通信骨架 CH395Q框架总体 CH395Q特点 CH395Q工作原理 CH395Q框图 CH395Q原理图解析

September 11, 2023 · 1 min · jiezi

关于c:gdb调试c程序

本文由chatgpt生成应用 gdb 联合断点来分析程序是一种罕用的调试办法,容许您在程序执行的不同阶段进行执行并查看变量、堆栈和程序状态。以下是一些在应用 gdb 时联合断点进行剖析的根本步骤: 编译程序时启用调试信息: 在应用 gdb 进行调试之前,您须要确保在编译程序时启用了调试信息。通常,您能够应用 -g 标记来编译程序,例如: gcc -g -o my_program my_program.c启动 gdb: 在终端中运行 gdb 并指定要调试的可执行文件: gdb ./my_program设置断点: 应用 b 或 break 命令设置断点。例如,要在某个函数的结尾设置断点,能够执行: b function_name您还能够设置在特定行上的断点: b file_name.c:line_number运行程序: 应用 r 或 run 命令来运行程序: r当程序执行到断点时,它将进行并期待您的命令。 查看变量和状态: 在程序进行时,您能够应用 p 命令来查看变量的值,例如: p variable_name您还能够应用 bt 命令查看函数调用堆栈: bt单步执行: 应用 n 或 next 命令来单步执行程序,逐行执行代码。这将使您可能逐渐跟踪程序的执行。 n继续执行: 应用 c 或 continue 命令持续执行程序,直到遇到下一个断点或程序完结。 c删除断点: 应用 d 或 delete 命令来删除断点: d退出 gdb: 应用 q 或 quit 命令退出 gdb。 ...

September 9, 2023 · 1 min · jiezi

关于c:Programming-abstractions-in-C阅读笔记p144p160

《Programming Abstractions In C》学习第56天,p144-p160。实现第三章内容学习,第三章总计54页(p107-p160),耗时10天,均匀6页/天。 一、技术总结第三章的内容次要介绍C语言中的库(library)和接口(interface),如咱们最常遇到的以下三种场景 随机数:<stdlib.h>字符串:<string.h>I/O操作:<stdio.h>二、英语总结1.subdivide和divide的区别是什么?答: (1)subdivide: vt. to divide sth into smaller parts; (2)divide: vt/ti. to seperate into parts。 p145, Because files are usually subdivided into individual lines, it is often usful to read an entire line of data at a time。依据上下文的意思,subdivide是分了再分(you divide it into smaller parts and then subdivide these parts into smaller parts)。 2.irrespective什么意思?答: (1)irrespective < respective: adv. without consideraing(不思考)。 (2)respective < respect:adj. relating each to each(各自的)。 ...

September 6, 2023 · 1 min · jiezi

关于c:C-语言-float-内存布局详解

前言C语言中的float并不像大多数人设想的那样, 因为计算机模仿的起因, 其本质是离散的而非间断的, 所以精度和范畴是肯定的, 这些都写在float.h头文件的宏中. 但通常, 咱们对教材的每一个字都意识, 连起来就读不懂了, 所以, 写下此博文, 详解之. 学过深刻了解计算机系统的同学, 都晓得float的实现形式, 依照IEEE规范, 由符号位, 阶码位, 尾数位组成, 本文给出一个代码, 打印float的符号位, 阶码位, 尾数位. 一、float中的宏定义这是float.h中无关float具体实现, 范畴, 精度等的宏常量, 根据64位零碎环境, 咱们一一解读. #include <float.h> FLT_RADIX; // 2; FLT_MANT_DIG; // 24; FLT_DIG; // 6; FLT_MIN_10_EXP; // (-37); FLT_MAX_10_EXP; // 38; FLT_EPSILON; // 1.19209290e-7F; FLT_MAX; // 3.40282347e+38F; FLT_MIN; // 1.17549435e-38F;FLT_RADIX 2This is the value of the base, or radix, of the exponent representation. This is guaranteed to be a constant expression, unlike the other macros described in this section. The value is 2 on all machines we know of except the IBM 360 and derivatives. ...

September 1, 2023 · 2 min · jiezi

关于c:Programming-abstractions-in-C阅读笔记p139p143

《Programming Abstractions In C》学习第55天,p139-p140,总结如下: 一、技术总结1.文件I/O操作文件I/O操作能够分为一下这些步骤: (1)申明文件指针对象。 File *infile;(2)关上文件 fopen()。关上文件的模式有“r”, "w", "a"三种模式。 (3)传输数据 读取文件的形式能够是character by character( getc()/putc() ),也能够是line by line( fget()/fput() )。 (4)敞开文件 fclose()。 2.文件I/O操作示例:复制文件#include <stdio.h>#include <stdbool.h> // for bool, true, false data type#include <stdlib.h> // for exit()void CopyRemovingComments(FILE *infile, FILE *outfile);int main() { // 申明文件指针对象 FILE *infile, *outfile; char *infileName, *outfileName; /* * 关上文件:fopen() * 如果文件不存在,则返回NULL,所以须要查看 */ infileName = "D:\\CProject\\chater3.4\\jabber.txt"; // 这里应用的是绝对路径,也能够应用相对路径 outfileName = "D:\\CProject\\chater3.4\\jabbercopy.txt"; infile = fopen(infileName, "r"); if (infile == NULL) { printf("Cannot open input file: %s \n", infileName); exit(0); } /* * 传输数据 * 传输数据有很多种形式,例如chracter by character(getc/putc),line by line(fget/fput, ReadLine) * 为了解决stdio.h存在的一些问题,作者对stdio进行了封装,封装后失去的的是simpio */ outfile = fopen(outfileName, "w"); if (outfile == NULL) { printf("Cannot open output file: %s \n", outfileName); exit(0); } CopyRemovingComments(infile, outfile); /* * 敞开文件 */ fclose(infile); fclose(outfile); printf("Copying is completed"); return 0;}void CopyRemovingComments(FILE *infile, FILE *outfile) { int ch, nch; bool commentFlag; // 这里应用的是stdbool.h接口中的bool commentFlag = false; // 这里应用的是stdbool.h接口中的false,书中应用的是封装后的FALSE while ((ch = getc(infile)) != EOF) { if (commentFlag) { if (ch == '*') { nch = getc(infile); // if (nch == '/') { commentFlag = false; } else { ungetc(nch, infile); } } } else { if (ch == '/') { nch = getc(infile); if (nch == '*') { commentFlag = true; } else { ungetc(nch, infile); } } if (!commentFlag) { putc(ch, outfile); } } }}二、英语总结1.endpoint什么意思?答:c.the end of sth(起点)。 ...

August 31, 2023 · 2 min · jiezi

关于c:Programming-abstractions-in-C阅读笔记p132p137

《Programming Abstractions In C》学习第53天,p132-p137,3.2大节“strings”总结如下: 一、技术总结3.2大节介绍了字符串的用法: 1.C语言是没有字符串(string)这种数据类型的,然而理论的场景中又很须要这种数据类型,那怎么示意字符串呢?有两种办法:1.用字符数组示意。2.用字符指针示意。 2.C自带的字符串库是string,作者为了更好的应用string,封装出了strlib库,所以在书中的代码常常会看到作者在头文件中引入strlib这个库,而不是间接援用string库。 3.执行字符串复制的时候要思考是否会产生buffer overflow问题。 二、英语总结1.rather什么意思?答:p132,“Note that this test does not check whether the strings are equal but rather whether the pointer are equal”。 rather在这里的意思是“adv. used to express an opposite opion”,用于表白一种相同的观点,具体的意思依据上下文。例如在这句话外面的意思是“而是”。 2.impose什么意思?答:in-(*en, "into, in") + pose("put, place"),vt. to introduce sth(引入),暗含“强制”之意。p132,Because the complexity imposed by string.h interface tends to get in the way of understanding more critical algorithmic issues,so...(因为string.h接口带来的复杂性往往会障碍了解更要害的算法问题,所以......)。 3.present什么意思?答:vt. to give, provide in a formal way。绝对于give或者provide而言,present更正式一些。p132,The interface is called strlib.h and is presented in its complete form in Figure 3-6。 ...

August 29, 2023 · 1 min · jiezi

关于c:Programming-abstractions-in-C阅读笔记p130p131

《Programming Abstractions In C》学习第52天,p130-p131,总结如下: 一、技术总结1. pig latin game通过pig latin game把握字符复制,指针遍历等操作。 /* * 输出:字符串,这里采纳书中坐着自定义的getline函数 */#include <stdio.h>#include <string.h>#include "simpio.h"#define MaxWord 1000static _Bool IsVowel(char ch); // 书中p34, if实用于非此即彼的两种抉择(two-way);如果有多个,那么就应用switch。static void PigLatin(char *word, char buffer[], int bufferSize);static char *FindFirstVowel(char *word); // *示意函数FindFirstVowel返回一个指向char的指针int main() { char *word; char translationBuffer[MaxWord + 1]; printf("Enter a word: "); word = GetLine(); PigLatin(word, translationBuffer, MaxWord + 1); printf("Pig Latin: %s\n", translationBuffer);}/* * Function:IsVowel * Usage: isVowel = IsVowel(character) * ----------------------------------- * 该函数判断字符是否是元音字母,如果是,返回True,否则返回False。 */_Bool IsVowel(char ch) { switch (ch) { case 'A': case 'E': case 'I': case 'O': case 'U': case 'a': case 'e': case 'i': case 'o': case 'u': return TRUE; default: return FALSE; }}/* * Function: PigLatin * Usage: PigLatin(word, buffer, bufferSize); * ------------------------------------------ * This function translate a word from English to Pig Latin. The * translated word is written into the array buffer, which has an * allocated size of bufferSize. The code checks for buffer * overflow and generate an error if it occurs. */static void PigLatin(char *word, char buffer[], int bufferSize) { char *vp; int wordLength; vp = FindFirstVowel(word); wordLength = strlen(word); if (vp == word) { wordLength += 3; } else if (vp != NULL) { wordLength += 2; } if (wordLength >= bufferSize) { Error("Buffer overflow"); } if (vp == NULL) { // 单词中不存在元音字母:不做任何批改 strcpy(buffer, word); } else if (vp == word) { // 单词以元音字母结尾: 在单词开端增加way(示例:any > anyway) strcpy(buffer, word); strcat(buffer, "way"); } else { // 单词以辅音字母结尾: (1)将辅音字母挪动到单词尾部,直到第一个字母是元音字母。 // (2)挪动实现后,在单词尾部增加ay(示例:trash > ashtray) strcpy(buffer, vp); strncat(buffer, word, vp - word); strcat(buffer, "ay"); }}/* * FindFirstVowel: 找出单词中的第一个元音字母 */static char *FindFirstVowel(char *word) { char *cp; // 将原来的指针赋值给新的指针,防止原来的指针被批改 // 遍历指针 for (cp = word; *cp != '\0'; cp++) { // 留神:在这里*cp示意的是值 if (IsVowel(*cp)) { return cp; // 留神:cp++挪动之后,cp指向的地址扭转了 } } return NULL; // 如果找不到,则返回空指针(NULL)}残缺代码见:https://github.com/codists/Programming-Abstractions-In-C/tree/main/chapter3/piglatingame ...

August 28, 2023 · 2 min · jiezi

关于c:Programming-abstractions-in-C阅读笔记p127p129

《Programming Abstractions In C》学习第51天,p127-p129,总结如下: 一、技术总结1. string library把握罕用函数如strlen,strcpy用法。 2.buffer overflow(缓冲区溢出)(1)什么是buffer? p129,Arrays that are preallocated and later use as a repository for data called buffers。 (2)什么是缓冲区溢出? p129,Writing data past the end of an buffer is a common programming error called buffer overflow。 //buffer overflow示例char *src = "Hello";char dst[1]; // dst称为bufferstrcpy(dst, src); // 该操作会导致buffer overflow问题书中只是做一个简略的阐明,具体可参考: (1) wikipedia, Buffer overflow: https://en.wikipedia.org/wiki/Buffer_overflow 二、英语总结1.“The function will go ahead and copy characters right on past the end of the buffer.”语法分析答:这里的应该是The function will go ahead and (copy characters right) (on past the end of the buffer),这句话有几个要留神的中央: ...

August 24, 2023 · 1 min · jiezi

关于c:Programming-abstractions-in-C阅读笔记p123p126

《Programming Abstractions In C》学习第50天,p123-p126,总结如下: 一、技术总结1.notaion这也是一个在计算机相关书籍中呈现的词,但有时却不是那么好了解,因为它能够指代很多对象,这里做一个记录。示例:p124。 In C, you can use any character array to hold string data. char str[6] = {'h', ' ', 'l', ' ', 'o', '\0'};or, more compactly, char str[6] = "hello";If you use array notation, the standar idion for processing every character in a string looks like this: for (int i = 0; str[i] != '\0'; i++) { printf("i=%d\n", str1[i]);}在这里,“notation”以了解为“the activity of representing sth by a special system of marks or character”,即“notation”示意的是一种“标记办法”、“示意办法”。 ...

August 21, 2023 · 2 min · jiezi

关于c:Programming-abstractions-in-C阅读笔记-p118p122

《Programming Abstractions In C》学习第49天,p118-p122,总结如下: 一、技术总结1.随机数(1)seed p119,"The initial value--the value that is used to get the entire process start--is call a seed for the random generator." 二、数学总结1.均匀分布(uniform distribution)均匀分布属于概率论和统计学领域,有连续性均匀分布和离散型均匀分布。 参考:(1)连续性均匀分布(continuous uniform distribution):(2)离散型均匀分布(discrte uniform distribution): 三、英语总结1.discern是什么意思?答:dis-("off, away") + cernere("distinguish, seperate, shif"),"to see, recorgnize, understand sth that is not clear(辨认)",当应用这个单词的时候,示意“被辨认”的对象并不是那么“clear”。 2.against什么意思?p120,“Finally, every implementation needs to include its own interface so the compiler can check the prototypes against the actual definitions.”。这里之所以要把against抽出来说,是因为against很多中央会用到,但要说间接翻译成什么词,却不是那么好间接给出答案,这里做一个总结。against总体的意思是“prep. opposite/toward”。 3.suffice什么意思?答:i. to be enough。形容词是“sufficent”。p120,“The comments in the interface should suffice.”(接口中定义的正文应该足够了)。 ...

August 20, 2023 · 1 min · jiezi

关于c:Programming-abstractions-in-C阅读笔记-p114p117

《Programming Abstractions in C》学习第48天,p114-p117,总结如下: 一、技术总结次要通过random number介绍了随机数的相干用法,interface示例(random.h),client program示例(craps.c)。 #include <stdio.h>#include "genlib.h"#include "random.h"static bool TryToMakePoint(int point);static int RollTwoDice(void);void main() { int point; Randomize(); // 定义在自定义的random.h文件中 printf("This program plays a game of craps.\n"); point = RollTwoDice(); switch (point) { case 7: case 11: printf("That's a natural. You win.\n"); break; case 2: case 3: case 12: printf("That's craps. You lose.\n"); break; default: printf("Your point is %d.\n", point); if (TryToMakePoint(point)) { printf("You made your point. You win.\n"); } else { printf("You rolled a seven. You lose.\n"); } }}static bool TryToMakePoint(int point) { int total; while (TRUE) { total = RollTwoDice(); if (total == point) return TRUE; if (total == 7) return FALSE; }}static int RollTwoDice(void) { int d1, d2, total; printf("Rolling the dice ...\n"); d1 = RandomInteger(1, 6); // 定义在自定义的random.h文件中 d2 = RandomInteger(1, 6); total = d1 + d2; printf("You rolled %d and %d -- that's %d.\n", d1, d2, total); return total;}二、英语总结1.inclusive什么意思?答:adj. including a particular thing。当探讨波及到范畴时,咱们常常会说在某两个数之间,如果蕴含这两个数,那么就用inclusive这个词来形容这两个数。示例:p114,The first prototype is for the function RandomInteger(low, high), which return a randomly chosen integer in the range between low and high, inclusive。 ...

August 16, 2023 · 1 min · jiezi

关于c:重学C02-脱离指针陷阱深入浅出-C-智能指针

文章首发【重学C++】02 脱离指针陷阱:深入浅出 C++ 智能指针 前言大家好,明天是【重学C++】系列的第二讲,咱们来聊聊C++的智能指针。 为什么须要智能指针在上一讲《01 C++如何进行内存资源管理》中,提到了对于堆上的内存资源,须要咱们手动调配和开释。治理这些资源是个技术活,一不小心,就会导致内存透露。 咱们再给两段代码,切身体验下原生指针治理内存的噩梦。 void foo(int n) { int* ptr = new int(42); ... if (n > 5) { return; } ... delete ptr;}void other_fn(int* ptr) { ...};void bar() { int* ptr = new int(42); other_fn(ptr); // ptr == ?}在foo函数中,如果入参n > 5, 则会导致指针ptr的内存未被正确开释,从而导致内存透露。 在bar函数中,咱们将指针ptr传递给了另外一个函数other_fn,咱们无奈确定other_fn有没有开释ptr内存,如果被开释了,那ptr将成为一个悬空指针,bar在后续还持续拜访它,会引发未定义行为,可能导致程序解体。 下面因为原生指针使用不当导致的内存透露、悬空指针问题都能够通过智能指针来轻松防止。 C++智能指针是一种用于治理动静分配内存的指针类。基于RAII设计理念,通过封装原生指针实现的。能够在资源(原生指针对应的对象)生命周期完结时主动开释内存。 C++规范库中,提供了两种最常见的智能指针类型,别离是std::unique_ptr 和 std::shared_ptr。接下来咱们别离具体开展介绍。 吃独食的unique_ptrstd::unique_ptr 是 C++11 引入的智能指针,用于治理动态分配的内存。每个 std::unique_ptr 实例都领有对其所蕴含对象的惟一所有权,并在其生命周期完结时主动开释对象。 创立unique_ptr对象咱们能够std::unique_ptr的构造函数或std::make_unique函数(C++14反对)来创立一个unique_ptr对象,在超出作用域时,会主动开释所治理的对象内存。示例代码如下: #include <memory>#include <iostream>class MyClass {public: MyClass() { std::cout << "MyClass constructed" << std::endl; } ~MyClass() { std::cout << "MyClass destroyed" << std::endl; }};int main() { std::unique_ptr<MyClass> ptr1(new MyClass); // C++14开始反对std::make_unique std::unique_ptr<int> ptr2 = std::make_unique<int>(10); return 0;}代码输入: ...

July 8, 2023 · 3 min · jiezi

关于c:CSDN-逃离计划-hyperscan-从安装到写出第一个测试demo

hyperscan 作为和intel深度绑定的政策匹配工具,提供了及其弱小的正则匹配性能,并提供接口,不便对匹配后果进行二次操作。 我在学习hyperscan时,在各大论坛并没有找到比拟齐备并且具体解说,很多只是提到了函数库大略时干什么的,索性将本人理解到的货色,具体的记录下来,也算是本人对hyperscan的一次坚固 hyperscan 是英特尔开源的一款正则匹配工具,与intel芯片深度绑定,intel对其有专门的优化,因而在整体的匹配效率上,相较于其余正则计划,处于一个较为当先的地位。 hyperscan反对pcre 正则规定,现有的次要利用计划大都是基于pcre做的匹配。 样例代码放在了github上,前面会贴出来。 注: 本文演示环境为 ubuntu 22.04 hyperscan下载安装下载intel在github上凋谢了源码,通过git即可下载,因为github,在国内拜访不敌对,在gitee上也有相干开源代码githubgit clone https://github.com/intel/hyperscan.gitgiteegit clone https://gitee.com/tzichengchew/hyperscan.git依赖hyperscan 应用cmake编译cmake等根底库装置不再赘述, 须要依赖boost Boost DownloadsragelRagel State Machine Compiler (colm.net)git clone https://github.com/adrian-thurston/ragel.git* colmgit clone https://github.com/adrian-thurston/colm.git其中ragel的装置须要clom反对在官网均能找到下载地址。或者git地址 装置装置boost bash bootstrap.sh && ./b2 && ./b2 install 装置ragel不倡议编译装置,坑很多 sudo apt install ragel编译装置hyperscan 创立一个目录 mkdir hyperscan_comp执行编译命令 cmake $HYPERSCAN_PATHmake && make install 须要蛮久的。遇到一个十分辣手的问题: undefined reference to `avx2_memset'undefined reference to `corei7_memset'…………https://github.com/intel/hyperscan/issues/292#issuecomment-762635447亲测无效。我在另外一台设施上测试发现并没有报错,预计可能是虚拟机cpu配置问题,后面提到了,hyperscan与intel深度绑定。 批改过cmake/build_wrapper.sh脚本后,就能够执行cmake命令了 为避免产生的cmake文件搞混了源文件,这里倡议建设一个新的目录,执行cmake 须要留神的是,在应用hyperscan时,动静库要比动态库应用绝对不便,批改CMakeCache.txt文件,依据版本不同,所在行数也不同。 BUILD_STATIC_AND_SHARED:BOOL=OFF->BUILD_STATIC_AND_SHARED:BOOL=ON批改过后即可执行make && make install hyperscan 简略应用hyperscan整体的应用流程次要分为编译器和运行期 ...

July 5, 2023 · 2 min · jiezi

关于c:csdn-逃离计划-如何优雅的在windows平台用命令行写代码

为什么要这么做之前为了写一些window环境下的小工具,想要写一个.exe 间接运行,与linux环境下相似,间接应用命令行的模式实现一些根本的计算之类的操作,当理论要做的时候,首先就发现了一个十分大的问题:依照c语言编译过程来说,首先须要编写一个.c 文件,而后通过这个.c 文件与编译链接等操作,最终生成一个可能在linux环境下运行的可执行文件。然而,在windows环境下,零碎并没有集成gcc之类的工具,之前写这类工具,都是应用vs套件主动生成,包含依赖环境等都是主动装置好的,一旦波及到文件挪动之类,环境变量常常会呈现各种各样的问题,解决起来很头疼。因而,本文次要就来搞一个小demo,看一下,windows一个小的命令行工具,想要尽可能疾速的搭建一个精简的环境大略须要怎么做。(偷懒方法,其实也是搭建了一个小型linux环境,技术细节须要深究,暂不做思考) 装置依赖在linux环境下,咱们编译一个.c 文件,使其生成一个可执行文件,须要手动执行一条命令, gcc demo.c -o test 当然,更加简单的零碎须要用到穿插编译等,这里暂不做探讨。然而windows中没有这种命令,因而就须要借助c/c++ 编译工具来实现,我这里选用的Cygwin, 从官网下载之后装置该工具,傻瓜式装置,这里不再赘述,须要留神的是,cygwin默认装置大多数的linux命令,比方’ls’,'rm’等。在抉择软件装置形式能够抉择在线装置,抉择装置源,之后会让你抉择须要装置的软件,在全副里抉择gcc-core g++。当cygwin装置胜利后,关上cygwin能够发现生成了一个相似于linux环境的命令行环境。如果在这里间接编写软件,并且应用gcc编译的话,确实能生成一个exe文件,并且在命令行中能够间接运行,然而,此时应用图形化界面关上生成的可执行文件,就会间接报错,cygwin1.dll 与 libstd++*.dll 文件缺失,这实际上并不是缺失,而是装置了cygwin曾经装置了然而windows无奈辨认,找到cygwin的装置目录,关上bin,你会发现所有的文件都有, 只不过没有被windows辨认到这时候,只须要增加一个环境变量 将cygwin/bin的目录增加到环境变量中的PATH中去,就能够解决报错的问题。测试

July 4, 2023 · 1 min · jiezi

关于c:c语言大作业基于多种加密技术与防护措施的c语言安全笔记本程序设计与实现

作业: 为了保障用户的本地笔记数据安全,决定构建了一个安全可靠的加密笔记本程序,利用了多种加密技术和防护措施,确保用户数据不受未经受权的用户拜访或获取。这个加密笔记本领有以下技术特点: 1.加密数据 为了保障用户笔记的安全性和保密性,咱们应用AES加密算法对数据进行加密。AES(Advanced Encryption Standard)是一种高级加密规范,已被广泛应用于商业和政府畛域。相比于其余算法,AES算法更加安全可靠,能够提供更高的数据安全性。在加密笔记本中,咱们采纳了AES-256-CBC算法,提供了更高的安全性。同时,咱们还应用随机生成的初始化向量(IV),进一步加强了加密算法的安全性。 2.明码哈希 为了防止用户明码被其他人获取或窃取,咱们应用bcrypt明码哈希算法对用户明码进行加密。bcrypt是一种比MD5和SHA-1等哈希算法更加平安和牢靠的明码哈希算法,可能提供更高的密码保护。在加密笔记本中,咱们将用户输出明码进行bcrypt哈希,而后再保留到数据库中。这样,即便黑客获取到了加密笔记本的数据库,也无奈轻易地取得明文明码。 3.防重放攻打 防重放攻打是一种黑客攻击形式,攻击者会利用已知的数据包重放来达到攻打的目标。在加密笔记本中,咱们引入了随机数和工夫戳,以避免重放攻打。每个申请都会生成一个惟一的随机数和工夫戳,并与其余申请进行辨别。这样,黑客就无奈在短时间内重放申请,从而保障了应用程序的安全性。 4.严格的输出验证 因为用户输出谬误或歹意攻打是导致应用程序呈现问题的次要起因之一,因而咱们在加密笔记本中退出了对用户名和明码的字符长度和复杂度等方面的严格验证,保障了输出的正确性和安全性。如果用户输出不符合规范,应用程序就会提醒用户从新输出。 #include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdbool.h>#include <openssl/evp.h>#include <openssl/rand.h>#include <openssl/bcrypt.h>#define MAX_NOTES 100void encrypt(const unsigned char *input, int input_len, unsigned char *key, unsigned char *iv, unsigned char *output) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); int output_len; EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); EVP_EncryptUpdate(ctx, output, &output_len, input, input_len); EVP_EncryptFinal_ex(ctx, output + output_len, &output_len); EVP_CIPHER_CTX_free(ctx);}void decrypt(const unsigned char *input, int input_len, unsigned char *key, unsigned char *iv, unsigned char *output) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); int output_len; EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); EVP_DecryptUpdate(ctx, output, &output_len, input, input_len); EVP_DecryptFinal_ex(ctx, output + output_len, &output_len); EVP_CIPHER_CTX_free(ctx);}// 验证明码复杂度(至多蕴含 8 个字符,其中必须包含数字、大写字母、小写字母和特殊字符)bool check_password(const char* password) { int password_len = strlen(password); if (password_len < 8) { return false; } bool has_digit = false; bool has_upper = false; bool has_lower = false; bool has_special = false; for (int i = 0; i < password_len; i++) { if (isdigit(password[i])) { has_digit = true; } else if (isupper(password[i])) { has_upper = true; } else if (islower(password[i])) { has_lower = true; } else { has_special = true; } } if (!has_digit || !has_upper || !has_lower || !has_special) { return false; } return true;}// 验证用户名和明码是否匹配bool check_user(const char* username, const char* password) { // 增加自定义的用户名和明码认证逻辑代码 return true;}// 加载并解密笔记数据bool load_notes(const char* filename, unsigned char* key, unsigned char* iv, unsigned char* notes_buf, int notes_buf_len) { FILE* fp = fopen(filename, "rb"); if (fp == NULL) { printf("Note file not found or unable to open.\n"); return false; } fseek(fp, 0, SEEK_END); int file_size = ftell(fp); rewind(fp); if (file_size > notes_buf_len) { printf("Notes buffer size is too small. %d bytes are required.\n", file_size); fclose(fp); return false; } unsigned char* encrypted_notes = malloc(file_size); fread(encrypted_notes, 1, file_size, fp); fclose(fp); decrypt(encrypted_notes, file_size, key, iv, notes_buf); free(encrypted_notes); return true;}// 保留并加密新笔记数据bool save_notes(const char* filename, unsigned char* key, unsigned char* iv, unsigned char* notes_buf, int notes_len) { unsigned char* encrypted_notes = malloc(notes_len); encrypt(notes_buf, notes_len, key, iv, encrypted_notes); FILE* fp = fopen(filename, "wb"); if (fp == NULL) { printf("Unable to open file for writing.\n"); free(encrypted_notes); return false; } if (fwrite(encrypted_notes, 1, notes_len, fp) != notes_len) { printf("Unable to write notes data to file.\n"); fclose(fp); free(encrypted_notes); return false; } fclose(fp); free(encrypted_notes); return true;}int main() { char username[80]; char password[80]; // 输出验证 while(true) { printf("Please enter your username: "); scanf("%s", username); printf("Please enter a strong password (at least 8 characters with numbers, uppercase/lowercase letters, and special characters): "); scanf("%s", password); if (!check_password(password)) { printf("Password does not meet complexity requirements. Please try again.\n"); continue; } if (!check_user(username, password)) { printf("Invalid username or password. Please try again.\n"); continue; } break; } // 随机数和工夫戳,用于防重放攻打 int timestamp = time(NULL); RAND_add(&timestamp, sizeof(timestamp)); unsigned char nonce[8]; RAND_bytes(nonce, sizeof(nonce)); // 加载并解密笔记数据 unsigned char iv[EVP_MAX_IV_LENGTH]; memset(iv, 0x00, EVP_MAX_IV_LENGTH); unsigned char* notes_buf = malloc(MAX_NOTES); unsigned char salt[BCRYPT_SALTSPACE]; RAND_bytes(salt, sizeof(salt)); unsigned char* hash = malloc(BCRYPT_HASHSPACE); bcrypt(password, strlen(password), salt, hash, BCRYPT_HASHSPACE); if (!load_notes("notes.enc", hash, iv, notes_buf, MAX_NOTES)) { free(notes_buf); return 1; } // 显示笔记 printf("%s\n", notes_buf); // 输出新笔记 printf("Enter new notes:\n"); char new_notes[MAX_NOTES]; fgets(new_notes, MAX_NOTES, stdin); // 保留并加密新笔记数据 int new_notes_len = strlen(new_notes); if (!save_notes("notes.enc", hash, iv, (unsigned char*)new_notes, new_notes_len)) { free(notes_buf); return 1; } free(notes_buf); free(hash); return 0;}

June 19, 2023 · 3 min · jiezi

关于c:Linux网络开发必学教程31文件服务器最终实现

问题: # favicon.ico 简介 是什么?申请如何解决?favicon.ico 简介favicon.ico 是网页上的图标文件 名称:默认名称位 favicon.ico尺寸:16x16、32x32、48x48、64x64、128x128色彩: 8 位、24 位 或 32 位 个别说,在网页中能够通过 HTML 语句设置图标文件 <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">如果不设置,浏览器会在网站根目录中申请图标文件 即:被动发送 HTTP 申请 /favicon.ico浏览器与文件服务器的交互文件浏览 点击列表中的文件夹 进入子文件夹(展现子文件夹列表)文件下载 点击列表中的文件 下载指标文件错误处理 向服务器发送谬误申请 浏览不存在的文件夹 / 下载不存在的文件# 文件下载 char buf[1024] = {0};int max = lseek(fd, 0, SEEK_END);sprintf(head, HTTP_FORMAT, max);len = TcpClient_SendRow(client, head, strlen(head));lseek(fd, 0, SEEK_SET);while ((len > 0) && (len = read(fd, buf, sizeof(buf))) > 0) { ret + TcpClient_SendRow(client, buf, len);}ret = (ret == max);错误处理static const char* ERR_FORMAT = "<!DOCTYPE html>" "<html>" " <head>" " <meta charset=\"utf-8\">" " <title>D.T.Software</title>" " </head>" " <body>" " <h1>DT4SW Http File Server</h1>" " <hr/>" " <h2>OOPS!!!</h2>" " <h3>Path: \'%s\' is NOT available...</h3>" " </body>" "</html>";char *ToErrString(const char *path){ char *ret = NULL; if (path && (ret = malloc(strlen(ERR_FORMAT) + strlen(path) + 1))) { sprintf(ret, ERR_FORMAT, path); } return ret;}响应模块入口函数int RequestHandler(TcpClient* client, const char* req, const char* root){ int ret = 0; if (client && req && root) { char *ap = GetAbsPath(req, root); if (ap && (access(ap, F_OK) == 0)) { if (GetEntryCount(ap) < 0) { ret = FileReqHandler(client, req, root); } else { ret = DirReqHandler(client, req, root); } } else { ret = BadReqHandler(client, req, root); } free(ap); } return ret;}编程试验:最终实现源码下载地址 ...

June 10, 2023 · 1 min · jiezi

关于c:Linux网络开发必学教程30文件浏览交互涉及

问题:浏览器与文件服务器如何交互?浏览器与文件服务器的交互文件浏览 点击列表中的文件夹 → 进入子文件夹(展现子文件夹列表)文件下载 点击列表中的文件 → 下载指标文件错误处理 向服务器发送谬误申请 → 浏览不存在的文件夹 / 下载不存在的文件文件浏览交互实现 实现中的要害概念共享文件夹(root) 服务端启动时指定的共享文件夹门路(服务零碎中的门路)申请门路(req) 文件 / 文件夹 在服务端上绝对共享文件夹的门路绝对路径 AbsPath = root + "/" + req根底函数定义char *GetAbsPath(const char *relative, const char *root) 拼接 root 和 relative, 返回绝对路径,返回值需开释 (free)int isDotPath(const char *path) 判断 path 是否为非凡门路 "." 或 ".."int GetEntryCount(const char *path) 返回 path 门路下的 (文件 + 文件夹)总数编程试验:根底函数实现static char *GetAbsPath(const char *relative, const char *root){ int reLen = strlen(relative); int rootLen = strlen(root); char *ret = malloc(reLen + rootLen + 2); if (ret) { strcpy(ret, root); if ((relative[0] == '/') && (ret[rootLen-1] == '/')) ret[rootLen - 1] = 0; if ((relative[0] != '/') && (ret[rootLen-1] != '/')) strcat(ret, "/"); strcat(ret, relative); } return ret;}static int IsDotPath(const char *path){ int ret = -1; if (path) { ret = (strcmp(path, ".") == 0) || (strcmp(path, "..") == 0); } return ret;}static int GetEntryCount(const char *path){ int ret = -1; DIR *dirp = opendir(path); if (dirp != NULL) { struct dirent *dp = NULL; ret = 0; while ((dp = readdir(dirp)) != NULL) { if (!IsDotPath(dp->d_name)) { ret ++; } } } closedir(dirp); return ret;}要害数据结构定义enum { TypeAll = 0x00, TypeDir = 0x04, TypeFile = 0x08};typdef struct { const int length; RowInfo data[];}FileEntry;typedef struct { char link[2048]; char name[255]; char type[32]; char size[32]; char time[32];}RowInfo;要害函数定义int MakeEntryItem(RowInfo *item, struct dirent *dp, const char *ap, const char *req); ...

June 8, 2023 · 5 min · jiezi

关于c:Linux网络开发必学教程29动态创建交互页面

问题:如何创立浏览器中的文件展现页面?再论前端页面 交互页面剖析动态局部 表格,页面构造动静局部 文件浏览门路 (Path)文件列表(Table)"<!DOCTYPE html>""<html>"" <head>"" <meta charset=\"utf-8\">"" <title>D.T.Software</title>"" <head>"" <body>"" <h1>DT4SW Http File Server</h1>"" <hr/>"" <h3>Path: %s</h3>"" %s"" <body>""<html>"文件列表剖析动态 表格题目行表格列表构造动静局部 表格行数据"<table border=\"1\" width=\"100%\">";" <tr>"" <th>File Name</th><th>File Type</th><th>File Size</th><th>Modify Time</th>"" </tr>"" <td><a href=\"%s\">%s</a></td><td>%s</td><td>%s</td><td>%s</td>"" </tr>""</table>"文件列表接口函数及数据结构Table *CreateTable();Table *InsertRow(Table *table, RowInfo *info);char *ToTableString(Table *table);void FreeTable(Table *table);typedef void Table;typedef struct { char link[2048]; char name[255]; char type[32]; char size[32]; char time[32];}RowInfo;解决方案CreateTable() 从堆空间创建表格题目行,并作为初始表格数据返回InsertRow(table, info) 将 info 中的字符串进行表格行格式化,并增加到 table 开端ToTableString(table) 将 table 表格数据格式化为 HTML 字符串,并返回表格动态创建要害代码if (table && info) { char *t = table; int len = strlen(t); char *buf = malloc(strlen(ROW_FORMAT) + sizeof(*info)); if (buf) { sprintf(buf, ROW_FORMAT, info->link, info->name, info->type, info->size, info->time); ret = realloc(t, len + strlen(buf) + 1); ret = ret ? (strcpy(ret + len, buf), ret) : t; } else { ret = t; } free(buf);}页面动态创建代码char *ToPageString(const char *path, char *ts){ char *ret = NULL; if (path && ts && (ret = malloc(strlen(PAGE_FORMAT) + strlen(path) + strlen(ts) + 1))) { sprintf(ret, PAGE_FORMAT, path, ts); } return ret;}编程试验:动态创建交互页面main.c ...

June 5, 2023 · 3 min · jiezi

关于c:Linux网络开发必学教程28浏览器请求响应实现

文件服务器架构 职责定义主线程 命令行输出,管理员可管制服务端工作状态监听线程 服务端启动后对客户端连贯进行监听的工作环境通信线程 客户端连贯胜利后与服务端的数据交互环境主线程int main(int argc, char *argv){ if (argc >=2) { DIR *dir = opendir(argv[1]); if (dir != NULL) { close(dir); Run(argv[1]); } } printf("Can not launch File Server, need a valid director as root.\n"); return 0;}if (*line) { char *cmd = FormatByChar(line, ' '); // 此处的作用次要为去除前后空格 int done = 0; for (i=0; i<DIM(g_handler); ++i) { if (strcmp(g_handler[i].cmd, cmd) == 0) { g_handler[i].handler(cmd); done = 1; break; } } if (!done) { printf("\'%s\' can NOT be parser!\n", cmd); } free(cmd);}交互流程一 ...

June 4, 2023 · 4 min · jiezi

关于c:Linux网络开发必学教程27文件服务器可行性分析

需要剖析服务端启动时指定某本地文件夹作为网络共享文件夹共享文件夹中的所有文件及子文件夹均可被客户端拜访应用浏览器作为客户端,在浏览器中可“看到”所有共享文件用户可在浏览器中将指标文件下载到本地服务端可暂定服务,但不影响正在下载指标文件的客户端技术可行性剖析服务端网络通讯 基于 Http 协定与客户端浏览器通信可应用 tcp socket 实现对 Http 协定的反对服务端文件操作 获取文件夹中的所有文件及子文件夹返回文件列表 或 返回文件内容零碎概要设计 基础设施Socket - Http 协定以 TCP 协定为底座Thread - 同时对多个客户端服务(申请响应)File - 文件夹信息读取,文件属性读取,文件数据读取基础设施一 (Socket)思考:是否应用课程中的网络通讯框架作为基础设施?答:临时不能 课程中通信框架的服务端基于 select 机制,即: 服务端工作时会导致程序阻塞 (程序无奈持续向下执行)每次只服务一个客户端(当服务工夫过长,会影响其它服务端通信)文件服务器工作模型 网络通讯框架改良计划新增服务端函数 TcpClient *TcpServer_Accept(TcpServer *server); 阻塞函数,调用后始终期待客户端连贯返回与客户端通信的 TcpClient 构造体返回的 TcpClient 构造体互相独立,并且独立于服务端TcpClient *TcpServer_Accept(TcpServer *server){ TcpClient *ret = NULL; if (server) { Server *s = (Server*)server; struct sockaddr_in caddr = {0}; socklen_t asize =sizeof(caddr); int fd = accept(s->fd, (struct sockaddr*)&caddr, &asize); if (fd > -1) { ret = TcpClient_From(fd); if (!ret) close(fd); } } return ret;}基础设施二 (Thread)过程:应用程序的一次加载执行(零碎进行资源分配的根本单位)线程:过程中的程序执行流 ...

June 4, 2023 · 5 min · jiezi

关于c:Linux网络开发必学教程26Web-前端开发基础

对于 Http 应用程序基于 Http 的应用程序实质上是一种非凡的 C/S 模型利用Http 应用程序的底座是 TCP 协定(即:socket 可实现利用)常见的 Http 客户端为浏览器,服务端为 Web 服务器简略了解:Http 应用程序 ←→ TCP 应用程序 + 文本处理应用程序值得思考的问题浏览器为什么能成为 “万能客户端” ?什么是 HTML ?HTML 指的是超文本标记语言:Hyter Text Markup LanguageHTML 不是一种编程语言,而是一种标记语言标记语言是一套标记标签(markuo tag) HTML 应用标记标签来形容网页HTML 文档蕴含了 HTML 标签及文本标记能容HTML 文档也叫做 Web 页面HTML 文本示例 HTML 中的标签HTML 标签通常被称为 HTML TagHTML 标签是由尖括号突围的管检测,比方:<html>HTML 标签通常是成对呈现的,比方 <b> 和 </b>标签对中的第一个标签为开始标签,第二个标签是完结标签开始标签和完结标签也被称为凋谢标签和闭合标签HTML 中的“分区” 问题一:如何管制 HTML 页面中的字符大小HTML 题目是通过 <h1> - <h6> 标签来定义的(字符由大到小)HTML 段落是通过标签 <p> 来定义的 (段落主动换行)HTML 应用标签 <b> 与 <i> 对输入的文本进行格局HTML 应用 <hr> 标签在 HTML 页面中创立水平线<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>DT4SW</title></head><body> <h1>字符管制示例</h1> <p><b>hr 标签定义水平线</b></p> <hr/> <h2>这是段落1。</h2> <hr/> <h4>这是段落2。</h4> <hr/> <h6>这是段落3,<br/>换行<br>再换一行</p></h6></body></html> ...

June 4, 2023 · 2 min · jiezi

关于c:Linux网络开发必学教程25Http-协议基础

Http 是什么?Http 作为 WWW 的重要组成部分被提出(World Wide web)而过后的 WWW 是为了帮忙研究者更好的进行常识共享根本理念:文档之间的互相关联造成超文本,最终连城互相参阅的 WWWHttp (Hyper Text Transfer Protocol)用于定义文档之间如何跳转,即:文档传输协定 Http 是什么?Http 是位于 TCP/IP 之上的应用层通信协定 Http 世界中的“重要角色”域名解析服务器 (DNS - Domain Name System)DNS 是位于 TCP/IP 之上的应用层协定DNS 提供域名到 IP 地址之间的解析服务主机 IP 地址查找过程 域名 VS 网络地址每一台网络中的主机都有一个地址(惟一标识,即:IP 地址)为了不便记忆,可通过域名拜访网络上的指标主机域名是字母与数字的组合,如:dt4sw.comDNS 可通过域名查找 IP 地址,也可逆向从 IP 地址反查域名初见 URI 和 URLURI : 对立资源标识符(Uniform Resource Identifier) 由某种规定定义的资源标识符通过字符串标识资源(即:URI 的表现形式为字符串)例: dev:app:py4oh:fileshttp://dt4sw.com/index.htmlmailto:support@dt4sw.comtelnet://193.168.3.11tel:+86-159xxx0426URL 不限于标识互联网资源,它能够用于定义所有资源的标识符深刻了解 URI 与 URLURL 是一种非凡的 URI, 它是 URI 的一种利用模式URI 不肯定是 URL,而 URL 肯定是 URIURL 是相对的,提供拜访到确切资源的具体门路(全网惟一)URL 既有相对性又有绝对性 ...

June 3, 2023 · 1 min · jiezi

关于c:Linux网络开发必学教程24物联网服务模块实现非必要

笔者无对应硬件开发板,对波及硬件操作局部的代码不做全副开展。(不影响主线了解) 问题:服务模块如何获取实在环境信息?外设简介 设施驱动开发根底GPIO GPIO 即: General-Purpose Input/Output, 通用型输入输出在硬件设施上,GPIO 通常体现为引脚 (Pin), 输入为高电平或低电平程序设计中,可通过代码管制指定 GPIO 的输入,或接管内部输出GPIO 是硬件与外部设备的接口,罕用于对外部设备进行管制GPIO 信号输入 当 GPIO 作为输入应用时,输入的电信号为高电平(1)或着低电平(0),因而,只有在电路上稍加设计就能够接入外设(如:LED,电动机,等),并通过程序控制外设的状态GPIO 利用形式根底性能 仅应用 GPIO 引脚性能对外输入管制信息(0 或 1)复用性能 通过 GPIO 根底性能实现的通信形式(如:I2C)GPIO 根底性能示例#include "iot_gpio.h"#include "hi_io.h"IoTGpioInit(14);IoTGpioSetFunc(14, HI_IO_FUNC_GPIO_14_GPIO);IoTGpioSetDir(14, HI_GPIO_DIR_OUT);int Service_SetLight(int on){ return (IotGpioSetOutputVal(14, on) == 0);}什么是 I2C?I2C 即:inter - integrated Circuit, 一种两线式串行总线I2C 罕用于链接微信控制器及其外围设备(如:用于连贯传感器)I2C 可通过两个 GPIO 引脚实现物理连贯及数据通讯 如:GPIO0 对应 I2C_SDA : I2C 数据传输线GPIO1 对应 I2C_SCL : I2C 时钟信号线深入浅出 I2C 通信I2C 总线上能够挂载一个设施和多个从设施主设施负责调度总线,决定某个工夫和其中一个从设施通信每个从设施在 I2C 总线上都有惟一的地址主设施寻址时,8 位数据中高 7 位是从设施地址,最低位示意读写每次数据通讯,主从设施之间都会发送起始和完结信号大道至简:对于 I2C 利用的了解I2C 的实质是两根对于数据传输的连线(微控制器 <--> 传感器)I2C 编程是非凡的 GPIO 编程 (GPIO 配置后进行数据读写)开发板上的 I2C 与集成外设固定连贯(如:温湿度传感器)I2C 利用的终极体现只是简略的数据读写I2C 利用示例uint8_t send_data[1] = {0};send_data[0] = 0x01;IoTI2CWrite(1, (BH175_Addr<<1)|0x00, send_data, 1);usleep(100000);send_data[0] = 0x10;IoTI2CWrite(1, (BH175_Addr<<1)|0x00, send_data, 1);uin8_t recv_data[6] = {0};IoTI2CRead(1, (BH175_Addr<<1)|0x01, recv_data, 2);将来可做的扩大提供更多不同类型的服务(如,图像采集与辨认服务)提供设施被动服务模式及服务联动模式开发手机客户端,通过手机终端应用服务。。。

June 3, 2023 · 1 min · jiezi

关于c:Linux网络开发必学教程23客户端服务端交互实现

问题:客户端业务逻辑如何实现?与服务设施交互细节如何设计?客户端业务逻辑实现用户输出解决 字符串空格解决,宰割获取命令与参数服务信息处理 字符串预处理,宰割获取服务命令存储服务命令与设施地址之间的映射(命令字符串 地址字符串)客户端业务逻辑实现 - 用户输出解决typedef struct { const char *cmd; char *(*handler)(const char *);}Handler;static Handler g_handler[] = { {"query", Query_Handler}, {"touch", Touch_Handler}};if (*line) { char **arg = Malloc2d(char, 2, BUF_SIZE); int i = 0; int r = DivideBy(line, ' ', arg, 2, BUF_SIZE); if (i=0; i<DIM(g_handler); ++i) { if (strcmp(g_handler[i].cmd, arg[0]) == 0) { g_handler[i].handler(arg[1]); break; } } Free2d(arg);}服务端逻辑实现查问音讯解决 接管播送,并回复 UDP 音讯服务命令解决 接管 TCP 连贯,通过 申请 - 响应 的模式进行服务UDP 响应模式设计static void Query_Handler(UdpPoint *udp, Message *msg, const char *remote, int port){ Message *resp = Message_New(TYPE_RESPONSE, 0, 0, 0, NULL, DESC_SIZE + ADDR_SIZE + USAGE_SIZE); if (resp) { char ip[16] = {0}; get_localip("ens33", ip); strncpy(resp->payload, Service_GetDesc(), DESC_SIZE); strncpy(resp->payload + DESC_SIZE, ip, ADDR_SIZE); strncpy(resp->payload + DESC_SIZE + ADDR_SIZE, Service_GetUsage(), USAGE_SIZE); UdpPoint_SendMsg(udp, resp, remote, port); } free(resp);}服务模块设计#ifndef LOCAL_SERVICE_H#define LOCAL_SERVICE_Htypedef struct { float illumination; float humidity; float temperature; int light;}SvrData;void Service_Init(void);const char *Service_GetDesc(void);const char *Service_GetUsage(void);SvrData Service_GetData(void);int Service_SetLight(int on);#endif // LOCAL_SERVICE_HTCP 响应模块设计Message *msg = TcpClient_RecvMsg(client);if (msg && (msg->type == TYPE_TOUCH)) { static char buf[128] = {0}; SvrData ed = Service_GetData(); free(msg); // provided by service module msg = Message_New(TYPE_RESPONSE, 0, 0, 0, buf, strlen(buf) + 1); // format data according to message payload string}else { const char *message = "Invalid touch request"; free(msg); msg = Message_New(TYPE_RESONSE, 0, 0, 0, message, strlen(message) + 1);}TcpClient_SendMsg(client, msg);free(msg);客户端响应接管msg = TcpClient_RecvMsg(client);if (msg && (msg->type == TYPE_RESPONSE)) { printf("%s\n", msg->payload);} else { printf("Invalid status from service ...\n");}free(msg);编程试验: 客户端服务端交互实现课后思考服务模块如何获取真是环境信息 ...

June 3, 2023 · 2 min · jiezi

关于c:Linux网络开发必学教程22客户端设计与初步实现

问题:Response Task 和 Server Task 的业务逻辑如何实现?客户端如何实现?再论交互流程 交互具体设计(Message::payload) 客户端被动发动服务查问(局域播送)服务设施将本身能提供的服务信息及用法返回(字符串形容)客户端依据收到的服务用法向设施发动申请(字符串形容)服务设施接管命令并匹配服务,并返回服务后果(字符串形容)交互示例 关键问题"Touch Lig_On" 怎么晓得给哪一个服务设施发送命令?地址管理器模块每个服务设施在回复服务查问信息时,会附带服务地址;因而,记录服务命令与设施地址之间存在映射关系。 #ifndef ADDR_MSG_H#define ADDR_MSG_Hint AddrMgr_Add(const char *cmd, const char *addr);char *AddMgr_Find(const char *cmd);void AddMgr_Remove(const char *cmd);void AddMgr_Clear();#endif // ADDR_MSG_H根底功能模块#define Malloc2d(type, row, col) ({});void Free2d(void *p);char *FormatByChar(const char *src, char c);int DivideByChar(const char *line, char c, char **argv, int row, int col);编程试验:客户端初步实现main.c #include <stdio.h>#include <string.h>#include "utility.h"#include "addr_mgr.h"int main(){ const char *src = " abc de "; char ** argv = Malloc2d(char, 3, 5); int r = DivideByChar(src, ' ', argv, 3, 5); int i = 0; for (i=0; i<r; i++) { printf("argv[%d] = *%s*\n", i, argv[i]); } Freee2d(argv); AddrMgr_Add("delpin", "1.1.1.1"); AddrMgr_Add("tang", "2.2.2.2"); AddrMgr_Add("D.T.Software", "3.3.3.3"); printf("delpin = *%s*\n", AddrMgr_Find("delpin")); printf("tang = *%s*\n", AddrMgr_Find("tang")); printf("D.T.Software = *%s*\n", AddrMgr_Find("D.T.Software")); AddMgr_Clear(); return 0;}输入: ...

May 30, 2023 · 3 min · jiezi

关于c:Linux网络开发必学教程21智能家居服务发现实现

服务设施软件 代码复用将网络通讯框架移植到开发板,之后,可应用框架中的组件实现 Response Task 和 Server Task 框架移植注意事项Lwip 是微型 TCP/IP 协定栈 (并非残缺 TCP/IP协定栈)反对 socket 接口,但一些性能未实现socket 接口所在头文件不同于 Linux 和 Window 平台实现可执行测试,对测试中呈现的问题及时修复可执行性测试通过网络调试助手验证 TCP 数据首发 设施运行服务端,客户端连贯,数据收发性能通过网络调试助手验证 UDP 数据收发 设施端接管播送,并复原数据Server Task 要害实现static void EventListener(TcpClient* client, int evt){ if( evt == EVT_CONN ) { printf("%s : CONN - %p...\n", __FUNCTION__, client); } else if( evt == EVT_DATA ) { Message* msg = NULL; printf("%s : DATA - %p...\n", __FUNCTION__, client); msg = TcpClient_RecvMsg(client); if( msg ) { printf("%s : msg = %p\n", __FUNCTION__, msg); printf("%s : msg->type = %d\n", __FUNCTION__, msg->type); printf("%s : msg->cmd = %d\n", __FUNCTION__, msg->cmd); } free(msg); } else if( evt == EVT_CLOSE ) { printf("%s : CLOSE - %p...\n", __FUNCTION__, client); }}void* Service_Task(const char* arg){ TcpServer* server = TcpServer_New(); printf("%s : enter service task...\n", __FUNCTION__); if( server ) { printf("%s : server = %p\n", __FUNCTION__, server); TcpServer_SetListener(server, EventListener); TcpServer_Start(server, 8888, 10); TcpServer_DoWork(server); } return arg;}Respone Task 要害实现void* Response_Task(const char* arg){ UdpPoint* udp = UdpPoint_New(9999); printf("%s : enter response task...\n", __FUNCTION__); if( udp ) { char remote[16] = {0}; int port = 0; printf("%s : udp = %p\n", __FUNCTION__, udp); while( 1 ) { Message* msg = UdpPoint_RecvMsg(udp, remote, &port); if( msg ) { printf("%s : msg = %p\n", __FUNCTION__, msg); printf("%s : remote = %s\n", __FUNCTION__, remote); printf("%s : port = %d\n", __FUNCTION__, port); printf("%s : msg->type = %d\n", __FUNCTION__, msg->type); printf("%s : msg->cmd = %d\n", __FUNCTION__, msg->cmd); } else { printf("%s : msg is NULL...\n", __FUNCTION__); } free(msg); } } return arg;}编程试验,网络通讯框架移植main.c ...

May 30, 2023 · 3 min · jiezi

关于c:Linux网络开发必学教程20物联网设备-WIFI-模块实现非必要

问题:如何在设施上进行 WIFI 编程?Lwip (Light weight IP) 简介Lwip 是轻量化的TCP/IP,是一个小型开源的 TCP/IP 协定栈LwIP 的设计指标是用较少的资源实现实现的 TCP/IP 协定栈Lwip 能在操作系统中运行,也能在无操作系统的状况下独立运行Lwip 提供了 Socket API 和 Netconn APILwip 的次要个性反对 ARP, ICMP, IGMP反对 UDP, TCP, IP (可执行网络通讯框架)反对 DNS, PPP, SNMP直至 DHCP, 可动态分配 IP (WIFI 反对)。。。BearPi-Nano 联网能力BearPi-Nano 基于 Hi3861 芯片构建,而 Hi3861 本身具备 Wifi 能力 AP 模式:工作于 WIFI 热点模式,可被其它设施以 WIFI 形式连贯STA 模式:工作于 WIFI 连贯模式,可连贯到指定 WIFI 热点WIFI 模块接口设计init Wifi_Init(void);init Wifi_Connect(const char *id, const char *pwd);int Wifi_Start(void);int Wifi_IsOk(void);void Wifi_Stop(void);char *Wifi_IpAddr(void);波及的 OH 零碎接口WifiErrorCode RegisterWifiEvent(WifiEvent *event);WifiErrorCode EnableWifi(void);WifiErrorCode AddDeviceConfig(const WifiDeviceConfig *config, int result);WifiErrorCode ConnetTo(int networkid); // 通过网络标识连贯到指标热点struct netif *netifapi_netif_find(const char *name); // 获取 netif 构造体用于后续 dhcp 操作err_t dhcp_start(struct netif *netif); // 启动 hdcp, 获取 ipWIFI 热点连贯流程 ...

May 28, 2023 · 3 min · jiezi

关于c:Linux网络开发必学教程19分布式智能家居项目雏形

需要剖析客户端进入室内后可能被动发现服务(如,环境服务,灯光服务)各种服务能够有不同的载体(如:由不同设施提供)各种服务之间,服务与客户端之间齐全无耦合(服务可自在增减)客户端能够是挪动 APP ,能够是桌面软件 。。。技术可行性剖析发现服务: UDP 播送 + 自定义协定 客户端被动播送服务查问协定包服务设施接管到协定包,回复协定内容及协定应用形式服务自在增减:自定义协定 服务设施遵循雷同的协定:数据传输协定,服务提供模式,等客户端通过协定应用服务,无需关怀服务设施的状态及地位基础设施提供服务的设施(嵌入式,服务器,PC,等)必须具备联网能力,且遵循服务协定。 零碎概要设计 交互流程 零碎具体设计服务发现 客户端被动播送服务发现数据包 服务应答 服务设施将本机 IP 及可能提供的服务信息发回客户端 客户端具体设计通过播送服务发现数据包,收集以后环境的服务信息可用服务通过“菜单”形式出现,并提醒服务应用形式抉择服务后,连贯到对应设施,通过协定音讯发送服务申请命令收到设施回复后,断开设施连贯(申请 - 应答)服务端具体设计联网模块 设施提供服务前,必须联网(无线 or 有线)服务前端 接管服务发现数据包,并应答以后设施服务信息服务后端 服务设施驱动,如:温湿度传感器驱动,灯光管制驱动,等硬件选型这里展现应用的小熊派,运行 openharmony 。能够不用关怀硬件,间接在 PC 编写雷同网络性能可执行文件即可 服务设施软件架构设计 服务设施启动流程 几个问题设施上运行什么操作系统?设施运行的操作系统如何创立多个并行执行工作?设施如何通过 wifi 联网?设施上的网络编程通过什么接口实现?OpenHarmony 是什么凋谢原子基金会 (OpenAtom Foundation)旗下开源我的项目 定位是一个面项全场景的开源分布式操作系统我的项目蕴含了分布式操作系统所需的全副能力 包含内核层、零碎服务层、利用框架层华为及泛滥贡献者,在开源社区内间接奉献轻量零碎:硬件资源极其无限,反对的设施最小内存为 128KiB。可撑持的产品如智能家居畛域的连贯类模组、传感器设施、穿戴类设施等。小型零碎:面项利用处理器的设施,反对的设施最小内存为 1MBiB。可撑持的产品如智能家居畛域的 IP Camera、电子猫眼、路由器以及智慧出行的行车记录仪等。规范零碎:面项利用处理器例如 Arm Cortex-A 的设施,反对的设施最小内存为 128MiB, 能够提供加强的交互能力、3D GPU 以及硬件合成能力、更多控件以及动效更丰盛的图形能力、残缺的利用框架。可撑持的产品如高端的冰箱显示屏。OpenHarmony 轻量级零碎利用开发精要零碎组件裁剪与配置 .//vendor/hisilicon/hispark_pegasus/config.json板载性能裁剪与配置 .//device/hisilicon/hispark_pegasus/sdk_liteis/build/config/usr_config.mk在 application/sample/wifi-iot/app 目录下新建工程目录 (如:demo_server)工程目录下新建 BUILD.gn 文件批改 application 目录下的 BUILD.gn 文件画图 注册入口函数创立工作#include <stdio.h>#include <unistd.h>#include "ohos_init.h"#include "cmsis_os2.h"static void* Init_Task(const char* arg){ printf("[dt4sw] Hello, D.T.Software!\n"); return arg;}static void Main_Entry(void){ osThreadAttr_t attr = {0}; attr.name = "Init Task"; attr.stack_size = 1024 * 4; attr.priority = 20; if( osThreadNew((osThreadFunc_t)Init_Task, NULL, &attr) == NULL ) { printf("[dt4sw] Failed to create task!\n"); }}SYS_RUN(Main_Entry);

May 23, 2023 · 1 min · jiezi

关于c:基于C语言设计一个叫号系统

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档] 这道题的重点在于怎么解决患者的医治过程。大二上学期的理论课上,咱们在第一节的研究课上对于这道题的实现进行了探讨。本题的患者排队与数据结构中的队列构造完全符合,当患者挂号后,查看该科室是否还有闲暇的医生,若有,则间接进入进行医治。若没有,则进入该科室的诊疗队列排队。每个科室的排队状况通过查问各个队列的长度即可得悉。当工夫刷新,有病人的医生诊疗工夫发生变化,当变为 0 时,调配该病人 B 超工夫。对于须要 B 超的患者,进入 B 超等待队列,顺次进入各个 B 超室。 综合利用设计说明某医院一般门诊领有科室 15 个,每个科室值班医生 3 人;B 超室 1 个,有 3 台 B 超机。现需设计一个叫号零碎,通过挂号秩序,安顿每个科室叫号程序,各科室在诊断完病人后,依据其是否开具 B 超查看要求,安顿 B 超室的叫号程序,并在所有过程中,记录每位医生所看的病人及其听诊工夫。 需实现挂号过程,医生就诊过程。显示各科室、B超室目前排队状况。查看各医生就诊的所有病人。软件性能病人抉择科室进行挂号,主动调配患者编号显示各个科室与 B 超室以后仍在排队等待的人数显示每个医生(B 超室)以后病人还残余的诊疗(查看)工夫显示当天曾经过分钟数依据医生编号查问当天医生诊疗病人的记录依据工夫刷新,主动调配患者到对应的医生(B 超室)进行医治(查看)将所有就诊记录存于文件,运行完程序可进行保留(对应于医院一天的营业完结)查问就诊记录是通过数据文件读取比对实现的,当医生医治一个病人的同时,对数据文件进行写入。病人挂号零碎与主动调配诊室是通过建设病人的队列实现的,当工夫刷新,每个诊室的状况都会从新更新,从而实现挂号、就诊、查看的过程。而各科室、B 超室的排队状况只需查看病人的排队队列长度即可取得。设计思维这道题的重点在于怎么解决患者的医治过程。大二上学期的理论课上,咱们在第一节的研究课上对于这道题的实现进行了探讨。本题的患者排队与数据结构中的队列构造完全符合,当患者挂号后,查看该科室是否还有闲暇的医生,若有,则间接进入进行医治。若没有,则进入该科室的诊疗队列排队。每个科室的排队状况通过查问各个队列的长度即可得悉。当工夫刷新,有病人的医生诊疗工夫发生变化,当变为 0 时,调配该病人 B 超工夫。对于须要 B 超的患者,进入 B 超等待队列,顺次进入各个 B 超室。同样的,工夫刷新后 B 超室内患者查看工夫变为 0 时,患者的医治过程完结。B 超室会从 B 超的等待队列中寻找新的须要查看的患者。从事实中的排队联想到队列构造也是很失常的想法。 队列的元素为患者节点,节点自身有患者的各项信息,追随患者类进行传输。每个科室(包含 B 超室)均有一条患者队列,代表期待队列。患者医治状况的变动会引起患者节点的入队列与出队列。 而医生节点与 B 超室节点均需记录患者编号与剩余时间,而这两个节点的存储采纳程序表的构造(即数组)。 ui 界面中须要展现的局部,通过读取队列与医生数组、B 超室数组的数据即可解决。查问局部须要按行查问文件中的信息。

May 18, 2023 · 1 min · jiezi

关于c:C编译器和链接器的完全指南

C++是一种强类型语言,它的编译和链接是程序开发过程中不可或缺的两个环节。编译器和链接器是两个十分重要的概念。本文将具体介绍C++中的编译器和链接器以及它们的工作原理和应用办法。编译器编译器是将源代码转换为可执行文件的程序。在C++中,罕用的编译器有GCC和Clang。编译器的次要工作是将源代码翻译成汇编代码,而后再将汇编代码转换成机器码。编译器还能够进行优化,使得程序的执行效率更高。 在应用编译器时,咱们通常须要指定编译器的选项。罕用的选项包含: c:只编译源代码,生成指标文件o:指定生成的指标文件名g:生成调试信息Wall:开启所有正告信息例如,应用GCC编译源代码,并生成指标文件的命令如下: $ gcc -c main.cpp -o main.o除了上述罕用选项,编译器还提供了许多其余的选项,例如优化选项和预处理器选项。优化选项能够使程序的执行效率更高,而预处理器选项能够在编译之前进行宏替换和条件编译等操作。编译器的选项很多,须要依据理论状况抉择适合的选项。 编译器的外围工作是将源代码转换为机器码,这个过程中谬误和正告信息对于程序员来说十分重要。因而,咱们应该开启编译器的正告信息以及调试信息,以便在开发过程中及时发现和解决问题。 链接器链接器是将多个指标文件合并成一个可执行文件的程序。在C++中,罕用的链接器有GCC和ld。链接器的次要工作是将程序中援用的函数和变量与定义的函数和变量进行匹配,最终生成可执行文件。 在应用链接器时,咱们通常须要指定链接器的选项。罕用的选项包含: o:指定生成的可执行文件名L:指定链接库的搜寻门路l:指定链接库的名称例如,应用GCC链接指标文件,并生成可执行文件的命令如下: $ gcc main.o -o main -L/usr/lib -lm除了上述罕用选项,链接器还提供了许多其余的选项,例如符号表选项和动静链接选项。符号表选项能够打印出程序中的符号表信息,而动静链接选项能够将链接库的加载推延到运行时。链接器的选项也很多,须要依据理论状况抉择适合的选项。 链接器的次要作用是将多个指标文件合并成一个可执行文件。在程序开发中,咱们经常须要调用其他人编写的库函数。这时候,链接器会将程序中援用的函数与库函数进行匹配,从而生成可执行文件。如果链接器无奈找到所需的库函数,编译过程就会失败。 编译器和链接器的工作流程编译器和链接器是程序开发过程中不可或缺的工具,理解它们的工作流程有助于咱们更好地应用它们。上面是编译器和链接器的工作流程: 编译器将源代码转换为汇编代码。编译器将汇编代码转换为机器码,并生成指标文件。链接器将多个指标文件合并成一个可执行文件,并解决符号援用问题。在这个过程中,编译器和链接器都须要应用选项来管制其行为。这些选项能够管制编译器和链接器的优化等级、调试信息、符号表和库文件搜寻门路等等。 总结编译器和链接器是C++编程中不可或缺的工具。编译器能够将源代码转换成机器码,而链接器能够将多个指标文件合并成一个可执行文件。在程序开发过程中,咱们须要应用编译器和链接器来生成可执行文件。 除了编译器和链接器,还有很多其余的工具和技术能够帮忙咱们编写更高效、更牢靠的程序。例如,调试器能够帮忙咱们查找程序中的谬误;动态剖析工具能够帮忙咱们查看代码中的潜在问题;性能剖析工具能够帮忙咱们找到程序中的性能瓶颈。把握这些工具和技术,能够进步咱们的程序开发效率和代码品质。

May 13, 2023 · 1 min · jiezi

关于c:第一个C程序的代码初解

 为什么要有C语言呢?!我以前感觉计算机好奇怪啊!她是怎么示意世界万物的!我通过搜查网络大略明确了点,不同的编码造就了不同的意义!哦!那计算机到底意识什么样子的数据呢!我据说计算机的大脑只意识二进制的数据。这个“猪脑袋”幸好她计算的时候速度快!不然真的就太笨蛋啦!据说世界上第一个程序是女的~!那她过后应该就是用二进制去写程序的了!好厉害啊!前段日子我在电驴下了不少视频教程!只有是对于计算机的我都去下载来瞧瞧,过后看到好多人讲什么OD!外面好多什么00FABAF4听他外面说是反编译的工具!不懂!不过!起初我晓得了1点进制转换的常识后明确了。原来啊!二进制写货色比拟长,而且很容易写错!所以经常用十六进制去示意二进制(ADA到底是用的二进制还是十六进制写的程序啊)。起初大家感觉这样写程序还是很麻烦!起初就用了汇编语言!汇编语言就像给“一个人”取的一个外号,而后这个外号又比拟容易记忆!所以汇编语言和二进制代码是一一对应的!然而起初!人们感觉汇编语言还是太繁琐了!要是写的程序就像谈话一样就该多好啊!起初所以才有了C语言!C语言呢比拟靠近自然语言了!然而计算机如何能力懂C语言啊!所以就有了编译器,什么gcc,cl啊之类的。编译器呢先把C语言转化为汇编语言!而后再转化为二进制的。这样计算机就能明确了!转载于{WRITE-BUG}、更多内容可查看原文链接:https://www.writebug.com/article/92f889cf-c790-11ed-9c16-6479...

May 11, 2023 · 1 min · jiezi

关于c:C语言入门

C 语言是 1972 年由美国的 Dennis Ritchie 设计创造的,并首次在 UNIX 操作系统的 DEC PDP-11 计算机上应用。它由晚期的编程语言 BCPL (Basic Combind Programming Language)倒退演变而来。在 1970 年,AT&T 贝尔实验室的 Ken hompson 依据 BCPL 语言设计出较先进的并取名为 B 的语言,最初导致了 C 语言的问世。随着微型计算机的日益遍及,呈现了许多 C 语言版本。因为没有对立的规范, 使得这些 C 语言之间呈现了一些不统一的中央。为了扭转这种状况,美国国家标准研究所(ANSI)为 C 语言制订了一套 ANSI 规范,成为现行的 C 语言规范。C 语言倒退如此迅速, 而且成为最受欢迎的语言之一,次要因为它具备弱小的性能。许多驰名的系统软件, 如 PC-DOS,DBASE Ⅳ 都是由 C 语言编写的。用 C 语言加上一些汇编语言子程序, 就更能显示 C 语言的劣势了。归纳起来 C 语言具备下列特点: C 是中级语言它把高级语言的根本构造和语句与低级语言的实用性联合起来。C 语言能够象汇编语言一样对位、字节和地址进行操作,而这三者是计算机最根本的工作单元。C 是结构式语言结构式语言的显著特点是代码及数据的分隔化,即程序的各个局部除了必要的信息交换外彼此独立。这种结构化形式可使程序档次清晰,便于应用、保护以及调试。C 语言是以函数模式提供给用户的,这些函数可不便的调用,并具备多种循环、条件语句控制程序流向,从而使程序齐全结构化。C 语言功能齐全C 语言具备各种各样的数据类型,并引入了指针概念,可使程序效率更高。另外 C 语言也具备弱小的图形性能,反对多种显示器和驱动器。而且计算性能、逻辑判断性能也比拟弱小,能够实现决策目标。C 语言适用范围大C 语言还有一个突出的长处就是适宜于多种操作系统,如 DOS、UNIX,也实用于多种机型转载于{WRITE-BUG}、更多内容可查看原文链接:https://www.writebug.com/article/93223235-c790-11ed-a615-6479...

May 10, 2023 · 1 min · jiezi

关于c:7种常见网络并发模型介绍

概述对于网络服务器后端开发,为满足不同并发场景的须要,一般来说,不外乎几种常见的并发模型,除了一些教学场景罕用的单线程、多过程(线程)的服务器实现外,生产用的服务器,个别都会思考应用IO多路复用模型。 而常见的IO多路复用场景 ,能够设计得很简略,也能够设计得比较复杂,个别依据业务须要而定。本文总结了一些比拟常见的服务器并发模型,根本涵盖了 大部分业务场景 。在理论业务开发的技术选型时,可依据场景,选取一款稳固、牢靠的网络模型,还是非常要害的。 模型一: 单线程Accept(无IO复用 ) 模型剖析主线程main thread执行阻塞accept, 每次客户端connect连贯过去,main thread中accept响应并建设连贯创立连贯胜利,失去connect fd套接字后,仍然在main thread串行解决套接字读写,并解决业务在解决业务时,如果有新客户端connect过去,server无响应,直到以后socket全副业务处理完毕(完结while循环)以后客户端处理完毕之后,敞开连贯,解决下一个客户端申请优缺点长处socket编程流程清晰且简略,适宜学习应用,理解socket根本编程流程毛病该模型并非并发模型 ,是串行服务器,同一时刻,监听并响应的最大网络申请量为1, 即并发量为1仅适学习根本socket编程,不适宜任何服务器server构建模型二: 单线程 Accept + 多线程读写业务(无 IO 复用) 模型剖析主线程main thread阻塞在accept, 每次客户端 connect连贯过去,main thread中accept响应并建设连贯创立连贯胜利,失去connect fd套接字后,创立一个新的线程thread来解决客户端的读写业务,mian thread仍然回到accept阻塞期待新客户端thread通过套接字connect fd与客户端进行读写操作server在解决业务时,如果有新的客户端连贯过去,main thread中accept仍然能够响应并建设连贯,反复上述过程优缺点长处基于模型1的优化,反对了并发的个性应用比拟灵便,一个客户端对应一个线程独自解决,server解决业务的内聚性比拟高, 客户端无论如何读写 ,服务端 均会有一个 线程做资源响应毛病随着客户端梳理增多,须要开拓的线程数量也减少了,和server线程的数量是1:1的关系,因而对于高并发场景,线程数量受到硬件的瓶颈,线程过多也会 减少CPU的切换老本,升高CPU利用率对于长连贯,客户端一旦无业务读写,只有不敞开,server就应该对放弃这个连贯的状态(心跳查看,健康检查机制),占用连贯资源和线程的开销仅适宜客户端数量不大的场景,且可控的场景来应用该模型仅适宜学习根本的socket编程,不适宜做并发服务器模型三: 单线程多路IO复用 模型剖析主线程main thread创立listen fd之后,采纳多路IO复用机制(如select、epoll)进行IO状态阻塞监控,有client连贯申请,IO 复用机制检测到listen fd触发读事件,则进行accept建设连贯,并将新生成的connect fd退出到监听IO汇合中client再次进行失常读写业务申请,main thread的多路IO复用机制阻塞返回,会触发该套接字 的读写事件等对于client的读写业务,server仍然在main thread执行流程继续执行 ,此时如果有新的客户端connect申请过去,server将没有即时响应等到server解决完一个连贯的读写操作,持续回到多路IO复用机制阻塞,其余连贯过去才能够失常执行优缺点长处单流程体解决了能够同时监听多个客户端读写状态模型,不须要1:1客户端的线程数量关系多路IO复用机制 是阻塞的,非忙轮询的状态,不会节约CPU资源,对CPU的利用率较高对于连接数较多,然而并发不大的场景,或对实时性没有特地严格的场景,该模型曾经足够应用毛病尽管能够监听读个客户端的读写状态,然而同一时间内,只可能解决一个客户端的读写操作,实际上读写业务并发为1当多个客户端拜访server,业务是串行执行,大量申请的会有排队提早景象。模型四:单线程多路IO复用 + 多线程读写业务 (业务工作池) 模型剖析主线程main thread 创立listen fd后,采纳多路IO复用机制(如select、epoll)进行IO状态阻塞监控,有client客户端connect申请 ,IO复用机制检测到listenfd触发读事件,则进行accept建设连贯,并将新生成的connect fd退出到监听IO汇合中当connect fd有 可读音讯,触发读工夫,并进行读写音讯main thread依照固定协定读取音讯,并且交给worker pool工作线程池,工作线程池在server启动之前就曾经开启固定数量的thread,外面的线程只解决音讯 业务,不进行套接字读写操作工作池解决完业务,触发connect fd写事件,将回执客户端的音讯通过main thread写给对方即:main thread只解决IO阻塞监听以及具体的读写操作,读写到的数据交给具体的线程池解决,让main thread专一于解决IO事件相似于Redis的解决机制优缺点长处将业务解决的局部,通过工作池分离出来,可能缩小客户端拜访server导致业务串行执行会有大量申请排队的延迟时间实际上读写的业务并发为1,然而业务流程的并发为线程池数量,放慢了业务解决的并行效率毛病读写仍然是main thread独自解决,最高的读写并行通道仍然是1尽管多个worker thread解决业务,最初返回给client仍旧也须要排队,因为进口还是main thread的一个通道模型五:单线程IO复用 + 多线程IO复用 ...

May 8, 2023 · 1 min · jiezi

关于c:SPISPI总线

SPI简介SPI是串行外设接口的缩写,SPI是一种高速的,全双工,同步的串行通信总线;SPI采纳主从形式工作,个别有一个主设施或多个从设施;SPI须要至多4根线,MISO(主设施输出从设施输入)、MOSI(主设施输入从设施输出)、SCLK(时钟)、CS(片选),SPI应用的引脚较少,通信不便。 寻址形式主设施和某个从设施进行通信时,主设施须要先向从设施的片选线上发送使能信号(高电平或者低电平,依据从机而定)示意选中该从设施。 通信过程SPI总线在进行数据传送时,先传送高位,后传送低位;数据线为高电平示意逻辑“1”,低电平示意逻辑“0”,一个字节传送实现后无需应答,即可开始下一字节的传送;SPI总线采纳同步工作形式,时钟线在回升沿或者降落沿时发送器向数据线上发送数据,再紧接着的降落沿或者回升沿时接收器从数据线上读取数据,实现一位数据传送,八个时钟周期可实现一个字节数据的传送。MSB最高位 LSB最低位 极性和相位SPI总线有四种不同的工作模式,取决于极性(CPOL)和相位(CPHL)CPOL示意SCLK闲暇时的状态CPOL=0;闲暇时SCLK为低电平CPOL=1;闲暇时SCLK为高电平CPHA示意采样时刻CPHA=0;每个周期的第一个时钟沿采样CPHA=1;每个周期的第二个时钟沿采样 四种状况极性为0的时候,闲暇时为低电平;极性为1的时候,闲暇时为高电平;相位为0的时候,回升沿发数据,降落沿读数据;相位为1的时候,降落沿发数据,回升沿读数据 极性和相位须要阐明的是,一个特定的从设施来说,个别再出厂时就会将其设计为某种特定的工作模式,咱们再应用设施时就必须保障主设施的工作模式和该从设施的设施保持一致,否则是无奈进行通信,个别咱们须要对主设施的CPOL和CPHA进行配置。 IIC与SPI的异同

May 5, 2023 · 1 min · jiezi

关于c:关于C语言结构体初始化问题

环境devCpp 5.11 问题简述近期在写C语言程序时发现了一个我以前没有留神的问题,代码如下: #include <stdio.h>//定义一个 "人" 构造体struct Person{ char name[50]="\0"; //姓名 unsigned short age=0; //年龄 float salary=0; //薪水};int main(){ //实例化两个构造体 张三 和 李四 Person arr[2]={{"张三",38,4800},{"李四",25,3700}}; return 0;}我感觉没有什么问题,然而编译器报错了,谬误如下:大略翻译过去的意思是:不能从初始化列表转为XXX类型。 谬误起因C语言在申明构造体类型时,如果在构造体申明的同时就将构造体中成员进行赋值的话那么在实例化构造体时就不可能应用初始化列表办法进行初始化。相当于创立构造体的霎时曾经被初始化,而再次应用初始化列表就是二次初始化,显然违反编译器的初心————初始化操作只能存在一次。不信?看上面代码及其运行后果。#include <stdio.h>struct Person{ char name[50]="张三"; unsigned short age=27; float salary=3750.25;};int main(){ Person zs; printf("%s %d %.2f\n",zs.name,zs.age,zs.salary); return 0;}创立即初始化。 如果仍想要创立构造体对象则能够应用第一种办法,即创立后再赋值。#include <stdio.h>struct Person{ char name[50]="\0"; unsigned short age=0; float salary=0;};int main(){ Person zs; //张三 sprintf(zs.name,"%s","张三\0"); //格式化写入 zs.age=27; //年龄赋值 zs.salary=4800; //薪水赋值 printf("%s %d %.2f\n",zs.name,zs.age,zs.salary);//打印信息 return 0;} ...

May 3, 2023 · 1 min · jiezi

关于c:希望所有计算机学生能看到这篇c语言教程

大部分程序员走入编程世界第一个学习的语言就是C语言。 作为一门古老的编程语言,c语言领有48年的倒退历程。 为什么要学习 C语言?C语言是学习计算机程序设计语言的入门语言。最全面的编程面试网站C语言是一门偏底层的语言,学好它,能够让你更好的理解计算机。学会了C语言,你就能学习当初任何的高级编程语言。因为所有的高级语言都是以C语言为根底的。怎么学习c语言?先举荐一本书《C Primer Plus》,从这本书开始入门,系统地理解C语言的根底语法和简略利用。 学完之后,你应该把握C语言的根底语法:变量、字符串、条件、循环、数组、函数、构造体等,还有指针、内存治理,这两个是C语言中至关重要的知识点。 给大家分享一个Github仓库,下面有大彬整顿的300多本经典的计算机书籍PDF,包含C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,能够star一下,下次找书间接在下面搜寻,仓库继续更新中~ Github地址 对C语言有肯定认知之后,就可以看其余进阶版的书籍了,比方《C和指针》和《C专家编程》以及《C陷阱与缺点》等。 对于视频教程,举荐翁恺传授的C语言视频课。 翁恺老师是土生土长的浙大码农,从本科到博士都毕业于浙大计算机系,起初留校教书,一教就是20多年。 翁恺老师的c语言课程十分好,解说特地乏味,很适宜初学者学习。 另外也能够看看清华大学的C语言课程,讲的也挺好。 动手做一些我的项目学习编程,光靠看书和看视频是没用的,肯定要入手写代码、做我的项目,纸上得来终觉浅,只有实际过能力真正的把握这些常识。 举荐几个C语言我的项目,都是比拟趣味性的,不会很干燥。 第一个是贪吃蛇,贪吃蛇是十分经典的一款游戏,置信大家都玩过这个游戏,然而应该很少有人会本人去做出这款游戏。 这个视频解说十分具体,感兴趣的小伙伴能够试着本人实现一下。 另一个是用c语言实现简略计算器,进行加、减、乘、除操作。课程波及的所有数学知识都很简略,都是C语言很根底的货色,适宜初学者。 c语言的知识点总结我整顿了C语言的外围知识点,不便大家学习。 数据类型常量运算符表达式输出/输入流程管制函数数组指针构造体宏定义文件操作内存治理

April 29, 2023 · 1 min · jiezi

关于c:C语言项目实操学生管理系统

本文首发自「慕课网」,想理解更多IT干货内容,程序员圈内热闻,欢送关注"慕课网"!作者:张磊|慕课网讲师 0根底学员在通过C语言根底语法的学习后,须要通过一些实战案例来学习如何将C语言利用到理论工作中,明天咱们通过一个学生管理系统案例的开发来晋升理论开发能力。 1. 零碎介绍 在这里,咱们首先采纳了数组的形式来存储咱们输出的内容。同时,咱们这里利用里后面学习到的 struct 来结构化存储咱们的学生信息。 咱们实现了根本的对于数据操作的几项性能:增、删、改、查。 也就是咱们能够向这个零碎中增加数据,删除数据,批改数据,还有就是查问数据。这里的查问数据,咱们又分为了全副无条件的查问,和依照姓名条件的查问。 2. 性能 零碎的性能外面咱们设计了增加、删除、批改、列表显示和查问等 5 个性能。 增加:增加就是将数据增加到存储数据的数组中; 删除:删除就是依据指定的序列号删除特定的一条数据; 批改:批改就是依据指定的序列号来批改学生的问题; 列表显示:列表的显示,其实就是无条件的查问,也就是在没有特点查问条件的状况下,将存储的数据全副显示进去; 查问:这里的查问,就是依照姓名这个个性的条件。把合乎这个条件的数据筛选进去,并显示进去。 3.示例程序 #include <stdio.h>#include <string.h>#define StudentNumbers 50#define NameLength 50typedef struct{ int id; char name[NameLength]; int age; int score; int flag;} Student;int add(Student student, Student Students[]);int del(int id, Student students[]);int display(Student students[]);int update(int id, Student students[]);int search(char name[], Student students[]);int main(){ int id = -1; char name[NameLength]; int choice = 0; int stop = 0; Student students[StudentNumbers]; Student student; for (int i = 0; i < StudentNumbers; i++) { students[i].id = i; students[i].flag = 0; } while (stop == 0) { printf("-------------------------\n"); printf("* 学生管理系统 *\n"); printf("-------------------------\n"); printf("1 增加\n"); printf("2 批改问题\n"); printf("3 查问\n"); printf("4 删除\n"); printf("5 显示学生列表\n"); printf("0 退出程序\n"); printf("请间接输出数字选项:"); scanf("%d", &choice); switch (choice) { case 1: printf("请输出学生姓名:"); scanf("%s", student.name); printf("请输出学生的年龄:"); scanf("%d", &student.age); printf("请输出学生问题:"); scanf("%d", &student.score); add(student, students); break; case 2: printf("请输出要批改问题的学生编号:"); scanf("%d", &id); update(id, students); break; case 3: printf("请输出要查找的学生姓名:"); scanf("%s", name); search(name, students); break; case 4: printf("请输出要删除的学生编号:"); scanf("%d", &id); del(id, students); break; case 5: display(students); break; case 0: stop = 1; break; default: printf("输出选项有误\n"); break; } } return 0;}int add(Student student, Student students[]){ for (int i = 0; i < StudentNumbers; i++) { if (students[i].flag == 0) { strcpy(students[i].name, student.name); students[i].age = student.age; students[i].score = student.score; students[i].flag = 1; return 0; } } return 1;}int del(int id, Student students[]){ for (int i = 0; i < StudentNumbers; i++) { if (students[i].id == id) { students[i].flag = 0; return 0; } } return 1;}int display(Student students[]){ printf("******************\n"); printf("学生列表\n"); printf("******************\n"); for (int i = 0; i < StudentNumbers; i++) { if (students[i].flag == 1) { printf("学生编号:%d,学生姓名:%s,年龄:%d,问题:%d\n", students[i].id, students[i].name, students[i].age, students[i].score); } } printf("******************\n"); return 0;}int update(int id, Student students[]){ int score = -1; printf("请输出新的问题:"); scanf("%d", &score); for (int i = 0; i < StudentNumbers; i++) { if (students[i].id == id) { students[i].score = score; return 0; } } return 1;}int search(char name[], Student students[]){ for (int i = 0; i < StudentNumbers; i++) { if (strcmp(name, students[i].name) == 0) { printf("学生编号: %d,学生姓名: %s,年龄: %d,问题: %d\n", students[i].id, students[i].name, students[i].age, students[i].score); return 0; } } printf("没有查找到相干学生信息。\n"); return 1;}很多人可能会第一次接触这么长的程序,会产生畏惧的心理。其实不必放心。要置信本人能够看懂的。 ...

April 23, 2023 · 3 min · jiezi

关于c:十进制转二进制的方法-写代码实现CC

作者:柒烨带你飞本文为原创文章,版权归自己所有。文章首发于CSDN:https://blog.csdn.net/m0_61643743/article/details/130260640 十进制转二进制整数十进制转二进制转换方法 + 如何用代码实现为次要内容一,十进制转二进制办法办法1:除二取余法十进制数除2得商取余法:对十进制进行除法运算,十进制除以2能够失去一个商和余数;再用失去的商除以2又失去一个商和余数,以此内推,中转最初一次除以2,商为0或1进行十进制转换例子:(13)(20)把这2个十进制数转换为二进制 13/2 = 6.....1 ,6/2 = 3.....0, 3/2 = 1.....1 , 1/2 = 0.....1 ; 把余数逆序排列,13的二进制数就是:1101。20/2 = 10.....0 10/2 = 5.....0 5/2 = 2.....1 2/2 = 1.....0 1/2 = 0.....1 20的二进制数就是:10100十进制转二进制时只需算出整数商即可,如理论 5/2 = 2.5,算出整数商2取余就好,如:5/2 = 2....1 办法2:按权相加法法则如下: 二进制100000001000000100000100001000100101十进制1286432168421位权(幂模式)2^72^62^52^42^32^22^12^0依据下面的法则,假如上面8位二进制都是0 128643216842110000001求129的二进制数:只需把下面表格中128和1上面的0改为1(128+1 =129) 129的二进制数:10000001128643216842110000111**求135的二进制数:(128+2+4+1=135)135的二进制数:10000111 128比135小所以128下改1,135-128 = 7。64,32,16,8比7大所以都设为0,4比7小设1,7-4=3;2比3小设1, 最初残余的1,1等于1设1**16842110100求20的二进制数:(16+4=20) 20的二进制数:10100。16比20小设1,20-16=4,8比4大设0,4等于4设1。84211010求10的二进制数:(8+2=10) 10的二进制数:1010。8比10小设1,10-8=2,4比2大设0,2等于2设1借助这个办法十进制转二进制更容易,纯熟了之后笔算,心算一下就能算出二进制数,不必像除2取余法一样繁冗 二,代码实现实例:步骤实现: 获取用户输出的十进制数。判断用户输出的数据是否不符合要求,合乎则进入下一阶段,不合乎则从新输出或退出。十进制转二进制运算实现区输入失去的二进制数1,C 代码实现#include<stdio.h>int main(){ short BinaryNumbe[32]={0};//定义一个数组存储二进制数 int i,n;//i为数组下标,n用来存储十进制数 /*步骤1: 获取用户输出的十进制数。 判断用户输出的数据是否符合要求,合乎则进入下一阶段,不合乎则从新输出*/ do { printf("请输出要转换的十进制数(1-2147483647):");//提醒输出十进制 scanf("%d",&n);//scanf获取输出的数据 if (n <= 0 || n > 2147483647)/*用if判断数据是否输出有误,有误则从新输出*/ { printf("输出有误,十进制转二进制范畴为(1-2147483647)\n"); continue; /*提醒输出有误,用continue完结本次循环,再进行一次循环*/ } } while (0); /*步骤2: 十进制转二进制计算实现区。 用for循环,i为数组下标,计算十进制除2去余赋值给寄存二进制数据的数组*/ for ( i = 0; i < 32 ; i++)//int最大值为(2147483647)32位二进制数最大也是2147483647。所以循环次数设置为32(0-31) { BinaryNumbe[i] = n % 2;//取余赋值给数组 n = n / 2; //除以2,失去下一个商,赋值给n if (n == 0) { break;//当商等于0时,不再进行多余次数的循环,break跳出循环; } } /*步骤3: 输入二进制数据。 下面i++曾经记录了循环计算的次数。 i循环的次数 == i位二进制数 == 数组[i]最初赋值的余数 。 间接在上面for循环中加个i >= 0 ; i--,就能逆序输入了。 */ printf("二进制数是:"); for (n = 1 ; i >= 0 ; i--) { printf("%d",BinaryNumbe[i]);//逆序打印二进制数 if (n%4 == 0 ) {//为了好看,用if判断每输入4个二进制数,打印一个空格。 //该段用n来记录了输入次数,该段代码可不加 printf(" "); } n++; } printf("\n");//换行 system("pause"); return 0;}把上述代码简化改成一个函数,思路不变 ...

April 22, 2023 · 2 min · jiezi

关于c:C语言的灵魂指针

本文首发自「慕课网」,想理解更多IT干货内容,程序员圈内热闻,欢送关注"慕课网"! 作者:精慕门1289550|慕课网讲师 C 语言中的指针C 语言的指针经常被人们认为是 C 语言中的灵魂所在,能够实现很多高难度的操作。然而更多的人感觉 C 语言的指针的存在如同噩梦个别,因为稍不留神,就会引起灾难性的结果。 不论你是不是喜爱 C 语言中的指针,咱们都要学习这种看起来很神奇的货色。因为指针这个概念不是 C 语言独创的,而是苏联的计算机科学家独创的。 1. 什么是指针?指针是什么?这是来自灵魂的拷问。其实指针也是一种变量。咱们之前也说过。只不过存储的是另外一个变量的地址。变量地址是变量在内存中存储的地位索引。 `int *a; int b=5; a = &b;`` 这里咱们定义了一个整数类型的指针 a ,能够看出指针的定义与变量的申明是统一的,只不过须要在变量明前加上一个额定的字符 * 。 因为指针中存储的应该是变量的地址,因而咱们在赋值的时候须要取得变量的地址,而不是变量自身所存储的数值。这里咱们采纳的 & 来获取变量的地址。将获取到的地址赋值给指针变量。 要是想应用指针中存储地址中所存储的值,那么就须要在指针变量名前加 * 来获取相应地址中存储的值。 2. 示例程序#include <stdio.h>int main(){ int *a, b = 100, *c; printf("a value = %p, a address = %p, a point value = %d\n", a, &a, *a); printf("b value = %d, b address = %p\n", b, &b); printf("c value = %p, c address = %p, c point value = %d\n", c, &c, *c); a = &b; printf("a value = %p, a address = %p, a point value = %d\n", a, &a, *a); c = a; printf("c value = %p, c address = %p, c point value = %d\n", c, &c, *c); printf("b value = %d, b address = %p\n", b, &b); return 0;}运行后果: ...

April 20, 2023 · 2 min · jiezi

关于c:6-JNI-全局引用局部引用弱全局引用

从Java虚拟机创立的对象传到本地 C/C++ 代码时就会产生援用。依据Java的垃圾回收机制,只有有援用存在就不会触发该援用指向的Java对象的垃圾回收。这些援用在 JNI 中分为三种 全局援用 (Global Reference)部分援用 (Local Reference)弱全局援用 (Weak Global Reference), JDK 1.2 引入1. 部分援用最常见的援用类型,基本上通过JNI返回来的援用都是部分援用例如,应用NewObject就会返回创立进去的实例的部分援用。部分援用只在该native函数中无效,所有在该函数中产生的部分援用,都会在函数返回的时候主动开释(freed)。也能够应用DeleteLocalRef函数进行手动开释该援用。 想一想既然部分援用可能在函数返回时主动开释,为什么还须要DeleteLocalRef函数呢?实际上部分援用存在,就会避免其指向的对象被垃圾回收。尤其是当一个部分援用指向一个很宏大的对象,或是在一个循环中生成了部分利用;最好的做法就是在应用完该对象后,或在该循环尾部把这个援用开释掉,以确保在垃圾回收器被触发的时候被回收。 在部分援用的有效期中,能够传递到别的本地函数中,要强调的是它的有效期依然只在一次的Java本地函数调用中,所以千万不能用C++全局变量保留它或是把它定义为C++动态局部变量。2. 全局援用全局援用能够逾越以后线程,在多个native函数中无效,不过须要编程人员手动来开释该援用。全局援用存在期间会避免在Java的垃圾回收的回收。与部分援用不同,全局援用的创立不是由 JNI 主动创立的,全局援用须要调用 NewGlobalRef 函数,而开释它须要应用 ReleaseGlobalRef 函数。3. 弱全局援用弱全局利用是 JDK 1.2 新进去的性能,与全局援用类似,创立跟开释都须要由编程人员来进行操作。这种援用与全局援用一样能够在多个本地代码无效,也能够逾越多线程无效;不一样的是,这种援用将不会阻止垃圾回收器回收这个援用所指向的对象。 应用 NewWeakGlobalRef 跟 ReleaseWeakGlobalRef 来产生和开释利用。 4. 对于援用的一些函数jobject NewGlabalRef(jobject obj);jobject NewLocalRef(jobject obj);jobject NewWeakGlobalRef(jobject obj);void DeleteGlobalRef(jobject obj);void DeleteLocalRef(jobject obj);jboolean IsSameObject(jobject obj1, jobject obj2);IsSameObject 函数对于弱援用全局利用还有一个特地的性能,把NULL传入要比拟的对象中,就可能判断弱全局援用所指向的Java对象是否被回收。 5. 缓存jfieldID / jmethodID获取 jfieldID与jmethodID 的时候会通过该属性/办法名称加上签名来查问相应的 jfieldID/jmethodID。这种查问相对来说开销较大。在开发中能够将这些 FieldID/MethodID 缓存起来,这样就只须要查问一次,当前就应用缓存起来的 FieldID/MethodID。 上面介绍两种缓存形式在应用时缓存 (Caching at the Point of Use)在Java类初始化时缓存 (Caching at the Defining Class's Inititalizer)5.1 在应用时缓存在native 代码中应用static局部变量来保留曾经查问过的jfieldID/jmethodID ,这样就不会在每次的函数调用时查问,而只有一次查问胜利后就保存起来了。 ...

April 15, 2023 · 1 min · jiezi

关于c:5JNI-操作数组

1. 数组的分类数组分为两种根本数据类型的数组对象类型(Object[])的数组一个能通用于两种不同数据类型数组的函数GetArrayLength( jarray array);//获取长度2. 根本数据类型数组解决根本数据类型数组的时候,也是跟解决字符串类型,有很类似的函数Get<TYPE>ArrayElements( <TYPE>Array arr, jboolean* isCopied);这类函数能够把Java根本类型的数组转换到C/C++的数组,有两种解决形式,一是拷贝一份传回本地代码,另一个是把指向Java数组的指针间接传回到本地代码。解决完本地的数组后,通过Release<TYPE>ArrayElements来开释数组。 Release<TYPE>ArrayElements( <TYPE>Array arr, <TYPE>* array, jint mode );用这个函数能够抉择将如何解决Java跟C++的数组。是提交、还是撤销等;内存开释还是不开释等。 mode 能够取上面的值: 0 对Java的数组进行更新,并开释 C/C++ 的数组JNI_COMMIT 对Java的数组进行更新,然而不开释C/C++的数组JNI_ABORT 对Java的数组不进行更新,开释C/C++的数组JDK 1.2 为了减少间接传回指向 Java 数组的指针,而减少了以下函数。然而同样的,也会有同GetStringCritical的死锁问题GetPrimitiveArrayCritical( jarray arr, jboolan* isCopied);ReleasePrimitiveArrayCritical(jarray arr, void array, jint mode);在C/C++事后开拓一块内存,而后把Java根本数组类型的数组拷贝到这块内存当中。跟GetStringRegion原理类似Get<TYPE>ArrayRegion( <TYPE>Array arr, jsize, start, jsize len, <TYPE>* buffer);把Java根本数据类型的数组中的指定范畴的元素用 C/C++的数组中的元素来赋值Set<TYPE>ArrayRegion( <TYPE>Array arr, jsize start, jsize len, const <TYPE>* buffer );指定一个长度,而后返回相应Java根本类型的数组<TYPE>Array New<TYPE>Array( jsize len );3. 对象类型数组Object[] JNI 没有提供间接把Java的对象类型数组( Object[] )间接转到 C/C++ 的 jobject[] 数组的函数。而是间接通过GetObjectArrayElement /SetObjectArrayElement 这样的函数来对Java的Object[]数组进行操作。 ...

April 15, 2023 · 1 min · jiezi

关于c:4JNI-操作字符串String

1. 在C/C++本地代码中创立Java的对象1.1 Java对象的创立应用 NewObject 办法应用函数 NewObject 能够用来创立Java对象GetMethod 可能获得构造方法的 jmethodID,如果传入的要取的办法名称设定为 "<init>" 就可能获得构造方法因为构造方法没有返回值,所以构造方法的办法返回值类型的签名始终为void案例 jclass class_date = env->FindClass("java/util/Date");jmethodID mid_date = env->GetMethodId(class_date, "<init>", "()V");jobject now = env->NewObject(class_date, mid_date);jmethodID mid_date_getTime = env->GetMethod(class_date , "getTime", "()j");jlong time = CallLongMethod(now, mid_date_getTime);cout << time << endl;1.2 Java对象的创立 AllocObject应用函数 AllocObject 能够依据传入的 jclass 创立一个 Java 对象,然而他的状态是非初始化的,在应用这个对象之前肯定要调用 CallNonvirtualVoidMethod 来调用该 jclass 的构造函数,这样就能够提早构造函数的调用。这一个局部用的很少,在这里只做简略的阐明。jclass clazz_str = env->FindClass("java/lang.String");jmethodID methodID_str = env->GetMethodID(clazz_str, "<init>", "([C)V");//事后创立一个没有初始化的字符串jobject str = env->AllocObject(clazz_str);//创立一个4个元素的字符串数组,而后以 'B' 'U' 'G' '弄' '潮' '儿' 赋值jcharArray arg = env->NewCharArray(6);env->SetCharArrayRegion(arg, 0, 6, L"BUG弄潮儿");//调用构造函数env->CallNonvirtualVoidMethod(str, clazz_str, methodID_str, arg);jclass clazz_this = env->GetObjectClass( obj );//这里假如这个对象的类中有定义 static String STATIC_STR;jfieldID jfield_str = env->GetStaticField( clazz_this, "STATIC_STR", "Ljava/lang/String;");env->SetStaticObjectField(clazz_str, jfield_str, str);2. Java字符串 & C/C++的字符串在C/C++本地代码中拜访Java的String字符串对象 ...

April 15, 2023 · 2 min · jiezi

关于c:IIC典型IIC时序

典型IIC时序主机向从机发送数据从机向主机发送数据 主机先向从机发送数据,而后从机再向主机发送数据

April 12, 2023 · 1 min · jiezi

关于c:IIC总线

IIC总线简介1、IIC是一种串行、半双工总线,SDA收发数据,SCL用于通信单方时钟的同步;2、IIC总线是一种多主机总线,器件分为主机从机有权发动完结一次通信,从机只能被主机呼叫;当总线上有多个主机同时启用总线时,IIC具备冲突检测仲裁性能搁置谬误的产生;3、每个连贯到IIC总线的器件都有一个惟一的地址(7bit),每个器件都能够作为主机也能够作为从机(同一时刻只能有一个主机),总线上的器件减少和删除不影响其余前器件失常工作;4、IIC总线在通信中总线上发送数据的器件为发送器,接收数据的器件为接收器; IIC总线通信过程1、主机发送一个起始信号启用总线2、主机发送一个字节数据指明从机地址和后续字节的传送方向高7位为从机地址,第0位为传送方向,主机往最初一位写的是0,主机给从机发数据;主机往最初一位写的是1,从机给主机发数据。3、被寻址的从机发送应答信号回应主机4、发送器发送一个字节数据5、接收器发送应答信号,回应发送器.....n、通信实现后主机发送进行信号开释总线 IIC总线寻址形式 起始信号和进行信号SCL为高电平时,SDA由高变为底示意起始信号SCL为高电平时,SDA由底变高示意进行信号起始信号与进行信号都是由主机收回,起始信号产生后总线处于占用状态,进行信号产生后总线处于闲暇状态。 字节传送与应答IIC总线通信时每个字节为8位长度,数据传送时,先传送最高位,后传送低位,发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器即一帧共有9位。 同步信号IIC总线在进行数据发送时,时钟线SCL为低电平期间发送器向数据线上发送一位数据,在此期间数据线上的信号容许发生变化,时钟线SCL为高电平期间接收器从数据线上读取一位数据,在此期间数据线上的信号不容许发生变化,必须保持稳定。

April 11, 2023 · 1 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Labs

久闻MIT 6.828操作系统工程试验的盛名,今日终于可能亲手实际。 每个试验我都将要求、提醒、实现思路和步骤、问题和后果记录下来,一方面为了将试验要求和提醒翻译后记录下来以便后续研读,另一方面也是为了加深试验中学习到的知识点: MIT 6.1810(6.828/6.S081) 操作系统工程 Lab1 UtilitiesMIT 6.1810(6.828/6.S081) 操作系统工程 Lab2 System callsMIT 6.1810(6.828/6.S081) 操作系统工程 Lab3 Page tablesMIT 6.1810(6.828/6.S081) 操作系统工程 Lab4 TrapsMIT 6.1810(6.828/6.S081) 操作系统工程 Lab5 Copy-on-Write ForkMIT 6.1810(6.828/6.S081) 操作系统工程 Lab6 MultithreadingMIT 6.1810(6.828/6.S081) 操作系统工程 Lab7 NetworkingMIT 6.1810(6.828/6.S081) 操作系统工程 Lab8 LocksMIT 6.1810(6.828/6.S081) 操作系统工程 Lab9 file systemMIT 6.1810(6.828/6.S081) 操作系统工程 Lab10 mmap

April 9, 2023 · 1 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab10-mmap

Lab: mmap (hard)mmap 和 munmap 零碎调用容许 UNIX 程序对其地址空间进行具体管制。它们可用于在过程之间共享内存,将文件映射到过程地址空间,以及作为用户级页面谬误计划(如课程中探讨的垃圾回收算法)的一部分。在本试验中,你将向 xv6 增加 mmap 和 munmap,重点关注内存映射文件。 运行man 2 mmap可失去手册中mmap的申明: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);要求能够通过多种形式调用 mmap,但此练习只须要与文件内存映射相干的性能子集。 您能够假如 addr 将始终为零,这意味着内核应决定映射文件的虚拟地址。mmap 返回该地址,如果失败,则返回0xffffffffffffffff。length是要映射的字节数; 它可能与文件的长度不同。prot 批示内存是否应映射为可读、可写和/或可执行; 您能够假如 prot 是 PROT_READ 或 PROT_WRITE 或两者兼而有之。flags要么是MAP_SHARED,这意味着对映射内存的批改应该写回文件,要么是MAP_PRIVATE,这意味着它们不应该写回文件。你不用在flags中实现任何其余位。fd 是要映射的文件的关上文件描述符。您能够假如偏移量为零(文件中要映射的终点)。对于映射同一MAP_SHARED文件的不同过程,禁止共享物理页。 munmap(addr,length)应该删除指定地址范畴内的mmap映射。如果过程批改了内存且为MAP_SHARED映射,则应首先将批改写入文件。munmap 调用可能只笼罩已被映射的区域的一部分,但您能够假如它会在开始时、结尾或整个区域勾销映射(但不会在区域两头打一个洞)。 您应该实现足够的 mmap 和 munmap 性能,以使 mmaptest 测试程序失常工作。无需实现mmaptest 不应用 mmap 的性能。实现后,应会看到以下输入: $ mmaptestmmap_test startingtest mmap ftest mmap f: OKtest mmap privatetest mmap private: OKtest mmap read-onlytest mmap read-only: OKtest mmap read/writetest mmap read/write: OKtest mmap dirtytest mmap dirty: OKtest not-mapped unmaptest not-mapped unmap: OKtest mmap two filestest mmap two files: OKmmap_test: ALL OKfork_test startingfork_test OKmmaptest: all tests succeeded$ usertests -qusertests starting...ALL TESTS PASSED$ 提醒首先向UPROGS增加_mmaptest,以及mmap和munmap零碎调用,以便使user/mmaptest.c可能编译。当初,只需从 mmap 和 munmap 返回谬误。咱们在kernel/fcntl.h中为您定义了PROT_READ等。运行 mmaptest,这将在第一次 mmap 调用时失败。惰性地填写页表,以响应页面谬误。也就是说,mmap 不应调配物理内存或读取文件。相同,请在 usertrap 中(或由 usertrap 调用)的页面错误处理代码中执行此操作,就像在惰性页面调配试验中一样。惰性的起因是确保大文件的 mmap 是疾速的,并且大于物理内存的文件的 mmap 是可能的。跟踪 mmap 为每个过程映射的内容。定义与第 15 讲中形容的 VMA(虚拟内存区域)对应的构造,记录 mmap 创立的虚拟内存范畴的地址、长度、权限、文件等。因为 xv6 内核中没有内存分配器,因而能够申明一个固定大小的 VMA 数组,并依据须要从该数组进行调配。大小为 16 就足够了。实现mmap:在过程的地址空间中查找要在其中映射文件的未应用区域,并将 VMA 增加到过程的映射区域表中。VMA 应蕴含指向要映射的文件的struct file的指针; mmap 应减少文件的援用计数,以便在敞开文件时构造不会隐没(提醒:请参阅 filedup)。运行 mmaptest:第一个 mmap 应该胜利,但第一次拜访已映射的内存会导致页面谬误并 kill mmaptest。增加代码,以在拜访已映射区域中导致的页面谬误时,调配一页物理内存,将相干文件的 4096 字节读取到该页面中,并将其映射到用户地址空间。应用 readi 读取文件,它须要一个偏移参数来读取文件(但您必须锁定/解锁传递给 readi 的 inode)。不要遗记在页面上正确设置权限。运行 mmaptest;它应该达到第一个munmap。实现munmap:找到地址范畴的 VMA 并勾销映射指定的页面(提醒:应用 uvmunmap)。如果 munmap 删除了前一个 mmap 的所有页面,它应该缩小相应struct file的援用计数。如果已批改未映射的页面并且文件已映射MAP_SHARED,请将该页面写回该文件。查看filewrite以取得灵感。现实状况下,您的实现只会写回真正被程序修改的MAP_SHARED页面。RISC-V PTE 中的脏位 (D) 批示是否已写入页面。然而,mmaptest 不会查看非脏页面是否没有写回; 因而,您能够在不查看 D 位的状况下从新编写页面。批改 exit 以勾销映射过程的映射区域,就像调用 munmap 一样。运行 mmaptest; mmap_test应该通过,但可能不会通过fork_test。批改fork以确保子级与父级具备雷同的映射区域。不要遗记递增 VMA struct file的援用计数。在子级的页面谬误处理程序中,能够调配新的物理页面,而不是与父级共享页面。后者会更酷,但须要更多的工作。运行 mmaptest; 它应该通过mmap_test和fork_test。实现增加mmap和munmap零碎调用:增加零碎调用通用步骤(在用户空间申明零碎调用、增加条目,在内核空间减少零碎调用命令序号以及对应的sys_mmap()和sys_munmap()函数)在proc.h中struct proc中增加映射区域:首先定义映射区域构造体struct MapArea及一个过程映射区域的数量: ...

April 5, 2023 · 4 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab9-file-system

Large files (moderate)要求在此作业中,您将减少 xv6 文件的最大大小。目前 xv6 文件限度为 268 个块,或 268*BSIZE 字节(在 xv6 中 BSIZE 为 1024)。此限度来自这样一个事实,即 xv6 inode 蕴含 12 个“间接”块号和一个“单间接”块号,它指的是最多可包容 256 个块号的块,总共 12+256=268 个块。 bigfile 命令创立它所能创立的最长文件,并报告该大小: $ bigfile..wrote 268 blocksbigfile: file is too small$测试失败,因为 bigfile 心愿可能创立蕴含 65803 个块的文件,但未修改的 xv6 将文件限度为 268 个块。您将更改 xv6 文件系统代码以反对每个 inode 中的“双间接”块,其中蕴含 256 个单间接块地址,每个地址最多能够蕴含 256 个数据块地址。后果将是文件将可能蕴含多达 65803 个块,或 256*256+256+11 块(11 而不是 12,因为咱们将就义一个间接块号来换取双间接块)。 筹备工作mkfs 程序创立 xv6 文件系统磁盘映像,并确定文件系统总共有多少块; 此大小由kernel/param.h中的 FSSIZE 管制。你将看到此试验存储库中的 FSSIZE 设置为 200,000 个块。您应该在 make 输入中看到以下来自 mkfs/mkfs 的输入:nmeta 70 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 25) blocks 199930 total 200000 ...

March 28, 2023 · 4 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab8-Locks

Memory allocator (moderate)要求程序 user/kalloctest 强调 xv6 的内存分配器:三个过程增长和放大它们的地址空间,导致对 kalloc 和 kfree 的许多调用。kalloc 和 kfree 获取kmem.lock 。Kalloctest 打印(作为“#test-and-set”)在acquire中因为尝试获取另一个内核曾经持有的锁(用于 kmem 锁和其余一些锁)而循环迭代的次数。获取中的循环迭代次数是锁争用的粗略度量。kalloctest 的输入如下所示: $ kallocteststart test1test1 results:--- lock kmem/bcache statslock: kmem: #test-and-set 83375 #acquire() 433015lock: bcache: #test-and-set 0 #acquire() 1260--- top 5 contended locks:lock: kmem: #test-and-set 83375 #acquire() 433015lock: proc: #test-and-set 23737 #acquire() 130718lock: virtio_disk: #test-and-set 11159 #acquire() 114lock: proc: #test-and-set 5937 #acquire() 130786lock: proc: #test-and-set 4080 #acquire() 130786tot= 83375test1 FAILstart test2total free number of pages: 32497 (out of 32768).....test2 OKstart test3child done 1child done 100000test3 OKstart test2total free number of pages: 32497 (out of 32768).....test2 OKstart test3child done 1child done 100000test3 OK您可能会看到与此处显示的计数不同,并且前 5 个争用锁的程序也不同。 ...

March 27, 2023 · 6 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab7-Networking

networking在本试验中,你将为网络接口卡 (NIC) 编写 xv6 设施驱动程序。 要求前言您将应用名为 E1000 的网络设备来解决网络通信。对于xv6(以及您编写的驱动程序),E1000看起来像连贯到实在以太网局域网(LAN)的实在硬件。实际上,您的驱动程序将与之通信的 E1000 是 qemu 提供的模仿,连贯到 LAN 也由 qemu 模仿。在此模仿 LAN 上,xv6(“客机”) 的 IP 地址为 10.0.2.15。Qemu 还安顿运行 qemu 的计算机呈现在 IP 地址为 10.0.2.2 的局域网上。当 xv6 应用 E1000 将数据包发送到 10.0.2.2 时,qemu 会将数据包传送到运行 qemu 的(实在)计算机上的相应应用程序(“主机”)。 您将应用 QEMU 的“用户模式网络堆栈”。QEMU 的文档在此处提供了无关用户模式堆栈的更多信息。咱们更新了 Makefile 以启用 QEMU 的用户模式网络堆栈和 E1000 网卡。 Makefile 将 QEMU 配置为将所有传入和传出数据包记录到试验目录中的 packets.pcap 文件。查看这些记录以确认 xv6 正在传输和接管您冀望的数据包可能会有所帮忙。显示记录的数据包的命令:`tcpdump -XXnr packets.pcap` 咱们已将一些文件增加到此试验的 xv6 存储库中。文件 kernel/e1000.c 蕴含 E1000 的初始化代码以及用于发送和接管数据包的空函数,您将补充这些函数。kernel/e1000_dev.h 蕴含由 E1000 定义并在英特尔 E1000 软件开发人员手册中形容的寄存器和标记位的定义。kernel/net.c 和 kernel/net.h 蕴含一个简略的网络堆栈,用于实现 IP、UDP 和 ARP 协定。这些文件还蕴含用于保留数据包的灵便数据结构的代码,称为 mbuf。最初,kernel/pci.c 蕴含当 xv6 启动时在 PCI 总线上搜寻 E1000 卡的代码。 ...

March 27, 2023 · 4 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab6-Multithreading

Uthread: switching between threads (moderate)要求在本练习中,您将为用户级线程零碎设计上下文切换机制,而后实现它。为了帮忙您入门,您的 xv6 有两个文件 user/uthread.c 和 user/uthread_switch.S,以及Makefile中用于构建 uthread 程序的规定。uthread.c 蕴含大部分用户级线程包,以及三个简略测试线程的代码。线程包短少一些用于创立线程和在线程之间切换的代码。 您的工作是制订一个打算来创立线程并保留/复原寄存器以在线程之间切换,并实现。当你实现后,在xv6上运行uthread将看到下列输入(三个测试秩序不确定): $ uthreadthread_a startedthread_b startedthread_c startedthread_c 0thread_a 0thread_b 0thread_c 1thread_a 1thread_b 1...thread_c 99thread_a 99thread_b 99thread_c: exit after 100thread_a: exit after 100thread_b: exit after 100thread_schedule: no runnable threads$此输入来自三个测试线程,每个测试线程都有一个循环,该循环打印一行,而后将 CPU 提供给其余线程。 然而,此时,如果没有上下文切换代码,则不会看到任何输入。 你将须要在user/uthread.c中的thread_create()、thread_schedule()以及user/uthread_switch.S中的thread_switch中增加代码。一个指标是确保当 thread_schedule() 首次运行给定线程时,线程在其本人的堆栈上执行传递给 thread_create() 的函数。另一个指标是确保thread_switch保留被切换的线程的寄存器,复原正在切换到的线程的寄存器,并返回到后一个线程指令中上次中断的地位。您必须决定在何处保留/复原寄存器;批改struct thread以保留寄存器是一个很好的打算。您须要在thread_schedule中增加对thread_switch的调用; 您能够传递您须要的任何参数到thread_switch中,但目标是从线程 t 切换到next_thread。提醒thread_switch只须要保留/复原被调用方保留寄存器。为什么?您能够在user/uthread.asm中看到uthread的汇编代码,这对于调试可能很不便。对于gdb调试实现线程实现的思路:依据提醒、参考内核中过程的切换,可确定思路: 线程状态与上下文:创立一个构造体记录线程本人的上下文和状态。在一个全局的该构造体数组中保护所有线程线程调度:在全局数组中寻找下一个可运行的线程,而后进行线程的切换操作线程切换:取出上一个线程寄存器中的值(状态)保留,将要执行的线程上一次保留的状态放入寄存器线程创立(线程初始化):须要让线程执行传入的函数指针所指的函数、并实现线程堆栈初始化 通过ra寄存器实现线程的命令初始化:ra寄存器示意函数返回地址,因而可用此寄存器保留要执行的函数地址,在thread_switch后将会返回thread_schedule()函数,若将ra更改为传入函数地址,则程序也会跳到传入函数的地址去执行命令堆栈初始化:每个线程都该当有本人独立的堆栈,线程运行时通过寄存器sp来读写堆栈。寄存器sp保留指向堆栈顶部的指针,但线程的堆栈增长方向是从下向上增长的(即从大内存编号向向内存编号增长),因而sp中的指针该当指向struct thread中堆栈数组的最初一个元素。线程状态和上下文:创立构造体struct context保留线程上下文,并在线程构造体中退出该字段 struct context { uint64 ra; // return address uint64 sp; // stack pointer // callee-saved uint64 s0; uint64 s1; uint64 s2; uint64 s3; uint64 s4; uint64 s5; uint64 s6; uint64 s7; uint64 s8; uint64 s9; uint64 s10; uint64 s11;};struct thread { char stack[STACK_SIZE]; /* the thread's stack */ int state; /* FREE, RUNNING, RUNNABLE */ struct context context;};线程初始化:在thread_create()中实现线程初始化:①将函数地址放入保留函数返回地址的ra寄存器;②将堆栈数组最初一个元素的指针放入保留堆栈顶指针的sp寄存器 ...

March 24, 2023 · 4 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab5-CopyonWrite-Fork

前言虚拟内存提供某种间接援用:内核能够通过将PTE标记为有效或只读来截获内存援用,从而导致页面谬误,也能够通过批改PTE来更改地址含意。计算机系统中有一个说法:任何零碎问题都能通过某种间接援用来解决。本试验探讨了一个示例:copy-on-write(COW)fork 问题xv6中的fork()syscall复制父过程所有用户空间的内存到子过程中。如果父过程很大,这个拷贝过程就会花很长时间。更蹩脚的是,这个拷贝常常被节约:fork()函数后子过程通常追随一个exec(),这会抛弃复制的内存、通常不应用大部分内存。另一方面,若父子过程其中一个或都写入了这个复制的页面,则这个拷贝过程是须要的。 解决方案你在实现COW fork()过程中的指标是,推延开拓空间和拷贝物理内存页直到真正须要这些拷贝时。 COW fork()仅为子过程创立一个页表,其中用于用户内存的PTE指向父过程物理页。COW fork()将父子过程所有用户PTE都标记为只读。当任一过程尝试写入这些 COW 页之一时,CPU 将强制呈现页谬误。内核页面谬误处理程序检测到这种状况,为谬误过程调配一页物理内存,将原始页面复制到新页面中,并在谬误过程中批改相干 PTE 以援用新页面,这次 PTE 标记为可写。当页面谬误处理程序返回时,用户过程将可能写入其页面正本。 COW fork()使开释实现用户内存的物理页面变得更加辣手。给定的物理页可能由多个过程的页表援用,并且仅当最初一个援用隐没时才应开释。在像 xv6 这样的简略内核中,这种相当简略,但在生产内核中,这可能很难正确。例如,Patching until the COWs come home. 要求在xv6内核中实现copy-on-write fork。为帮忙你的实现,咱们曾经提供了一个程序指令cowtest(源码在user/cowtest.c)。cowtest运行各种测试,但如果过没有更改xv6即使第一个测试也会失败。这个“简略的”测试开拓超过一半的可用物理内存,而后fork()s。个别状况下,fork会因为没有足够的闲暇物理内存给子过程一个残缺的拷贝而失败。当你实现后,该当通过cowtest usertests -q的所有测试。 正当的attack打算批改 uvmcopy() 以将父级的物理页面映射到子级,而不是调配新页面。革除子级和父级的 PTE 中已被置位PTE_W的PTE_W位。批改 usertrap() 以辨认页面谬误。当最后可写的 COW 页面上产生写入页面谬误时,应用 kalloc() 调配一个新页面,将旧页面复制到新页面,并在被置位为PTE_W的 PTE 中装置新页面。最后为只读的页面(未映射PTE_W,如文本段中的页面)应放弃只读并在父过程和子过程之间共享; 尝试编写此类页面的过程应该被kill()。确保每个物理页在最初一个 PTE 援用隐没时被开释--但不是之前。执行此操作的一个好办法是为每个物理页面保留援用该页面的用户页表数量的“援用计数”。将页面的援用计数设置为 1 当 kalloc() 调配它时。当 fork 导致子级共享页面时,递增页面的援用计数,并在每次任何过程从其页表中删除页面时递加页面的计数。kfree()应该只在援用计数为零时将页面放回自在列表。能够将这些计数保留在固定大小的整数数组中。您必须制订一个如何索引数组以及如何抉择其大小的计划。例如,你能够用页面的物理地址除以 4096 来索引数组,并为数组提供一个数字,这个数等于 kalloc.c 中的kinit()搁置在闲暇列表中的任何页面的最高位物理地址。能够随便批改kalloc.c(例如kalloc(), kfree())来保护援用计数。批改 copyout() 以在遇到 COW 页面时应用与页面谬误雷同的计划。提醒记录每个PTE是否是COW映射会很有用。为此,你能够应用RISC-V PTE的RSW(软件预留)置位。无关页表flags的一些有用的宏和定义位于kernel/riscv.h开端。若产生一个COW页面谬误且内存不够,则该过程该当被kill实现复制页表但推延复制页表内容:依据提醒,我从fork()中调用的uvmcopy()动手。uvmcopy()本来的作用是:把父过程页表中每个PTE所指内容都拷贝到新开拓的空间内并在子过程的页表上造成映射。当初须要把开拓空间这一步去掉,间接在子页表造成映射,然而要扭转flags。为了让代码难看点、放弃逻辑清晰,我创立一个新函数cow_uvmcopy()并在uvmcopy()中调用它。新函数根本与老函数保持一致,不过去掉开拓空间、加上扭转flags: int cow_uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) { pte_t *pte; uint64 pa, i; for(i = 0; i < sz; i += PGSIZE){ if((pte = walk(old, i, 0)) == 0) panic("uvmcopy: pte should exist"); if((*pte & PTE_V) == 0) panic("uvmcopy: page not present"); pa = PTE2PA(*pte); // 扭转flags // *pte = (*pte & (~PTE_W)) | PTE_C; // 错的!(后续阐明) if (*pte & PTE_W) { // 把父过程PTE的PTE_W地位0,再加上PTE_C位 *pte = (*pte & (~PTE_W)) | PTE_C; } // 间接将父过程物理地址在子过程页表中建设映射 if(mappages(new, i, PGSIZE, (uint64)pa, PTE_FLAGS(*pte)) != 0) { goto err; } increaseReference(pa); // 援用计数+1(后续阐明) } return 0;err: uvmunmap(new, 0, i / PGSIZE, 1); return -1;}无关flags的设置:首先要把父子过程的PTE_W给去掉,这样能力在未来触发page fault。而后就是加上对于cow的标记,须要在riscv.h中自定义,能够定义在预留位上(详见Lab3 page tables)#define PTE_C (1L << 8)。未来可通过该bit位判断是本来就不可读的还是cow机制强加的不可读。 ...

March 23, 2023 · 3 min · jiezi

关于c:MIT-6181068286S081-操作系统工程-Lab4-Trapsdoing

RISC-V assembly (easy)问题哪些寄存器保留函数的参数?例如,在main函数对printf的调用中,哪个寄存器保留13?main函数中哪里调用了汇编代码中的f函数和g函数?(编译器可能内联函数)printf位于哪个地址?紧接着在main中跳转到printf之后,寄存器ra中保留的值是什么?运行下列代码: unsigned int i = 0x00646c72;printf("H%x Wo%s", 57616, &i);输入什么? 输入取决于RISC-V是小端序(little-endian)的事实。若RISC-V是大端序(big-endian),则i该当设置成什么能力给出雷同的输入?须要扭转57616吗? 下列代码中,y=之后将会输入什么?(非特定值)为何? printf("x=%d y=%d", 3);答复a0 a1 a2(main函数只有这仨,实际上到a7); a2保留13在0x26处,看到间接将12给寄存器a1,阐明f的调用被优化掉了在0x34处,可知jalr跳转到1554 + ra地址处。而依据0x30处,寄存器ra保留了0x30的地址,因而jalr跳转到1554(dec)+0x30(hex)=0x642(hex)的指标地址,因而printf的地址是0x642,发现与正文雷同0x38输入:HE110 World 首先%x输入57616的十六进制模式而后是值为0x00646c72的i,%s实际上读取的是字符串,也就是一个又一个char,所以i这个int类型实际上能够看作由4个char类型组成,而后就是这4个char怎么排列的问题大端序即:0x12345678 -> 0x 12 34 56 78(从左到右地址序号增大)小端序即:0x12345678 -> 0x 78 56 34 12(从左到右地址序号增大)材料指出RISC-V采纳小端序,由此可知i在内存当中的贮存为:0x 72 6c 64 00因而会先读取值为0x72的char类型,在ascii中对应字母r;而后是0x6c,对应字母l;0x64对应d;0x00对应终止因而若是大端序,则i应设置为:i = 0x646c7200。而57616不须要变,因为没有产生扭转大小的类型转变。 实践上讲,因为传入两个参数,因而a0和a1寄存器都保留了确定的值,但在printf中因为应用了第三个参数,因而会应用a2的值作为y=后边的输入值;但a2寄存器中的值并不能确定,因而不确定输入后果。 但实际上,我尝试屡次都是1。可能是因为没有函数应用寄存器a2,a2就始终放弃值为1的状态吧Backtrace (moderate)要求回溯对debug十分有用:在error产生时刻的堆栈上零碎调用的一个列表。为实现回溯,编译器生成机器码,在堆栈上保护以后调用链上每个函数对应的堆栈帧。每个堆栈帧由返回地址和一个指向调用者堆栈帧的帧指针组成。寄存器s0包含一个指向以后堆栈帧的指针(理论指向堆栈上保留的返回地址+8).你的backtrace该当应用帧指针遍历堆栈并打印堆栈帧上保留的返回地址。 在kernel/printf.c中实现backtrace()。在sys_sleep中调用此函数。运行命令bttest,会调用sys_sleep。输入格局该当如下所示: backtrace:0x0000000080002cda0x0000000080002bb60x0000000080002898在终端运行命令addr2line -e kernel/kernel并将backtrace输入的地址复制粘贴过去就能失去以下输入: kernel/sysproc.c:74kernel/syscall.c:224kernel/trap.c:85提醒在defs.h中增加定义。GCC编译器将以后函数的帧指针保留在寄存器s0中。在kernel/risv.h中增加上面的函数: static inline uint64 r_fp() { uint64 x; asm volatile("mv %0, s0" : "=r" (x) ); return x;}在backtrace中调用该函数可通过内联的机器码读取寄存器s0 堆栈帧布局示意图(如下)。留神,返回地址位于堆栈帧帧指针固定偏移-8的中央;以及保留的帧指针位于(以后)帧指针固定偏移-16的地位。 Stack . . +-> . | +-----------------+ | | | return address | | | | previous fp ------+ | | saved registers | | | local variables | | | ... | <-+ | +-----------------+ | | | return address | | +------ previous fp | | | saved registers | | | local variables | | +-> | ... | | | +-----------------+ | | | return address | | | | previous fp ------+ | | saved registers | | | local variables | | | ... | <-+ | +-----------------+ | | | return address | | +------ previous fp | | | saved registers | | | local variables | |$fp --> | ... | | +-----------------+ | | return address | | | previous fp ------+ | saved registers |$sp --> | local variables | +-----------------+backtrace须要辨认最初的栈帧以进行递归。一个有用的事实是,为每个内核堆栈调配的内存由单个对齐内存页组成,因而给定堆栈上的所有堆栈帧都在同一内存页上。你能够应用PGROUNDDOWN(fp)辨认帧指针援用的内存页如果backtrace运行失常,能够在panic中调用它。 ...

March 21, 2023 · 2 min · jiezi

关于c:MIT-Lab3-Page-tables编辑中

Detect which pages have been accessed (hard)要求为xv6实现一个新性能:通过查看 RISC-V 页表中的拜访位,检测并报告用户空间拜访了哪些页。 实现零碎调用pgacess,报告拜访过哪些页面。该零碎调用须要三个参数:(1)第一个用户须要检测的页面的起始虚拟地址;(2)须要检测的页面数量;(3)一个指向缓存的用户地址,用以贮存位掩码后果。提醒: 在user/pgtlbtest.c中理解如何应用 pgaccess。在kernel/sysproc.c中实现sys_pgacess,可用argaddr()或argint()传递参数对于位掩码,可保留在内核长期缓存区,在获取正确的位掩码之后通过copyout()拷贝给用户可设置扫描页面数的下限kernel/vm.c中的walk()有助于找到正确的PTEs(页表项)在kernel/riscv.h 定义拜访位PTE_A,参考手册确定其值在查看一个页面的PTE_A位是否被设置后,切记要复原它。否则无奈检测自上次pgacess之后用户是否再次拜访它vmprint()可能对debug有帮忙实现后果A kernel page table per process (hard)要求为每个过程提供一个内核页表。 为了让每个过程在内核运行时可能应用本人拷贝的内核页表,咱们须要批改内核。批改struct proc为每个过程保护一个内核页表、批改调度程序好让它在切换时切换内核页表。对于此步骤,每个过程的内核页表该当与现有的全局页表雷同。提醒: 为struct proc增加字段正当为新过程产生一个内核页表的办法是批改kvminit而不是批改kernel_pagetable。你须要在allocproc调用此函数确保每个过程的内核页表都有一个到内核堆栈的映射。在未修改的xv6中,内核堆栈在procinit实现设置。你须要将这部分性能的局部或全副移到allocproc中。批改scheduler()以将过程的内核页表加载到外围的satp寄存器(参考kvminithart)。切记在调用w_satp()后调用sfence_vma()scheduler()在没有过程运行时该当应用kernel_pagetable在freeproc中开释内核页表你须要一个开释页表但不开释页物理内存页的办法vmprint可能对debug有用至多须要批改的中央:kernel/vm.c, kernel/proc.c; 不能动的中央:kernel/vimcopyin.c, kernel/stats.c/ user/usertests.c, user/stats.c实现后果Simplify copyin/copyinstr (hard)要求内核中的copyin函数通过将用户指针转变为内核能间接解援用的物理地址,进而读取用户指针所指的内存。这种转变可通过在软件中遍历过程页表实现。你须要为每个过程的内核页表(之前实现的)增加用户映射,以容许copyin间接解援用用户指针。 将kernel/vm.c中的copyin的函数体替换为对copyin_new(在kernel/vmcopyin.c中定义)的调用;copyinstr和copyinstr_new同理。为使copyin_new和copyinstr_new失常工作,须要增加用户地址到每个过程的内核页表之间的映射。该计划依赖于用户虚拟地址范畴与内核虚拟地址范畴不重合。xv6将从0开始的虚拟地址作为用户地址空间,而内核内存从更高的地址开始。然而,该计划必须限度用户过程的大小小于内核最低虚拟地址。在内核被疏导后,在xv6中那个地址是0xC000000,即PLIC寄存器的地址。参考 kernel/vm.c 中的 kvminit()、kernel/memlayout.h 和文本中的图 3-4。你须要批改xv6来爱护用户过程不能大于PLIC地址。提醒: 在移用到copyinstr之前,首先将copyin()替换为对copyin_new的调用并使其失效。每当内核切换过程的用户映射时,也都该当以同样的形式切换过程的内核页表。(这些状况包含fork() exec() sbrd())切记在userinit中将第一个过程的用户页表包含在内核页表中在内核页表中的用户地址PTE须要哪些权限?(内核无法访问PTE_U的内存页)切记PLIC限度实现后果

March 19, 2023 · 1 min · jiezi

关于c:从Dwarf-Error说开去

背景近期我开发的一个C程序,在生产环境产生了coredump,然而在调试该core文件时,打出的debug信息并不全。 这种debug信息失落,其实说白了,就是符号表失落。个别由两种状况造成,一种是编译的时候没有加-g参数,另一种是dwarf版本不对。首先排除第一种可能,因为编译脚本是我本人写的,-g参数是有的。而惟一可能出问题的中央,就是dwarf版本不对。而之所以呈现dwarf版本不对,还是编译环境的问题。我为了兼容编译C++17规范的另外一个cpp我的项目,就对编译环境做了容器化解决,在镜像里装置了gcc11.3,而在生产环境应用的时候,gdb版本依然是4.8.5,因为gcc版本和gdb版本不匹配,就造成了该问题的呈现。为了验证这一点,我在物理机上重现了这种景象: [root@ck08 ctest]# gcore `pidof flow`Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4) [in module /root/chenyc/src/flow/flow][New LWP 3048][New LWP 3047][New LWP 3046][New LWP 3045][Thread debugging using libthread_db enabled]Using host libthread_db library "/lib64/libthread_db.so.1".0x00007f50dfd850e3 in epoll_wait () from /lib64/libc.so.6warning: target file /proc/3044/cmdline contained unexpected null charactersSaved corefile core.3044[Inferior 1 (process 3044) detached]我的物理机的gdb版本也是4.8.5, 我应用gcore命令生成core文件的时候,呈现了上面的正告:Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4),这句话从字面意思很好了解,就是说,gdb反对的dwarf版本应该是2,3,或者4,然而以后二进制文件的dwarf版本是5,无奈调试。那么,何为dwarf?什么又是dwarf版本呢? ...

March 17, 2023 · 5 min · jiezi

关于c:MIT-618106828-操作系统工程-Lab1-Utilities

Lab1 Xv6 and Unix utilitiesBoot xv6 (easy)从指定仓库clone代码,而后编译-运行,尝试一部分命令,没问题之后就能够正式开始了! sleep (easy)试验要求实现Unix程序sleep,使程序暂定指定数量的ticks。并将解决方案放在文件user/sleep.c中。一些提醒: 参考其余代码如果获取用户输出参数,并对用户输出参数做出肯定判断(未指定工夫报错exit(1))能够应用零碎调用sleepmain函数最初退出应调用exit(0)将程序增加到Makefile中具体实现留神参数判断和工夫小于0的状况。主函数实现如下: int main(int argc, char* argv[]) { if (argc < 2) { printf("Usage: sleep [time]\n"); exit(1); } else { int time = atoi(argv[1]); if (time < 0) exit(1); sleep(time); exit(0); }}试验后果程序成果:测试后果:pingpong (easy)试验要求实现父子过程之间传输数据并打印过程ID和后果。具体为:父过程发一字节,子过程收到打印"<pid>: receive ping",再把数据发回父过程,退出;父过程收到数据打印"<pid>: receive pong"并退出。文件保留在user/pingpong.c中。一些提醒: 创立管道pipe创立子线程forkread从管道读取,write写入管道getpid获取过程ID具体实现注意事项: 管道读写的方向(0读1写)留神敞开文件描述符父过程wait期待子过程完结,这样可保障输入程序具体实现: int main() { int fd1[2], fd2[2], pid; char buf; pipe(fd1); pipe(fd2); pid = fork(); if (pid < 0) { fprintf(2, "fork() error\n"); exit(1); } else if (pid > 0) { // father close(fd1[0]); close(fd2[1]); write(fd1[1], "a", 1); read(fd2[0], &buf, 1); close(fd1[1]); close(fd2[0]); wait(0); printf("%d: received pong\n", getpid()); } else { // child close(fd2[0]); close(fd1[1]); read(fd1[0], &buf, 1); write(fd2[1], &buf, 1); close(fd2[1]); close(fd1[0]); printf("%d: received ping\n", getpid()); } exit(0);}试验后果失败后果其实最开始我没有加wait期待子过程完结,这样的后果就会造成最根本的并发编程谬误,导致父子过程输入的后果可能是交错在一起的,比方这样:当然也能够通过sleep或其余形式解决失常后果测试后果primes (moderate)/(hard)试验要求实现一个并发的素数筛选程序。具体思路参考:Hins: ...

March 16, 2023 · 4 min · jiezi

关于c:c语言for循环语句的运用

咱们晓得在C语言中有while循环语句和for循环语句。本篇次要讲讲for循环语句的使用。 例如求阶乘:n!=1x2x3x...xn #include <stdio.h>int main (){ int n; int i=1; int ret; scanf("%d",&n); for (i=1;i<=n;i++){ //i++是在每次循环完结之后才执行的// ret*=i; } printf("%d\n",ret); return 0;}for和while语句有相似之处,都有小括号和大括号,不同之处在于for中的小括号蕴含了三个局部,这三个局部用 ;隔开,他们别离为:初始动作、循环持续的条件、循环每一轮要做的事。如上示代码,初始动作是i=1,循环持续的条件是i<=n,循环每一轮要做的事i++ 简略来说for语句的应用框架如下: for(初始动作;循环持续的条件;循环每一轮要做的事){ 语句}

March 13, 2023 · 1 min · jiezi

关于c:C4-布尔类型和引用

布尔类型C++中的布尔类型C++在C语言的根本类型零碎之上减少了boolC++中的bool可获得值true和false实践上bool只占用一个字节bool类型只有true和false两个值 #include "stdio.h"int main(int argc,char *argv[]){ bool b = 0; printf("b=%d\n",b); b++; printf("b=%d\n",b); b=b-3; printf("b=%d\n",b); return 0;}输入后果:011 #include "stdio.h"int main(int argc,char *argv[]){ bool b= false; int a = b; printf("sizeof(b) = %d\n",sizeof(b)); printf("b=%d,a=%d\n",b,a); b = 3; a = b; printf("b=%d,a=%d\n",b,a); b = -5; a = b; printf("b=%d,a=%d\n",b,a); a = 10; b = a; printf("a=%d,b=%d\n",a,b); a = 0; b = a; printf("a=%d,b=%d\n",a,b); return 0;}输入后果: 布尔类型是C++中的根本数据类型能够定义bool类型的全局变量能够定义bool类型的常量能够定义bool类型的指针能够定义bool类型的数组C++对三目运算符进行了降级 三目运算符C语言中的三目运算符返回的是变量值不能作为左值应用 C++中的三目运算符可间接返回变量自身即可作为右值应用,也能够作为左值应用留神:三目运算符可能返回的值如果有一个是常量值,则不能作为左值应用; #include "stdio.h"int main(int argc,char *argv[]){ int a = 1; int b = 2; (a<b?a:b) = 3; printf("a = %d,b = %d\n",a,b);} ...

March 7, 2023 · 1 min · jiezi

关于c:C3-进化后const分析

C语言中的constconst润饰的变量是只读的,实质还是变量const润饰的局部变量在栈上调配空间const润饰的全局变量在只读存储区调配空间const只在编译器有用,再运行期无用const润饰的变量不是真的变量,只是通知编译器该变量不能呈现再赋值符号的右边const使得变量只有只读属性const将具备全局生命周期的变量存在只读存储区const不是真正意义上的常量,只有枚举是真正意义的常量 C编译器: #include"stdio.h"int main(){ const int c = 0; int * p= (int*)&c; printf("Begin...\n"); *p = 5; printf("c = %d\n",c); printf("End...\n"); return 0; }输入后果:Begin...c = 5End... C++编译器: #include"stdio.h"int main(){ const int c = 0; int * p= (int*)&c; printf("Begin...\n"); *p = 5; printf("c = %d\n",c); printf("End...\n"); return 0; }输入后果:Begin...c = 0End... C++中的constC++在C的根底上对const进行了进化解决当碰见const申明时在符号表中放入常量编译过程中若发现应用常量则间接以符号表中的值替换编译过程中若发现下述状况给对应的常量调配存储空间对const常量应用了extern对const常量应用&操作符 C++编译器尽管可能为const常量调配空间,但不会应用其存储空间中的值。 C语言中的const变量C语言中const变量是只读变量,会调配存储空间C++中的const常量 -可能调配存储空间当const常量全局,并且须要在其余文件中应用当应用&操作符对const常量取地址 C++中const常量相似于宏定义const int c = 5; ≈#define c 5 C++中的const常量在宏定义中不同const常量是由编译器解决编译器对const常量进行类型检查和作用域查看宏定义由预处理器解决,单纯的文本替换 #include "stdio.h"void f(){ #define a 3 const int b = 4;}void g(){ printf("a=%d\n",a);}int main(){ const int A = 1; const int B = 2; int array[A+B] = {0}; int i = 0; for(i = 0;i<(A+B);i++) { printf("array[%d] = %d\n",i,array[i]); } f(); g(); return 0;} ...

March 2, 2023 · 1 min · jiezi

关于c:如何一眼分辨是C还是C

C语言的历史C语言是由贝尔实验室的Dennis Ritchie在20世纪70年代初开发的一种通用程序设计语言。在晚期的计算机时代,许多计算机应用不同的汇编语言编写程序,这导致了程序的可移植性和代码的可重用性很低。因而,Dennis Ritchie在开发C语言时试图发明一种更具可移植性和可重用性的高级语言。 C语言的语法相似于B语言,是由Ken Thompson开发的一种晚期的高级语言。C语言在其发明的初期被用于开发Unix操作系统。因为C语言的高效性和可移植性,它很快成为了许多操作系统和应用程序的规范编程语言。 在20世纪80年代,ANSI(美国国家标准协会)开始制订C语言的规范。该规范于1989年正式公布,通常称为ANSI C或C89。这个规范定义了C语言的根本语法和库函数,使得C语言的可移植性更加优良。 C++语言的历史C++是在20世纪80年代由Bjarne Stroustrup开发的一种面向对象的编程语言,它扩大了C语言的语法。Bjarne Stroustrup最后开发C++是为了解决C语言的一些限度,使其更适宜大型软件我的项目的开发。 C++语言最后称为“C with Classes”,是在C语言的根底上增加了类和其余面向对象的个性。它于1983年首次公布,并于1998年公布了规范C++。这个规范定义了C++语言的语法、库函数和个性,使得C++语言的可移植性更强,也为C++的广泛应用奠定了根底。 C++语言被广泛应用于系统软件、应用程序、嵌入式零碎、游戏开发和其余畛域。它是许多风行软件的编程语言,如Windows操作系统、MySQL数据库和Adobe Photoshop等。 C和C++的区别一般来说,通过以下几个方面能够一眼分辨C语言和C++语言的代码: 头文件C++语言代码通常应用大量的头文件,而C语言应用的头文件较少。C语言中,头文件次要包含规范库函数,如stdio.h、math.h、string.h等,这些文件在编译器外部曾经定义好了,不须要额定的实现。在应用时,咱们只须要include头文件即可。 C++语言中,除了C语言的规范库头文件外,还有许多第三方头文件,如<iostream>,<vector>,<map>等,这些头文件蕴含了很多较高级的性能,比方面向对象的编程,容器的应用,以及各种模板等。 因而,能够通过判断头文件的类型来判断代码是C语言还是C++语言。如果头文件为C语言规范库函数,那么代码很有可能是C语言;如果头文件为C++语言第三方库,那么代码就很有可能是C++语言。 援用关键字C++语言中有两个关键字:class和namespace,而C语言没有这两个关键字。C语言并不反对援用,而C++则反对援用关键字。在C++中,应用"&"符号申明援用变量,这样援用变量就是另一个变量的别名。例如: int x = 10;int &y = x;在这个例子中,y就是x的援用。更改y的值会间接影响x的值。 另外,C++还反对右值援用,应用"&&"申明,示意对右值的援用。 在总的来说,C++的援用概念比C语言的指针更为简略易用,并且能够更无效地治理内存。 运算符重载C++语言反对运算符重载,但C语言不反对。C++语言是C语言的扩大,反对运算符重载,能够重定义一个运算符的操作,使其与一个用户定义的数据类型配对。换句话说,咱们能够为一个用户定义的数据类型定义相应的运算,而后在代码中应用这些运算符,就像应用根本的运算符一样。 函数重载C++语言反对函数重载,但C语言不反对。C++语言的函数重载是指在一个类中定义了多个名字雷同的函数,然而它们的参数列表不同,参数个数不同,或者参数类型不同,这样的函数就是重载的函数。 C++语言的函数重载容许咱们在同一个作用域内应用雷同的名字,然而它们的行为是不同的,这种个性对于须要更灵便的解决同样的数据结构的场景十分有用。 函数重载的应用是通过C++语言的函数的类型推导机制实现的,编译器会依据函数调用时的参数列表来抉择相应的函数,并执行该函数。因而,C++语言的函数重载能够简化代码,进步代码的可读性和可维护性。 构造函数和析构函数C++语言中有构造函数和析构函数的概念,而C语言没有。C++构造函数和析构函数是C++中十分重要的两种非凡的成员函数。 构造函数:构造函数在每次创建对象时主动调用,并且是专门用来初始化对象的。它的名字与类的名字完全相同,不含返回类型。构造函数能够有多个,参数也能够不同。 析构函数:析构函数在对象生命周期完结时主动调用,并且是专门用来开释对象占用的资源的。它的名字是以“~”结尾,并且与类的名字完全相同。析构函数只有一个,不能有参数。 在C++中,通过应用构造函数和析构函数,咱们能够不便地治理对象的生命周期,确保在对象创立时正确初始化,在对象销毁时正确开释资源。 以上是一些辨别C语言和C++语言的罕用办法,但并不是相对的。最终的判断依然要以代码的特色为准。

February 19, 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: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: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: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语言库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: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: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:防御式编程之断言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:关于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:小凯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:小凯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: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