Linux期末复习——文件I/O编程

news2024/11/27 1:15:08

Linux系统调用以及用户编程接口

三者关系

        系统调用、API以及系统命令之间关系:

什么是文件描述符?

        是一个非负整数,索引值

        打开或者创建一个文件的时候,内核会向进程返回一个文件描述符

        读写文件时,会向函数传递一个文件描述符 

底层文件I/O操作

        特点:不带缓存,直接对文件(设备)进行读写操作

基本文件操作(五个函数)

        open():打开或者创建文件,可以指定文件属性以及用户权限

        

int open(文件名,打开方式,权限)
第一个参数被打开文件名,可以包括路径
第二个参数
O_RDONLY只读方式打开
O_WRONLY只写方式打开
O_RDWR 读写方式打开
O_CREAT不存在就创建
O_NOCTTY如果文件为中断,这个中断不会调用open()的那个进程的控制中断

第三个参数
被打开文件权限S_I(R/W/X)(USR/GRP/OTH)
(读/写/运行)(用户/组/其他人)
返回值

成功:返回文件描述符

失败:-1

        close():关闭一个被打开的文件,成功返回0,失败返回-1

close(文件描述符)
唯一参数fd:文件描述符
返回值

0:成功

-1:出错

        read():把指定的文件描述符中读出的数据放入缓存区

 read(文件描述符,读的地方,读多少)
第一个参数文件描述符
第二个参数指定读的缓存在哪
第三个参数指定读的字节数
返回值

成功:已读字节数

失败:-1

到达文件尾部:0

        write():向打开的文件写数据,从当前指针位置开始。

 write(文件描述符,写的地方,写多少)
第一个参数文件描述符
第二个参数指定写的缓存在哪
第三个参数指定写的字节数
返回值

成功:已写字节数

失败:-1

        lseek():将文件指针定位到相应位置

        lseek(要定位文件的描述符,往那边便宜并且移动多少,从哪开始偏移)
第一个参数文件描述符
第二个参数偏移量,+向前移动,-向后移动
第三个参数
SEEK_SET当前位置是文件开头,新位置是偏移量
SEEK_CUR当前位置是文件指针位置,新位置是当前位置+偏移量
SEEK_END当前位置是文件结尾,新位置是文件大小+偏移量
返回值

成功:文件的当前位移

失败:-1

示例代码: 

/* copy_file.c */

/*头文件部分*/
#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdlib.h>

#include <stdio.h>

/*宏定义*/

#define	BUFFER_SIZE	1024		/* 每次读写缓存大小,影响运行效率,也就是都多少写多少*/

#define SRC_FILE_NAME	"src_file"	/* 源文件名 */

#define DEST_FILE_NAME	"dest_file"	/* 目标文件名文件名 */

#define OFFSET		10240		/* 复制的数据大小,用来算偏移多少 */

 	

int main()

{
    /*首先定义一些整形变量和数组*/
	int src_file, dest_file;/*整形变量用来存放文件描述符*/

	unsigned char buff[BUFFER_SIZE];/*无符号字符数组,用来存储源文件src_file内容*/	

    int real_read_len;	/*用来存储read返回的读到的字节数*/


	/* 以只读方式打开源文件 ,为了之后读取文件,返回的文件描述符赋值给src_file*/
    /*这里文件名字是宏定义*/
	src_file = open(SRC_FILE_NAME, O_RDONLY);

	

	/* 以只写方式打开目标文件,若此文件不存在则创建, 访问权限值为644,即user:读写、group:读、others:读权限,三位八进制数是664,返回的文件描述符赋值给dest_file*/
    /*这里文件名字是宏定义*/
	dest_file = open(DEST_FILE_NAME, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

	/*判断文件描述符是什么,如果open函数返回-1表示打开失败*/
	if (src_file < 0 || dest_file < 0)

	{

		printf("Open file error\n");

		exit(1);

	}	


	/* 将源文件的读写指针移到最后10KB的起始位置*/
    /*SEEK_END将起点设置为文件的结尾,向前偏移10K字节,定为当前文件读写位置*/
    /*如果是SEEK_SET起点就是文件开头,SEEK_CUR起点就是当前指针位置*/
	lseek(src_file, -OFFSET, SEEK_END);

	/* 读取源文件src_file的数据,放在数组buff中,读取的量是sizeof(buff)字节即1024字节*/
/*最后10KB数据并写到目标文件中,每次读写1KB */

    /*当read正常返回独到的字节数的时候*/
	while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0)

	{                     
        /*写入dest_file文件,从buff字符数组,写real_read_len个字节数据*/
		write(dest_file, buff, real_read_len);     
	}

	
    /*最后关闭两个文件*/
	close(dest_file);

	close(src_file);

	
	return 0;

}

  文件锁

        如果文件已经共享,如何让多个用户共同操作一个文件:给文件上锁,避免共享的资源产生竞争状态。 fcntl()函数就是实现的方法

        fcntl()函数:不仅可以施加建议锁,还能上强制锁,还能对文件某一记录上记录锁

        记录锁分为读取锁(共享锁)和写入锁(排斥锁)

        

