文章目录

  • 表示字符串
    • 字符串字面量
    • 指针表示法创建字符串
    • 数组表示法创建字符串
    • 数组和指针创建字符串的区别
    • 字符串数组
  • 字符串输入输出
    • gets() 和 puts()
    • fgets() 和 fputs()
    • gets_s()
    • 自定义输入输出函数
  • string.h 函数
    • 字符串比较
    • 字符串排序
  • ctype.h 函数
  • 命令行参数
  • 将字符串转换为数字
    • sprintf() 和 sscanf()
    • atoi() aotl() aotf()
    • strtol() strtoul() strtod()
    • itoa() ftoa()

C程序中,字符串是用双引号括起来的字符序列。

表示字符串

字符串字面量

程序中用双引号括起来的内容称为字符串字面量(string literal),也叫做字符串常量

字符串常量属于静态存储类别(static storage class)。字符串作为可执行文件的一部分储存在数据段中,当把程序载入内存时,字符串被载入静态存储区。在程序中使用一个字符串常量,该字符串常量就会被存储到静态存储区中:

  • 可以使用多次
  • 只会被储存一次
  • 被视为const数据不可修改
  • 在程序的生命期内存在。

指针表示法创建字符串

用双引号括起来的内容被视为指向该字符串储存首字符位置的指针。类似于把数组名作为指向数组首元素的指针。

int main(void) { printf("%s, %p, %c", "We", "are", *"space farers");return 0;
}
输出
We, 00AE7B44, s
const char * pt1 = "Something is pointing at me.";
/* 该字符串被存储到静态存储区, 且在末尾存储了一个 \0 来标记字符串结束然后把字符串首字符位置赋给指针 */

数组表示法创建字符串

// 不指定数组长度
const char m1[] = "If you can't think of anything, fake it.";
/* 编译器自动确定字符串数组大小 */
/* 当把程序载入内存时,也将字符串载入静态存储区。程序在开始运行时才会为数组分配内存,此时才将储存在静态存储区(static memory)中的字符串拷贝到数组中 。程序应确保字符数组长度比字符串长度多1(为了存储\0)此时字符串有两个副本。一个是在静态内存中的字符串常量,另一个是储存在 m1 数组中的字符串 */// 指定数组长度
const char m2[40] = "Limit yourself to one line's worth.";
/* 要确保数组长度比字符串长度多1,用于存储 \0所有未被使用的元素都被自动初始化为 \0  *///创建一个稍后填充的字符数组
char m3[40]; /* 字符数组名和其他数组名一样,是该数组首元素的地址 */
char car[10] = "Tata";
car == &car[0]
*car == 'T'
*(car+1) == car[1] == 'a'

从ANSI C标准起,如果字符串字面量之间没有间隔,或者用空白字符分隔,C会将其视为串联起来的字符串字面量。

char greeting[50] = "Hello, and"" how are" " you"" today!";//等价
char greeting[50] = "Hello, and how are you today!"; //如果要在括号内使用引号,需要使用转义序列 \"
printf("\"Run,Spot, run!\" exclaimed Dick.\n");

数组和指针创建字符串的区别

首先,用双引号括起来的字符串字面量都会存储在静态存储区中。
初始化数组把静态存储区的字符串拷贝到数组中,
初始化指针只把静态存储区中·字符串的地址拷贝给指针。

/* 数组表示法创建字符串 */
const char ar1[] = "A copy of the static store.";
/* 数组存储的是静态存储区中字符串常量的副本, 可随意修改  *//* 指针表示法创建字符串 */
const char * pt1 = "Something is pointing at me.";
/* 指针存储的是静态存储区中字符串常量的首字符地址, 不可通过指针修改字该符串常量 */

字符串数组

