【C语言】结构体与内存操作函数 总结

结构体

一、结构体简介

C 语言内置的数据类型,除了最基本的几种原始类型,只有数组属于复合类型,可以同时包含多个值,但是只能包含相同类型的数据,实际使用中并不够用。

实际使用中,主要有下面两种情况,需要更灵活强大的复合类型。

  • 复杂的物体需要使用多个变量描述,这些变量都是相关的,最好有某种机制将它们联系起来。
  • 某些函数需要传入多个参数,如果一个个按照顺序传入,非常麻烦,最好能组合成一个复合结构传入。

为了解决这些问题,C 语言提供了struct关键字,允许自定义复合数据类型,将不同类型的值组合在一起。这样不仅为编程提供方便,也有利于增强代码的可读性。

C 语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能。

struct fraction {int numerator;int denominator;
};
/*定义了新的数据类型以后,就可以声明该类型的变量,这与声明其他类型变量的写法是一样的。
*/
/*
声明自定义类型的变量时,类型名前面,不要忘记加上struct关键字。也就是说,必须使用struct fraction f1声明变量,不能写成fraction f1。
*/
struct fraction f1;f1.numerator = 22;
f1.denominator = 7;
/*
逐一对属性赋值。上面示例中,先声明了一个struct fraction类型的变量f1,这时编译器就会为f1分配内存,接着就可以通过.运算符为f1的不同属性赋值。
*/
struct car {char* name;float price;int speed;
};struct car saturn = {"Saturn SL/2", 16000.99, 175};
/*
可以使用大括号,一次性对 struct 结构的所有属性赋值。
*//*
变量saturn是struct car类型,大括号里面同时对它的三个属性赋值。如果大括号里面的值的数量,少于属性的数量,那么缺失的属性自动初始化为0。大括号里面的值的顺序,必须与 struct 类型声明时属性的顺序一致。否则,必须为每个值指定属性名。
struct car saturn = {.speed=172, .name="Saturn SL/2"};
*/
struct car saturn = {.speed=172, .name="Saturn SL/2"};
saturn.speed = 168;
/*
声明变量以后,可以修改某个属性的值。
*/
struct book {char title[500];char author[100];float value;
} b1 = {"title", "Author", 56.7};
/*
struct 的数据类型声明语句与变量的声明语句,可以合并为一个语句。
*/
struct {char title[500];char author[100];float value;
} b1;
/*
该语句同时声明了数据类型book和该类型的变量b1。如果类型标识符book只用在这一个地方,后面不再用到,这里可以将类型名省略。(什么是只用在一个地方???)
*/
struct {char title[500];char author[100];float value;
} b1 = {"Harry Potter", "J. K. Rowling", 10.0},b2 = {"Cancer Ward", "Aleksandr Solzhenitsyn", 7.85};
/*
`struct`声明了一个匿名数据类型,然后又声明了这个类型的变量`b1`。与其他变量声明语句一样,可以在声明变量的同时,对变量赋值。
*/
// 指针变量也可以指向struct结构。
struct book {char title[500];char author[100];float value;
}* b1;// 或者写成两个语句
struct book {char title[500];char author[100];float value;
};
struct book* b1;
/*
变量b1是一个指针,指向的数据是struct book类型的实例。
*/
// struct 结构也可以作为数组成员。
struct title numbers[1000];numbers[0].numerator = 22;
numbers[0].denominator = 7;

二、结构体内存对齐

struct 结构占用的存储空间,不是各个属性存储空间的总和,而是最大内存占用属性的存储空间的倍数,其他属性会添加空位与之对齐。

为什么浪费这么多空间进行内存对齐呢?这是为了加快读写速度,把内存占用划分成等长的区块,就可以快速在 struct 结构体中定位到每个属性的起始地址。

由于这个特性,在有必要的情况下,定义 struct 结构体时,可以采用存储大小递增(或递减)的顺序,定义每个属性,这样就能节省一些空间。