fcntl(上锁文件的描述符,具体的操作,&lock)函数语法要点
第一个参数文件描述符
第二个参数
F_GETLK根据lock参数,决定上不上锁
F_SETFL设置lock参数值的文件锁
F_SETLKW无法获取锁就进入睡眠

第三个参数lock:记录锁的具体状态

lock结构体1_type变量取值
F_RDLCK读取锁
F_WRLCK写入锁
F_UNLCK解锁

                记录锁源码:

/* lock_set.c 该函数作用:给fd文件描述符所对应的文件上type类型的锁(包括:读锁、写锁或解锁) */

/*测试代码中通过访问lock_set方法来上锁解锁*/
int lock_set(int fd, int type)

{

	struct flock old_lock, lock;/*创建lock结构图*/

	lock.l_whence = SEEK_SET;/* 文件读写指针起始位置在文件开头 */

	lock.l_start = 0;        /*相对位移量;加锁整个文件*/

	lock.l_len = 0;          /*加锁区域长度;加锁整个文件*/

	lock.l_type = type;      /*type包含:读锁、写锁和解锁三个类型;给唯一需要赋值的锁类型l_type赋值文件锁,这里是关键*/

	lock.l_pid = -1;         /*该文件拥有文件锁的进程号,默认为-1*/	

	
    /* 首先需要执行第一次fcntl()函数判断文件是否可以上锁,因为只有文件为解锁(不上锁)状态才能上锁 */
    /*F_GETLK:根据lock参数值,决定是否上锁,get lock*/
    /*&lock:取flock结构体变量lock的首地址*/
	fcntl(fd, F_GETLK, &lock);

                                  

                                    

        /*此处的fcntl()函数只是为了测试fd是否能上锁,因为只有文件为解锁(不上锁)状态才能上锁;如果文件已经上锁,

          根据F_GETLK参数就要看情况判断:

          如果之前已经存在的是读锁则还可以上读锁,如果是写锁则不能上读锁或写锁;

          也就是说:要给一个没上锁的文件上锁要执行两次fcntl()函数;

          第一次执行:F_GETLK参数将文件设置为解锁状态;fcntl()函数返回-1,表达的含义经过测试该文件可以上锁;

          第二次fcntl()函数执行就可以放心大胆的上锁,fcntl()函数会返回0,上锁成功*/

 



	/*如果lock.l_type不是解锁状态,意味着已经存在其他锁*/

	if (lock.l_type != F_UNLCK)

	{

		/* 判断文件不能上锁的原因 */

		if (lock.l_type == F_RDLCK) /* 该文件已有读锁 */

		{

			printf("Read lock already set by process PID:%d\n", lock.l_pid);

		}

		else if (lock.l_type == F_WRLCK) /* 该文件已有写锁 */

		{

			printf("Write lock already set by process PID:%d\n", lock.l_pid);

		}			

	}



       	

	/* l_type 可能已被F_GETLK修改过,所以需要重新赋值*/
	lock.l_type = type;

	


/* 根据不同的type值,第二次执行fcntl函数,进行阻塞式上锁或解锁,所谓阻塞式:意味着如果没有成功上锁或解锁或捕捉到信号,则程序一直处于阻塞状态 */

/*F_SETLKW:在无法获取锁时,进入睡眠,set lock  wait;如果可获得锁或捕捉到信号,则返回*/

/*&lock:取flock结构体变量lock的首地址*/

	if ((fcntl(fd, F_SETLKW, &lock)) < 0) 

    /*此处fcntl()函数作用:给fd文件描述符所对应的文件上lock结构体变量所确定的l_type类型的锁;
      此时用F_GETLKW直接给文件上文件锁,因为第一次fcntl()函数已经测试过
      该文件可以上锁,于是本次fcntl()函数可以成功执行,并返回0 */




	{

		printf("Lock failed:type = %d\n", lock.l_type);

		return 1;

	}

		

	switch(lock.l_type)

	{

		case F_RDLCK:

		{

			printf("Read lock set by process PID:%d\n", getpid());/*该文件已被getpid()进程上了读锁*/

		}

		break;



		case F_WRLCK:

		{

			printf("Write lock set by process PID:%d\n", getpid());/*该文件已被getpid()进程上了写锁*/

		}

		break;



		case F_UNLCK:

		{

			printf("Release lock by process PID:%d\n", getpid());/*该文件已被getpid()进程解锁*/

			return 1;

		}

		break;



		default:

		break;

	}

	

	return 0;

}

