13.多種I/O函數

前言

之前的示例中,基于Linux的使用read&write函數完成數據I/O,基于Windows的則使用send&recv函數。這次的Linux示例也將使用send& recv函數,并講解其與read&write函數相比的優點。還將介紹幾種其他的I/O函數。

一、send & recv 函數

1.Linux 中的 send & recv

#include <sys/socket.h>
ssize_t send(int sockfd, const void * buf, size_t nbytes, int flags);
// 成功時返回發送的字節數,失敗時返回-1。
// sockfd:表示與數據傳輸對象的連接的套接字文件描述符。
// buf:保存待傳輸數據的緩沖地址值。
// nbytes:待傳輸的字節數。
// flags:傳輸數據時指定的可選項信息。
#include <sys/socket.h>
ssize_t recv(int sockfd, void * buf, size_t nbytes, int flags);
// 成功時返回接收的字節數(收到EOF時返回0),失敗時返回-1。
// sockfd:表示數據接收對象的連接的套接字文件描述符。
// buf:保存接收數據的緩沖地址值。
// nbytes:可接收的最大字節數。
// flags:接收數據時指定的可選項信息。

send函數和recv函數的最后一個參數是收發數據時的可選項。該可選項可利用位或(bitOR)
運算(運算符)同時傳遞多個信息。通過表查看選項的種類及含義。
可選項(Option) 含 義 send recv
MSG_OOB 用于傳輸帶外數據(Out-of-band data) * *
MSG_PEEK 驗證輸人緩沖中是否存在接收的數據 *
MSG_DONTROUTE 數據傳輸過程中不參照路由(Routing)表,在本地(Local)網絡中尋找目的地 *
MSG_DONTWAIT 調用I/O函數時不阻塞,用于使用非阻塞(Non-blocking)I/O * *
MSG_WAITALL 防止函數返回,直到接收全部請求的字節數 *
另外,不同操作系統對上述可選項的支持也不同。因此,為了使用不同可選項,各位需要對實際開發中采用的操作系統有一定了解。下面選取表中的一部分(主要是不受操作系統差異影響的)進行詳細講解。

2.MSG_OOB:發送緊急消息

MSG_OOB可選項用于發送“帶外數據”緊急消息。假設醫院里有很多病人在等待看病,此時若有急診患者該怎么辦?
“當然應該優先處理。”
如果急診患者較多,需要得到等待看病的普通病人的諒解。正因如此,醫院一般會設立單獨的急診室。需緊急處理時,應采用不同的處理方法和通道。MSG_OOB可選項就用于創建特殊發送方法和通道以發送緊急消息。下列示例將通過MSG_OOB可選項收發數據。使用MSG_OOB時

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define BUF_SIZE 30
void error_handling(char *message);int main(int argc, char *argv[])
{int sock;struct sockaddr_in recv_adr;if(argc!=3) {printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock=socket(PF_INET, SOCK_STREAM, 0);memset(&recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family=AF_INET;recv_adr.sin_addr.s_addr=inet_addr(argv[1]);recv_adr.sin_port=htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&recv_adr, sizeof(recv_adr))==-1)error_handling("connect() error!");write(sock, "123", strlen("123"));send(sock, "4", strlen("4"), MSG_OOB);write(sock,"567",strlen("567"));send(sock, "890", strlen("890"), MSG_OOB);close(sock);return 0;
}void error_handling(char *message)
{fputs(message,stderr);fputc('\n', stderr);exit(1);
}

第29~32行:傳輸數據。第30和第32行緊急傳輸數據。正常順序應該是123、4、567、890,但緊急傳輸了4和890,由此可知接收順序也將改變。