struct foo {int a; // 4 -> 8 空4个char* b; // 8char c; // 1 -> 8 空7个
};
printf("%d\n", sizeof(struct foo)); // 24
struct foo {char c; // 1 -> 4 空三个int a;  // 4char* b; // 8
};
printf("%d\\n", sizeof(struct foo)); // 16

上面示例中,占用空间最小的char c排在第一位,其次是int a,占用空间最大的char* b排在最后。整个strct foo的内存占用就从24字节下降到16字节。

三、struct 的赋值

第一种方法,struct 变量也类似于其他变量,可以使用赋值运算符(=),赋值给另一个变量。

struct cat { char name[30]; short age; } a, b;strcpy(a.name, "Hello");
a.age = 3;b = a; // struct赋值
b.name[0] = 'M';printf("%s\n", a.name); // Hello
printf("%s\n", b.name); // Mello

注意:

  1. =赋值要求两个变量是同一个类型,不同类型的 struct 变量无法互相赋值。
  2. C 语言没有提供比较两个自定义数据结构是否相等的方法,无法用比较运算符(比如==!=)比较两个数据结构是否相等或不等

第二种方法,可以使用函数memcpy来为结构体赋值,将一个结构体的指定长度的内存区域复制到另一个结构体的指定位置

// 定义两个学生结构体
Student student1 = { 1001, "Alice", 90.5 };
Student student2;// 为 student2 赋值
memcpy(&student2, &student1, sizeof(Student));

四、struct 指针

struct类型的指针和其他数据类型的指针是相似的,完全可以按照其他指针的理解方式来学习struct指针。

传入struct变量,函数内部得到的是一个原始值的副本。

#include <stdio.h>struct turtle {char* name;char* species;int age;
};void fun(struct turtle t) {t.age = t.age + 1;
}int main() {struct turtle myTurtle = {"Turtle", "sea turtle", 18};fun(myTurtle);printf("Age is %i\\n", myTurtle.age); // 输出 18return 0;
}

上面示例中,函数happy()传入的是一个 struct 变量myTurtle,函数内部有一个自增操作。但是,执行完happy()以后,函数外部的age属性值根本没变。原因就是函数内部得到的是 struct 变量的副本,改变副本影响不到函数外部的原始数据。

通常情况下,开发者希望传入函数的是同一份数据,函数内部修改数据以后,会反映在函数外部。而且,传入的是同一份数据,也有利于提高程序性能。这时就需要将 struct 变量的指针传入函数,通过指针来修改 struct 属性,就可以影响到函数外部。

传入struct指针,那么通过指针修改数据就会改变之前保存的数据(不再是副本)

void happy(struct turtle* t) {...}
happy(&myTurtle);

上面代码中,t是 struct 结构的指针,调用函数时传入的是指针。struct 类型跟数组不一样,类型标识符本身并不是指针,所以传入时,指针必须写成&myTurtle

函数内部也必须使用(*t).aget->age的写法从指针拿到 struct 结构本身。

void happy(struct turtle* t) {(*t).age = (*t).age + 1;// t->age = t->age + 1// 大大增强了代码的可读性
}

上面示例中,(*t).age不能写成*t.age,因为点运算符.的优先级高于**t.age这种写法会将t.age看成一个指针,然后取它对应的值,会出现无法预料的结果。

现在,重新编译执行上面的整个示例,happy()内部对 struct 结构的操作,就会反映到函数外部。

这里提及一个在链表中容易犯错的知识:

move = l1;move->next = l1; 是不完全等价的。

  1. move = l1; 是将指针 move 直接指向 l1 所指向的节点。这意味着 movel1 同时指向同一个节点,它们指向的是同一块内存地址。
  2. move->next = l1; 是将 move 指针所指向节点的 next 指针指向 l1 所指向的节点。这意味着将 l1 插入到 move 所指向节点的后面。