#include<stdio.h>
#define SLEN 40
#define LIM 5int main(void) { const char *mytalents[LIM] = {"Adding numbers swiftly","Multiplying accurately", "Stashing data","Following instructions to the letter","Understanding the C language"};char yourtalents[LIM][SLEN] = {"Walking in a straight line","Sleeping", "Watching television","Mailing letters", "Reading email"};int i;puts("Let's compare talents.");printf("%-36s  %-25s\n", "My Talents", "Your Talents");for(i = 0; i < LIM; i++)printf("%-36s  %-25s\n", mytalents[i], yourtalents[i]);printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd\n",sizeof(mytalents), sizeof(yourtalents));return 0;
}
/* 使用一个下标时都分别表示一个字符串, 如 mytalents[0]和yourtalents[0]使用两个下标时都分别表示一个字符, 如 mytalents[1][2]和yourtalents[1][2]mytalents 是一个内含5个指针的数组, 在系统中共占用40字节。yourtalents 是一个内含5个数组的数组, 每个数组内含40个char类型的值,共占用200字节。mytalents 中的指针指向静态存储区中的字符串字常量的位置yourtalents 中的数组则储存着静态存储区中字符串字常量的副本 */

字符串输入输出

如果想把一个字符串读入程序,首先必须预留储存该字符串的空间, 然后用输入函数获取该字符串。 可以定义一个字符数组或使用C库函数分配内存空间。

gets() 和 puts()

gets() 和 puts() 常常搭配使用。由于gets()函数无法检查数组是否装得下输入行,因此使用它会造成安全隐患,C11标准委员会已经将其废除,尽量不要使用。

char *gets(char *str);
/* 从标准输入流(stdin)读取一行输入, 直到遇到换行符或文件结尾停止, 读取并丢弃遇到的换行符。然后将读取到的字符存入str指向的内存中, 并在末尾添加一个 \0 使其成为一个 C 字符串。成功读取则返回str, 读取出错或读取时文件为空, 则返回一个空指针(null pointer) */int puts(const char *str);
/* 将str指向的字符串输出到标准输出流(stdout), 并额外输出一个换行符执行成功返回非负数。发生错误返回EOF */char str[100];
printf("%d\n",puts(gets(str)));
#include <stdio.h>
#define STLEN 81
int main(void) {char words[STLEN];puts("Enter a string, please.");gets(words); //读取一行字符,丢弃换行符printf("Your string twice:\n");printf("%s\n", words);puts(words); // 输出字符串并额外输出一个换行符puts("Done.");return 0;
}

fgets() 和 fputs()

char *fgets(char *str, int n, FILE *stream)
/* 从指定的流 stream 读取一行当读取(n-1)个字符时, 或者读取到换行符时(读取并保留), 或者到达文件末尾时, 停止读取。然后将读取到的字符存入str指向的内存中, 并在末尾添加一个 \0 使其成为一个 C 字符串成功读取返回 str, 读取出错或读取时文件为空,则返回一个空指针 null pointerfgets()会读取并保留换行符, 而gets()会读取并丢掉换行符 */int fputs(const char * str, FILE * stream);
/* 将 str 指向的字符串输出到 stream 流成功执行返回一个非负值, 发生错误则返回 EOF(-1)fputs()不会额外输出一个换行符, puts()会额外输出一个换行符 */

fgets()函数的第3个参数指明要读入的文件。 如果要读入从键盘输入的数据, 应以stdin(标准输入)作为参数。

fputs()函数的第2个参数指明要写入的文件。 如果要显示在显示器上, 应使用 stdout(标准输出) 作为该参数。

空字符和空指针

  • 都可以用数值0来表示
  • 空字符是整数类型, 空指针是指针类型
  • 空字符是一个字符, 占1字节
    空指针是一个地址, 占4字节
  • 空字符用于标记字符串结尾
  • 函数返回空指针表示某些特殊情况发生,如遇到文件结尾或未能按预期执行。

