嵌入式C语言自我修养《数据存储与指针》学习笔记

news2024/11/25 0:35:58

目录

 一、数据类型和存储

 1.大端模式和小端模式

 2.有符号数和无符号数

 二、数据对齐

1.为什么要数据对齐

 2.结构体对齐

 3.联合体对齐

三、数据的可移植性

四、 Linux内核中的size_t类型

 五、typedef的使用

1. typedef的基本用法

 2.使用typedef的优势

3. typedef的作用域

 六.常量和变量

 1.变量的本质


程序是什么?程序=数据结构+算法。我们写程序为了什么?为了解决现实中的一些问题,尤其是一些需要大量重复计算的问题。ANSI C标准为我们提供的32个关键字里,除了控制程序结构的一些关键字,绝大部分都与数据类型和存储相关,如表7-1所示。

        C99/C11标准新增的关键字,也大都与数据类型和存储相关,如表7-2所示。

 一、数据类型和存储

        类型,是一组数值及对该组数值进行各种操作的集合。同一种类型的数据,在不同的处理器平台下,存储方式可能不一样。不同类型的数据,在同一个处理器平台下,存储方式和运算规则也可能不一样。

 1.大端模式和小端模式

        在计算机中,位(bit)是最小的存储单位,在一个DDR SDRAM内存电路中,通常使用一个电容器来表示:充电时高电位表示1,放电时低电位表示0。8个bit组成一字节(Byte)字节是计算机最基本的存储单位,也是最小的寻址单元,计算机通常以字节为单位进行寻址。在一个32位的计算机系统中,通常4字节组成一个字(Word)字是软件开发者常用的存储单位

        我们使用C语言提供的int关键字,可以定义一个整型变量。

int i = 0x12345678

        编译器根据变量i的类型,在内存中分配4字节大小的存储空间来存储i变量的值0x12345678。一个数据在内存中有2种存储方式:高地址存储高字节数据,低地址存储低字节数据;或者高地址存储低字节数据,而低地址则存储高字节数据。不同字节的数据在内存中的存储顺序被称为字节序。根据字节序的不同,我们一般将存储模式分为大端模式和小端模式

        不同架构的处理器,存储模式一般也不同。ARM、X86、DSP一般都采用小端模式,而IBM、Sun、PowerPC架构的处理器一般都采用大端模式。判断大小端示例:

#include <stdio.h>

int main()
{
    int a = 0x11223344;
    char b = a;
    if(b = 0x44)
    {
        printf("littel duan\n");
    }
    else
    {
        printf("da duan\n");
    }
    return 0;
}

        在数据存储模式中,除了字节序,还有位序的概念。位序指在一字节的存储中,各个比特位的存储顺序。以十六进制数据0x78=01111000(B)为例,其在内存中可能有2种存储方式。

        一般情况下字节序和位序是一一对应的。小端模式下,低端地址存储低字节数据,在一字节中,bit0地址也用来存储这个字节的bit0位。大端模式则相反,bit0用来存储一字节的高比特位

        为什么不同架构的处理器在存储模式上会有大小端之分呢?

         一般来讲,小端模式低地址存储低字节数据,比较符合人类的思维习惯;而大端模式则更适合计算机的处理习惯:不需要考虑地址和数据的对应关系,以字节为单位,把数据从左到右,按照由低到高的地址顺序直接读写即可。大端模式一般用在网络字节序、各种编解码中。

 2.有符号数和无符号数

        C语言为了能表示负数,引入了有符号数和无符号数的概念,在声明数据类型时分别使用关键字signed和unsigned修饰。我们定义的变量如果没有使用signed或unsigned显式修饰默认是signed型的有符号数

        一个字符型的有符号数,最高的位bit7是符号位:0表示正数,1表示负数,其余的比特位用来表示大小。而一个字符型的无符号数,所有的比特位都用来表示数的大小。因此有符号数和无符号数能表示的数值范围是不一样的,对于一个字符型数据而言,有符号数能表示的数值范围为[-128,127],而无符号数的数值范围为[0,255]。我们使用printf()函数打印数据时,可以使用%d和%u格式符分别格式化打印有符号数和无符号数。 

 二、数据对齐