如果只执行 move = l1;,则 l1 并没有插入到新链表中,而是直接将 move 指向了 l1 所指向的节点。

如果只执行 move->next = l1;,则相当于将 l1 插入到了 move 所指向的节点的后面,但没有将 move 指针移动到新的节点。

五、struct 的嵌套

struct 结构的成员可以是另一个 struct 结构。

struct species {char* name;int kinds;
};struct fish {char* name;int age;struct species breed;
};// fish的属性breed是另一个struct结构species。

赋值的时候有多种写法。

// 写法一
struct fish shark = {"shark", 9, {"Selachimorpha", 500}};// 写法二
struct species myBreed = {"Selachimorpha", 500};
struct fish shark = {"shark", 9, myBreed};// 写法三
struct fish shark = {.name="shark",.age=9,.breed={"Selachimorpha", 500}
};// 写法四
struct fish shark = {.name="shark",.age=9,.breed.name="Selachimorpha",.breed.kinds=500// 引用breed属性的内部属性,要使用两次点运算符
};printf("Shark's species is %s", shark.breed.name);
struct name {char first[50];char last[50];
};struct student {struct name name;short age;char sex;
} student1;/*
对字符数组属性赋值,要使用strcpy()函数,不能直接赋值,因为直接改掉字符数组名的地址会报错。
*/
strcpy(student1.name.first, "Harry");
strcpy(student1.name.last, "Potter");// or
struct name myname = {"Harry", "Potter"};
student1.name = myname;

struct 结构内部不仅可以引用其他结构,还可以自我引用,即结构内部引用当前结构。比如,链表结构的节点就可以写成下面这样。

struct node {int data;struct node* next;
};
struct node {int data;struct node* next;
};struct node* head;// 生成一个三个节点的列表 (11)->(22)->(33)
head = malloc(sizeof(struct node));head->data = 11;
head->next = malloc(sizeof(struct node));head->next->data = 22;
head->next->next = malloc(sizeof(struct node));head->next->next->data = 33;
head->next->next->next = NULL;// 遍历这个列表
for (struct node *cur = head; cur != NULL; cur = cur->next) {printf("%d\\n", cur->data);
}

六、typedef关键字

  • 起别名

typedef type name; 为某个类型起别名

type代表类型名,name代表别名。

typedef unsigned char BYTE;
BYTE c = 'z';

typedef 可以一次指定多个别名。

~~typedef int antelope, bagel, mushroom;~~

typedef 可以为指针起别名。

typedef int* intptr;
int a = 10;
intptr x = &a;/*
使用的时候要小心,这样不容易看出来,变量x是一个指针类型。
*/

typedef 也可以用来为数组类型起别名。

typedef int five_ints[5];
five_ints x = {11, 22, 33, 44, 55};

typedef 为函数起别名的写法如下。

typedef signed char (*fp)(void);
/*
类型别名fp是一个指针,代表函数signed char (*)(void)。
*/

typedef为结构体起别名

typedef struct cell_phone {int cell_no;float minutes_of_charge;
} phone;
// 或者把typedef带下来,typedef struct cell_phone phone
// 还可以省略结构体的名字
phone p = {5551234, 5};
/*
typedef命令可以为 struct 结构指定一个别名,
这样使用起来更简洁。phone是别名,而不是结构体变量!
*/
  • 起别名的好处

(1)更好的代码可读性。

(2)为 struct、union、enum 等命令定义的复杂数据结构创建别名,从而便于引用。

(3)typedef 方便以后为变量改类型。

typedef float app_float;
app_float f1, f2, f3;
/*
变量f1、f2、f3的类型都是float。如果以后需要为它们改类型,只需要修改typedef语句即可。
*/

(4)可移植性

某一个值在不同计算机上的类型,可能是不一样的。

int i = 100000;

上面代码在32位整数的计算机没有问题,但是在16位整数的计算机就会出错。

C 语言的解决办法,就是提供了类型别名,在不同计算机上会解释成不同类型,比如int32_t