//读取多行输入行, 删除储存在字符串中的换行符, 如果没有换行符, 则丢弃一行内多余字符
#include <stdio.h>
#define STLEN 10
int main(void) {char words[STLEN];int i;puts("Enter strings (empty line to quit):");while(fgets(words, STLEN, stdin) != NULL && words[0] != '\n') {i = 0;while(words[i] != '\n' && words[i] != '\0') i++;if (words[i] == '\n')words[i] = '\0';else // 如果word[i] == '\0'则执行这部分代码while (getchar() != '\n') continue;puts(words);}puts("done");return 0;
}
/* s_gets.c -- 读取一行输入行, 删除储存在字符串中的换行符, 如果没有换行符, 则丢弃一行内多余字符 */
char * s_gets(char * st, int n) {char * ret_val;int i = 0;ret_val = fgets(st, n, stdin);if (ret_val) // 即,ret_val != NULL{while(st[i] != '\n' && st[i] != '\0')i++;if(st[i] == '\n')st[i] = '\0';elsewhile(getchar() != '\n')continue;}return ret_val;
}

gets_s()

char *gets_s( char *str, rsize_t n );

https://cloud.tencent.com/developer/section/1009156

自定义输入输出函数

void put1(const char * string) {while (*string)putchar(*string++);
}char *get1(char *str, int length){ }

string.h 函数

C库提供了多个处理字符串的函数, ANSI C把这些函数的原型放在 string.h头文件中。

strlen() 求字符串长度

size_t strlen(const char *str);
/* 返回字符串的长度, 不包括空字符  */

strcat() 拼接字符串

char *strcat(char *dest, const char *src);
/* 把 src 指向的字符串追加到 dest 指向的字符串的末尾, 直到遇到空字符, 并在末尾添加 \0从dest 的 \0 的位置开始追加返回deststrcat()无法检查 dest 剩余内存是否能容纳src,有安全隐患 */char *strncat(char *dest, const char *src, size_t n);
/* 把 src 指向的字符串追加到 dest 指向的字符串的末尾, 直到 n 个字符或遇到空字符为止, 并在末尾添加 \0 从dest 的 \0 的位置开始追加返回dest  */

strcmp() 比较字符串

int strcmp(const char *str1, const char *str2);
/* 把 str1 所指向的字符串和 str2 所指向的字符串进行比较1. 如果返回值小于 0, 则表示 str1 小于 str22. 如果返回值大于 0, 则表示 str1 大于 str23. 如果返回值等于 0, 则表示 str1 等于 str2 */int strncmp(const char *str1, const char *str2, size_t n);
/* 把 str1 和 str2 进行比较,最多比较前 n 个字符 */

strcpy() 拷贝字符串

char *strcpy(char *dest, const char *src);
/* 把 src 指向的字符串复制到 dest, 并在末尾添加一个 \0, src 的内容将覆盖 dest 的内容返回 dest 不能检查 dest 指向的内存能否容纳 src , 有安全隐患 */char *strncpy(char *dest, const char *src, size_t n)
/* 把 src 指向的字符串复制到 dest, 复制 n 个字符或遇到 \0 停止, 并在末尾添加 \0 返回dest */

strchr() 查找字符首次出现的位置

char *strchr(const char * str, int c);
/* 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL */

strrchr() 查找字符最后一次出现的位置

char *strrchr(const char *str, int c);
/* 在 str 所指向的字符串中搜索最后一次出现字符 c 的位置返回 str 中最后一次出现字符 c 的位置, 如果未找到该值,则函数返回 NULL */

strpbrk() 公共字符第一次出现的位置

char *strpbrk(const char *str1, const char *str2);
/* 检索字符串 str1 中第一个匹配字符串 str2 中任意字符的字符, 不包含 \0返回 str1 中第一个匹配字符串 str2 中任意字符的字符位置, 如果未找到字符则返回 NULL */

strstr() 子字符串匹配

char *strstr(const char *haystack, const char *needle);
/* 在字符串 haystack 中查找第一次出现字符串 needle 的位置, 不包含 \0返回在 haystack 中第一次出现 needle 字符串的位置, 如果未找到则返回 NULL */

字符串比较

1 两个字符串自左向右逐个按 ASCII 值大小相比
2 直到出现差异,ASCII 码大的字符串大
3 或者其中一个字符串先结束,先结束的字符串小

 "A"<"B" "A"<"AB" "Apple"<"Banana" "A"<"a" "compare"<"computer"

字符串排序

#include <stdio.h>
#include <string.h>#define SIZE 81        /* 限制字符串长度,包括 \0 */
#define LIM 20      /* 可读入的最多行数 */
#define HALT ""   /* 空字符串停止输入 */
void stsrt(char *strings[], int num);  /* 字符串排序函数 */
char * s_gets(char * st, int n);
int main(void)
{char input[LIM][SIZE]; /* 储存输入的数组   */char *ptstr[LIM];      /* 内含指针变量的数组 */int ct = 0;            /* 输入计数      */int k;  /* 输出计数 */printf("Input up to %d lines, and I will sort them.\n", LIM);printf("To stop, press the Enter key at a line's start.\n");while (ct < LIM && s_gets(input[ct], SIZE) != NULL && input[ct][0] != '\0'){ptstr[ct] = input[ct];  /* 设置指针指向字符串  */ct++;}stsrt(ptstr, ct);           /* 字符串排序函数  */puts("\nHere's the sorted list:\n");for (k = 0; k < ct; k++)puts(ptstr[k]);   /* 排序后的指针  */return 0;
}/* 字符串-指针-排序函数 */
void stsrt(char *strings[], int num)
{char *temp;int top, seek;for (top = 0; top < num - 1; top++)for (seek = top + 1; seek < num; seek++)if (strcmp(strings[top], strings[seek]) > 0){temp = strings[top];strings[top] = strings[seek];strings[seek] = temp;}
}
/* 读取字符串 */
char * s_gets(char * st, int n)
{char * ret_val;int i = 0;ret_val = fgets(st, n, stdin);if (ret_val){while (st[i] != '\n' && st[i] != '\0')i++;if (st[i] == '\n')st[i] = '\0';elsewhile (getchar() != '\n')continue;}return ret_val;
}
/* 输入:
O that I was where I would be,
Then would I be where I am not;
But where I am I must be,
And where I would be I can not.
*/

ctype.h 函数


命令行参数

/* repeat.c -- 把命令行参数转换为数字 */
#include <stdio.h>
#define STLEN 81
int main(int argc, char *argv []) {int count;printf("The command line has %d arguments:\n", argc - 1);for (count = 1; count < argc; count++) printf("%d: %s\n", count, argv[count]);printf("\n");return 0;
}
//将程序编译成可执行文件 repeat.exe

命令行(command line) 是在命令行环境中, 用户为运行程序输入命令的行。Win10运行 repeat. exe 的命令行:
C:\Users\liwei\Desktop>repeat

命令行参数(command-line argument) 是同一行的附加项。 一个C程序可以读取并使用这些附加项:
C:\Users\liwei\Desktop>repeat Resistance is futile
The command line has 3 arguments:
1:  Resistance
2:  is
3:  futile

C编译器允许main()没有参数或者有两个参数(一些实现允许main()有更多参数, 属于对标准的扩展) 。

main()有两个参数时, 第1个参数是命令行中的字符串数量。 这个int类型的参数被称为argc(表示参数计数 argument count)。

系统用空格分隔字符串。 该程序把命令行字符串储存在内存中, 并把每个字符串的地址储存在指针数组中。 而该数组的地址则被储存在 main()的第 2 个参数中。 这个指向指针的指针称为argv(表示参数值 argument value)。 如果系统允许(一些操作系统不允许这样), 就把程序本身的名称赋给 argv[0], 然后把随后的第1个字符串赋给argv[1]。

许多环境(包括UNIX和DOS) 都允许用双引号把多个单词 括起来形成一个参数。 C:\Users\liwei\Desktop>repeat " I am hungry " now
这行命令把字符串 “I am hungry” 赋给argv[1],把 “now” 赋给argv[2]。


将字符串转换为数字

printf()通过转换说明, 把数字从数值形式转换为字符串形式
scanf()通过转换说明,把字符串转换为数值形式

sprintf() 和 sscanf()

int sprintf(char *str, const char *format, ...);
/* 格式化输出到字符串 str, 格式说明与printf() 一样 */int sscanf(const char *str, const char *format, ...);
/* 从字符串 str 读取格式化输入, 格式说明与scanf()一样 */// 位于<stdio.h>
#include<stdio.h>int main(void) {char str1[50]="";sprintf(str1, "PI 的值 = %f", 3.1415926);puts(str1);int n;char str2[50]="9527";sscanf(str2,"%d",&n);printf("n = %d",n);return 0;
}

