【C语言】字符串,补充:单引号和双引号, const和define, 常量指针和指针常量

【C语言】字符串:一串数据类型为字符(char)的数组(字符数组),结束符是'\0'。

一个字符占一个字节的内存,结束符'\0'也占一个字节的内存。

结束符 '\0' :缩写NUL,空字符。表示字符串的结束。

NULL:空指针。定义在标准库中的值为零的常量。

字符串名就是内存地址,也是第一个字符的内存地址。也可以理解为字符串是指针,指向字符数组。

单个字符:用单引号,例如:'a',  '\0'。

空字符串或多个字符组成的字符串:用双引号,例如:"good"。

操作字符串(字符数组)的标准库为string.h。

#include <string.h>

0、字符串变量

数组:char 变量名[字符数量] = 字符串;    【字符数量可省略,字符数量包括结束符'\0'】

指针:char *指针变量名;

// 字符数组
char s[6] = "hello";                     // 5个字符 + 1个结束符'\0'
char s[6] = {'h','e','l','l','o','\0'};  // 单个字符,用单引号// 指针变量
char *s;
s = "hello";

1、strlen, sizeof

strlen:字符串共有多少个字符(不包括结束符'\0')。

sizeof:字符串共占多少字节内存(包括结束符'\0')。

#include <stdio.h>
#include <string.h>int main(void)
{char strings[] = "hello";int number = strlen(strings);int size = sizeof(strings);printf("strings: \"%s\" has %d elements, memory size is %d bytes",strings, number, size);return 0;
}// 结果为:
strings: "hello" has 5 elements, memory size is 6 bytes

2、memchr,strchar,strrchr  搜索字符串中的字符

memchr:void *memchr(const void *str, int c, size_t n)

参数:str是指定内存区域,c为要搜索的字符(单个字符,需用单引号),n为要搜索的字节数(指定搜索的内存大小)。

返回:指针,指向第一次出现的字符c的内存地址。若没有,返回NULL。

strchar:char *strchr(const char *str, int c)

参数:str是字符串,c为要搜索的字符(单个字符,需用单引号)。

返回:指针,指向第一次出现的字符c的内存地址。若没有,返回NULL。

strrchr:char *strrchr(const char *str, int c)

参数:str是字符串,c为要搜索的字符(单个字符,需用单引号)。

返回:指针,指向最后一次出现的字符c的内存地址。若没有,返回NULL。

 memchr, strchar,strrchr基本用法相同:

1、都可以在字符串中搜索字符。

2、都返回指针,指向搜索到的字符的内存地址。若没有搜索到,返回NULL。

 memchr, strchar,strrchr的区别是:

1、memchr有3个参数:指定内存区域,搜索的字符,搜索的字节数。

      strchar和strrchr只有2个参数:字符串,搜索的字符。

2、memchr和strchr返回的指针都指向第一次出现字符c的内存地址。

     strrchr返回的指针是指向最后一次出现字符c的内存地址。

3、memchr指定搜索的内存大小。

      strchar和strrchr都是字符串结束(结束符'\0')就停止搜索。

4、memchr可以搜索任意内存区域的字节。

      strchar和strrchr专门搜索字符串中的字符。

// 以 memchr 为例
#include <stdio.h>
#include <string.h>int main(void)
{const char strings[] = "hello";char *s;s = (char *)memchr(strings, 'l', strlen(strings));printf("strings=\"%s\", the first \"l\" location is %d\n",strings, s-strings+1);printf("strings=\"%s\", from the first \"l\" to the end are \"%s\"",strings, s);return 0;
}// 结果为:
strings="hello", the first "l" location is 3
strings="hello", from the first "l" to the end are "llo"

补充:strstr (字符串中查找子字符串)

strstr:char *strstr(const char *haystack, const char *needle)

在字符串haystack查找子字符串needle。

返回:若找到,返回指针,指向haystack中第一次出现needle的内存地址。若没有,返回NULL。