int32_t i = 100000;

上面示例将变量i声明成int32_t类型,保证它在不同计算机上都是32位宽度,移植代码时就不会出错。

这一类的类型别名都是用 typedef 定义的。下面是类似的例子。

typedef long int ptrdiff_t;
typedef unsigned long int size_t;
typedef int wchar_t;

这些整数类型别名都放在头文件stdint.h,不同架构的计算机只需修改这个头文件即可,而无需修改代码。

因此,typedef有助于提高代码的可移植性,使其能适配不同架构的计算机。

(5)简化类型声明

C 语言有些类型声明相当复杂,比如下面这个。

char (*(*x(void))[5])(void);

typedef 可以简化复杂的类型声明,使其更容易理解。首先,最外面一层起一个类型别名。

typedef char (*Func)(void);
Func (*x(void))[5];

这个看起来还是有点复杂,就为里面一层也定义一个别名。

typedef char (*Func)(void);
typedef Func Arr[5];
Arr* x(void);

上面代码就比较容易解读了。

  • x是一个函数,返回一个指向 Arr 类型的指针。
  • Arr是一个数组,有5个成员,每个成员是Func类型。
  • Func是一个函数指针,指向一个无参数、返回字符值的函数。

内存操作函数

一、内存简介

C 语言的内存管理,分成两部分。

一部分是系统管理的

系统管理的内存,主要是函数内部的变量(局部变量)。这部分变量在函数运行时进入内存,函数运行结束后自动从内存卸载。这些变量存放的区域称为”栈“(stack),”栈“所在的内存是系统自动管理的。

另一部分是用户手动管理的

用户手动管理的内存,主要是程序运行的整个过程中都存在的变量(全局变量),这些变量需要用户手动从内存释放。如果使用后忘记释放,它就一直占用内存,直到程序退出,这种情况称为**”内存泄漏“(memory leak)。**这些变量所在的内存称为”堆“(heap),”堆“所在的内存是用户手动管理的。

二、malloc()

功能:手动分配内存,系统就会在“堆”中分配一段连续的内存;动态数组

函数原型:void *malloc(size_t size)

参数:size是指分配的字节数

返回值:是一个指向分配内存首地址的指针;若分配失败,则返回NULL

注意:

  1. malloc分配内存并不会进行初始化,所以内存中还保留着之前的值

  2. 有时候为了增加代码的可读性,可以对malloc()返回的指针进行一次强制类型转换。

  3. 一定要注意检查malloc是否分配失败!

    /* malloc()最常用的场合,就是为数组和自定义数据结构分配内存。 */
    int *p = (int *) malloc(sizeof(int) * 10);for (int i = 0; i < 10; i++)p[i] = i * 5;
    
    /* malloc()用来创建数组,有一个好处,就是它可以创建动态数组,即根据成员数量的不同,而创建长度不同的数组。 */
    int *p = (int *) malloc(n * sizeof(int));/*注意,malloc()不会对所分配的内存进行初始化,里面还保存着原来的值。如果没有初始化,就使用这段内存,可能从里面读到以前的值。程序员要自己负责初始化,比如,字符串初始化可以使用strcpy()函数。
    */
    
    char *p = malloc(sizeof(char) * 4);
    strcpy(p, "abc");// or
    p = "abc";
    

三、 calloc()

功能:手动分配内存空间,并将内存存放的值初始化为0

函数原型:void* calloc(size_t n, size_t size);

返回值:一个指向分配内存首地址的指针;分配失败时,返回 NULL

参数:第一个参数是某种数据类型的值的数量;第二个是该数据类型的字节长度。分配内存的大小是由这两者的乘积决定。

注意:

  • malloc() 不会对内存进行初始化,如果想要初始化为0,还要额外调用memset()函数。

    	int* p = calloc(10, sizeof(int));// 等同于
    int* p = malloc(sizeof(int) * 10);
    memset(p, 0, sizeof(int) * 10);
    