atoi() aotl() aotf()

int atoi(const char *str);
/* 把参数 str 所指向的字符串转换为一个整数(类型为 int 型) 返回转换后的双精度浮点数, 如果没有执行有效的转换, 则返回零 */long int atol(const char *str);
/* 把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)返回转换后的双精度浮点数, 如果没有执行有效的转换,则返回零 */double atof(const char *str);
/* 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)返回转换后的双精度浮点数, 如果没有执行有效的转换,则返回零(0.0) */// 这三个函数处理以数字或小数点(strtod().)的字符串, 如果字符串以其它字符开头, 返回0/0.0// 位于<stdlib.h>
#include <stdio.h>
#include <stdlib.h>int main(void) {puts("atoi() -----------------------------------");int val1;char str1[20]="93489";val1 = atoi(str1);printf("字符串值 = %s, 整型值 = %d\n",str1, val1);puts("atol() -----------------------------------");long val2;char str2[20]="93489ABCD";val2 = atol(str2);printf("字符串值 = %s, 长整型值 = %ld\n", str2, val2);puts("atof() -----------------------------------");float val3;char str3[20]="93.489=&-";val3 = atof(str3);printf("字符串值 = %s, 浮点值 = %f\n", str3, val3);return 0;
}

strtol() strtoul() strtod()