1.为什么要数据对齐

        一个程序在编译过程中,编译器在给我们定义的变量分配存储空间时,并不是随机分配的,它会根据不同数据类型的对齐原则给变量分配合适的地址和大小。所谓数据对齐原则,就是C语言中各种基本数据类型要按照自然边界对齐:一个char型的变量按1字节对齐,一个short型的整型变量按sizeof(short)字节对齐,一个int型的整型变量要按sizeof(int)字节对齐。每种数据类型的对齐字节数一般也被称为对齐模数。不同数据类型的对齐模数如图7-3所示。

 示例代码:

#include <stdio.h>

int main(void)
{
    char a = 1;
    short b = 2;
    int c = 3;
    printf("&a = %p\n",&a);
    printf("&b = %p\n",&b);
    printf("&c = %p\n",&c);
    return 0;
}

程序运行结果:

        通过打印结果可以看到,不同类型的变量在内存中的地址对齐是不一样的,每种变量都按照各自的对齐模数对齐

        每个变量在内存中为什么非要地址对齐呢?

        这主要是由CPU硬件决定的。不同处理器平台对存储空间的管理不同,为了简化CPU电路设计,有些CPU在设计时简化了地址访问,只支持边界对齐的地址访问,因此编译器也会根据处理器平台的不同,选择合适的地址对齐方式,以保证CPU能正常访问这些存储空间。在图7-4中,我们定义了一个int型变量,如果编译器把它分配到了内存中2字节对齐的地址空间上,那么它的存储地址就没有自然对齐,CPU在读写这个数据时,本来一个指令周期就可以搞定的事情,现在可能就需要花2个指令周期了。先从零地址开始,读取4字节,保留高2字节的数据;再从偏移为4的地址开始,读取4字节,保留低2字节的数据;最后将两部分数据合并,就是我们实际要读取的int型变量的值了。

 2.结构体对齐

        C语言的基本数据类型不仅要按照自然边界对齐复合数据类型(如结构体、联合体等)也要按照各自的对齐原则对齐。以结构体为例,当我们定义一个结构体变量时,编译器会按照下面的原则在内存中给这个变量分配合适的存储空间。

● 结构体内各成员按照各自数据类型的对齐模数对齐。
● 结构体整体对齐方式:按照最大成员的size或其size的整数倍对齐。

 示例如下:

#include <stdio.h>
struct student
{
    char sex;
    int num;
    short age;
};

int main(void)
{
    struct student stu;
    printf("&stu.sex = %p\n",&stu.sex);
    printf("&stu.num = %p\n",&stu.num);
    printf("&stu.age = %p\n",&stu.age);
    printf("struct size: %d\n",sizeof(struct student));
    return 0;
}