四、realloc()

功能:realloc()函数用于修改已经分配的内存块的大小,可以放大也可以缩小

函数原型:void* realloc(void* block, size_t size)

返回值:返回一个指向新的内存块的指针。如果分配不成功,返回 NULL。

参数:

  • block:已经分配好的内存块指针(由malloc()calloc()realloc()产生)。
  • size:该内存块的新大小,单位为字节。

注意:

  1. realloc()可能返回一个全新的地址(数据也会自动复制过去),也可能返回跟原来一样的地址。

  2. realloc()优先在原有内存块上进行缩减,尽量不移动数据,所以通常是返回原先的地址。如果新内存块小于原来的大小,则丢弃超出的部分;如果大于原来的大小,则不对新增的部分进行初始化(程序员可以自动调用memset())。

  3. realloc()的第一个参数可以是 NULL,这时就相当于新建一个指针。

    char* p = realloc(NULL, 3490); // 等同于 char* p = malloc(3490);
  4. 如果realloc()的第二个参数是0,就会释放掉内存块。

  5. 由于有分配失败的可能,所以调用realloc()以后,最好检查一下它的返回值是否为 NULL。分配失败时,原有内存块中的数据不会发生改变。

    float* new_p = realloc(p, sizeof(*p * 40));if (new_p == NULL) {printf("Error reallocing\\n");return 1;
    }
  6. realloc()不会对内存块进行初始化。

五、free()

功能:释放手动分配的内存

函数原型:void free(void *block)

参数:指向开辟内存的指针block

返回值:无返回值

注意:

  1. 若手动分配的内存不释放掉,那这个内存块会一直占用到程序运行结束。

    // free()的参数是malloc()返回的内存地址
    // 分配内存后,若不再使用,一定要及时释放,否则容易造成内存泄露!
    int* p = (int*) malloc(sizeof(int));*p = 12;
    free(p);
    
  2. 分配的内存块一旦释放,就不应该再次操作已经释放的地址,也不应该再次使用free()对该地址释放第二次。

六、restrict 说明符

声明指针变量时,可以使用restrict说明符,告诉编译器,该块内存区域只有当前指针一种访问方式,其他指针不能读写该块内存。这种指针称为“受限指针”(restrict pointer)。

int *restrict p;
p = malloc(sizeof(int));

上面示例中,声明指针变量p时,加入了restrict说明符,使得p变成了受限指针。后面,当p指向malloc()函数返回的一块内存区域,就味着,该区域只有通过p来访问,不存在其他访问方式。

int* restrict p;
p = malloc(sizeof(int));int* q = p;
*q = 0; // 未定义行为

上面示例中,另一个指针q与受限指针p指向同一块内存,现在该内存有pq两种访问方式。这就违反了对编译器的承诺,后面通过*q对该内存区域赋值,会导致未定义行为。

七、memcpy()

功能:memcpy()用于将一块源地址内存拷贝到另一块目标地址内存中,**不能有重叠内存区域。**该函数的原型定义在头文件string.h

函数原型:void *memcpy(void *restrict dest, void *restrict source, size_t n);

参数:dest是目标地址,source是源地址,第三个参数n是要拷贝的字节数n

返回值:memcpy()的返回值是第一个参数,即目标地址的指针。