long int strtol(const char *str, char **endptr, int base);
/* 把参数 str 所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型)endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中读取完数值后的下一个字符base -- 基数, 必须介于 2 和 36(包含)之间,或者是特殊值 0。返回转换后的长整数, 如果没有执行有效的转换,则返回零 */unsigned long int strtoul(const char *str, char **endptr, int base);
/* 把参数 str 所指向的字符串根据给定的 base 转换为一个无符号长整数(类型为 unsigned long int 型)endptr -- 对类型为 char* 的对象的引用, 其值由函数设置为 str 中读取完数值后的下一个字符base 必须介于 2 和 36(包含)之间, 或者是特殊值 0返回转换后的无符号长整数, 如果没有执行有效的转换,则返回一个零 */double strtod(const char *str, char **endptr);
/* 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)endptr -- 对类型为 char* 的对象的引用, 其值由函数设置为 str 中读取完数值后的下一个字符返回转换后的双精度浮点数,如果没有执行有效的转换,则返回零(0.0) */// 位于<stdlib.h>
#include<stdio.h>
#include <stdlib.h>int main(void) {puts("strtol() -----------------------");char str1[30] = "20221130 This is test";char *ptr1;long ret1;ret1 = strtol(str1, &ptr1, 10);printf("数字(无符号长整数)是 %ld\n", ret1);printf("字符串部分是 |%s|\n", ptr1);puts("strtoul() ----------------------");char str2[30] = "20221130 This is test";char *ptr2;long ret2;ret2 = strtoul(str2, &ptr2, 10);printf("数字(无符号长整数)是 %lu\n", ret2);printf("字符串部分是 |%s|\n", ptr2);puts("strtod() -----------------------");char str3[30] = "20.221130 This is test";char *ptr3;double ret3;ret3 = strtod(str3, &ptr3);printf("数字(double)是 %f\n", ret3);printf("字符串部分是 |%s|\n", ptr3);return(0);
}

把命令行参数转换为数字

/* repeat.c -- 把命令行参数转换为数字 */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv []) {int i, times;if (argc < 2 || (times = atoi(argv[1])) < 1)printf("Usage: %s positive-number\n", argv[0]);else for (i = 0; i < times; i++)puts("Hello, good looking!");return 0;
}

C:\Users\liwei\Desktop>repeat 3

itoa() ftoa()

许多实现使用 itoa()和 ftoa()函数分别把整数和浮点数转换成字符串。 但是这两个函数并不是 C标准库的成员, 可以用sprintf()函数代替它们, 因为sprintf()的兼容性更好。

