05. C语言数组
数组用于将多个数据集中存储,方便管理,此文将任何集中存储一组数据的语句都称为数组,数组根据存储数据的类型和方式分为以下类型:数组、结构体、共用体、枚举。
【数组】
数组用于存储多个类型相同的数据,可以理解为同型数组,数组内的数据称为数组元素,数组元素占用连续的虚拟地址,每个元素都有一个数组内部编号,称为数组下标,数组元素通过“数组名+下标”的方式调用,下标从0开始分配,第一个元素下标为0。
#include <stdio.h>
int main()
{
    int a[5] = {1,2,3,4,5};      //int指定元素类型,a为数组名,[]符号内指定元素个数,{}符号内设置数组元素的值
    a[0] = 0;                    //使用“数组名[下标]”调用数组元素
    
    int b[5] = {1,2,3};          //只为部分元素赋值,未赋值的元素编译器自动赋值为0
    
    int c[] = {1,2,3,4,5};       //定义数组并赋值,可以不指定长度,编译器以赋值元素数量确定数组长度
    
    int d[5];                    //定义数组不赋值,但是必须指定数组长度
    
    const int e[3] = {1,2,3};    //常量数组,不能修改内部元素
    
    return 0;
}注意:数组下标从0开始计算,数组元素数量从1开始计算,比如 int a[5],数组a有5个元素,下标最大为4。
调用数组元素时,下标可以使用常量、变量两种数据指定,变量的值可以在程序执行期间临时确定,默认情况下两种方式都不会进行数组越界访问检查,若程序需要使用变量指定数组下标,则应该首先判断变量的值,防止超过数组长度导致越界访问。
#include <stdio.h>
int main()
{
    int a[5] = {1,2,3,4,5};
    unsigned int b;
    
    printf("输入访问下标\n");
    scanf("%d",&b);            //终端输入函数,输入b的值,以回车结束
    
    /* 数组下标从0开始,下标上限为数组长度-1 */
    if(b > 4)
    {
        printf("禁止越界访问\n");
    }
    else
    {
        printf("a[%d]的值为%d\n", b, a[b]);
    }
    
    return 0;
}使用变量作为下标时,编译器使用如下指令调用数组元素:
mov  edx, DWORD PTR [rbp+rax*4-0x20]rbp-0x20 为数组的虚拟地址,rax为数组下标,4为数组元素长度,rax乘以4定位到元素所在数组的内部地址,rbp-0x20+rax*4定位到数组元素的虚拟地址。
多维数组
数组可以嵌套定义,在一个数组中存储其它数组,这种数组称为多维数组,多维数组存储的其它数组的长度和元素类型需要相同。
二维数组的元素是一维数组,三维数组的元素是二维数组,以此类推,还可以有四维数组。
#include <stdio.h>
int main()
{
    /* 定义二维数组,其中包含3个一维数组,一维数组的长度为5 */
    int a[3][5] =
    {
        {1, 2, 3, 4, 5},
        {11, 12, 13, 14, 15},
        {21, 22, 23, 24, 25}
    };
    
    /* 二维数组使用两个下标调用元素,这里调用第1个一维数组的第2个元素 */
    a[0][1] = 0;
    
    /* 定义三维数组,内部包含3个二维数组,每个二维数组包含4个一维数组,每个一维数组包含5个元素 */
    int b[3][4][5] =
    {
        {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}, 
        {{1,1,1,1,1}, {1,1,1,1,1}, {1,1,1,1,1}, {1,1,1,1,1}},
        {{2,2,2,2,2}, {2,2,2,2,2}, {2,2,2,2,2}, {2,2,2,2,2}}
    };
    
    /* 调用三维数组元素 */
    b[2][3][4] = 0;
    return 0;
}有些教材将二维数组比喻为一张表,将三维数组比喻为一个立体结构,其实这样解释是不准确的,甚至基于这个思路就无法理解三维以上的数组。
二维数组将多个一维数组集中存储,三维数组将多个二维数组集中存储,四维数组将多个三维数组集中存储,内存单元并没有分为平面结构和立体结构,多维数组的存储也是线性的,所有的数组元素全部依次排列在虚拟地址中。
比如定义一个二维数组:
int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}};内部元素的存储顺序如下:
a[0][0]
a[0][1]
a[0][2]
a[1][0]
a[1][1]
a[1][2]
a[2][0]
a[2][1]
a[2][2]使用变量调用二维数组元素
#include <stdio.h>
int main()
{
    int a[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
    unsigned int b,c;
    scanf("%u%u", &b, &c);      //输入变量b、c的值
    
    if(b<3 && c<3)
    {
        printf("元素值为:%d\n", a[b][c]);    //使用两个变量调用数组元素
    }
    
    return 0;
}调用二维数组元素步骤如下:
1.变量b乘以12,定位到二维数组内部地址。
2.变量c乘以4,定位到一维数组内部地址。
3.二维数组地址+b+c。
汇编代码如下:
mov    eax,DWORD PTR [rsp+0x8]           ;变量b写入eax
mov    edx,DWORD PTR [rsp+0xc]           ;变量c写入edx
lea    rax,[rax+rax*2]                   ;b*3
add    rax,rdx                           ;b+c
mov    esi,DWORD PTR [rsp+rax*4+0x10]    ;rsp+0x10为二维数组地址,rax*4为元素占用的数组内部地址编译器对调用二维数组元素的复杂数学运算进行了优化,首先b乘以3,之后与c相加,此时b和c都需要再乘以4,所以让b+c的结果统一乘以4,再与二维数组地址相加得出元素具体地址。
变长数组
变长数组的长度在程序执行期间确定,而不是在编译期间确定,变长数组的长度一旦确定就不能再修改,长度的改变只有一次机会。
变长数组使用方式:
1.因为变长数组的长度不能在编译期间确定,所以它只能定义为局部数据,只能存储在程序执行时临时分配的栈空间中。
2.变长数组在定义时不能为成员赋值,只能在确定长度后赋值。
3.线程使用的栈空间容量是有限的,如果变长数组长度过大可能会导致栈溢出,所以变长数组的长度应该做一个限制,超过指定长度则禁止设置,具体限制长度为多少要看程序自身复杂度、会剩余多少栈空间供其使用。
#include <stdio.h>
int main()
{
    int a;
    scanf("%d", &a);    //输入a的值
    
    int b[a];           //使用变量a指定数组长度,实现变长数组
    b[0] = 1;
    
    return 0;
}一般不推荐使用变长数组,可以使用申请内存函数代替它。
【字符串】
存储字符编码的char类型数组称为字符串,字符串变量的元素经常需要修改,不同时期的值会占用字符串不同的长度,为了确定字符串使用了哪些数组元素,C语言规定字符串需要在已用元素之后添加一个空字符(字符编码为0),程序通过空字符的位置确定字符串有效字符长度。
空字符只在程序内部使用,将字符串存储在文件中时一般不存储空字符。
#include <stdio.h>
int main()
{
	char a[10] = "ali";    //使用多个字符赋值,字符放在""符号内,编译器转换为对应的UTF8编码,未赋值元素自动赋值为0
	char b[3] = "ali";     //错误,没有剩余位置放置空字符
	char c[50] = "阿狸\0喜羊羊";    //\0表示空字符
	printf("%s\n", c);            //输出“阿狸”,空字符及之后的字符不会使用
	char d[10] = {1, 2, 3};       //为元素单独赋值,一般表示当做普通数组,而非字符串
	return 0;
}若字符串的长度太大,不方便单行存储,可以使用多行存储,有以下两种方式。
#include <stdio.h>
int main()
{
    /* 使用\符号换行存储,\符号以及换行字符不算入字符串元素,但是多行之间的空格算入字符串,此方式需要保证多行之间没有空格 */
    char a[50] = "阿狸\
喜羊羊";
    
    /* 使用多组""符号换行存储,多组""符号之间的换行和空格都不算入字符串 */
    char b[50] = "阿狸"
    "喜羊羊";
    
    return 0;
}转义字符
有些字符起控制作用,不用于显示,比如回车、换行,这些字符无法直接定义,可以使用转义字符表示。
有些字符与C语言关键词重复,比如 \ ' " ? 符号,这些字符也不能直接编写,需要使用转义字符表示。
转义字符以\符号开始(注意不是/符号),之后定义转义字符类型,常用转义字符如下:
\r,表示回车,将打印位置移动到最左侧
\n,表示换行,将打印位置向下移动一行
\b,表示退格,将打印位置向左移动一个字符
\0,表示空字符
\t,表示水平空白
\v,表示垂直空白
\',表示 ' 符号
\",表示 " 符号
\?,表示 ? 符号
\\,表示 \ 符号
#include <stdio.h>
int main()
{
    char a[] = "ali\n";    //"ali\n"会被编译器转换为如下字符编码:97,108,105,10,0
    printf(a);             //输出ali并换行
    
    return 0;
}【结构体】
结构体用于存储一组类型不同的数据,可以理解为异型数组,因为结构体成员的类型不同、长度不同,所以结构体成员不能使用下标的方式调用,每个结构体成员都有自己的名称,使用“结构体名+成员名”的方式调用结构体成员。
编写代码时经常需要使用多个成员类型相同的结构体,比如存储一个班级学生的属性信息,此时需要使用几十个内部成员相同的结构体,为了简化结构体定义代码,C语言将结构体的定义分为两个部分:声明部分、实体部分。
1.声明部分,定义结构体成员的类型、名称,声明是一种伪指令,不会被编译为任何数据,作用只是简化定义多个成员类型相同的结构体、无需重复指定成员类型、成员名称,此部分也称为结构体类型,表示其用于确定结构体的整体长度以及内部成员的类型。
2.实体部分,根据声明部分定义具体的结构体,也称为结构体实例。
#include <stdio.h>
int main()
{
	/* 使用struct关键词定义结构体的声明部分和实体部分,zoo为声明部分的名称,成员的类型和名称放在{}符号内,声明语句以;符号结尾 */
	struct zoo
	{
		char name[50];      //学生姓名
		char gender[10];    //学生性别
		float score;        //考试分数
		int rank;           //考试排名
	};
	struct zoo ali = {"阿狸", "男", 90, 3};    //使用声明定义结构体实例
	ali.score = 91;                           //使用“实例名.成员名”调用内部成员
	struct zoo taozi = {"桃子", "女", 92};     //只为部分成员赋值时,剩余成员编译器默认赋值为0,这一点与同型数组相同
	const struct zoo xyy = {"喜羊羊", "男", 95, 1};   //常量结构体,定义时赋值,之后不能修改
	struct zoo zoo[3] = {ali, taozi, xyy};           //结构体作为数组元素
	printf("%s\n", zoo[0].name);                     //调用方式
	struct zoo x = ali;    //结构体可以引用其它同类型实例赋值
	x = xyy;               //结构体可以整体修改,若结构体中定义了常量成员则禁止整体修改
	return 0;
}可以使用typedef关键词为结构体类型设置另一个名称。
#include <stdio.h>
int main()
{
    typedef struct zoo
    {
        char name[50];
        int age;
    } student;
    
    student ali = {"阿狸", 8};
    student xyy = {"喜羊羊", 9};
    
    return 0;
}匿名结构体
声明部分可以不设置名称,称为匿名结构体类型,匿名结构体类型需要在声明时定义所有实例,之后不能再定义新的实例。
#include <stdio.h>
int main()
{
    struct
    {
        char name[50];
        int age;
    } ali={"阿狸", 8}, xyy={"喜羊羊", 9};
    return 0;
}有名称的结构体类型也可以使用这种简化方式定义实例。
嵌套结构体
结构体内部可以定义另一个结构体实例,这种功能很好理解,另外结构体的声明部分也可以嵌套定义,从而将结构体内的数据继续分类管理。
#include <stdio.h>
int main()
{
    struct zoo
    {
        char name[50];      //姓名
        char gender[10];    //性别
        
        /* grades记录各科考试成绩,嵌套结构体声明需要直接定义出所有实例,确定占位长度,但是不能在内部直接赋值 */
        struct grades
        {
            float score;   //分数
            int rank;      //排名
        } math, chinese, english;
    };
    
    struct zoo ali = {"阿狸", "男", {90,1}, {80,2}, {70, 3}};
    
    printf("阿狸的数学分数为:%f\n", ali.math.score);
    
    return 0;
}功能等同于如下代码,只不过下面代码中的 struct grades 是公用的,struct zoo 之外的代码也可以使用。
#include <stdio.h>
int main()
{
    struct grades
    {
        float score;
        int rank;
    };
    
    struct zoo
    {
        char name[50];
        char gender[10];
        
        struct grades math, chinese, english;
    };
    
    struct zoo ali = {"阿狸", "男", {90,1}, {80,2}, {70, 3}};
    
    printf("阿狸的数学分数为:%f\n", ali.math.score);
    
    return 0;
}结构体的实际长度
结构体在内存中存储时的长度并非所有成员长度的总和,因为结构体成员的长度不同,地址对齐值也不同,多个成员中间往往会有地址对齐额外占用的内存单元,所以结构体的实际长度会大于内部成员长度的总和,可以使用sizeof查询结构体的长度。
#include <stdio.h>
int main()
{
    struct k
    {
        int i;
        char c;
    };
    
    printf("k的长度为%d字节\n", sizeof(struct k));    //在x86-64计算机中k类型的长度为8字节
    
    return 0;
}实现数组整体修改
C语言规定数组只能在定义时整体赋值,之后不能整体修改,两个数组之间也不能引用赋值,即使两个数组的元素类型、元素数量都相同也不行,但是同类型的结构体实例之间可以互相引用赋值,若需要整体修改数组,除了使用库函数或者使用循环代码外还可以将数组放在结构体内。
#include <stdio.h>
int main()
{
    typedef struct
    {
        char name[50];
    } string;
    
    /* 注意:实际项目中数组成员赋值应该做越界访问检查,这里简化代码,不做检查 */
    
    string ali = {"阿狸"};
    string xyy = {"喜羊羊"};
    
    string x = ali;
    printf("%s\n", x.name);    //输出阿狸
    
    x = xyy;
    printf("%s\n", x.name);    //输出喜羊羊
    
    return 0;
}变长数组成员
结构体内的数组成员可以在声明时不指定长度,而是在定义实例时确定长度,长度可以使用变量指定,若多个同类型结构体实例为变长数组成员设置了不同的长度,则他们本质上是不同类型的结构体实例,他们的长度不同,他们之间互相引用赋值时会产生错误。
使用注意事项:
1.变长数组成员不能单独出现,否则结构体长度默认为0,不能编译。
2.变长数组成员最多定义一个,并且只能定义在结构体成员的末尾。
3.使用sizeof计算结构体长度时,变长数组成员不计入结构体长度。
此类结构体实例不能定义为函数局部数据,不能存储在栈中,存储方式如下:
1.若变长数组成员可以在编译期间确定长度,可以将结构体实例定义为全局成员、静态局部成员,放在全局数据区存储。
2.若变长数组成员不能在编译期间确定长度,需要在程序执行期间向操作系统申请内存存储,注意结构体总长度需要加上变长数组成员长度。
#include <stdio.h>
struct zoo
{
    int age;
    char name[];
};
struct zoo ali = {8, "阿狸"};
struct zoo xyy = {9, "喜羊羊"};
int main()
{
    printf("%s:%d岁\n%s:%d岁\n\n", ali.name, ali.age, xyy.name, xyy.age);
    return 0;
}位字段
结构体也可以用于存储自定义长度的数据,这种结构体称为位字段,编译器会将多个自定义长度的数据进行整合,最先定义的数据排在最低位,最后定义的数据排在最高位,中间不会进行地址对齐,一般用于设置计算机底层功能相关数据。
#include <stdio.h>
int main()
{
    struct k
    {
        int a : 4;    //int表示成员a最大可以设置为32位长度,而非固定为32位长度,这里设置为4位二进制数字长度
        int b : 2;    //b长度2位
        int c : 2;    //c长度2位
        int d : 8;    //d长度8位,结构体总长度16位,等于2字节
    };
    
    struct k k1 = {1,2,1,5};    //为每个自定义长度数据赋值
    
    printf("0x%hX\n", k1);      //输出0x561,转二进制 = 0101 01 10 0001,按自定义的4个长度将二进制数据分为4组观察,每组的值对应k1设置的值
    
    return 0;
}【共用体】
一个数据使用汇编语言编写代码时,可以使用指令随意操作一段内存数据,比如可以进行如下操作:
1.多个数据使用同一块内存存储,这些数据不会同时使用,放在一起存储不会发生冲突。
2.将一段内存中的数据当做任意类型使用,比如有符号整数、无符号整数、浮点数、数组、结构体。
C语言不像汇编那样灵活,所以C语言提供了共用体,使用共用体可以实现上述两种功能,当然使用指针一样可以实现随意操作内存,从而实现共用体的功能,但是使用指针太过繁琐。
共用体的使用方式类似结构体,也是先声明后使用,共用体的声明部分用于设置共用体可以存储、转换的数据类型,使用共用体转换数据类型时只能在声明中的类型之间转换。
#include <stdio.h>
int main()
{
	/* 使用union关键词定义共用体的声明和实体部分,共用体的长度为其中最大成员的长度 */
	union aliun
	{
		int i;
		float f;
	};
	union aliun ali1;          //定义共用体实例,不可直接赋值
	ali1.i = -1;               //通过成员名确定共用体现在的类型,之后为共用体赋值
	printf("%d\n", ali1.i);    //输出-1
	ali1.f = 3.14;             //使用共用体存储float类型数据
	printf("%f\n", ali1.f);    //输出3.14
	ali1.i = 9;                //再次存储int类型数据
	printf("%d\n", ali1.i);    //输出9
	ali1.f = 3.1415;           //再次存储float类型数据
	printf("%f\n", ali1.f);    //输出3.1415
	return 0;
}实现某些底层功能时,可能需要将一个数据保持原值不变、同时修改其类型,比如将数据在数组与单个数据之间进行转换,此时可以使用共用体。
#include <stdio.h>
int main()
{
	/* 定义匿名共用体 */
	union
	{
		char c[4];
		int i;
	} x;
	x.c[0] = 1;
	x.c[1] = 2;
	x.c[2] = 3;
	x.c[3] = 4;
	printf("0x%x\n", x.i);    //转换为int类型,输出0x4030201,实际存储值为0x04030201
	return 0;
}【枚举】
枚举,意为一一列举,将一个变量可以使用的值全部列举出来,之后此变量只能使用其中之一进行赋值,枚举用于限制一个变量的可用值,防止为变量设置一个不合适的值导致功能出错,这个限制是由编译器提供的,对程序进行逆向分析并修改时并不存在任何限制。
定义枚举时无需指定数据类型,枚举默认为int类型,并且不能修改为其它类型。
枚举同样是先声明后使用,声明部分用于设置枚举变量的可用值,每个可用值都有一个名称,之后通过声明部分定义枚举变量,为枚举变量赋值时只能使用声明中设置的可用值。
#include <stdio.h>
int main()
{
    enum alienum{m1=1, m2=2, m3=3};    //声明枚举,alienum为声明部分的名称,{}符号内设置可用值
    enum alienum a = m1;               //定义枚举变量,a为实体部分的名称,使用声明部分设置的可用值名称进行赋值
    enum alienum b = m2;
    a = m2;                            //修改枚举变量的值
    a = b;
    
    return 0;
}声明部分设置的可用值等于定义的int常量,每个常量都有自己的名称,若只设置常量名而不为常量赋值,则编译器默认从0开始依次为每个常量赋值,声明部分的常量在语意上属于函数的局部常量,这些常量可以直接使用,并且不能与其它局部数据同名。
#include <stdio.h>
int main()
{
	int a;
	//enum alienum{a, b, c};    //错误,与已经存在的变量a同名
	enum alienum{m1, m2, m3};   //正确
	printf("%d\n%d\n%d\n", m1, m2, m3);    //输出0、1、2
	return 0;
}
05. C语言数组的更多相关文章
- GO语言数组和切片实例详解
		本文实例讲述了GO语言数组和切片的用法.分享给大家供大家参考.具体分析如下: 一.数组 与其他大多数语言类似,Go语言的数组也是一个元素类型相同的定长的序列. (1)数组的创建. 数组有3种创建方式: ... 
- C语言 数组 列优先 实现
		C语言数组结构列优先顺序存储的实现 (GCC编译). 从行优先转换为列优先存储方式, 与行优先相比, 不同之处在于改变了数组维界基址的先后顺序, 从而改变了映像函数常量基址. /** * @brief ... 
- C语言 数组 行优先 实现
		C语言数组结构行优先顺序存储的实现 (GCC编译). /** * @brief C语言 数组 行优先 实现 * @author wid * @date 2013-11-02 * * @note 若代码 ... 
- 不可或缺 Windows Native (5) - C 语言: 数组
		[源码下载] 不可或缺 Windows Native (5) - C 语言: 数组 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 数组 示例cArray.h #ifn ... 
- C语言数组
		在C语言中,对于三维或三维以上数组的使用并没有很好的支持,而且使用率也非常的低,后面会对三维数组做一些简单的分析,这篇文章主要以二维数组来探讨一些C语言中数组使用的相关概念和技巧. 1 一个var[i ... 
- C语言数组:C语言数组定义、二维数组、动态数组、字符串数组
		1.C语言数组的概念 在<更加优美的C语言输出>一节中我们举了一个例子,是输出一个 4×4 的整数矩阵,代码如下: #include <stdio.h> #include &l ... 
- Go语言数组的使用
		Go 语言数组 Go 语言提供了数组类型的数据结构. 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形.字符串或者自定义类型. 相对于去声明number0 ... 
- Go 语言数组
		Go 语言提供了数组类型的数据结构. 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形.字符串或者自定义类型. 相对于去声明number0, number ... 
- C语言 > 数组和指针
		C语言 数组和指针 const: 关于指针和const需要注意一些规则.首先,把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的. 然而,只能把非const数据的地 ... 
- Go语言数组和切片的原理
		目录 数组 创建 访问和赋值 切片 结构 初始化 访问 追加 拷贝 总结 数组和切片是 Go 语言中常见的数据结构,很多刚刚使用 Go 的开发者往往会混淆这两个概念,数组作为最常见的集合在编程语言中是 ... 
随机推荐
- KingbaseES sys_restore 恢复表时默认不包括表上的索引
			前言 最近碰到一个案例,在使用sys_restore恢复指定表时,默认不恢复表上的索引,如果想恢复需要单独指定. 测试过程 [](javascript:void(0) 查看表的有关属性:test=# ... 
- 实际项目中如何使用Git做分支管理
			前言 Git是一种强大的分布式版本控制系统,在实际项目开发中使用Git进行分支管理是非常常见的做法,因为它可以帮助团队高效的协作和管理项目的不同版本,今天我们来讲讲在实际项目中最常用的Git分支管理策 ... 
- #树形dp#洛谷 3687 [ZJOI2017]仙人掌
			题目 给定一个简单无向连通图,问有多少种加边方案使得这个图变成简单仙人掌. 分析 首先找到一棵生成树,考虑其它非树边所对应的树的路径上的边最多只能用一次, 这可以用树上差分做,如果一个点到其父节点的边 ... 
- Python 数组和列表:创建、访问、添加和删除数组元素
			Python 没有内置支持数组,但可以使用 Python 列表来代替. 数组 本页将向您展示如何使用列表作为数组,但要在 Python 中使用数组,您需要导入一个库,比如 NumPy 库.数组用于在一 ... 
- IDEA Tab键设置为4个空格
			在不同的编辑器里 Tab 的长度可能会不一致,这会导致有 Tab 的代码,用不同的编辑器打开时,格式可能会乱. 而且代码压缩时,空格会有更好的压缩率. 所以建议将 IDEA 的 Tab 键设置为 4 ... 
- 【FAQ】HarmonyOS SDK 闭源开放能力 —Scan Kit
			1.问题描述 Scan Kit扫描专用底层码流接口需要鉴权,鉴权失败后功能还能用吗? 解决方案 如果已经申请过白名单,因为异常导致的鉴权失败会优先放通,保障业务成功. 2.问题描述 调用Scan Ki ... 
- Taurus.MVC 性能压力测试(ap 压测 和 linux 下wrk 压测):.NET Core 版本
			前言: 最近的 Taurus.MVC 版本,对性能这一块有了不少优化,因此准备进行一下压测,来测试并记录一下 Taurus.MVC 框架的性能,以便后续持续优化改进. 今天先压测 .NET Core ... 
- Godot Label样式 Textrue纹理,实现样式修改,背景填充
			目录 前言 运行环境 新建项目 Style 样式讲解 StyleBoxEmpty:普通样式 StyleBoxTexture:字体样式 StyleBoxFlat:填充样式 StyleBoxLine:行样 ... 
- sql 语句系列(多表之链二)[八百章之第四章]
			从多个表中返回缺失值 比如说查询每个员工的部门,且查看部门的所有员工. 这里考虑一种情况就是可能有部门没有员工,同样有些员工还没有分配部门. 解析使用 full outer join. select ... 
- mysql 重新整理——性能下降的原因[四]
			前言 什么是性能下降? 其实就是代码运行的环境变了,那么环境变化是什么? 比如cpu上升了,内存满了.有或者表中数量增加了,量变了. 其实这些是dba干的,但是呢,我们也需要去了解下,并且优化我们的c ... 