读取锁测试:

/* read_lock.c */

#include <unistd.h>

#include <sys/file.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <stdio.h>

#include <stdlib.h>

#include "lock_set.c"


int main(void)

{

	int fd;/*整形变量fd存放文件标识符*/

	
    /*读写方式打开,不存在就创建,权限0644*/
	fd = open("hello",O_RDWR | O_CREAT, 0644);

	if(fd < 0)

	{

		printf("Open file error\n");

		exit(1);

	}	

	

	lock_set(fd, F_RDLCK);/* 给文件上读取锁,上锁后程序处于等待键盘输入字符的等待状态; */



	getchar();/*取键盘键入的字符,可以让程序继续执行下去*/	

	

	lock_set(fd, F_UNLCK);/* 给文件解锁 */



	getchar();

	

	close(fd);

	exit(0);

}

写入锁测试:

/* write_lock.c */

#include <unistd.h>

#include <sys/file.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <stdio.h>

#include <stdlib.h>

#include "lock_set.c"


int main(void)

{

	int fd;	

	

	fd = open("hello",O_RDWR | O_CREAT, 0644);/* 读写打开文件 */

	if(fd < 0)

	{

		printf("Open file error\n");

		exit(1);

	}	

	

	lock_set(fd, F_WRLCK);/* 给文件上写入锁 */

	getchar();	

	

	lock_set(fd, F_UNLCK);/* 给文件解锁 */

	getchar();

	

	close(fd);

	

	exit(0);

}

多路复用

        处理I/O操作可能会被阻塞的情况

        select()函数有什么用:

  • 提高性能:select能够帮助程序在处理多个输入/输出源时更加高效。通过使用select,可以使得程序在等待一个I/O操作完成时继续执行其他任务,从而提高整体性能。
  • 可扩展性:select使得程序可以处理更多的并发连接。这对于开发服务器应用程序尤为重要,因为它们需要同时处理多个客户端连接。
  • 跨平台兼容性:select是一个通用的I/O复用技术,它在不同的操作系统和平台上都有实现。这意味着使用select编写的代码具有较好的可移植性。
  • 建立基础知识:掌握select这类基本的I/O复用技术,有助于理解更高级和更复杂的技术,例如poll和epoll等。

摘取自:https://blog.csdn.net/qq_21438461/article/details/130143669
 

以下源码演示一个调用select(函数)监听三个终端(两个管道文件虚拟终端,一个主程序虚拟终端)的输入,并且进行相应的处理。在主程序的虚拟终端中输入q/Q实现程序结束,在管道终端数据输入在主程序终端中显示。

/* multiplex_select.c */

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>

/*宏定义*/
#define MAX_BUFFER_SIZE		1024			/* 缓冲区大小*/
#define IN_FILES		3			/* 多路复用时输入文件数目*/
#define TIME_DELAY		600			/* 超时时间秒数 */
#define MAX(a, b)		((a > b)?(a):(b))