注意:

  1. **destsource都是 void 指针,表示可以移动任何类型的内存数据**
  2. 如果要拷贝10个 double 类型的数组成员,n就等于10 * sizeof(double),而不是10。该函数会将从source开始的n个字节,拷贝到dest
  3. **destsource都是 void 指针,表示这里不限制指针类型,各种类型的内存数据都可以拷贝**
  4. 两者都有 restrict 关键字,表示dest指针所指的区域,不能让别的指针来修改(包括source),同样,source指针所指的区域也不能让别的指针修改(也包括dest),即这两个内存块不应该有互相重叠的区域。
  5. 因为memcpy()只是将一段内存的值,复制到另一段内存,所以不需要知道内存里面的数据是什么类型。
  • 复制字符串

    	/* `memcpy()`可以取代`strcpy()`进行字符串拷贝,而且是更好的方法,不仅更安全,速度也更快,它不检查字符串尾部的`\0`字符。 
    */#include <stdio.h>
    #include <string.h>int main(void) {char s[] = "Goats!";char t[100];memcpy(t, s, sizeof(s));  // 拷贝7个字节,包括终止符printf("%s\n", t);  // "Goats!"return 0;
    }
    
  • my_memcpy.c

    void* my_memcpy(void* dest, void* src, int byte_count) {char* s = src;char* d = dest;while (byte_count--) {*d++ = *s++;}return dest;
    }
    /*不管传入的`dest`和`src`是什么类型的指针,将它们重新定义成一字节的 char 指针,这样就可以逐字节进行复制。`*d++ = *s++`语句相当于先执行`*d = *s`(源字节的值复制给目标字节),然后各自移动到下一个字节。最后,返回复制后的`dest`指针,便于后续使用。
    */
    

八、memmove()

功能:memmove()函数用于将一段源地址内存数据复制到另一段目标地址内存中。允许dest和source所指向的内存区域有重叠。该函数的原型定义在头文件string.h

函数原型:void *memmove(void *dest, void *source, size_t n);

参数:dest是目标地址,source是源地址,n是要移动的字节数。

返回值:memmove()返回值是第一个参数,即目标地址的指针。

注意:

  1. 如果dest和source所指向的内存区域发生重叠,源区域的内容会被更改;如果没有重叠,它与memcpy()行为相同。

    char x[] = "Hello world!";// 输出 world!world!
    printf("%s\n", (char *) memmove(x, &x[6], 6));
    

    上面示例中,从字符串x的6号位置开始6个字节,就是“world!”,memmove()将其前移到0号位置,所以x就变成了“world!world!”。

九、memcmp()

功能:memcmp()函数用来比较两个内存区域。它的原型定义在string.h

函数原型:int memcmp(const void *s1, const void *s2, size_t n);

参数:前两个参数是用来比较的指针,第三个参数指定比较的字节数。

返回值:两块内存区域的每个字节以字符形式解读,按照字典顺序进行比较(从左到右比较ASCII码值),如果两者相同,返回0;如果s1大于s2,返回大于0的整数;如果s1小于s2,返回小于0的整数。

注意:

  1. strncmp()不同,memcmp()可以比较内部带有字符串终止符\0的内存区域。

    char s1[] = {'b', 'i', 'g', '\0', 'c', 'a', 'r'};
    char s2[] = {'b', 'i', 'g', '\0', 'c', 'a', 't'};if (memcmp(s1, s2, 3) == 0) // true
    if (memcmp(s1, s2, 4) == 0) // true
    if (memcmp(s1, s2, 7) == 0) // false
    if (strncmp(s1, s2, 3) == 0) // true
    if (strncmp(s1, s2, 7) == 0) // true 注意strncmp不会比较'\0'之后的内容
    

十、memchr()

功能:在内存区域中查找指定字符。

函数原型:void *memchr(const void *s, int c, size_t n);

参数:第一个参数是一个指针,指向内存区域的开始位置;第二个参数是所要查找的字符,第三个参数是待查找内存区域的字节长度。

返回值:一旦找到,它就会停止查找,并返回指向该位置的指针。如果直到检查完指定的字节数,依然没有发现指定字符,则返回 NULL。

十一、memset()

功能:将一段内存全部设置为指定值。

函数原型:void *memset(void *s, int c, size_t n);

参数:第一个参数是一个指针,指向内存区域的开始位置;第二个参数是待写入的字符值;第三个参数是一个整数,表示需要待格式化的字节数。

返回值:返回第一个参数(指针)。

简单例子

char str[] = "BBBBBBBBBBBB";// 输出 bbbbbbbBBBBB
printf("%s\n", (char*) memset(str, 'b', 7));