#include <stdio.h>
#include <string.h>int main(void)
{char strings[] = "hello world wow";char *s;s = strstr(strings, "world");       // 找到子字符串printf("s point to \"%s\"\n",s); return 0;
}// 结果为:
s point to "world wow"#include <stdio.h>
#include <string.h>int main(void)
{char strings[] = "hello world wow";char *s;s = strstr(strings, "good");      // 没有找到子字符串printf("s point to \"%s\"\n",s); return 0;
}
// 结果为:
s point to "(null)"

3、memcpy,memmove,strcpy,strncpy 复制字符串

memcpy:void *memcpy(void *dest, const void *src, size_t n)

参数:dest是存储复制内容的内存区域(目标字符串),src是要复制的数据源(内存区域),n是复制的字节数。

返回:指针,指向目标字符串。

注意:

memcpy是内存复制,可复制任意类型的数据源。

memcpy指定复制内存大小。不会自动添加结束符'\0'。注意目标字符串中是否有结束符。若没有,需一个字节的内存空间手动添加结束符'\0'。

若目标字符串的内存不够大,可能缓冲区溢出。注意边界检查。

 memmove: void *memmove(void *dest, const void *src, size_t n)

参数:dest是存储复制内容的内存区域(目标字符串),src是要复制的字符串,n是要复制的字节数。

返回:指针,指向目标字符串。

注意:

没有重叠的内存区域时,与memcpy功能相同。

有重叠的内存区域时,复制前会将重叠部分拷贝到目标区域,复制后,源区域的内容会被修改。

// 以 memcpy 为例
#include <stdio.h>
#include <string.h>int main(void)
{char s[] = "hello";                // 数据源“hello”(5个字符+1个结束符'\0')char strings[8];memcpy(strings,s,strlen(s)+1);     // 复制字符串(包括结束符'\0')printf("strings = \"%s\",strings[5] = %p\n",strings, strings[5]);   return 0;
}// 结果为:
strings = "hello",strings[5] = 0000000000000000

strcpy:char *strcpy(char *dest, const char *src)

参数:dest是存储复制内容的内存区域(目标字符串),src是要复制的字符串。

返回:指针,指向目标字符串。

注意:

strcpy专门复制字符串,包括结束符'\0'。依赖结束符'\0'来确定复制的终点,注意要复制的字符串需有正确的结束符。

若没有正确的结束符或目标字符串的内存不够,可能缓冲区溢出。注意边界检查。

#include <stdio.h>
#include <string.h>int main(void)
{char strings[6]; // char strings[5];        // warning: '__builtin_memcpy' writing 6 bytes into a region of size 5 overflows the destination [-Wstringop-overflow=]strcpy(strings,"hello");   // 复制字符串printf("strings = \"%s\"",strings);   return 0;
}// 结果为:
strings = "hello"

strncpy:char *strncpy(char *dest, const char *src, size_t n)

参数:dest是存储复制内容的内存区域(目标字符串),src是要复制的字符串,n是复制的字节数。

返回:指针,指向目标字符串。

注意:

strncpy指定复制内存大小。

要复制的字符串若小于指定复制的内存大小,则自动填充结束符'\0'。

要复制的字符串若大于等于指定复制的内存大小,则可能需要一个字节的内存空间手动添加结束符'\0',若复制的内容中有结束符,则无需手动添加。

若目标字符串的内存不够大,可能缓冲区溢出。注意边界检查。

#include <stdio.h>
#include <string.h>int main(void)
{char strings[8];strncpy(strings,"hello",8);       // strings 内存存储为:h e l l o \0 \0 \0printf("strings = \"%s\"",strings);   return 0;
}// 结果为:
strings = "hello"
#include <stdio.h>
#include <string.h>int main(void)
{char strings[6];strncpy(strings,"hello",6);       // 无需手动添加结束符,复制内容中已包含printf("strings = \"%s\"",strings);   return 0;
}// 结果为:
strings = "hello"
#include <stdio.h>
#include <string.h>int main(void)
{char strings[6];strncpy(strings,"helloworld",5);  // 复制原字符串的一部分内容(不包括结束符)strings[5] = '\0';                // 手动添加结束符printf("strings = \"%s\"",strings);   return 0;
}// 结果为:
strings = "hello"
memcpy,strcpy,strncpy的区别
memcpystrcpystrncpy
3个参数:目标字符串,要复制的数据源,复制内存大小2个参数:目标字符串,要复制的字符串3个参数:目标字符串,要复制的字符串,复制内存大小
复制内存区域,可复制任意类型的数据源(包括字符串)专门复制字符串专门复制字符串
指定复制的内存大小从要复制的字符串开始到结束符(包括结束符\0)指定复制的内存大小

