主頁 > 後端開發 > webbench網站測壓工具原始碼分析

webbench網站測壓工具原始碼分析

2020-09-21 16:15:25 後端開發

  1 /*
  2  * (C) Radim Kolar 1997-2004
  3  * This is free software, see GNU Public License version 2 for
  4  * details.
  5  *
  6  * Simple forking WWW Server benchmark:
  7  *
  8  * Usage:
  9  *   webbench --help
 10  *
 11  * Return codes:
 12  *    0 - sucess
 13  *    1 - benchmark failed (server is not on-line)
 14  *    2 - bad param
 15  *    3 - internal error, fork failed
 16  * 
 17  */ 
 18 #include "socket.c"
 19 #include <unistd.h>
 20 #include <sys/param.h>
 21 #include <rpc/types.h>
 22 #include <getopt.h>
 23 #include <strings.h>
 24 #include <time.h>
 25 #include <signal.h>
 26 
 27 /* values */
 28 volatile int timerexpired=0;//判斷測壓市場是否已經達到設定的時間
 29 int speed=0;//記錄行程成功得到服務器相應的數量
 30 int failed=0;//記錄失敗的數量(speed表示成功數,failed表示失敗數)
 31 int bytes=0;//記錄行程成功讀取的位元組數
 32 /* globals */
 33 int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 *///HTTP版本
 34 /* Allow: GET, HEAD, OPTIONS, TRACE */
 35 #define METHOD_GET 0 
 36 #define METHOD_HEAD 1 
 37 #define METHOD_OPTIONS 2
 38 #define METHOD_TRACE 3
 39 #define PROGRAM_VERSION "1.5"
 40 int method=METHOD_GET;//定義HTTP請求方法:默認方式GET請求
 41 int clients=1;//并發數目,默認只有一個行程發送請求,通過 -c 引數設定
 42 int force=0;//是否需要等待讀取從server回傳的資料,0表示要等待讀取
 43 int force_reload=0;//是否使用快取,1表示不快取,0表示可以快取頁面
 44 int proxyport=80;//代理服務器的埠
 45 char *proxyhost=NULL;//代理服務器的埠
 46 int benchtime=30;//測壓時間,默認30秒,通過 -t 引數設定
 47 /* internal */
 48 int mypipe[2];//使用管道進行父行程和子行程的通信
 49 char host[MAXHOSTNAMELEN];//服務器端IP
 50 #define REQUEST_SIZE 2048
 51 char request[REQUEST_SIZE];//發送HTTP請求的內容
 52 
 53 static const struct option long_options[]=
 54 {
 55  {"force",no_argument,&force,1},
 56  {"reload",no_argument,&force_reload,1},
 57  {"time",required_argument,NULL,'t'},
 58  {"help",no_argument,NULL,'?'},
 59  {"http09",no_argument,NULL,'9'},
 60  {"http10",no_argument,NULL,'1'},
 61  {"http11",no_argument,NULL,'2'},
 62  {"get",no_argument,&method,METHOD_GET},
 63  {"head",no_argument,&method,METHOD_HEAD},
 64  {"options",no_argument,&method,METHOD_OPTIONS},
 65  {"trace",no_argument,&method,METHOD_TRACE},
 66  {"version",no_argument,NULL,'V'},
 67  {"proxy",required_argument,NULL,'p'},
 68  {"clients",required_argument,NULL,'c'},
 69  {NULL,0,NULL,0}
 70 };
 71 
 72 /* prototypes */
 73 static void benchcore(const char* host,const int port, const char *request);
 74 static int bench(void);
 75 static void build_request(const char *url);
 76 
 77 /*
 78      webbench在運行時可以設定壓測的持續時間,以秒為單位,
 79      例如我們希望測驗30秒,也就意味著壓測30秒后程式應該退出了,
 80      webbench中使用信號(signal)來控制程式結束,
 81      函式1是在到達結束時間時運行的信號處理函式,
 82      它僅僅是將一個記錄是否超時的變數timerexpired標記為true,
 83      后面會看到,在程式的while回圈中會不斷檢測此值,
 84      只有timerexpired=1,程式才會跳出while回圈并回傳,
 85 */
 86 static void alarm_handler(int signal)
 87 {
 88    timerexpired=1;
 89 }    
 90 
 91 /*
 92     教你如何使用webbench的函式,
 93     在linux命令列呼叫webbench方法不對的時候運行,作為提示,
 94     有一些比較常用的,比如-c來指定并發行程的多少;
 95     -t指定壓測的時間,以秒為單位;
 96     支持HTTP0.9,HTTP1.0,HTTP1.1三個版本;
 97     支持GET,HEAD,OPTIONS,TRACE四種請求方式,
 98     不要忘了呼叫時,命令列最后還應該附上要測的服務端URL,
 99 */