int main(void)
{
	int fds[IN_FILES];/*fds[0],fds[1],fds[2]*/
	char buf[MAX_BUFFER_SIZE];
	int i, res, real_read, maxfd;
	struct timeval tv;
	fd_set inset,tmp_inset;/*文件描述符集fd_set,用来保存由select函数监控的文件所对应的文件描述符*/	
	

	fds[0] = 0;

	/*首先按一定的权限打开两个管道源文件*/
    /*按照只读|非阻塞方式打开管道文件in1,非阻塞就是不能等待立即返回结果的含义*/
	if((fds[1] = open ("in1", O_RDONLY|O_NONBLOCK)) < 0)
        
	{
		printf("Open in1 error\n");
		return 1;
	}
  		    
 	if((fds[2] = open ("in2", O_RDONLY|O_NONBLOCK)) < 0)
 	{
 		printf("Open in2 error\n");
		return 1;
	}

	/*取出两个文件描述符中的较大者*/
  	maxfd = MAX(MAX(fds[0], fds[1]), fds[2]);  	

  	/*初始化文件描述符集inset,并在该集合中加入相应的三个文件描述符:标准输入,in1和in2的文件描述符*/
  	FD_ZERO(&inset); 
  	for (i = 0; i < IN_FILES; i++)
  	{
  		FD_SET(fds[i], &inset);
  	}

/*fd_set是一组文件描述字(fd)的集合,它用一位来表示一个fd*/
 
/*过去,一个fd_set通常只能包含小于32的fd(文件描述字),因为fd_set其实只用了一个32位矢量来表示fd;现在,UNIX系统通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,*/ 

/*linux shell 输入如下命令就知道,其中的“-n: file descriptors”就是linux系统允许的文件描述符的最大限制值。
# ulimit -a
-f: file size (blocks) unlimited
-t: cpu time (seconds) unlimited
-d: data seg size (kb) unlimited
-s: stack size (kb) 8192
-c: core file size (blocks) 0
-m: resident set size (kb) unlimited
-l: locked memory (kb) 64
-p: processes 128
-n: file descriptors 1024*/ 

/*FD_SET(0, &set); 将set的0号位置1,如set原来是00000000,则现在变为10000000(对应10进制就是1,2进制转10进制从左往右算),这样fd==0文件描述字就被加进set */ 
   
/*FD_CLR(4, &set); 将set的4号位置0,如set原来是10001000,则现在变为10000000,这样fd==4的文件描述字就被从set中清除了 */  
   
/*FD_ISSET(5, &set); 测试set的5号位是否为1,如果set原来是10000100(对应10进制就是33),则返回非零,表明fd==5的文件描述字在set中;否则返回0*/


  	FD_SET(0, &inset);

  	tv.tv_sec = TIME_DELAY;
  	tv.tv_usec = 0;
  	
  	/*循环测试该文件描述符是否准备就绪,并调用select函数对相关文件描述符做对应操作*/
  	while(FD_ISSET(fds[0],&inset) || FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2], &inset))
  	{ 
        /*文件描述符集合的备份,避免每次进行初始化*/
  		tmp_inset = inset;

  		res = select(maxfd + 1, &tmp_inset, NULL, NULL, &tv);
        /*测试写文件描述符集;
          res = select(maxfd + 1, NULL,&tmp_inset , NULL, &tv);*/

 
                /*maxfd+1:需要监视的文件描述符最大值+1,减少监控二进制位数,提高效率*/
                /* &tmp_inset:由select()监视的读文件描述符集合*/
                /*NULL:由select()监视的写文件描述符集合*/
                /*NULL:由select()监视的异常出错文件描述符集合*/
                /*&tv:若等待了tv时间还没有检测到任何文件描述符准备好,则立即返回*/
              
                /*select()执行成功则返回准备好的文件描述符的数目,如果返回-1代表出错
                该函数执行过后只有数据输入的文件描述符才能继续存在tmp_inset文件描述符集当中,没有输入的则清除出tmp_inset*/
  		
  		switch(res)
  		{
  			case -1:
  			{
  				printf("Select error\n");
  				return 1;
  			}
  			break;
  			
  			case 0: /* Timeout */
  			{
  				printf("Time out\n");
  				return 1;
  			}  			
  			break;
  			
  			default:/*if res非0、非-1,即存在已经准备好的文件描述符,也就是说有某个管道文件或标准输入文件有数据输入*/
  			{
  				for (i = 0; i < IN_FILES; i++)
  				{
  					if (FD_ISSET(fds[i], &tmp_inset))
		  			{
                        /*对buf字符数组的MAX_BUFER_SIZE(1024)个字符赋初值为0*/
		  				memset(buf, 0, MAX_BUFFER_SIZE);
                                               
                        /*从fds[i]指向的文件中读1024个字符到buff字符数组*/
		  				real_read = read(fds[i], buf, MAX_BUFFER_SIZE);
		  		               

		  				if (real_read < 0)
		  				{
		  					if (errno != EAGAIN)
 /*在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。从字面上来看,是提示再试一次。这个错误经常出现在当应用程序进行一些非阻塞(non-blocking)操作(对文件或socket)的时候。例如,以 O_NONBLOCK的标志打开文件/socket/FIFO,如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。*/

		  					{
		  						return 1;
		  					}
		  				}
		  				else if (!real_read)/*real_read等于0:已读到文件末尾*/
		  				{
		  					close(fds[i]);         /*关闭文件描述符*/
		  					FD_CLR(fds[i], &inset);/*将该文件描述符从文件描述符集inset清除出去*/
		  				}
		  				else   /*从文件描述符fds[i]中读到有效数据*/
		  				{
		  					if (i == 0)/*如果是标准输入文件(键盘)输入的数据,看是否是退出标记q或Q*/
		  					{
		  						if ((buf[0] == 'q') || (buf[0] == 'Q'))/*如果主程序收到q或Q字符,则程序终止*/
		  						{
		  							return 1;/*主程序退出*/
		  						}
                                                                else{printf("if you want to quit,please type q!\n"}

		  					}
		  					else 
                                                /*如果不是标准输入文件输入的数据,那只能是管道文件输入的数据,将cat从管道文件输入的字符串打印在屏幕上*/
		  					{
		  						buf[real_read] = '\0';/*在字符数组最后一位添加\0,使之成为字符串*/
		  						printf("%s", buf);    /*按照字符串格式打印输出字符数组buf*/
		  					}
		  				}
		  			} /* end of if */  					
  				} /* end of for */
  			}
  			break;  		
  			
  		} /* end of switch */ 		
  	} /*end of while */
  	
  	exit(0);
}