目标字符串中可能手动添加结束符'\0'

注意要复制的字符串中需有正确的结束符'\0'目标字符串中可能手动添加结束符'\0'
确保目标字符串的内存足够大确保目标字符串的内存足够大确保目标字符串的内存足够大

补充:memset (将某内存区域设置为指定值)

memset:void *memset(void *str, int c, size_t n)

参数:str是存储填充内容的内存区域(目标字符串),c是指定要填充的内容(单个字符,需单引号),n是填充的字节数。

返回:指针,指向目标字符串。

注意:

memset是将指定字符填充到指定内存区域的前n个字节。

用于快速初始化大块内存,或清空内存,或为内存赋值。

memset不做边界检查,需确保目标字符串的内存区域足够大。

#include <stdio.h>
#include <string.h>int main(void)
{char strings[8] = "hihello";printf("Before: strings = %s\n", strings);memset(strings, '#', 2);                    // 将字符数组前2个字符设为'#'printf("After: strings = %s\n", strings);memset(strings, '\0', sizeof(strings));     // 将字符数组清零,使用'\0'代替0printf("End: strings = %s\n", strings);return 0;
}// 结果为:
Before: strings = hihello
After: strings = ##hello
End: strings =

4、strcat,strncat 字符串结尾追加内容

注意:确保目标字符串的内存区域必须足够大,能够容纳追加的内容。

从目标字符串的结束符'\0'开始追加,'\0'被第一个追加的字符替换,追加完会加上结束符'\0'。

strcat:char *strcat(char *dest, const char *src)

参数:dest是目标字符串,src是要追加的内容。

返回:指针,指向目标字符串。

#include <stdio.h>
#include <string.h>int main(void)
{char strings[16] = "hello";printf("Before: strings = %s\n", strings);strcat(strings, "world");         // 追加内容printf("After: strings = %s\n", strings);return 0;
}// 结果为:
Before: strings = hello
After: strings = helloworld

strncat:char *strncat(char *dest, const char *src, size_t n)

参数:dest是目标字符串,src是要追加的内容,n是追加的字节数。

返回:指针,指向目标字符串。

#include <stdio.h>
#include <string.h>int main(void)
{char strings[16] = "hello";printf("Before: strings = %s\n", strings);strncat(strings, "world", sizeof("world"));         // 追加内容(指定字节数)printf("After: strings = %s\n", strings);return 0;
}// 结果为:
Before: strings = hello
After: strings = helloworld

5、memcmp,strcmp,strncmp  比较字符串

memcmp: int memcmp(const void *str1, const void *str2, size_t n)

将两个内存区域str1和str2的前n个字节数进行比较。

返回:若str1>str2,返回值>0;若str1=str2,返回值=0;若str1<str2,返回值<0。

strcmp:int strcmp(const char *str1, const char *str2)

将两个字符串str1和str2进行比较。

返回:若str1>str2,返回值>0;若str1=str2,返回值=0;若str1<str2,返回值<0。

strncmp:int strncmp(const char *str1, const char *str2, size_t n)

将两个字符串str1和str2的前n个字节数进行比较。

返回:若str1>str2,返回值>0;若str1=str2,返回值=0;若str1<str2,返回值<0。

memcmp,strcmp,strncmp
memcmpstrcmpstrncmp
比较两个内存区域(可以字符串,但通常用于比较结构体或数组等)比较两个字符串,整个字符串比较比较两个字符串,指定比较长度
指定比较的字节数/指定比较的字节数
碰到不同的字符或比较完限定的字节数就停止碰到不同的字符或结束符'\0'就停止碰到不同的字符或结束符'\0'或比较完限定的字节数就停止
// 以 strcmp 为例
#include <stdio.h>
#include <string.h>int main(void)
{char s[] = "Hello";if(strcmp(s, "hello") == 0)       // 比较字符串{printf("the same string");}else{printf("\"%s\" and \"hello\" are different", s);}return 0;
}// 结果为:
"Hello" and "hello" are different