运行结果如下:

 结构体成员调整位置结果如下:

        因为结构体内各个成员都要按照自身数据类型的对齐模数对齐,所以在结构体内部难免会有“空洞”产生,导致结构体的大小也不一样。结构体之所以要对齐,根本原因就是为了加快CPU访问内存的速度,在具体实现上,一般都采用每种数据类型的默认对齐模数sizeof(type)对齐

        不同的编译器有时候可能会采取不同的对齐标准,以GCC为例,GCC默认的最大对齐模数为4(我的gcc是8,好像是因为x64位,为了提高系统运行性能,当一种数据类型的大小超过4字节时会仍然按照4字节对齐,这是GCC和VC++6.0、Visual Studio、arm-linux-gcc等编译器不一样的地方

 代码示例如下:

//#pragma pack (4)
#include <stdio.h>

struct student
{
    char sex;
    double num;
};

int main(void)
{
    struct student stu;
    printf("&stu.sex = %p\n",&stu.sex);
    printf("&stu.num = %p\n",&stu.num);
    printf("struct size: %d\n",sizeof(struct student));
    printf("num size: %d\n",sizeof(stu.num));
    return 0;
}

运行结果如下:

加上第一行代码#pragma pack (4),运行结果如下:

        如果在结构体里内嵌其他结构体,那么结构体作为其中一个成员也要按照自身类型的对齐模数对齐。结构体自身的对齐模数是该结构体中最大成员的size,或者其size的整数倍

 3.联合体对齐

除了结构体,联合体也有自己的对齐原则。
● 联合体的整体大小:最大成员对齐模数或对齐模数的整数倍。
● 联合体的对齐原则:按照最大成员的对齐模数对齐。

 示例如下:

//#pragma pack (4)
#include <stdio.h>

union u
{
    char sex;
    double num;
    int age;
    char a[11];
};

int main(void)
{
    union u stu;
    printf("&stu.sex = %p\n", &stu.sex);
    printf("&stu.num = %p\n", &stu.num);
    printf("&stu.age = %p\n", &stu.age);
    printf("&stu.a = %p\n", stu.a);
    printf("union size: %d\n", sizeof(union u));
    return 0;
}

运行结果如下:

 

当开头加上#pragma pack (4) ,设置最大对齐模数为4时,运行结果如下:

三、数据的可移植性

        什么是数据的可移植性呢?我们可以先从一个简单的程序开始,从一个sizeof关键字开始我们的思考。

#include <stdio.h>

int main(void)
{
    printf("%d\n",sizeof(int));
    return 0;
}

        我们可以使用sizeof关键字去查看int类型的数据在内存中的大小,在不同的编译环境下编译上面的程序并运行,你会发现运行结果可能不一样。在Turbo C环境下编译运行int型数据大小可能是2字节;使用VC++6.0或GCC编译运行结果可能是4字节;如果使用64位编译器在64位处理器上编译运行,运行结果则可能是8字节。在一个跨平台的程序中,有时候我们会需要一个固定大小的存储空间,或者一个固定长度的数据类型。如果使用int型来表示,那么当程序在不同的编译环境下运行时,int型数据的大小就可能发生改变,也就是说int型数据不具备可移植性。

        那么如何解决这个问题呢?我们可以使用C语言提供的typedef关键字来定义一些固定大小的数据类型。

        我们可以将使用typedef关键字定义的数据类型封装在一个头文件data_type.h中,在实际编程中,当你需要使用一个32位固定大小的无符号数据时,先#include这个data_type.h头文件,然后就可以直接使用u32来定义变量了。当程序在另一个平台上运行,unsigned int的大小变成了2字节时,也没关系,我们可以修改data_type.h,将u32使用unsigned long重新定义一遍即可。

//data_type.h
//typedef unsigned int u32;
typedef unsigned long u32;

//main.c
#include <stdio.h>
#include "data_type.h"

int main(void)
{
    u32 s;
    printf("size:%d\n",sizeof(s));
    return 0;
}

        long的运行结果是8,int的运行结果是4。

        通过修改data_type.h对u32类型重新定义后,u32的长度还是32位,你的程序代码中所有使用u32的地方就不需要修改了,u32这个数据类型因此就具备了可移植性,可以在多个平台上运行。
        在C99标准定义的标准库中,新增加了stdint.h和intypes.h头文件,用来支持可移植的数据类型。stdint.h头文件主要用来定义可移植的数据类型,我们在编程中可以直接使用。

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    __int16_t s;
    int16_t s1;
    printf("size: %d\n",sizeof(s));
    printf("size: %d\n",sizeof(s1));
    return 0;
}

运行结果如下:

        现在的操作系统一般都支持多种CPU架构、多种处理器平台。操作系统为了实现跨平台运行,一般都会考虑数据的可移植性,如大小端存储模式、数据对齐、字长等。我们在编程时,可以把程序中与系统、平台相关的部分隔离封装在一个单独的头文件或配置文件中,整个程序的可移植部分和不可移植部分也就变得泾渭分明,更加方便后续的管理、维护和升级。

四、 Linux内核中的size_t类型

        Linux内核中定义了很多变量,使用了各种不同的数据类型,总的来说,可以分为3类。

● C语言基本数据类型:int、char、short。
● 长度确定的数据类型:long。
● 特定内核对象的数据类型:pid_t、size_t。 

        我们以内核中经常使用的size_t数据类型为例,带大家体验一下使用可移植数据的好处。数据类型size_t一般使用#define宏定义,后面使用一个_t的后缀表示Linux内核中在某些地方特定使用的数据类型

        size_t数据类型一般用在表示长度、大小等无关正负的场合,如数组索引、数据复制长度、大小等。在Linux内核源码中可以随处看到它的身影。 

        不仅在内核中,C标准库中定义的各种库函数也大量使用这种数据类型。

        使用size_t不仅仅是考虑到数据类型的可移植性,size_t的另一个优点是其大小并非是固定的,而是用来表征针对某平台的最大长度。当我们使用无符号型的size_t用来表示一个地址或者数据复制的长度时,根本不用担心它表示的数值范围够不够用

 五、typedef的使用

        typedef是C语言的一个关键字,用来为某个类型起别名。大家在阅读代码的过程中,会经常见到typedef与结构体、联合体、枚举、函数指针声明结合使用