第八章 字符串和字符串函数相关推荐

  1. PHP源代码分析-字符串搜索系列函数实现详解

    今天和同事在讨论关键字过虑的算法实现,前几天刚看过布隆过滤算法,于是就想起我们公司内部的查找关键字程序,好奇是怎么实现的.于是查找了一下源代码,原来可以简单地用stripos函数查找, stripos ...

  2. smarty_modifier_truncate,无或者有md_substr的情况下都能正确截取字符串的php函数,可用于smarty。...

    smarty_modifier_truncate,无或者有md_substr的情况下都能正确截取字符串的php函数,可用于smarty. function smarty_modifier_trunca ...

  3. R语言使用str_remove函数和str_remove_all函数删除模式匹配的字符串:str_remove函数删除第一个模式匹配的字符串、str_remove_all函数删除所有模式匹配的字符串

    R语言使用str_remove函数和str_remove_all函数删除模式匹配的字符串:str_remove函数删除第一个模式匹配的字符串.str_remove_all函数删除所有模式匹配的字符串 ...

  4. R语言使用str_locate函数和str_locate_all函数来定位特定字符串或者字符串模式在字符串中的位置:str_locate函数第一个位置、str_locate_all函数定位所有位置

    R语言使用str_locate函数和str_locate_all函数来定位特定字符串或者字符串模式在字符串中的位置:str_locate函数第一个位置.str_locate_all函数定位所有位置 目 ...

  5. R语言str_starts函数和str_ends函数检查在字符串的开头或者结尾是否存在特定字符串或者字符串模式

    R语言str_starts函数和str_ends函数检查在字符串的开头或者结尾是否存在特定字符串或者字符串模式 目录

  6. R语言str_subset函数和str_which函数:str_subset函数提取字符串向量中所有包含匹配字符的字符串、str_which函数返回字符串向量中所有包含匹配字符的位置(索引)

    R语言str_subset函数和str_which函数:str_subset函数提取字符串向量中所有包含匹配字符的字符串.str_which函数返回字符串向量中所有包含匹配字符的位置(索引) 目录

  7. R语言使用str_replace函数和str_replace_all函数替换字符串中匹配到的模式:str_replace函数替换第一个匹配到的字符串、str_replace_all函数替换所有匹配到的

    R语言使用str_replace函数和str_replace_all函数替换字符串中匹配到的模式:str_replace函数替换第一个匹配到的字符串.str_replace_all函数替换所有匹配到的 ...

  8. R语言使用str_split函数和str_split_fixed函数将字符串分割(分裂、split)成几个部分:str_split函数使用指定的字符或者字符串分割字符串、str_split_fixed

    R语言使用str_split函数和str_split_fixed函数将字符串分割(分裂.split)成几个部分:str_split函数使用指定的字符或者字符串分割字符串.str_split_fixed ...

  9. c语言字符串作为函数返回值的类型,返回字符串类型的函数怎么写?

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 描述 请判断一个数是不是水仙花数. 其中水仙花数定义各个位数立方和等于它本身的三位数. 输入 有多组测试数据,每组测试数据以包含一个整数n(100< ...

最新文章

  1. $@ 与 $* 差在哪?
  2. python oop求三角形面积公式_python学习日记(OOP——类的内置方法)
  3. python3最新版本-Python3第三方组件最新版本追踪实现
  4. adb连接夜神模拟器
  5. 有关Java中json字符串与map的转换使用
  6. UVA 11136——Hoax or what
  7. Zabbix监控(十六):分布式监控-Zabbix Proxy
  8. 2020深度文本匹配最新进展:精度、速度我都要!
  9. How do I UPDATE from a SELECT in SQL Server?
  10. python写抽奖转盘_[宜配屋]听图阁
  11. FreeMarker笔记 第二章 数值和类型
  12. 南理工OJ-1098 - 字符串问题
  13. spark 动态预加载数据_Spark+TDengine 在中国电信电力测功系统监控平台上的应用实践...
  14. 移植wireless extension
  15. 【网络是怎样连接的】—— TCP/IP 传输数据
  16. 【ubuntu】禁用IP和端口
  17. vue路由报错Navigating to current location (“/login“) is not allowed踩坑总结
  18. day11 - 每日总结及作业
  19. [图示]做人36字诀:一)社会交往--教你建功立业
  20. android content

热门文章

  1. 超文本传输协议(HTTP)
  2. python制作二维码_利用Python制作二维码
  3. 基于Java+SpringBoot+Thymeleaf+Mysql新冠疫苗预约系统设计与实现
  4. 离线在Jenkins安装CoBOT安装插件
  5. 各类游戏对应的服务端架构
  6. 技嘉电脑怎么开启vt模式?
  7. 独木舟上的旅行(船问题贪心)
  8. 苹果电脑开机慢怎么办 苹果笔记本开机特别慢的处理方法
  9. 中国大陆开源(Linux)镜像站汇总
  10. 【Python 实战基础】如何绘制树状图展示Python数据分析师的知识结构