6、匹配字符串中的字符是否在另一个字符串中出现

strspn(str1,str2):str1中第一个不在str2的字符下标。

strcspn(str1,str2):str1中从开头连续多少字符不在str2中。

strpbrk(str1,str2):str1中第一个在str2中的字符,返回指针,指向str1中匹配到的字符。

strspn:size_t  strspn ( const  char *str1,  const  char  *str2)
strcspn:size_t  strcspn ( const  char  *str1,  const  char  *str2)
strpbrk:char  *strpbrk ( const  char  *str1,  const  char  *str2)
#include <stdio.h>
#include <string.h>int main(void)
{char s1[] = "hellogoodworld";char s2[] = "good";int j = strspn(s1,s2);            // s1中第一个不在s2中的字符下标printf("\"%s\" , the first char not in \"%s\" is index %d\n", s1, s2, j);int k = strcspn(s1,s2);           // s1中从开头连续多少字符不在s2中printf("\"%s\" , %d char not in \"%s\"\n", s1, k, s2);char *x = strpbrk(s1,s2);          // s1中第一个在s2中的字符printf("\"%s\" , the first char in \"%s\" is %c\n", s1, s2, *x);return 0;
}// 结果为:
"hellogoodworld" , the first char not in "good" is index 0
"hellogoodworld" , 4 char not in "good"
"hellogoodworld" , the first char in "good" is o

7、与LC_COLLATE设置相关

strxfrm(目标字符串, 源字符串, 转换的字节数n):将源字符串根据当前LC_COLLATE转换n个字节,再放置到目标字符串指向的内存区域中。返回被转换的字符串的长度(不包括结束符)。

strxfrm 转换字符串格式,便于后续字符串比较。需确保有足够的内存空间存放转换后的字符串。

strcoll(str1,str2):比较两个字符串。返回int类型的值(str1>str2,返回值>0;str1=str2,返回值=0;str1<str2,返回值<0)。

strcoll 根据LC_COLLATE环境变量设置的字符排列次序来比较,若LC_COLLATE设置为"POSIX"或者"C",strcoll 函数与strcmp 函数完全相同。

strxfrm:转换字符串格式。size_t  strxfrm ( char  *dest,  const  char  *src,  size_t  n)
strcoll:比较字符串。int  strcoll ( const  char  *str1,  const  char  *str2)

通过 标准库locale.h 中的 setlocale 来设置 LC_COLLATE 。

获取或设置地域化信息:char *setlocale(int category, const char *locale) 

8、strerror(错误号):通过错误号返回对应的错误信息。

  • 当使用系统调用或其它库函数时,如果发生错误,通常由整数变量errno(标准库errno.h中通过系统调用设置的宏) 记录错误号。
  • 可使用strerror将错误号转换为对应的错误信息字符串。
  • char *strerror(int errnum)   【参数:错误号。返回指针,指向对应的错误信息字符串】
  • 具体错误信息取决于开发平台和编译器。
  • strerror不应在多线程环境中使用。

9、strtok 将字符串分割

strtok:char *strtok(char *str, const char *delim)

参数:str是要分割的字符串,delim是分割符。

返回:指针,指向被分割的第一个子字符串。若没有可检索的字符串,返回空指针。

获取下一个子字符串:继续调用strtok函数。strtok(NULL,分割符);

注意:strtok函数会改变被操作的字符串,最好拷贝。

#include <stdio.h>
#include <string.h>int main(void)
{char s[] = "hello world wow";printf("Before: s = \"%s\"\n",s);char *x;x = strtok(s, " ");            // 使用空格" "来分割字符串while(x != NULL){printf("%s\n",x);x = strtok(NULL, " ");     // 获取下一个子字符串}printf("After: s = \"%s\"\n",s);return 0;
}// 结果为:
Before: s = "hello world wow"
hello
world
wow
After: s = "hello"

补充:

1、单引号、双引号

单引号:只能一个字符(占用一个字节)。不能用于空字符串和多个字符组成的字符串。