1. typedef的基本用法

        以结构体类型的声明和使用为例,C语言提供了struct关键字来定义一个结构体类型。

struct student
{
    char name[20];
    int age;
    float score;
};
struct student stu = {"wit", 20, 99};

        在C语言中定义一个结构体变量,我们通常的写法如下。

        struct 结构类型 变量名;

        前面必须有一个struct关键字做前缀,编译器才会理解你要定义的对象是一个结构体变量。而在C++语言中,则不需要这么做,可以直接使用。

        结构类型 变量名;

struct student
{
    char name[20];
    int age;
    float score;
};
int main(void)
{
    student stu = {"wit", 20, 99};
    return 0;
}

        我们使用typedef关键字,可以给student声明一个别名student_t和一个结构体指针类型student_ptr,然后可以直接使用student_t类型去定义一个结构体变量,不用再写struct,这样会显得代码更加简捷。

 示例代码如下:

#include <stdio.h>

typedef struct student
{
    char name[20];
    int age;
    float score;
}student_t, *student_ptr;

int main(void)
{
    student_t stu = {"wit", 20, 99};
    student_t *p1 = &stu;
    student_ptr p2 = &stu;
    printf("name: %s\n",p1->name);
    printf("name: %s\n",p2->name);
    return 0;
}

运行结果如下:

        typedef除了与结构体结合使用,还可以与数组结合使用。定义一个数组,通常使用int array[10];即可。我们也可以使用typedef先声明一个数组类型,然后使用这个类型去定义一个数组。

 

        在上面的程序中,我们声明了一个数组类型array_t,然后使用该类型定义一个数组array,这个array效果其实就相当于int array[10]。typedef还可以与指针结合使用。在下面的demo程序中,PCHAR的类型是char*,我们使用PCHAR类型去定义一个变量str,其实就是一个char*类型的指针。

        typedef还可以和函数指针结合使用。定义一个函数指针,我们通常采用下面的形式。

int (*func)(int a, int b)

        我们同样可以使用typedef声明一个函数指针类型:func_t。

typedef int (*func_t)(int a, int b)
func_t fp; //定义一个函数指针变量

 代码示例如下:

#include <stdio.h>

typedef int (*func_t)(int a, int b);
int sum(int a, int b)
{
    return a + b; 
}

int main(void)
{
    func_t fp = sum;
    printf("%d\n",fp(1,2));
    return 0;
}

运行结果为:3

为了增加程序的可读性,我们经常在代码中看到下面的声明形式。

typedef int (func_t)(int a, int b);
func_t *fp = sum;

        函数都是有类型的,我们使用typedef为函数类型声明一个新名称func_t。这样声明的好处是,即使你没有看到func_t的定义,也能够清楚地知道fp是一个函数指针。

        在实际编程中,typedef还可以与枚举结合使用。枚举与typedef的结合使用方法和结构体类似:可以使用typedef为枚举类型color声明一个新名称color_t,然后使用这个类型就可以直接定义一个枚举变量。 

 2.使用typedef的优势

        不同的项目,有不同的代码风格,也有不同的代码“癖好”。代码看得多了,你就会发现:有的代码里宏用得多,有的代码里typedef用得多。 

         1.可以让代码更加清晰简捷

        如上面的程序代码所示,使用typedef,我们可以在定义一个结构体、联合、枚举变量时,省去关键字struct,让代码更加简捷。

        2.增加代码的可移植性 

        3.比宏定义更好用

        4.让复杂的指针声明更加简捷