100 static void usage(void)
101 {
102    fprintf(stderr,
103     "webbench [option]... URL\n"
104     "  -f|--force               Don't wait for reply from server.\n"
105     "  -r|--reload              Send reload request - Pragma: no-cache.\n"
106     "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"
107     "  -p|--proxy <server:port> Use proxy server for request.\n"
108     "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
109     "  -9|--http09              Use HTTP/0.9 style requests.\n"
110     "  -1|--http10              Use HTTP/1.0 protocol.\n"
111     "  -2|--http11              Use HTTP/1.1 protocol.\n"
112     "  --get                    Use GET request method.\n"
113     "  --head                   Use HEAD request method.\n"
114     "  --options                Use OPTIONS request method.\n"
115     "  --trace                  Use TRACE request method.\n"
116     "  -?|-h|--help             This information.\n"
117     "  -V|--version             Display program version.\n"
118     );
119 };
120 int main(int argc, char *argv[])
121 {
122  int opt=0;
123  int options_index=0;
124  char *tmp=NULL;
125 
126  if(argc==1)
127  {
128       usage();
129           return 2;
130  } 
131 
132  while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
133  {
134   switch(opt)
135   {
136    case  0 : break;
137    case 'f': force=1;break;
138    case 'r': force_reload=1;break; 
139    case '9': http10=0;break;
140    case '1': http10=1;break;
141    case '2': http10=2;break;
142    case 'V': printf(PROGRAM_VERSION"\n");exit(0);
143    /**
144        *C 庫函式 int atoi(const char *str) 把引數 str 所指向的字串轉換為一個整數(型別為 int 型)
145        *int atoi(const char *str)
146        *str -- 要轉換為整數的字串,
147        *該函式回傳轉換后的長整數,如果沒有執行有效的轉換,則回傳零,
148        */
149    case 't': benchtime=atoi(optarg);break;         
150    case 'p': 
151          /* proxy server parsing server:port */
152            /**
153            *strrchr() 函式用于查找某字符在字串中最后一次出現的位置,其原型為:
154         *char * strrchr(const char *str, int c);
155         *【引數】str 為要查找的字串,c 為要查找的字符,
156         *strrchr() 將會找出 str 字串中最后一次出現的字符 c 的地址,然后將該地址回傳,
157         *注意:字串 str 的結束標志 NUL 也會被納入檢索范圍,所以 str 的組后一個字符也可以被定位,
158         *【回傳值】如果找到就回傳該字符最后一次出現的位置,否則回傳 NULL,
159         *回傳的地址是字串在記憶體中隨機分配的地址再加上你所搜索的字符在字串位置,設字符在字串中首次出現的位置為 i,那么回傳的地址可以理解為 str + i,
160            */
161            /**
162            *optarg : char *optarg;  //選項的引數指標
163            */
164          tmp=strrchr(optarg,':');
165          proxyhost=optarg;
166          if(tmp==NULL)
167          {
168              break;
169          }
170          if(tmp==optarg)
171          {
172              fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
173              return 2;
174          }
175          /**
176          *C 庫函式 size_t strlen(const char *str) 計算字串 str 的長度,直到空結束字符,但不包括空結束字符,
177          *size_t strlen(const char *str)
178          *引數:str -- 要計算長度的字串,
179          *該函式回傳字串的長度,
180          */
181          if(tmp==optarg+strlen(optarg)-1)
182          {
183              fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
184              return 2;
185          }
186          *tmp='\0';//把:替換為\0
187          //從\0之后到\0之前
188          proxyport=atoi(tmp+1);break;
189    case ':':
190    case 'h':
191    case '?': usage();return 2;break;
192    case 'c': clients=atoi(optarg);break;
193   }
194  }
195  //int optind:argv的當前索引值,當getopt函式在while回圈中使用時,剩下的字串為運算元,下標從optind到argc-1
196  //argc,argv 參考:https://www.cnblogs.com/lanshanxiao/p/11568037.html
197  //getopt_long()中的函式,參考:https://www.cnblogs.com/xhg940420/p/7016574.html
198  //掃描完畢后,optind指向非長選項和非短選項和非引數的欄位,這里應該指向URL
199  if(optind==argc) {
200                       fprintf(stderr,"webbench: Missing URL!\n");
201               usage();
202               return 2;
203                     }
204 
205  if(clients==0) clients=1;
206  if(benchtime==0) benchtime=60;
207  /* Copyright */
208  fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
209      "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
210      );
211  /**
212   *命令讀取完成后,argv[optind]中應該存放著URL,
213   *建立完整的http請求,http請求存放在變數char request[REQUEST_SIZE]中
214   */
215  build_request(argv[optind]);
216  /* print bench info *///輸出平臺資訊
217  printf("\nBenchmarking: ");
218  switch(method)
219  {
220      case METHOD_GET:
221      default:
222          printf("GET");break;
223      case METHOD_OPTIONS:
224          printf("OPTIONS");break;
225      case METHOD_HEAD:
226          printf("HEAD");break;
227      case METHOD_TRACE:
228          printf("TRACE");break;
229  }
230  printf(" %s",argv[optind]);//列印出URL
231  switch(http10)
232  {
233      case 0: printf(" (using HTTP/0.9)");break;
234      case 2: printf(" (using HTTP/1.1)");break;
235  }
236  printf("\n");
237  if(clients==1) printf("1 client");
238  else
239    printf("%d clients",clients);
240 
241  printf(", running %d sec", benchtime);
242  if(force) printf(", early socket close");
243  if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
244  if(force_reload) printf(", forcing reload");
245  printf(".\n");
246  //壓力測驗最后一句話,所有的壓力測驗都在bench函式中實作
247  return bench();
248 }
249 
250 /*
251     函式主要操作全域變數char request[REQUEST_SIZE],根據url填充其內容,
252     典型的HTTP的GET請求:
253     GET /test.jpg HTTP/1.1
254     User-Agent: WebBench 1.5
255     Host:192.168.10.1
256     Pragma: no-cache
257     Connection: close
258 
259     build_request函式的目的就是要把
260     類似于以上這一大坨資訊全部存到全域變數request[REQUEST_SIZE]中,
261     其中換行操作使用的是”\r\n”,
262     而以上這一大坨資訊的具體內容是要根據命令列輸入的引數,以及url來確定的,
263     該函式使用了大量的字串操作函式,
264     例如strcpy,strstr,strncasecmp,strlen,strchr,index,strncpy,strcat,
265     對這些基礎函式不太熟悉的同學可以借這個函式復習一下,
266 */
267 void build_request(const char *url)
268 {
269   char tmp[10];
270   int i;
271 
272   bzero(host,MAXHOSTNAMELEN);//bzero():置host(位元組字串)前MAXHOSTNAMELEN個位元組為0,包括'\0')
273   bzero(request,REQUEST_SIZE);
274   
275   if(force_reload && proxyhost!=NULL && http10<1) http10=1;//滿足一定條件,更換HTTP協議
276   if(method==METHOD_HEAD && http10<1) http10=1;
277   if(method==METHOD_OPTIONS && http10<2) http10=2;
278   if(method==METHOD_TRACE && http10<2) http10=2;
279 
280   switch(method)
281   {
282       default:
283       //strcpy() 函式用于對字串進行復制(拷貝),
284       //char* strcpy(char* strDestination, const char* strSource);
285       //strSource 指向的字串復制到 strDestination
286       case METHOD_GET: strcpy(request,"GET");break;
287       case METHOD_HEAD: strcpy(request,"HEAD");break;
288       case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
289       case METHOD_TRACE: strcpy(request,"TRACE");break;
290   }
291 
292   //char*strcat(char* strDestination, const char* strSource);
293   /*
294       strcat() 函式用來將兩個字串連接(拼接)起來,
295       strcat() 函式把 strSource 所指向的字串追加到 strDestination 所指向的字串的結尾,
296       所以必須要保證 strDestination 有足夠的記憶體空間來容納兩個字串,否則會導致溢位錯誤,
297       注意:strDestination 末尾的\0會被覆寫,strSource 末尾的\0會一起被復制過去,最終的字串只有一個\0,
298   */
299   strcat(request," ");
300 
301   /*
302     char *strstr(const char *haystack, const char *needle) 
303     在字串 haystack 中查找第一次出現字串 needle 的位置,不包含終止符 '\0',
304     該函式回傳在 haystack 中第一次出現 needle 的地址,如果未找到則回傳 null,
305   */
306   if(NULL==strstr(url,"://"))
307   {
308       fprintf(stderr, "\n%s: is not a valid URL.\n",url);
309       exit(2);
310   }
311 
312   /*
313       strlen(char *);
314       檢測字串實際長度,
315       strlen(char *)檢測的是'\0',strlen(char *)碰到'\0'就回傳'\0'以前的字符數(不包括'\0'),
316       strlen(char*)函式求的是字串的實際長度,它求得方法是從開始到遇到第一個'\0',
317       如果你只定義沒有給它賦初值,這個結果是不定的,它會從aa首地址一直找下去,直到遇到'\0'停止,
318   */
319   if(strlen(url)>1500)
320   {
321      fprintf(stderr,"URL is too long.\n");
322      exit(2);
323   }
324   if(proxyhost==NULL)
325          /*
326                  int strncasecmp(const char *s1, const char *s2, size_t n);
327                  strncasecmp()用來比較引數s1 和s2 字串前n個字符,比較時會自動忽略大小寫的差異,
328                  若引數s1 和s2 字串相同則回傳0,s1 若大于s2 則回傳大于0 的值,s1 若小于s2 則回傳小于0 的值,
329          */
330        if (0!=strncasecmp("http://",url,7)) 
331        { 
332                fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
333             exit(2);
334        }
335   /* protocol/host delimiter */
336   i=strstr(url,"://")-url+3;
337   /* printf("%d\n",i); */
338 
339   /*
340     char *strchr(const char *str, char c) 
341     該函式回傳在字串 str 中第一次出現字符 c 的地址,如果未找到該字符則回傳 NULL,
342   */
343   if(strchr(url+i,'/')==NULL) {
344                                 fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
345                                 exit(2);
346                               }
347   if(proxyhost==NULL)
348   {
349    /* get port from hostname */
350    if(index(url+i,':')!=NULL &&
351       index(url+i,':')<index(url+i,'/'))
352    {
353           /*
354             char * strncpy(char *s1,char *s2,size_t n);
355               將字串s2中最多n個字符復制到字符陣列s1中,回傳指向s1的指標,
356               注意:如果源串長度大于n,則strncpy不復制最后的'\0'結束符,
357             所以是不安全的,復制完后需要手動添加字串的結束符才行,
358            */
359        strncpy(host,url+i,strchr(url+i,':')-url-i);
360        bzero(tmp,10);
361        strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
362        /* printf("tmp=%s\n",tmp); */
363 
364        /*
365             C語言庫函式名: atoi
366               功 能: 把字串轉換成整型數.
367               名字來源:array to integer 的縮寫.
368               函式說明: atoi()會掃描引數nptr字串,如果第一個字符不是數字也不是正負號回傳零,
369             否則開始做型別轉換,之后檢測到非數字或結束符 \0 時停止轉換,回傳整型數,
370               原型: int atoi(const char *nptr);
371        */
372        proxyport=atoi(tmp);
373        if(proxyport==0) proxyport=80;
374    } else
375    {
376         /*
377         size_t strcspn(const char *s, const char * reject);
378         函式說明:strcspn()從引數s 字串的開頭計算連續的字符,
379         而這些字符都完全不在引數reject 所指的字串中,
380         簡單地說, 若strcspn()回傳的數值為n,則代表字串s 開頭連續有n 個字符都不含字串reject 內的字符,
381         回傳值:回傳字串s 開頭連續不含字串reject 內的字符數目,
382          */
383      strncpy(host,url+i,strcspn(url+i,"/"));
384    }
385    // printf("Host=%s\n",host);
386    strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
387   } else
388   {
389    // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
390    strcat(request,url);
391   }
392   if(http10==1)
393       strcat(request," HTTP/1.0");
394   else if (http10==2)
395       strcat(request," HTTP/1.1");
396   strcat(request,"\r\n");
397   if(http10>0)
398       strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
399   if(proxyhost==NULL && http10>0)
400   {
401       strcat(request,"Host: ");
402       strcat(request,host);
403       strcat(request,"\r\n");
404   }
405   if(force_reload && proxyhost!=NULL)
406   {
407       strcat(request,"Pragma: no-cache\r\n");
408   }
409   if(http10>1)
410       strcat(request,"Connection: close\r\n");
411   /* add empty line at end */
412   if(http10>0) strcat(request,"\r\n"); 
413   // printf("Req=%s\n",request);
414 }
415 
416 /**
417 *先進行了一次socket連接,確認能連通以后,才進行后續步驟,
418 *呼叫pipe函式初始化一個管道,用于子進行向父行程匯報測驗資料,
419 *子行程根據clients數量fork出來,
420 *每個子行程都呼叫函式5進行測驗,并將結果輸出到管道,供父行程讀取,
421 *父行程負責收集所有子行程的測驗資料,并匯總輸出,
422 */
423 /* vraci system rc error kod */
424 static int bench(void)
425 {
426   int i,j,k;    
427   pid_t pid=0;
428   FILE *f;
429 
430   /* check avaibility of target server */
431   //檢測目標服務器的可用性,呼叫socket.c檔案中的函式
432   i=Socket(proxyhost==NULL?host:proxyhost,proxyport);
433   if(i<0) {//處理錯誤
434        fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
435            return 1;
436          }
437   close(i);
438   /* create pipe */
439   if(pipe(mypipe))//管道用于子行程向父行程回報資料
440   {//錯誤處理
441       perror("pipe failed.");
442       return 3;
443   }
444 
445   /* not needed, since we have alarm() in childrens */
446   /* wait 4 next system clock tick */
447   /*
448   cas=time(NULL);
449   while(time(NULL)==cas)
450         sched_yield();
451   */
452 
453   /* fork childs */
454   for(i=0;i<clients;i++)//根據clients大小fork出來足夠的子行程進行測驗
455   {
456        pid=fork();
457        if(pid <= (pid_t) 0)
458        {
459            /* child process or error*/
460                sleep(1); /* make childs faster */
461            break;
462        }
463   }
464 
465   if( pid< (pid_t) 0)
466   {//錯誤處理
467           fprintf(stderr,"problems forking worker no. %d\n",i);
468       perror("fork failed.");
469       return 3;
470   }
471 
472   if(pid== (pid_t) 0)//若是子行程,呼叫benchcore進行測驗
473   {
474     /* I am a child */
475     if(proxyhost==NULL)
476       benchcore(host,proxyport,request);
477          else
478       benchcore(proxyhost,proxyport,request);
479 
480          /* write results to pipe */
481      f=fdopen(mypipe[1],"w");//子行程將測驗結果輸出到管道
482      if(f==NULL)
483      {//錯誤處理
484          perror("open pipe for writing failed.");
485          return 3;
486      }
487      /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
488      fprintf(f,"%d %d %d\n",speed,failed,bytes);
489      fclose(f);
490      return 0;
491   } else
492   {//若是父行程,則從管道讀取子行程輸出,并做匯總
493       f=fdopen(mypipe[0],"r");
494       if(f==NULL) 
495       {//錯誤處理
496           perror("open pipe for reading failed.");
497           return 3;
498       }
499       setvbuf(f,NULL,_IONBF,0);
500       speed=0;
501           failed=0;
502           bytes=0;
503 
504       while(1)
505       {//從管道讀取資料,fscanf為阻塞式函式
506           pid=fscanf(f,"%d %d %d",&i,&j,&k);
507           if(pid<2)
508                   {//錯誤處理
509                        fprintf(stderr,"Some of our childrens died.\n");
510                        break;
511                   }
512           speed+=i;
513           failed+=j;
514           bytes+=k;
515           /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
516           if(--clients==0) break;//這句用于記錄已經讀了多少個子行程的資料,讀完就退出
517       }
518       fclose(f);
519  //最后將結果列印到螢屏上
520   printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
521           (int)((speed+failed)/(benchtime/60.0f)),
522           (int)(bytes/(float)benchtime),
523           speed,
524           failed);
525   }
526   return i;
527 }
528 
529 /**
530 *benchcore是子行程進行壓力測驗的函式,被每個子行程呼叫,
531 *這里使用了SIGALRM信號來控制時間,
532 *alarm函式設定了多少時間之后產生SIGALRM信號,一旦產生此信號,將運行alarm_handler(),
533 *使得timerexpired=1,這樣可以通過判斷timerexpired值來退出程式,
534 *另外,全域變數force表示我們是否在發出請求后需要等待服務器的回應結果
535 */
536 void benchcore(const char *host,const int port,const char *req)
537 {
538  int rlen;
539  char buf[1500];//記錄服務器回應請求所回傳的資料
540  int s,i;
541  struct sigaction sa;
542 
543  /* setup alarm signal handler */
544  sa.sa_handler=alarm_handler;//將函式alarm_handler地址賦值給sa.alarm_handler,作為信號處理函式
545  sa.sa_flags=0;
546  if(sigaction(SIGALRM,&sa,NULL))//超時會產生信號SIGALRM,用sa中的指定函式處理
547     exit(3);
548  alarm(benchtime);//開始計時
549 
550  rlen=strlen(req);
551  nexttry:while(1)
552  {
553     if(timerexpired)//一旦超時則回傳
554     {
555        if(failed>0)
556        {
557           /* fprintf(stderr,"Correcting failed by signal\n"); */
558           failed--;
559        }
560        return;
561     }
562     s=Socket(host,port);//呼叫socket建立TCP連接                          
563     if(s<0) { failed++;continue;} 
564     if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}//發出請求
565     if(http10==0) //針對http0.9做的特殊處理
566         if(shutdown(s,1)) { failed++;close(s);continue;}
567     if(force==0) //全域變數force表示是否要等待服務器回傳的資料
568     {
569             /* read all available data from socket */
570         while(1)
571         {
572               if(timerexpired) break; 
573           i=read(s,buf,1500);//從socket讀取回傳資料
574               /* fprintf(stderr,"%d\n",i); */
575           if(i<0) 
576               { 
577                  failed++;
578                  close(s);
579                  goto nexttry;
580               }
581            else
582                if(i==0) break;
583                else
584                    bytes+=i;
585         }
586     }
587     if(close(s)) {failed++;continue;}
588     speed++;
589  }
590 }

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/98165.html

標籤:C++

上一篇:可變lambda, lambda使用mutable關鍵字

下一篇:QJsonObject與QString轉化封裝

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more