双引号:0到多个字符组成的字符串,可用于空字符串。若双引号中只有一个字符,则占用两个字节(一个字符和结束符'\0')。

例如:
char ch = 'A';          // 一个字符'A', 占用一个字节char s[] = "A";         // 一个字符"A"和结束符'\0', 占用两个字节
char s[] = "hello";     // 五个字符"hello"和结束符'\0', 占用六个字节
char s[] = "";          // 空字符串, 空字符(结束符)'\0',占用一个字节

输出单引号和双引号:使用转义字符 \' 和 \"  。

#include <stdio.h>int main(void)
{printf("\"hello\",the first char is \'h\'");   return 0;
}// 结果为:
"hello",the first char is 'h'

2、const、define

const:定义常量,告诉编译器某变量的值在初始化后不能修改。

可以修饰变量、数组、指针、函数参数、函数返回值等。

const int a = 10;                  // 变量a的值不能被修改const char s[] = "hello";          // 数组只读,不能被修改const int *ptr = &a;               // 常量指针,指向a的内存地址。指针可修改,指针指向的内存地址的内容不能修改int *const ptr = &a;               // 指针常量,指向a的内存地址。指针不可修改,指针指向的内存地址的内容可修改。int fun1(const int m)              // 函数中传入的参数m不能被修改
{...
}const fun1(void)                    // 函数的返回值不能被修改
{return "hello";
}

 define:预处理指令。#define 又称宏定义。定义一个标识符来表示常量。在预处理阶段就将标识符出现的地方文本替换为常量值。

#define  常量名(大写字母)  常量值