标准I/O变成

        用到的函数带缓冲,用法类似

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1160745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ModuleNotFoundError: No module named ‘paddle.fluid.incubate.fleet‘

在使用rocketqa的时候可能会遇到下面的问题&#xff1a; 问题&#xff1a; 解决方法&#xff1a; 这完全是paddlepaddle的问题。 在rocketqa/utils/optimization.py出现下面的语句&#xff0c;这个时候直接把出错的注释掉就可以&#xff0c;因为它完全没有用到。&#xff08;…

Win10奇怪的部分文字乱码问题

1.打开Windows设置的时间和语言 2.打开区域下方的的其他日期、时间和区域设置 3.点击更改日期、时间或数字格式。切换到管理 4.点击更改系统区域设置&#xff0c;取消Beta版&#xff1a;使用Unicode UTF-8提供全球语言支持。 按提示重启电脑即可。

Windows电脑怎么下载桌面便签小工具?

Windows电脑是日常办公中常用的工具&#xff0c;电脑上可以安装许多软件来辅助日常办公&#xff0c;其中桌面便签工具可以为大家记录很多日常办公中的各项工作计划&#xff0c;而且便签软件通常可以悬挂于电脑桌面显示&#xff0c;方便大家一边工作一边查看备忘记录。 谈及Win…

探索网络攻击:ARP断网、ARP欺骗和DNS欺骗实验解析

目录 前言 一、ARP概述 1.1 什么是ARP 1.2 ARP协议的基本功能 1.3 ARP缓存表 1.4 ARP常用命令 二、ARP断网实验 三、ARP欺骗实验 3.1 内网截获图片 3.2 HTTP账户密码获取 四、DNS欺骗实验 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&…

OpenGL ES入门教程(一)编写第一个OpenGL程序

OpenGL ES入门教程&#xff08;一&#xff09;编写第一个OpenGL程序 前言 从本文开始我将参考学习OpenGL ES应用开发实践指南 Android卷 [&#xff08;美&#xff09;KevinBrothaler著]&#xff08;提取码: 394m&#xff09;&#xff0c;并基于自己的理解以更加通俗易懂的方式…

[.NET]桃源网络硬盘 v7.4

桃源网络硬盘是一个以.net进行开发的网络硬盘系统源码。 开发环境&#xff1a;Microsoft.NET Framework SDK 运行要求&#xff1a; Windows 2003及以上版本 或 Windows XP及以上版本&#xff0c;安装 .Net Framework 要求上传目录有写权限 请将其放在虚拟目录或单独的网页空间…

Kubernetes包管理工具Helm简介及使用

文章目录 前言技术积累什么是HelmHelm的核心概念Helm可以解决哪些痛点Helm中文官方文档 Helm安装Helm安装nginx用例写在最后 前言 大家都知道K8S是云原生devops的一大利器&#xff0c;可以直接让我们的中间件、应用服务直接运行在云端&#xff0c;让我们可以只关心自身的业务功…

【Unity】【VR开发疑难】Unity运行就报无法启动XR Plugin