從上述示例可以看出,緊急消息的傳輸比即將介紹的接收過程要簡單,只需在調用send函數時指定MSG_OOB可選項。接收緊急消息的過程要相對復雜一些。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>#define BUF_SIZE 30
void error_handling(char*message);
void urg_handler(int signo);int acpt_sock;
int recv_sock;int main(int argc, char *argv[])
{struct sockaddr_in recv_adr, serv_adr;int str_len, state;socklen_t serv_adr_sz;struct sigaction act;char buf[BUF_SIZE];if(argc!=2){printf("Usage: %s <port>\n", argv[0]);exit(1);}act.sa_handler=urg_handler;sigemptyset(&act.sa_mask);act.sa_flags=0;acpt_sock=socket(PF_INET, SOCK_STREAM, 0);memset(&recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family=AF_INET;recv_adr.sin_addr.s_addr=htonl(INADDR_ANY);recv_adr.sin_port=htons(atoi(argv[1]));if(bind(acpt_sock,(struct sockaddr*)&recv_adr, sizeof(recv_adr))==-1)error_handling("bind() error");listen(acpt_sock, 5);serv_adr_sz=sizeof(serv_adr);recv_sock=accept(acpt_sock, (struct sockaddr*)&serv_adr, &serv_adr_sz);fcntl(recv_sock, F_SETOWN, getpid());state=sigaction(SIGURG, &act, 0);while((str_len=recv(recv_sock, buf, sizeof(buf), 0))!= 0){if(str_len==-1)continue;buf[str_len]=0;puts(buf);}close(recv_sock);close(acpt_sock);return 0;
}void urg_handler(int signo)
{int str_len;char buf[BUF_SIZE];str_len=recv(recv_sock, buf, sizeof(buf)-1, MSG_OOB);buf[str_len]=0;printf("Urgent message: %s \n", buf);
}void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}

第29、47行:該示例中需要重點觀察SIGURG信號相關部分。收到MSG_OOB緊急消息時,操作系統將產生SIGURG信號,并調用注冊的信號處理函數。另外需要注意的是,第61行的信號處理函數內部調用了接收緊急消息的recv函數。第46行:調用fcntl函數,關于此函數將單獨說明。

上面的v示例中插人了未曾講解的函數調用語句,關于此函數只講解必要部分,過多的解釋將脫離本章主題(之后將再次說明)。
fcntl(recv_sock, F_SETOwN, getpid(O));
fcntl函數用于控制文件描述符,上述調用語句的含義:
“將文件描述符recv_sock指向的套接字擁有者(F_SETOWN)改為把getpid函數返回值用作ID的進程。”
大家或許感覺“套接字擁有者”的概念有些生疏。操作系統實際創建并管理套接字,所以從嚴格意義上說,“套接字擁有者”是操作系統。只是此處所謂的“擁有者”是指負責套接字所有事務的主體。上面的描述可簡要概括成這樣:
“文件描述符recv_sock指向的套接字引發的SIGURG信號處理進程變為將getpid函數返回值用作ID的進程。”

當然,上述描述中的“處理SIGURG信號”指的是“調用SIGURG信號處理函數”。但之前講過,多個進程可以共同擁有1個套接字的文件描述符。例如,通過調用fork函數創建子進程并同時復制文件描述符。此時如果發生SIGURG信號,應該調用哪個進程的信號處理函數呢?可以肯定的是,不會調用所有進程的信號處理函數(想想就知道這會引發更多問題)。因此,處理SIGURG
信號時必須指定處理信號的進程,而getpid函數返回調用此函數的進程ID。上述調用語句指定當前進程為處理SIGURG信號的主體。該程序中只創建了1個進程,因此,理應由該進程處理SIGURG信號。接下來先給出運行結果,再討論剩下的問題。
在這里插入圖片描述
輸出結果可能出乎大家預料,尤其是如下事實令人極為失望:
“通過MSG_OOB可選項傳遞數據時只返回1個字節?而且也不是很快啊!”
的確!令人遺憾的是,通過MSG_OOB可選項傳遞數據時不會加快數據傳輸速度,而且通過信號處理函數urg_handler讀取數據時也只能讀1個字節。剩余數據只能通過未設置MSG_OOB可選項的普通輸人函數讀取。這是因為TCP不存在真正意義上的“帶外數據”。實際上,MSG_OOB中的OOB是指Out-of-band,而“帶外數據”的含義是:
“通過完全不同的通信路徑傳輸的數據。”
即真正意義上的Out-of-band需要通過單獨的通信路徑高速傳輸數據,但TCP不另外提供,只利用TCP的緊急模式(Urgent mode)進行傳輸。

3.緊急模式工作原理