#include <stdio.h>
#define NUMBER 10       // 定义常量NUMBERint main(void)
{const int k = 8;    // 定义常量k// k = 5;           // 常量不能修改。error: assignment of read-only variable 'k'printf("[  const  ] k = %d\n", k);// NUMBER = 2;      // 预处理阶段已经文本替换。不能作为变量修改。error: lvalue required as left operand of assignmentprintf("[ #define ] NUMBER = %d\n", NUMBER);return 0;   
}// 结果为:
[  const  ] k = 8
[ #define ] NUMBER = 10

const和define的区别:

1、const在编译和运行时都有效。define只在预处理阶段有效。

2、const占用存储空间。define不占用存储空间。

3、const有明确的类型信息,需要类型检测。define是宏定义,没有类型,只是文本替换。

4、const可在调试时查看和修改。define在预处理阶段已通过文本替换的方式插入常量的值,在调试时很难追踪。

5、const遵循作用域,可以是局部也可以是全局。define默认是全局。

6、const定义后不能取消,可通过extern在其它地方重新定义。define可以通过#undef预处理器指令取消之前的宏定义。

3、常量指针、指针常量

常量指针:指针指向的内存地址中的内容不能修改,但指针指向的内存地址可以改变。

常量指针:指向可修改,内容不能改

const 基类型 *常量指针名;              // 或者 基类型 const *常量指针名; 

常量指针名 = 内存地址;                  // 修改指针的指向

#include <stdio.h>int main(void)
{int x = 10;int y = 8;const int *n = &x;       // 常量指针,初始化指向x的内存地址printf("Before: n point to %p, content is %d\n", n, *n);n = &y;            // 修改指针的指向,指向y的内存地址// *n = 5;         // 不能修改指向的内存地址中的内容。报错:error: assignment of read-only location '*n'printf("After: n point to %p, content is %d\n", n, *n);y = 6;             // 通过引用修改指向的内存地址中的内容。指针指向y的内存地址,不能通过*n修改值,但可修改y的值printf("End: n point to %p, content is %d\n", n, *n);return 0;
}// 结果为:
Before: n point to 000000000061FE14, content is 10
After: n point to 000000000061FE10, content is 8
End: n point to 000000000061FE10, content is 6

指针常量:指针指向的内存地址不能修改,但指向的内存地址的内容可以改变。

                  指针常量必须初始化,一旦初始化,指针的指向便不能修改。

指针常量:指向初始化后不能改,内容可修改

基类型 *const 指针常量名 = 初始化值;         // 指针常量必须初始化

*指针常量名 = 内存地址中的内容;                // 修改指向的内存地址的内容

#include <stdio.h>int main(void)
{int x = 10;int y = 8;int *const n = &x;     // 指针常量,必须初始化,一旦初始化,不能修改指针的指向printf("Before: n point to %p, content is %d\n", n, *n);// n = &y;             // 不能修改指针的指向。报错:error: assignment of read-only variable 'n'*n = 5;                // 修改指向的内存地址中的内容printf("After: n point to %p, content is %d\n", n, *n);return 0;
}// 结果为:
Before: n point to 000000000061FE0C, content is 10
After: n point to 000000000061FE0C, content is 5

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

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

相关文章

vue+nodejs+小区物业报修系统2e1fq

在对平台的各个功能模块建成整合后&#xff0c;使系统能够正常的运行&#xff0c;让用户能够通过使用本系统进行物业管理。 &#xff08;1&#xff09;前台业主端预期结果&#xff1a;业主能够查看物业相关公告信息&#xff0c;能够查看自己的缴费信息以及能够报修和投诉。 &am…

Android Studio调试中的坑

1、新建Android工程后编译发现报错 2、发现无33和34的SDK更新 3、查看google相关文档 Android Gradle 插件 8.3 版本说明 | Android Studio | Android Developers 如果需要支持相关更高SDK需要的Android Studio版本和Android Gradle 插件是有版本要求的&#xff0c;相关要…

汇编——SSE对齐(一. 未对齐情况)

SIMD是(Single Instrument Multi Data)&#xff0c;MMX实现了SIMD&#xff1b;SSE是&#xff08;Streaming SIMD Extension&#xff09;&#xff0c;它取代了MMX&#xff1b;后来AVX&#xff08;Advanced Vector Extension&#xff0c;高级向量扩展&#xff09;对SSE进行了扩展…

acwing算法提高之图论--floyd算法及其扩展应用

目录 1 介绍2 训练 1 介绍 本专题介绍使用floyd算法求解的题目。 使用floyd算法&#xff0c;可以求解如下问题&#xff1a; 最短路。传递闭包。找图中的距离总和最小的环路。求恰好经过k条边的最短路。 floyd算法的原理讲解&#xff1a;基于动态规划。 状态表示d[k,i,j]&a…

nginx配置代理kibana

一、编辑kibana配置文件&#xff0c;修改server.basePath 配置文件中这行默认是注释掉的&#xff0c;我们取消注释并修改保存 server.basePath: "/elk" 二、重启kibana&#xff0c;root启动记得跟allow参数 nohup ./kibana --allow-root & 三、配置nginx代理…

Vision-Language Models for Vision Tasks: A Survey

论文地址&#xff1a;https://arxiv.org/pdf/2304.00685.pdf 项目地址&#xff1a;https://github.com/jingyi0000/VLM_survey 一、综述动机 视觉语言模型&#xff0c;如CLIP&#xff0c;以其独特的训练方式显著简化了视觉识别任务的流程。它减少了对大量精细标注数据的依赖&a…

Python selenium 使用Chrome 禁止弹出保存密码弹窗

chrome_optionwebdriver.ChromeOptions()#禁用“保存密码”弹出窗口 chrome_option.add_experimental_option("prefs",{"credentials_enable_service":False,"profile.password_manager_enabled":False})driverwebdriver.Chrome(optionschrome_o…

小组分享内容

目录 1.网络爬虫的作用&#xff08;人话&#xff09;&#xff1a; 2.使用的工具 3.不使用程序进行网页信息的获取的操作步骤 4.如何使用程序来进行上述操作 1.打开浏览器 2.输入网址 3.发送请求 4.获取响应 5.判断响应是否成功 6.获取响应里需要的内容 7.记得最后关…

2_3.Linux系统中的日志管理

# 1.journald # 服务名称&#xff1a;systemd-journald.service journalctl 默认日志存放路径&#xff1a; /run/log &#xff08;1&#xff09; journalctl命令的用法 journalctl -n 3 ##日志的最新3条--since "2020-05-01 11:00:00" ##显示11&#xff1a;00后的日…

基于STM32的汽车防窒息系统

文章目录 基于STM32的汽车防窒息系统系统简介材料展示视频制作硬件连接原理图PCB实物图GSM模块使用GSM模块代码 SGP30模块SGP30模块代码 步进电机驱动步进电机代码 其他模块主逻辑代码 总结 基于STM32的汽车防窒息系统 系统简介 随着社会的发展目前汽车的流行&#xff0c;汽车大…

GPT带我学-设计模式11-组合模式

设计模式类型 结构型设计模式 使用场景 将对象组合成树状结构来表现"部分-整体"的层次结构。这种模式能够使得客户端对单个对象和组合对象的使用具有一致性。这句话太抽象了&#xff0c;拿一个实际的网站菜单树例子来说。 例子&#xff1a;网页菜单树 一个网站的…

C++经典面试题目(十八)

1、请解释虚函数表的概念&#xff0c;并说明其在多态中的应用。 虚函数表&#xff08;Vtable&#xff09;是用于实现多态的一种机制&#xff0c;它是一个存储虚函数地址的表。每个含有虚函数的类都会有一个对应的虚函数表&#xff0c;其中存放着该类的虚函数的地址。当对象被创…

【CSS】伪元素

伪元素 常见的伪元素 ::first-line::first-letter::before 在元素第一个子元素前增加内容::after 在元素最后一个子元素后增加内容 为了区分伪类和伪元素&#xff0c;建议伪元素都使用2个冒号::first-line可以针对首行文本设置属性::first-letter可以针首字母设置属性::before…

【技巧】如何解除Excel“打开密码”?

给Excel表格设置“打开密码”&#xff0c;可以保护表格不被他人随意打开&#xff0c;那如果后续不需要保护了&#xff0c;不想每次打开Excel都需要输密码&#xff0c;要怎么去除“打开密码”呢&#xff1f; 今天分享3个方法&#xff0c;最后一个方法记得收藏起来&#xff0c;以…

基于51单片机的智能报警系统Proteus仿真

地址&#xff1a;https://pan.baidu.com/s/10dhkyV5O629UpFHHV67LpQ 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectroni…

源码|并发一枝花之CopyOnWriteArrayList

CopyOnWriteArrayList的设计思想非常简单&#xff0c;但在设计层面有一些小问题需要注意。 JDK版本&#xff1a;oracle java 1.8.0_102 本来不想写的&#xff0c;但是github上CopyOnWriteArrayList的code results也有165k&#xff0c;为了流量还是写一写吧。 实现 看两个方法你…

LVS、HAProxy

集群&#xff1a;将很多个机器组织到一起&#xff0c;作为一个整体对外提供服务。集群在扩展性、性能方面都可以做到很灵活。集群的分类&#xff1a;负载均衡集群&#xff1a;Load Balance。高可用集群&#xff1a;High Available。高性能集群&#xff1a;High Performance Com…

Python学习从0到1 day20 第二阶段 面向对象 ② 封装

缘分 朝生暮死犹如露水 —— 24.4.1 学习目标&#xff1a; 1.理解封装的概念 2.掌握私有成员的使用 一、面向对象三大特性&#xff1a; 面向对象编程&#xff0c;是许多编程语言都支持的一种编程思想 简单理解是&#xff1a;基于模板&#xff08;类&#xff09;去创建实体&…

请解释一下Velocity模板引擎的基本工作原理是什么?在Velocity中,如何定义和使用变量?

请解释一下Velocity模板引擎的基本工作原理是什么&#xff1f; Velocity模板引擎的基本工作原理可以概括为将数据模型与模板文件相结合&#xff0c;通过解析模板中的指令和占位符&#xff0c;将数据模型的内容填充到模板中&#xff0c;最终生成所需的文本输出。 具体来说&…

一个基于大数据的派单管理系统

通常需要处理大量的订单数据&#xff0c;并确保这些数据能够在各个相关部门之间高效、准确地传递。这样的系统不仅有助于提高派单效率&#xff0c;还能优化资源配置&#xff0c;减少不必要的浪费。以下是一个包含所需元素的系统设计方案概述&#xff1a; 数据结构 订单号&…