【现象】 连接Link后运行Unity的VR项目Link也无反映&#xff0c;Unity控制台报&#xff1a;无法启动XR Plugin&#xff0c;并说是由于Oculus头盔未连接导致。 【分析】 打开Oculus PC客户端&#xff0c;发现状态是连接正常。重启机器后&#xff0c;提示Oculus没有出于RunTim…

概念解析 | 揭开心电图测量的神秘面纱

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:ECG的测量原理 揭开心电图测量的神秘面纱 How to read an ECG – Physical Therapy Reviewer 1. 背景介绍 心电图(ECG)是记录心脏电活动的过程,它反映了心脏在收缩和舒张期间的…

新一代构建工具Vite-xyphf

一、什么vite? vite:是一款思维比较前卫而且先进的构建工具,他解决了一些webpack解决不了的问题——在开发环境下可以实现按需编译&#xff0c;加快了开发速度。而在生产环境下&#xff0c;它使用Rollup进行打包&#xff0c;提供更好的tree-shaking、代码压缩和性能优化&…

创建javaEE项目(无maven),JSP(九大内置对象)、Servlet(生命周期)了解

一、Servlet和jsp 0.创建web项目(无maven)&#xff1a; 1.创建一个普通的java项目 2.项目根目录右键&#xff0c;添加模板 3.配置tomcat服务器 4.配置项目tomcat依赖 1.Servlet(Server Applet)服务端小程序 用户通过浏览器发送一个请求&#xff0c;服务器tomcat接收到后&…

串口通信(7)判断数据帧头来接收一串数据

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

微信小程序:两层循环的练习,两层循环显示循环图片大图(大图显示、多层循环)

效果 代码分析 外层循环 外层循环的框架 <view wx:for"{{info}}" wx:key"index"></view> wx:for"{{info}}"&#xff1a;这里wx:for指令用于指定要遍历的数据源&#xff0c;即info数组。当遍历开始时&#xff0c;会依次将数组中的每…

链表的结点个数统计及查找

链表节点个数统计 要统计链表中的节点个数&#xff0c;只需要遍历整个链表&#xff0c;并在遍历的过程中计数即可。具体实现代码如下&#xff1a;(仍然使用C#) 先定义一个整型函数(节点个数的返回值一定是整型变量) int getLinkNodeNum(struct Test *head) {int cnt 0;whil…

STM32:AHT20温湿度传感器驱动程序开发

注&#xff1a;温湿度传感器AHT20数据手册.pdf http://www.aosong.com/userfiles/files/AHT20%E4%BA%A7%E5%93%81%E8%A7%84%E6%A0%BC%E4%B9%A6(%E4%B8%AD%E6%96%87%E7%89%88)%20B1.pdf 一、分析AHT数据手册文档 (1).准备工作 1.新建工程。配置UART2 2.配置I2C1为I2C标准模式&…

数据链路层中存在的报文ip,arp,rarp

IP数据报 ARP请求/应答报 RARP请求/应答报 IP数据报 这里的目的地址和源地址是MAC地址。 这个被称为 MAC 地址&#xff0c;是一个网卡的物理地址&#xff0c;用十六进制&#xff0c;6 个 byte 表示。 MAC 地址是一个很容易让人误解的地址。因为 MAC 地址号称全球唯一&…

深度学习_7_实战_点集最优直线解_优化版代码解析

完整版优化代码&#xff1a; import torch from torch.utils import data from d2l import torch as d2l # 特定导入 from torch import nndef load_array(data_arrays, batch_size, is_trainTrue):dataset data.TensorDataset(*data_arrays) #解包传递&#xff0c;转成张量…

【Linux】Nignx的入门使用负载均衡前端项目部署---超详细

一&#xff0c;Nignx入门 1.1 Nignx是什么 Nginx是一个高性能的开源Web服务器和反向代理服务器。它使用事件驱动的异步框架&#xff0c;可同时处理大量请求&#xff0c;支持负载均衡、反向代理、HTTP缓存等常见Web服务场景。Nginx可以作为一个前端的Web服务器&#xff0c;也可…

使用docker进行nextcloud+onlyoffice环境搭建(在线 or 离线)

1.安装 MySQL&#xff08;有MySQL就可以不装&#xff09; docker run -itd --name mysql8.0 -p 3306:3306 -e MYSQL_ROOT_PASSWORDroot --restartalways -e TZ"Asia/Shanghai" -v /home/docker/workspace/mysql:/var/lib/mysql mysql:latest --lower_case_table_na…

【案例】3D地球

效果图&#xff1a; 直接放源码 <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><meta name"viewport" content"initial-scale1.0, user-scalableno" …