3. typedef的作用域

        和宏的全局性相比,typedef作为一个存储类关键字,是有作用域的。使用typedef声明的类型和普通变量一样,都遵循作用域规则,包括代码块作用域、文件作用域等。

        宏定义在预处理阶段就已经替换完毕,是全局性的,只要保证引用它的地方在定义之后就可以了。而使用typedef声明的类型则和普通变量一样,都遵循作用域规则。上面代码的运行结果如下。

 六.常量和变量

 1.变量的本质

        在C语言中,不同类型的数据有不同的存储方式,在内存中所占的大小不同,地址对齐方式也不相同。我们可以使用不同的数据类型来定义变量,不同类型的变量在内存中的存储方式和大小也不相同。

        例如我们定义一个int型变量i,编译器会根据变量i的类型,在内存中分配4字节的存储空间;而对于定义的变量j,编译器也会根据变量j的类型,在内存中分配1字节的存储空间。从汇编语言的角度来看,汇编语言是没有数据类型的概念的,当我们使用DCB、DCD伪指令去为一个数据对象分配存储空间时,要考虑的主要是存储地址、存储大小和存储内容这3个基本要素,它和我们高级语言中的变量名、变量类型和变量值是一一对应的。变量与存储的对应关系如图7-5所示。

        当我们想通过变量名去读写内存时,必须要遵循C语言标准定义的语法规则,而不是随便引用,否则就会出现问题。在C语言中,一块可以存储数据的内存区域,一般被称为对象,而操作这片内存的表达式,即引用对象的表达式,我们称之为左值。左值可以改变对象,一般放在赋值语句的左边,如a=1;这条赋值语句,表达式a就是一个左值,放在了赋值运算符的左边。我们可以通过变量名a去修改这片内存,即通过左值去改变对象。与左值相对应的是右值,即非左值表达式,一般放在赋值运算符=的右边。

        我们常见的左值有变量、e[n]、e.name、p->name、*e等这些常见的表达式。需要注意的是,并不是所有的表达式都可以作为左值,如数组名、函数、枚举常量、函数调用等都不能作为左值,也不能通过它们去修改对象。如下面的程序代码。

 

        如果你想通过上面的语句为数组赋值,你会发现编译器报编译错误,因为数组名不是左值,不能放到赋值运算符的左边。

 

        有些引用对象的表达式既可以作为左值,也可以作为右值,如变量。一个变量作为左值时,通常表示对象的地址,我们对变量名的引用其实就是对该地址区域进行各种操作。一个变量名作为右值时,通常表示对象的内容,我们此时对变量的引用就相当于取该地址区域上的内容。

 

        在a=1;语句中,表达式a是一个左值,代表的是一个地址,这条语句的作用就是将数据1写到变量名a表征的这片内存中,这片内存又称为对象,即我们可以通过左值来改变的对象。在b=a;这条语句中,a是右值,代表的是对象的内容,即表达式a表征的这片内存地址上的内容,直接赋值给左值b。

        不同类型的变量有不同的存储方式、作用域和生命周期。在定义一个变量时,我们可以使用char、int、float、double等关键字来指定变量的类型,再加上short和long这两个整型限定符,基本上就确定了这个变量在内存中的存储空间的大小。有时候我们还可以使用一些变量修饰限定符来改变变量的存储方式,常用的修饰符有auto、register、static、extern、const、volatile、restrict、typedef等。这些修饰限定符往往会决定变量的存储位置、作用域或生命周期,所以一般也被称为存储类关键字static关键字修饰一个局部变量,可以改变变量的存储方式,将变量的存储从栈中转移到数据段中,但不能改变变量的作用域,因为变量的作用域是由{}决定的。不同类型的变量在内存中的存储对应关系如图7-6所示。 

        全局变量一般存储在数据段中,使用extern关键字可以将一个全局变量的作用域扩展到另一个文件中,也可以使用static关键字将其作用域限定在本文件中一个变量如果使用register修饰,意在告诉编译器这个变量将会被频繁地使用,如果有可能,可以将这个变量存储在CPU的寄存器中,以提高其读写效率。至于编译器会不会这样做,就要视具体情况而定了在一个函数内定义的变量,如果没有使用其他存储类修饰符修饰,默认就是auto类型,即自动变量。自动变量存储于当前函数的栈帧内,函数中的每一个局部变量只有在函数运行时才会给其分配存储空间,在函数执行结束退出时自动释放,其生命周期只存在于函数运行期间,这也是我们称这些局部变量为自动变量的根本原因。对自动变量的读写操作不能像全局变量那样通过变量名引用,一般由栈指针SP和帧指针FP共同管理和维护在一个函数内部定义的自动变量如果没有初始化,那么它的值将是随机的,这是因为在函数运行期间分配的存储单元地址是随机的,存储单元的数据也是随机的。如果我们没有注意到这些细节,未初始化而直接使用,就可能导致程序出现意想不到的错误

        我们在程序中,定义变量的目的,就是方便对存储在内存中的数据进行读写,不是直接通过地址,而是通过变量名来访问内存。编译器根据我们定义的变量类型,会在内存中分配合适大小的存储空间和地址对齐。变量名的本质,其实就是一段内存存储空间的别名,通过变量名可以直接对这段内存进行读写