注意:

  1. malloc开辟内存后没有初始化,memset()可以将开辟内存区域全部初始化为0。

    int *arr = (int *)malloc(100 * sizeof(int));
    memset(arr, 0, 100 * sizeof(int)); // 第三个参数不是sizeof(arr)// ...
    free(arr);
    

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

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

相关文章

temu跨境电商怎么样?做temu蓝海项目有哪些优势?

在全球电商市场激烈的竞争中&#xff0c;Temu跨境电商平台以其独特的优势和策略&#xff0c;逐渐崭露头角。对于许多想要拓展海外市场的商家来说&#xff0c;Temu的蓝海项目提供了一个充满机遇的新平台。本文将深入探讨Temu跨境电商的优势以及在蓝海市场中的发展前景。 全球化市…

redis 工具类

在Spring Boot项目中&#xff0c;Redis是一个常用的分布式缓存解决方案。下面展示的RedisCache工具类封装了对Redis进行基本操作的方法&#xff0c;包括存储和获取各种类型的数据、设置过期时间以及处理集合类型的缓存。 /*** redis 工具类***/ SuppressWarnings(value { &q…

编程笔记 html5cssjs 056 CSS不透明度

编程笔记 html5&css&js 056 CSS不透明度 一、CSS 不透明度 / 透明度二、使用 RGBA 的透明度三、透明盒中的文本小结 不透明度/透明度。利用透明度可以提高页面的层次效果。 一、CSS 不透明度 / 透明度 opacity 属性指定元素的不透明度/透明度。 opacity 属性通常与 :h…

仅使用 Python 创建的 Web 应用程序(前端版本)第08章_商品详细

在本章中,我们将实现一个产品详细信息页面。 完成后的图像如下。 Model、MockDB、Service都是在产品列表页实现的,所以创建步骤如下。 No分类内容1Page定义PageId并创建继承自BasePage的页面类2Application将页面 ID 和页面类对添加到 MultiPageApp 的页面中Page:定义PageI…

Ruby安装与使用指南

Ruby安装与使用指南 介绍 Ruby是一种动态、开源的编程语言&#xff0c;以简洁、灵活的语法而闻名。它被广泛应用于Web开发、脚本编写以及构建各种应用程序。本教程将带领你一步步学习如何在不同操作系统上安装和使用Ruby。 第一部分&#xff1a;安装Ruby 1. 在Windows上安装…

Git管理秘籍:Python项目中的.gitignore策略

.gitignore的配置与使用规则 注意: .gitignore最好放在根目录中。如果gitignore放在被忽略的父目录中&#xff0c;那忽略文件就无法生效。 1.1 .gitignore使用规则 .gitignore只能忽略那些原来没有被track的文件&#xff0c;如果某些文件已经被纳入了版本管理中&#xff0c…

VsCode提高生产力的插件推荐-持续更新中

别名路径跳转 自定义配置// 文件名别名跳转 "alias-skip.mappings": { "~/": "/src", "views": "/src/views", "assets": "/src/assets", "network": "/src/network", "comm…

代码CE:reference to ‘XX‘ is ambiguous

代码CE:reference to ‘XX’ is ambiguous 今天提交代码的时候一直错误&#xff0c;CE&#xff0c;搞不明白明明在dev上成功&#xff0c;为什么提交失败。 现在懂了&#xff0c;因为定义的变量和C内部函数或变量重名了。修改之后即可AC。 int data[21][21]{0}; int maxsum[21…

《动手学深度学习(PyTorch版)》笔记3.2

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过。…

Android App开发基础(2)—— App的工程结构

本专栏文章 上一篇 Android开发修炼之路——&#xff08;一&#xff09;Android App开发基础-1 2 App的工程结构 本节介绍App工程的基本结构及其常用配置&#xff0c;首先描述项目和模块的区别&#xff0c;以及工程内部各目录与配置文件的用途说明&#xff1b;其次阐述两种级别…