先給出結論,再補充說明緊急模式。MSG_OOB可選項可以帶來如下效果:
– “嗨!這里有數據需要緊急處理,別磨蹭啦!” –
MSG_OOB的真正的意義在于督促數據接收對象盡快處理數據。這是緊急模式的全部內容,而且TCP“保持傳輸順序”的傳輸特性依然成立。
“那怎能稱為緊急消息呢!”
這確實是緊急消息!因為發送消息者是在催促數據處理的情況下傳輸數據的。急診患者的及時救治需要如下兩個條件:
■ 迅速入院
■ 醫院急救
無法快速把病人送到醫院,并不意味著不需要醫院進行急救。TCP的緊急消息無法保證及時人院,但可以要求急救。當然,急救措施應由程序員完成。之前的示例oob_recv.c的運行過程中也傳遞了緊急消息,這可以通過事件處理函數確認。這就是MSG_OOB模式數據傳輸的實際意義。
在這里插入圖片描述
send(sock, “890”, strlen(“890”), MSG_OOB);
如果將緩沖最左端的位置視作偏移量為0,字符0保存于偏移量為2的位置。另外,字符0右側偏移量為3的位置存有緊急指針(UrgentPointer)。緊急指針指向緊急消息的下一個位置(偏移量加1),同時向對方主機傳遞如下信息:
– “緊急指針指向的偏移量為3之前的部分就是緊急消息!” –
也就是說,實際只用1個字節表示緊急消息信息。
在這里插入圖片描述
TCP數據包實際包含更多信息,但圖中只標注了與我們的主題相關的內容。TCP頭中含有如下兩種信息:
■ URG=1:載有緊急消息的數據包
■ URG指針:緊急指針位于偏移量為3的位置
指定MSG_OOB選項的數據包本身就是緊急數據包,并通過緊急指針表示緊急消息所在位置。但通過圖無法得知以下事實:
“緊急消息是字符串890,還是90?如若不是,是否為單個字符0?”
但這并不重要。如前所述,除緊急指針的前面1個字節外,數據接收方將通過調用常用輸人函數讀取剩余部分。換言之,緊急消息的意義在于督促消息處理,而非緊急傳輸形式受限的消息。

4.檢查輸入緩沖