2.常量存储

        对于C程序中定义的每一个变量,编译器都会根据变量的类型在内存中为它分配合适大小的存储空间:可以分配在数据段中,也可以分配在栈中。在一个C程序中,除了变量,还有很多常量和常量表达式,它们在内存中是如何存储的呢?

#include <stdio.h>

#define HELLO "world!\n"
char *p = "hello ubuntu!\n";

int main(void)
{
    char c_val = 'A';
    printf("hello %s",HELLO);
    printf("%s",p);
    printf("c_val = %c\n",c_val);
    return 0;
}

        在上面的程序中,我们使用宏HELLO定义了一个字符串,使用指针变量p指向一个常量字符串。除此之外,在printf() 函数中还有很多打印格式的字符串,编译器在编译程序时会把它们单独放在一个叫作.rodata的只读数据段中,我们可以使用objdump命令查看它们。

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

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

相关文章

前端常用的版本管理工具

一、是什么 版本控制&#xff08;Version control&#xff09;&#xff0c;是维护工程蓝图的标准作法&#xff0c;能追踪工程蓝图从诞生一直到定案的过程。此外&#xff0c;版本控制也是一种软件工程技巧&#xff0c;借此能在软件开发的过程中&#xff0c;确保由不同人所编辑的…

汽车驾驶智能座舱太阳光模拟器老化试验

一、太阳光模拟器老化试验目的 太阳光模拟器氙光灯老化试验是一种常用的材料老化测试方法&#xff0c;通过模拟自然光照条件下的老化过程&#xff0c;评估材料的耐光性能和耐候性能其主要目的有: 1.评估材料在长时间暴露于自然光照条件下的耐久性能: 2.比较不同材料的耐光性…

龙芯loongarch64服务器编译安装scipy

前言 根据我之前的文章介绍&#xff0c;龙芯loongarch64服务器中的很多python依赖包安装有问题&#xff0c;发现其中安装的"scikit-learn"就无法正常使用&#xff0c;所有这里在 pip3 install scikit-learn -U -i https://pypi.tuna.tsinghua.edu.cn/simple 的时候发…

telnet的使用

用于查看 某个ip某个端口通不通 telnet 136.126.122.123 6380 这个代表通了

NLP项目windows环境搭建

一、安装Anaconda Free Download | Anaconda 二、安装PyCharm PyCharm&#xff1a;JetBrains为专业开发者提供的Python IDE 三、创建Python虚拟环境 1. 打开命令窗口 "开始"菜单中找到Anaconda3 - Anaconda Powershell Prompt 说明&#xff1a; Anaconda Power…

应用在触摸显示屏中的高性能低功耗触摸芯片

触摸屏显示器(Touch Screen)可以让使用者只要用手指轻轻地碰计算机显示屏上的图符或文字就能实现对主机操作&#xff0c;这样摆脱了键盘和鼠标操作&#xff0c;使人机交互更为直截了当。简单的说&#xff0c;只是在显示器上安装了触摸屏&#xff0c;成为带有触摸功能的显示器。…

el-input-number输入框超过限制后自动变为最大值

input输入框使用了el-input-number 需求&#xff1a;目标室温输入框数据库设置最大是4位整数&#xff0c;限制一位小数&#xff0c;且后面要加单位&#xff0c;当输入数字超过限制&#xff0c;默认显示限制的最大值 &#xff0c;所以就有了输入完图一自动变为图二的数字。 el-i…

CV计算机视觉每日开源代码Paper with code速览-2023.11.3

精华置顶 墙裂推荐&#xff01;小白如何1个月系统学习CV核心知识&#xff1a;链接 点击CV计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【基础网络架构】&#xff08;NeurIPS2023&#xff09;Ai…

MySQL第六讲·where和having的异同?