THM学习笔记——OSI模型和TCP/IP模型

全是文字 比较枯燥 建议视频 OSI模型由七个层次组成&#xff1a; 第7层 -- 应用层&#xff1a; OSI模型的应用层主要为在计算机上运行的程序提供网络选项。它几乎专门与应用程序一起工作&#xff0c;为它们提供一个界面以用于传输数据。当数据传递到应用层时&#xff0c;它…

Python代码操作ES

1,ElasticSearch准实时索引实现 Es在保存数据的时候时分区/分片存储的,每一个分区/分片都对应着一个Lucene实例 每一个分区/分片对应多个文件,一个文件就是一个Segment(段)Segment就是可以被检索的最小单元,每一个Segment都对应着一个倒排索引Refresh到内存Segment: 从内存…

【医学图像隐私保护】联邦学习:密码学 + 机器学习 + 分布式 实现隐私计算,破解医学界数据孤岛的长期难题

联邦学习&#xff1a;密码学 机器学习 分布式 提出背景&#xff1a;数据不出本地&#xff0c;又能合力干大事联邦学习的问题 分布式机器学习&#xff1a;解决大数据量处理的问题横向联邦学习&#xff1a;解决跨多个数据源学习的问题纵向联邦学习&#xff1a;解决数据分散在多…

[] == ! [] 为什么返回 true ?

的隐式转换规则 类型相同的比较&#xff1a; 如果类型是 Undefined 或 Null&#xff0c;返回 true。 null null; // true如果一个是 0&#xff0c;另一个是 -0&#xff0c;返回 true&#xff1a; 0 -0; // true如果类型是对象&#xff0c;二者引用同一个对象&#xff0c;…

【grafana】使用教程

【grafana】使用教程 一、简介二、下载及安装及配置三、基本概念3.1 数据源&#xff08;Data Source&#xff09;3.2 仪表盘&#xff08;Dashboard&#xff09;3.3 Panel&#xff08;面板&#xff09;3.4 ROW&#xff08;行&#xff09;3.5 共享及自定义 四、常用可视化示例4.1…

内网穿透frpc记录

配置信息 /frp/frpc.ini [common] server_addr 47.109.91.139 server_port 7000[ssh] type tcp local_ip 192.168.86.10 local_port 22 remote_port 6000[https] type tcp local_ip 192.168.86.10 local_port 443 remote_port 443[http] type tcp local_ip 192.1…

湿法蚀刻酸洗槽—— 应用半导体新能源光伏光电行业

PFA清洗槽又被称为防腐蚀槽、酸洗槽、溢流槽、纯水槽、浸泡槽、水箱、滴流槽&#xff0c;是四氟清洗桶后的升级款&#xff0c;是为半导体光伏光电等行业设计&#xff0c;一体成型&#xff0c;无需担心漏液。主要用于浸泡、清洗带芯片硅片电池片的花篮。 由于PFA的特点它能耐受…

【linux-虚拟化】 SR-IOV技术

文章目录 参考1. 什么是 SR-IOV?1.2. 将 SR-IOV 网络设备附加到虚拟机1.3. SR-IOV 分配支持的设备 参考 管理 SR-IOV 设备 1. 什么是 SR-IOV? 单根 I/O 虚拟化(SR-IOV)是一种规范&#xff0c;它允许单个 PCI Express(PCIe)设备向主机系统呈现多个独立的 PCI 设备&#xff…

QT获取本机网络信息

QT获取本机网络信息 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);~Widget();void getinform…

vue3中的vuex理解

vuex,概念理论什么的&#xff0c;我就不多说了。懂的人都懂。不懂的&#xff0c;请自己谷歌。本博文主要讲解它的一些常用方法和持数据的持久化&#xff08;本文是以模块化来写的&#xff09;。 1、安装 npm install vuexnext --save npm i vuex-persistedstate #持久化插件2、…