同時設置MSG_PEEK選項和MSG_DONTWAIT選項,以驗證輸入緩沖中是否存在接收的數據。設置MSG_PEEK選項并調用recv函數時,即使讀取了輸人緩沖的數據也不會刪除。因此,該選項通常與MSG_DONTWAIT合作,用于調用以非阻塞方式驗證待讀數據存在與否的函數。下面我們通過示例了解二者含義。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
void error_handling(char *message);int main(int argc, char *argv[])
{int sock;struct sockaddr_in send_adr;if(argc!=3) {printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock=socket(PF_INET,SOCK_STREAM,0);memset(&send_adr,0, sizeof(send_adr));send_adr.sin_family=AF_INET;send_adr.sin_addr.s_addr=inet_addr(argv[1]);send_adr.sin_port=htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&send_adr, sizeof(send_adr))==-1)error_handling("connect() error!");write(sock,"123456789", strlen("123456789"));close(sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

上述示例在第24行發起連接請求,第27行發送字符串123。下個例子給出了使用MSG_PEEK和MSG_DONTWAIT選項的結果。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define BUF_SIZE 30
void error_handling(char *message);int main(int argc, char *argv[])
{int acpt_sock, recv_sock;struct sockaddr_in acpt_adr, recv_adr;int str_len, state;socklen_t recv_adr_sz;char buf[BUF_SIZE];if(argc!=2) {printf("Usage : %s <port>\n", argv[0]);exit(1);}acpt_sock=socket(PF_INET, SOCK_STREAM, 0);memset(&acpt_adr, 0, sizeof(acpt_adr));acpt_adr.sin_family=AF_INET;acpt_adr.sin_addr.s_addr=htonl(INADDR_ANY);acpt_adr.sin_port=htons(atoi(argv[1]));if(bind(acpt_sock, (struct sockaddr*)&acpt_adr, sizeof(acpt_adr))==-1)error_handling("bind() error");listen(acpt_sock, 5);recv_adr_sz=sizeof(recv_adr);recv_sock=accept(acpt_sock, (struct sockaddr*)&recv_adr, &recv_adr_sz);while(1){str_len=recv(recv_sock, buf,sizeof(buf)-1, MSG_PEEK|MSG_DONTWAIT);if(str_len>0)break;}buf[str_len]=0;printf("Buffering %d bytes: %s \n", str_len, buf);str_len=recv(recv_sock, buf, sizeof(buf)-1, 0);buf[str_len]=0;printf("Read again: %s \n",buf);close(acpt_sock);close(recv_sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n',stderr);exit(1);
}

第38行:調用recv函數的同時傳遞MSG_PEEK可選項,這是為了保證即使不存在待讀取數據也不會進入阻塞狀態。
第46行:再次調用recv函數。這次并未設置任何可選項,因此,本次讀取的數據將從輸入緩沖中刪除。
在這里插入圖片描述
通過運行結果可以驗證,僅發送1次的數據被讀取了2次,因為第一次調用recv函數時設置了MSG_PEEK可選項。以上就是MSG_PEEK可選項的功能啦!

二、readv & writev 函數

這次介紹的readv&writev函數有助于提高數據通信效率。先介紹這些函數的使用方法,再討論其合理的應用場景哦。

1.使用readv & writev 函數

readv&writev函數的功能可概括如下:
“對數據進行整合傳輸及發送的函數。”
也就是說,通過writev函數可以將分散保存在多個緩沖中的數據一并發送,通過readv函數可以由多個緩沖分別接收。因此,適當使用這2個函數可以減少I/O函數的調用次數。下面先介紹writev函數。

#include <sys/uio.h>
ssize_t writev(int filedes, const struct iovec * iov, int iovcnt);
// 成功時返回發送的字節數,失敗時返回-1。
// filedes:表示數據傳輸對象的套接字文件描述符。但該函數并不只限于套接字,因此,可以像read函數一樣向其傳遞文件或標準輸出描述符。
// iov:iovec結構體數組的地址值,結構體iovec中包含待發送數據的位置和大小信息。
// iovcnt:向第二個參數傳遞的數組長度。

可以看到,結構體iovec由保存待發送數據的緩沖(char型數組)地址值和實際發送的數據長度信息構成。
在這里插入圖片描述
圖13-4中writev的第一個參數1是文件描述符,因此向控制臺輸出數據,ptr是存有待發送數據信息的iovec數組指針。第三個參數為2,因此,從ptr指向的地址開始,共瀏覽2個iovec結構體變量,發送這些指針指向的緩沖數據。接下來仔細觀察圖中的iovec結構體數組。ptr[0](數組第一個元素)的iov_base指向以A開頭的字符串,同時iov_len為3,故發送ABC。而ptr[1](數組的第二個元素)的iov_base指向數字1,同時iov_len為4,故發送1234。
相信大家已掌握writev函數的使用方法和特性,接下來栗子來了:

#include <stdio.h>
#include <sys/uio.h>int main(int argc, char *argv[])
{struct iovec vec[2];char buf1[]="ABCDEFG";char buf2[]="1234567";int str_len;vec[0].iov_base=buf1;vec[0].iov_len=3;vec[1].iov_base=buf2;vec[1].iov_len=4;str_len=writev(1, vec, 2);puts("");printf("Write bytes: %d \n", str_len);return 0;
}

第11、12行:寫入第一個傳輸數據的保存位置和大小。
第13、14行:寫入第二個傳輸數據的保存位置和大小。
第16行:writev函數的第一個參數為1,故向控制臺輸出數據。
在這里插入圖片描述
下面介紹readv函數,它與writev函數正好相反:

#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovec * iov, int iovcnt);
// 成功時返回接收的字節數,失敗時返回-1。
// filedes:傳遞接收數據的文件(或套接字)描述符。
// iov:包含數據保存位置和大小信息的iovec結構體數組的地址值。
// iovcnt:第二個參數中數組的長度。

我們已經學習了writev函數,因此直接通過例子給出readv函數的使用方法。

#include <stdio.h>
#include <sys/uio.h>
#define BUF_SIZE 100int main(int argc, char *argv[])
{struct iovec vec[2];char buf1[BUF_SIZE]={0, };char buf2[BUF_SIZE]={0, };int str_len;vec[0].iov_base=buf1;vec[0].iov_len=5;vec[1].iov_base=buf2;vec[1].iov_len=BUF_SIZE;str_len=readv(0, vec, 2);printf("Read bytes: %d \n",str_len);printf("First message: %s \n",buf1);printf("Second message: %s \n",buf2);return 0;
}

第12、13行:設置第一個數據的保存位置和大小。接收數據的大小已指定為5,因此,無論buf1的大小是多少,最多僅能保存5個字節。
第14、15行:vec[0]中注冊的緩沖中保存5個字節,剩余數據將保存到vec[1]中注冊的緩沖。結構體iovec的成員iov_len中應寫入接收的最大字節數。
第17行:「eadv函數的第一個參數為0,因此從標準輸入接收數據。
在這里插入圖片描述

2.合理使用 readv&writev函數

哪種情況適合使用readv和writev函數?實際上,能使用該函數的所有情況都適用。例如,需要傳輸的數據分別位于不同緩沖(數組)時,需要多次調用write函數。此時可以通過1次writev函數調用替代操作,當然會提高效率。同樣,需要將輸人緩沖中的數據讀人不同位置時,可以不必多次調用read函數,而是利用1次readv函數就能大大提高效率。
即使僅從C語言角度看,減少函數調用次數也能相應提高性能。但其更大的意義在于減少數據包個數。假設為了提高效率而在服務器端明確阻止了Nagle算法。其實writev函數在不采用Nagle算法時更有價值。
這個示例中待發送的數據分別存在3個不同的地方,此時如果使用write函數則需要3次函數調用。但若為提高速度而關閉了Nagle算法,則極有可能通過3個數據包傳遞數據。反之,若使用writev函數將所有數據一次性寫人輸出緩沖,則很有可能僅通過1個數據包傳輸數據。所以writev函數和readv函數非常有用。
再考慮一種情況:將不同位置的數據按照發送順序移動(復制)到1個大數組,并通過1次write函數調用進行傳輸。這種方式是否與調用writev函數的效果相同?當然!但使用writev函數更為便利。因此,如果遇到writev函數和readv函數的適用情況,希望大家不要錯過機會。


總結

`這些函數大家理解一下,應該沒有那么難哦!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89506.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89506.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89506.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

設計模式五:橋模式(Bridge Pattern)

橋模式是一種結構型設計模式&#xff0c;它將抽象部分與其實現部分分離&#xff0c;使它們可以獨立變化。這種模式通過提供橋梁結構將抽象和實現解耦。橋模式的結構橋模式包含以下主要角色&#xff1a;Abstraction&#xff08;抽象類&#xff09;&#xff1a;定義抽象接口&…

深入理解設計模式之模板模式:優雅地定義算法骨架

在軟件開發中&#xff0c;我們經常會遇到這樣的情況&#xff1a;多個類執行相似的操作流程&#xff0c;但每個類在流程的某些步驟上有自己特定的實現。如果為每個類都完整地編寫整個流程&#xff0c;會導致大量重復代碼&#xff0c;且難以維護。這時候&#xff0c;模板模式&…

基于單片機寵物喂食器/智能寵物窩/智能飼養

傳送門 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目速選一覽表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目功能速覽 概述 深夜加班時&#xff0c;你是否擔心家中寵物餓肚子&#xff1f;出差旅途中&#xff0c;是否焦慮寵…

靜態補丁腳本 - 修改 libtolua.so

直接改arm64的so&#xff0c; 使用python腳本。#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 靜態補丁腳本 - 修改 libtolua.so 主要功能&#xff1a; 1. 修改 luaL_loadbuffer 函數&#xff0c;將跳轉目標從 luaL_loadbufferx 改為 luaL_loadfilex 2. …

2-大語言模型—理論基礎:詳解Transformer架構的實現(2)

目錄 1-大語言模型—理論基礎&#xff1a;詳解Transformer架構的實現(1)-CSDN博客https://blog.csdn.net/wh1236666/article/details/149443139?spm1001.2014.3001.5502 2.3、殘差連接和層歸一化 2.3.1、什么是層歸一化&#xff1f; 2.3.2、層歸一化的核心特點&#xff08…

SmartX 用戶建云實踐|富士康:基于榫卯企業云平臺構建分布式云,支撐全球多地工廠重要產線

作為全球最大的電子科技智造服務商&#xff0c;富士康集團在全球范圍內構建生產制造網絡。為實現多廠區統一管理與降本增效&#xff0c;在逐步替代 VMware 虛擬化架構的過程中&#xff0c;富士康對比了自研 OpenStack Ceph 平臺和 SmartX 超融合方案&#xff0c;最終選擇基于 …

ADC選型設計

1、最大擺伏FSR&#xff1a; 0 ~ 4.096V&#xff0c;一般Vref要等于FSR 2、最大頻率&#xff1a;根據奈奎斯特采樣定理大于2倍的信號頻率才夠還原信號&#xff0c;所以選擇20/50倍更好&#xff0c; 3、最小精度&#xff0c;對于一給定模擬輸入&#xff0c;實際數字輸出與理論預…

基于深度學習的火災智能檢測系統設計與實現

在各類安全事故中&#xff0c;火災因其突發性強、破壞力大&#xff0c;一直是威脅人們生命財產安全的重大隱患。傳統的火災檢測方式多依賴煙霧傳感器、溫度傳感器等&#xff0c;存在響應滯后、易受環境干擾等問題。隨著深度學習技術的飛速發展&#xff0c;基于計算機視覺的火災…

HIVE實戰處理(二十四)留存用戶數

留存概念: 次X日活躍留存&#xff0c;次X日新增留存&#xff0c;也就是看今天的新增或活躍用戶在后續幾天的留存情況一、留存表的生成邏輯 因為用戶活躍日期和留存的日期無法對齊所以搞了2級分區&#xff08;dt,static_day&#xff09; 1&#xff09;首先獲得計算日D、根據要出…

W3C XHTML 活動:標準化的未來與交互式體驗

W3C XHTML 活動:標準化的未來與交互式體驗 概述 W3C(World Wide Web Consortium)是全球領先的互聯網技術標準制定組織。XHTML,作為W3C推薦的標準之一,是一種基于XML的標記語言,旨在提供一個更加結構化、兼容性和可擴展性更高的網頁內容表示方式。本文將圍繞W3C的XHTML活…

Java-數構鏈表

1.鏈表 1.1鏈表的概念和結構 鏈表是一種物理存儲結構上非連續存儲結構&#xff0c;數據元素的邏輯順序是通過鏈表中引用鏈接次序實現的。 這里大多討論無頭單向非循環鏈表。這種結構&#xff0c;結構簡單&#xff0c;一般與其他數據結構結合&#xff0c;作為其他數據結構的子…

Windows系統軟件游戲丟失找不到mgmtapi.dll修復解決方法

在使用電腦系統時經常會出現丟失找不到某些文件的情況&#xff0c;由于很多常用軟件都是采用 Microsoft Visual Studio 編寫的&#xff0c;所以這類軟件的運行需要依賴微軟Visual C運行庫&#xff0c;比如像 QQ、迅雷、Adobe 軟件等等&#xff0c;如果沒有安裝VC運行庫或者安裝…

初識C++——開啟新旅途

從今天開始主包也是掉入C這個深坑&#xff0c;上完課也是跟沒上一樣&#xff0c;所以寫好博客復習還是很重要的&#xff0c;話不多說&#xff0c;進入正題~~1、命名空間(1)namespace的價值與作用在C/C中&#xff0c;變量、函數和后面要學到的類都是大量存在的&#xff0c;這些變…

vue2 面試題及詳細答案150道(141 - 150)

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

第十三章 Go包管理

文章目錄使用logurs處理程序日志logrus 常用配置使用viper處理程序配置使用logurs處理程序日志 下載包&#xff0c;在終端執行命令 go get github.com/sirupsen/logrus官方示例 package mainimport (log "github.com/sirupsen/logrus" )func main() {log.WithFiel…

EP01:【Python 第一彈】基礎入門知識

一、基礎入門知識 1.1 代碼規范 1.1.1 語句分隔符 ; 換行 1.1.2 格式化 對 Windows 和 Linux 操作系統&#xff0c;快捷鍵是Ctrl Alt L對 macOS 操作系統&#xff0c;快捷鍵是Cmd Option L 1.1.3 注釋 單行注釋 # 這是一行注釋多行注釋 """ 這 是 …

實用的文件和文件夾批量重命名工具

在日常工作中&#xff0c;文件和文件夾的命名管理常常讓人頭疼。尤其是面對大量文件時&#xff0c;手動重命名不僅耗時&#xff0c;還容易出錯。今天&#xff0c;我要給大家推薦一款超級實用的工具——OncePower 文件批量重命名&#xff0c;它不僅能批量重命名文件和文件夾&…

【Git】報錯:git config --global http.sslBackend “openssl“

問題解決 報錯&#xff1a;git config --global http.sslBackend “openssl”解決方法&#xff1a; git config --global http.sslBackend "openssl"之后再 push 即可正常提交。 &#x1f50d; 原因分析 ??系統環境不支持 OpenSSL 后端?? Git 在某些平臺&#xf…

Redisson RLocalCachedMap 核心參詳解

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;精通Java編…

AI輔助編程時代的高效規范開發指南:工具、原則與提效策略

引言&#xff1a;AI輔助編程的時代背景與核心挑戰 人工智能在編程領域的應用雖可追溯至20世紀50年代&#xff0c;但近十年實現了革命性突破&#xff0c;推動其從早期的代碼補全工具演進為能理解上下文、生成完整函數乃至項目架構的智能系統。關鍵發展里程碑包括&#xff1a;20…