你好&#xff0c;我是安然无虞。 文章目录 面试常考&#xff1a;where与having有什么不同&#xff1f;一个实际查询需求wherehaving怎么正确的使用where和having&#xff1f; 面试常考&#xff1a;where与having有什么不同&#xff1f; 我们在进行查询的时候&#xff0c;经常…

C语言运行代码示例

这是一个基本的 C 爬虫程序&#xff0c;使用了 C11 版本。这个程序使用了 C11 的标准库&#xff0c;包括了网络编程库&#xff08;<net/http>&#xff09;&#xff0c;字符串处理库&#xff08;<string>&#xff09;和文件操作库&#xff08;<fstream>&#…

Centos7下通过docker安装Rancher2.7搭建Kubernetes

Rancher官方网站&#xff08;中文&#xff09; Rancher单节点 Rancher2.7与Kubernetes部署在同一台设备上 关闭防火墙与selinux #关闭防火墙 systemctl stop firewalld && systemctl disable firewalld && iptables -F #关闭selinux sed -i s/enforcing/di…

聚观早报 |小鹏P7i 550版上市;零一万物发布大模型

【聚观365】11月7日消息 小鹏P7i 550版上市 零一万物发布大模型 vivo X100现身Geekbench 小马智行与丰田联合发布Robotaxi 王云鹏出任百度IDG负责人 小鹏P7i 550版上市 小鹏P7i 550版正式上市&#xff0c;新车共推出550 Pro、550 Max 两款新版型&#xff0c;售价分别为22…

Photoshop Web版本用了哪些CSS技术

本文翻译自 CSS Findings From Photoshop Web Version &#xff0c;作者&#xff1a;Ahmad&#xff0c; 略有删改。 几周前&#xff0c;Adobe发布了一个Web版的Photoshop&#xff0c;它是用WebAssembly、Web组件、P3颜色等网络技术构建的。 Photoshop是我14岁时学会的第一个专…

【I2C】熟悉I2C的传输时序。根据I2C的时序图,标出每段时序对应的含义

参考&#xff1a;I2C -- I2C总线详解_i2c频率-CSDN博客 首先看下I2C协议中数据传输的时序&#xff1a; I2C空闲&#xff1a;SCL和SDA同时处于“高电平”。 I2C起始&#xff1a;SCL维持高电平&#xff0c;SDA由“高电平跳变为低电平”&#xff0c;下降沿。 I2C终止&#xff1a…

机组 硬件

典型的冯诺伊曼计算机是以运算器为中心 现代的计算机已转化为以存储器为中心 运算器&#xff1a;完成算术运算和逻辑运算&#xff0c;并将运算的中间结果暂存在运算器内存储器&#xff1a;存放数据和程序控制器&#xff1a;控制、指挥程序和数据的输入、运行以及处理运算结果输…

【LeetCode】每日一题 2023_11_7 统计范围内的元音字符串数

文章目录 刷题前唠嗑题目&#xff1a;最大单词长度乘积题目描述代码与解题思路偷看大佬题解 结语 刷题前唠嗑 LeetCode? 启动&#xff01;&#xff01;&#xff01; 题目&#xff1a;最大单词长度乘积 题目链接&#xff1a;2586. 统计范围内的元音字符串数 题目描述 代码与…

上海亚商投顾:创业板指放量涨3.26% 两市近4500股飘红

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日高开后展开震荡&#xff0c;深成指涨超2%&#xff0c;创业板指大涨超3%&#xff0c;宁德时代涨超5%。…

世界土壤数据库(HWSD)土壤数据集

简介&#xff1a; HWSD&#xff08;Harmonized World Soil Database&#xff09;是联合国粮食及农业组织&#xff08;FAO&#xff09;和国际土壤参考与信息中心&#xff08;ISRIC&#xff09;共同开发的世界土壤数据集。该数据集是一份高分辨率&#xff08;1 km&#xff09;&a…

Gin框架如何使用并搭建一个后台管理系统(四)

到这个时候&#xff0c;我们应该安装上Mysql 开始工作。首先下载安装包mysql-installer-community-8.0.20.0.msi ps&#xff1a;看到有的人window环境下安装不成功可以下载一下依赖的包 NDP452-KB2901907-x86-x64-AllOS-ENU.exe 一、安装mysql 环境和可视化工具 二、导入…