【C语言篇】C 语言总复习(上):点亮编程思维,穿越代码的浩瀚星河
我的个人主页
我的专栏:C语言,希望能帮助到大家!!!点赞❤ 收藏❤
在计算机科学的广袤宇宙中,C语言犹如一颗璀璨的恒星,散发着持久而耀眼的光芒。它作为一种基础且强大的编程语言,承载着无数程序员的梦想与创造力,是开启编程世界大门的关键钥匙。当我们踏上 C 语言总复习的征程时,就如同踏上了穿越代码浩瀚星河的奇妙之旅,每一个知识点都是一颗独特的星辰,等待我们去探索、去领悟,从而点亮我们的编程思维,指引我们在编程的宇宙中自由翱翔。
引言
C语言是计算机科学领域的重要基石,广泛应用于嵌入式开发、系统编程、游戏开发等多个领域。本文将带你从基础到高级,系统性复习C语言知识,并通过代码示例加深理解。
一、C 语言基础概述
- C 语言的发展历程与特点
- C 语言的起源与重要版本演进
C 语言起源于 20 世纪 70 年代初,由丹尼斯·里奇(Dennis Ritchie)在贝尔实验室为开发 UNIX
操作系统而设计。最初的 C 语言是在 B 语言的基础上发展而来,它继承了 B 语言简洁、高效的特点,并增加了丰富的数据类型和强大的控制结构。在随后的几十年里,C 语言经历了多个重要版本的演进。其中,C89(也称为 ANSI C)是第一个被广泛接受的标准版本,它对 C 语言的语法、语义和库函数进行了规范和标准化,使得 C 语言在不同的编译器和平台上具有更好的可移植性。C99 标准则进一步扩展了 C 语言的功能,引入了一些新的特性,如变长数组、内联函数、复数类型等,以满足现代编程的需求。而 C11 标准在 C99 的基础上,增加了多线程支持、原子操作、对齐描述符等特性,进一步提升了 C 语言在系统编程和高性能计算领域的应用能力。
简洁高效、可移植性强等特点的深入剖析
- 简洁高效:C 语言的语法简洁明了,关键字数量相对较少,程序结构紧凑。它提供了丰富的数据类型和运算符,能够直接对硬件进行操作,生成高效的机器代码。例如,通过指针可以直接访问内存地址,实现对数据的快速读写和处理,这使得 C 语言在系统级编程和对性能要求较高的应用中表现出色。
- 可移植性强:由于 C 语言的标准规范,其代码在不同的操作系统和硬件平台上具有较高的可移植性。只要遵循 C 语言的标准,编写的程序可以在各种主流平台上编译和运行,只需进行少量的修改甚至无需修改。这使得 C 语言成为开发跨平台软件和系统软件的首选语言之一。例如,许多操作系统内核、数据库管理系统、编译器等都是用 C 语言编写的。
- 直接访问硬件:C 语言能够直接访问计算机的硬件资源,如内存地址、寄存器等。这使得程序员可以编写底层驱动程序、操作系统内核等与硬件紧密相关的代码。通过使用特定的头文件和库函数,C 语言可以与硬件设备进行交互,实现对硬件的控制和数据传输。
- 丰富的数据类型和运算符:C 语言提供了多种基本数据类型,如整型、浮点型、字符型等,还支持自定义数据类型,如结构体、联合体和枚举类型。这些数据类型能够满足不同场景下的数据表示和处理需求。同时,C 语言的运算符丰富多样,包括算术运算符、关系运算符、逻辑运算符、位运算符等,可以进行复杂的数学运算、逻辑判断和位操作,为程序员提供了强大的编程工具。
- C 语言程序的基本结构
- 头文件包含的作用与常用头文件介绍
- 头文件包含的作用:头文件包含是 C 语言程序中引入外部声明和定义的重要方式。头文件中通常包含了函数原型、宏定义、结构体声明、全局变量声明等内容。通过包含头文件,我们可以在多个源文件中共享这些声明和定义,避免了重复编写代码,提高了代码的可维护性和可读性。例如,当我们使用标准库函数(如 printf、scanf 等)时,需要包含相应的头文件(如stdio.h),这样编译器才能知道这些函数的原型和参数信息,从而正确地进行编译和链接。
- 常用头文件介绍:
- stdio.h:这是 C 语言标准输入输出头文件,提供了用于输入输出操作的函数原型,如 printf(格式化输出)、scanf(格式化输入)、getchar(获取字符)、putchar(输出字符)等。它是 C 语言程序中最常用的头文件之一,几乎所有涉及到控制台输入输出的程序都需要包含它。
- stdlib.h:标准库头文件,包含了一些通用的函数和宏定义,如内存分配函数(malloc、calloc、free)、随机数生成函数(rand、srand)、系统命令执行函数(system)等。这些函数在动态内存管理、生成随机数、与操作系统交互等方面具有重要作用。
- string.h:字符串处理头文件,提供了一系列字符串操作函数的原型,如字符串复制函数(strcpy)、字符串连接函数(strcat)、字符串比较函数(strcmp)、字符串长度计算函数(strlen)等。在处理字符串相关的任务时,该头文件是必不可少的。
- math.h:数学库头文件,包含了各种数学函数的原型,如三角函数(sin、cos、tan)、指数函数(exp)、对数函数(log、log10)、幂函数(pow)等。当程序需要进行数学计算时,可以包含该头文件来使用这些数学函数。
- main 函数的地位与格式规范
- main 函数的地位:main 函数是 C 语言程序的入口点,程序的执行从 main 函数开始。它是整个程序的核心,负责调用其他函数并协调程序的整体流程。无论一个 C 语言程序多么复杂,都必须有且仅有一个 main 函数。
- 格式规范:main 函数的一般格式如下:
int main()
{// 函数体return 0;
}
或者
int main(int argc, char *argv[])
{// 函数体return 0;
}
第一种形式是最简单的 main 函数形式,没有参数传递。第二种形式中的 argc 表示命令行参数的数量,argv 是一个指向字符串数组的指针,用于存储命令行参数。在函数体中,我们可以编写各种语句来实现程序的功能,最后通过 return 语句返回一个整数值给操作系统,表示程序的结束状态。通常情况下,返回 0 表示程序正常结束,非零值表示程序出现异常或错误。
- 函数体的构成要素与代码编写规范
- 函数体的构成要素:函数体是 main 函数或其他自定义函数中包含实际代码的部分,它由一系列的语句组成。这些语句可以包括变量声明、赋值语句、控制结构语句(如 if - else、for、while 等)、函数调用语句、表达式语句等。函数体中的代码按照从上到下的顺序依次执行,除非遇到控制结构语句改变执行流程。
- 代码编写规范:
- 缩进与代码布局:为了提高代码的可读性,通常采用缩进的方式来表示代码块的层次关系。一般使用 4 个空格或一个制表符进行缩进。在函数体内部,不同的代码块(如 if 语句块、for 循环块等)应该有明显的缩进,使代码结构清晰明了。
- 注释的使用:注释是代码中用于解释说明程序功能、逻辑和算法的重要部分。合理地使用注释可以帮助其他程序员(包括自己在后续维护代码时)更好地理解代码的意图。C 语言中有两种注释方式:单行注释(// 注释内容)和多行注释(/* 注释内容 */)。在编写代码时,应该对关键的代码段、函数功能、变量含义等进行注释。
- 变量命名规范:变量名应该具有一定的描述性,能够反映变量所代表的含义。通常采用小写字母开头,多个单词组成的变量名采用驼峰命名法(如 studentName)或下划线分隔法(如 student_name)。避免使用过于简单或模糊的变量名,如 a、b、x 等,除非它们在特定的短代码片段中有明确的含义。
- 语句结束符:C 语言中的语句以分号(;)作为结束符。在编写代码时,不要忘记在每条语句的末尾添加分号,否则会导致语法错误。
二、数据类型与变量
1. 基本数据类型
整型(int、short、long 等)的取值范围与存储细节
- int 类型:在大多数常见的编译器和系统环境中,
int
类型通常占用 4 个字节(32 位)的存储空间。它能够表示的取值范围是有限的,一般为 -2147483648 到 2147483647(即 -2^31 到 2^31 - 1)。其存储方式是采用二进制补码形式,这种存储方式使得计算机在进行整数的运算(如加法、减法等)时,能够方便地处理正数和负数,并且可以利用硬件电路高效地实现算术逻辑单元(ALU)的操作。例如,当定义一个int
变量num
并赋值为 10 时,在内存中会以二进制补码的形式存储该数值,以便后续的计算和处理。在实际应用中,int
类型适用于表示一般的整数数值,如循环计数、数组下标等。例如,在一个循环中:
for (int i = 0; i < 100; i++) {// 循环体代码
}
这里的 i
作为循环变量,使用 int
类型足以满足计数范围的需求。
- short 类型:
short
类型通常占用 2 个字节(16 位)的存储空间,其取值范围大约为 -32768 到 32767(即 -2^15 到 2^15 - 1)。由于其占用空间较小,在某些对内存资源较为敏感且数值范围要求不高的场景中具有优势。例如,在处理一些小型数组的下标或者表示简单的状态码时,如果能够确定数值不会超出short
类型的取值范围,可以选择使用short
类型来节省内存空间。比如,在一个简单的游戏中,用于表示角色的一些状态标识:
short statusCode;
if (/* 某种条件 */) {statusCode = 1; // 表示正常状态
} else {statusCode = -1; // 表示异常状态
}
- long 类型:
long
类型一般占用 4 个字节或 8 个字节的存储空间,具体取决于编译器和系统环境。在 32 位系统中,它与int
类型通常具有相同的大小和取值范围;而在 64 位系统中,long
类型往往占用 8 个字节,其取值范围可达到 -9223372036854775808 到 9223372036854775807(即 -2^63 到 2^63 - 1)。long
类型适用于表示较大范围的整数数值,例如在处理一些涉及到大规模数据计算、时间戳表示或者内存地址等场景中可能会用到。例如,在计算一个非常大的整数序列的和或者表示从某个特定起始时间点以来的时间戳时:
long sum = 0;
for (long i = 0; i < 1000000000L; i++) { // 注意这里的 1000000000L 表示长整型常量sum += i;
}
浮点型(float、double)的精度与表示方法
- float 类型:
float
类型占用 4 个字节的存储空间,它使用 IEEE 754 标准来表示浮点数。在这种表示方法中,一部分二进制位用于表示指数,另一部分用于表示尾数(也称为有效数字)。float
类型能够提供大约 6 - 7 位的有效数字精度。例如,当定义一个float
变量f
并赋值为 3.14159f 时,由于其精度限制,实际存储的值可能是一个近似值。在一些对精度要求不是极高,但对内存使用较为关注的科学计算或图形处理的初步计算中,float
类型可以发挥作用。比如在简单的 3D 图形渲染中,用于表示顶点坐标的初步计算:
float x = 1.23f, y = 4.56f, z = 7.89f;
// 进行一些简单的坐标变换计算
- double 类型:
double
类型占用 8 个字节的存储空间,同样遵循 IEEE 754 标准。它能够提供大约 15 - 16 位的有效数字精度,相比float
类型具有更高的精度。在大多数需要精确数值计算的场景中,如金融计算、科学研究中的精确数据分析等,double
类型更为常用。例如,在计算复利或者进行高精度的物理模拟计算时:
double amount = 1000.0;
double interestRate = 0.05;
for (int i = 0; i < 10; i++) {amount *= (1 + interestRate);
}
这里使用 double
类型可以更精确地计算复利的增长情况,避免因精度不足而导致的计算误差。
字符型(char)的存储与字符编码基础(ASCII 等)
- 字符型存储:
char
类型在大多数系统中占用 1 个字节的存储空间。它用于存储单个字符,例如字母、数字、标点符号等。在内存中,字符是以其对应的字符编码值进行存储的。例如,字符 ‘A’ 在 ASCII 编码中对应的十进制值是 65,当定义一个char
变量c
并赋值为 ‘A’ 时,在内存中实际存储的是 65 的二进制表示形式。 - 字符编码基础 - ASCII:ASCII(美国信息交换标准代码)是最常见的字符编码之一。它定义了 128 个字符的编码,包括英文字母(大写和小写)、数字、标点符号、控制字符等。例如,数字 0 - 9 的 ASCII 码值分别为 48 - 57,大写字母 A - Z 的 ASCII 码值为 65 - 90,小写字母 a - z 的 ASCII 码值为 97 - 122。在 C 语言中,我们可以利用字符型变量和 ASCII 码进行一些简单的字符处理和转换。例如:
char ch = 'a';
if (ch >= 'a' && ch <= 'z') {// 将小写字母转换为大写字母ch -= 32; printf("%c is the uppercase version of 'a'.\n", ch);
}
这里利用了小写字母和大写字母在 ASCII 码值上相差 32 的关系,实现了字符的大小写转换。除了 ASCII 编码外,还有其他字符编码如 Unicode 等,以适应更多语言和字符集的需求,但在 C 语言的基础应用中,ASCII 编码是较为常用的基础。
2. 变量的声明与初始化
不同数据类型变量的声明语法
- 基本数据类型变量声明:声明一个变量时,首先需要指定数据类型,然后是变量名。例如,声明一个整型变量可以使用
int num;
,这里int
是数据类型,num
是变量名。对于浮点型变量,如float f;
声明了一个单精度浮点型变量f
,double d;
则声明了一个双精度浮点型变量d
。字符型变量的声明为char c;
。在声明多个相同类型的变量时,可以使用逗号分隔,如int a, b, c;
同时声明了三个整型变量a
、b
和c
。 - 变量命名规则:变量名必须以字母(a - z 或 A - Z)或下划线(_)开头,后面可以跟字母、数字或下划线。变量名不能是 C 语言中的关键字,如
if
、else
、for
等。此外,变量名应该具有一定的描述性,以便于代码的阅读和理解。例如,使用studentAge
来表示学生的年龄变量,比使用简单的x
或y
更能清晰地传达变量的含义。
变量初始化的多种方式与意义
- 声明时初始化:变量可以在声明的同时进行初始化,即在变量名后面紧跟赋值运算符
=
和初始值。例如,int count = 0;
不仅声明了count
为整型变量,还将其初始值设为 0。这种方式可以确保变量在使用前具有确定的初始值,避免因未初始化而导致的不确定行为。在很多情况下,如数组下标初始化、循环变量初始化等,声明时初始化是一种常用且方便的方式。例如:
int array[5] = {1, 2, 3, 4, 5}; // 数组元素初始化
for (int i = 0; i < 5; i++) { // 循环变量 i 初始化// 循环体代码
}
- 先声明后初始化:变量也可以先声明,然后在后续的代码中进行初始化。例如:
int value;
// 其他代码
value = 10;
这种方式在某些情况下可能更灵活,例如根据程序的运行逻辑或条件来确定变量的初始值。例如,在一个游戏中,根据玩家的选择或游戏进度来初始化某个变量:
int playerLevel;
if (/* 玩家完成特定任务 */) {playerLevel = 5;
} else {playerLevel = 1;
}
- 初始化的意义:变量初始化的主要意义在于为变量赋予一个确定的初始值,这有助于提高程序的稳定性和可预测性。未初始化的变量可能会包含垃圾数据,在后续的计算或操作中可能会导致错误的结果或不可预期的行为。例如,如果一个整型变量未初始化就参与加法运算,结果将是不可靠的。此外,初始化还可以根据程序的需求为变量设定特定的起始状态,方便后续的代码逻辑处理。
变量作用域(局部变量与全局变量)的详细讲解
- 局部变量:局部变量是在函数内部或代码块内部声明的变量。其作用域仅限于声明它的函数或代码块。例如:
void function() {int localVariable = 5;// localVariable 只能在这个函数内部使用printf("%d\n", localVariable);
}
当函数执行完毕后,局部变量所占用的内存空间会被释放。局部变量的优点在于它们只在需要的地方存在,不会干扰其他函数或代码块的变量,有助于提高程序的模块化和可维护性。例如,在不同的函数中可以使用相同名称的局部变量,它们彼此独立,互不影响。
- 全局变量:全局变量是在所有函数外部声明的变量。其作用域从声明点开始,到整个源文件结束。例如:
int globalVariable = 10;void anotherFunction() {// 可以在这个函数中访问 globalVariableprintf("%d\n", globalVariable);
}int main() {// 也可以在 main 函数中访问 globalVariableprintf("%d\n", globalVariable);return 0;
}
全局变量可以在多个函数之间共享数据,在某些情况下可以方便数据的传递和共享。然而,过度使用全局变量可能会导致程序的可读性和可维护性变差,因为全局变量可能会被多个函数修改,难以追踪变量值的变化和错误来源。例如,如果在一个大型项目中,多个函数都对同一个全局变量进行修改,一旦出现错误,很难确定是哪个函数导致的问题。因此,在实际编程中,应谨慎使用全局变量,优先考虑使用局部变量和参数传递来实现数据的处理和传递。
三、运算符与表达式
1. 算术运算符
加、减、乘、除、取余运算符的运算规则与优先级
- 加法运算符(+):用于将两个操作数相加,其操作数可以是整型、浮点型或字符型(字符型操作数会被转换为对应的 ASCII 码值进行计算)。例如,
int sum1 = 3 + 5;
结果为 8,float sum2 = 2.5f + 3.5f;
结果为 6.0f。加法运算满足交换律,即a + b
等于b + a
。 - 减法运算符(-):实现两个操作数的减法操作,同样适用于整型、浮点型等数据类型。如
int diff1 = 10 - 3;
得到 7,double diff2 = 5.5 - 2.5;
得到 3.0。减法运算不满足交换律,a - b
与b - a
结果通常不同。 - 乘法运算符(*):将两个操作数相乘,对于整型和浮点型数据都适用。例如,
int product1 = 4 * 5;
等于 20,float product2 = 2.5f * 3.0f;
等于 7.5f。乘法运算也满足交换律。 - 除法运算符(/):当操作数为整型时,进行整数除法,结果取整(向零取整)。如
int quotient1 = 10 / 3;
结果为 3。而当操作数为浮点型时,进行浮点数除法,得到精确的商。例如,float quotient2 = 10.0f / 3.0f;
约为 3.333333f。需要注意的是,除数不能为 0,否则会导致程序出错(在 C 语言中,除以 0 会引发运行时错误)。 - 取余运算符(%):取余运算符只适用于整型操作数,它返回除法运算的余数。例如,
int remainder = 10 % 3;
结果为 1。取余运算在判断一个数是否能被另一个数整除、循环分组等场景中有广泛应用,如判断一个年份是否为闰年(能被 4 整除但不能被 100 整除,或者能被 400 整除)就可以利用取余运算符来实现。
在一个表达式中包含多个算术运算符时,遵循特定的优先级规则:先乘除后加减,有括号先算括号内的。例如,int result = 2 + 3 * 4;
先计算乘法 3 * 4 = 12
,再计算加法 2 + 12 = 14
。而 int anotherResult = (2 + 3) * 4;
则先计算括号内的加法 2 + 3 = 5
,再计算乘法 5 * 4 = 20
。如果表达式中有多个相同优先级的运算符,则按照从左到右的顺序依次计算,如 int yetAnotherResult = 5 * 4 / 2;
先计算 5 * 4 = 20
,再计算 20 / 2 = 10
。
整数除法与浮点数除法的差异及应用场景
- 差异:如前面所述,整数除法在操作数均为整型时进行,结果会舍去小数部分,只保留整数。例如,
7 / 2
的结果是 3,而不是 3.5。这种取整方式可能会导致数据精度的损失。浮点数除法则是对浮点数操作数进行精确的除法运算,能得到包含小数部分的结果,如7.0 / 2.0
等于 3.5。浮点数除法的结果精度取决于数据类型的有效数字位数,例如float
类型能提供约 6 - 7 位有效数字精度,double
类型约有 15 - 16 位有效数字精度。 - 应用场景:整数除法常用于一些只需要整数结果的场景,如计算数组的下标、循环次数的确定等。例如,将一个数组分为若干等份,计算每份的元素个数时可以使用整数除法:
int elementsPerPart = arraySize / numParts;
,这里arraySize
和numParts
通常为整型,结果也是整型,表示每份的元素数量。浮点数除法则在需要精确数值计算的领域广泛应用,如科学计算、金融计算等。在计算物理公式中的比例关系、金融领域的利率计算等场景中,浮点数除法能够提供更准确的结果。例如,计算复利时,double amount = principal * (1 + interestRate / compoundingFrequency) ^ (compoundingFrequency * time);
,其中涉及到浮点数的除法和其他运算,以精确计算资金的增长情况。
2. 关系运算符与逻辑运算符
关系运算符(<、>、==、!= 等)的判断逻辑与返回值
- 小于运算符(<):判断左边的操作数是否小于右边的操作数,如果是,则表达式的值为真(在 C 语言中通常用 1 表示),否则为假(用 0 表示)。例如,
int a = 3, b = 5;
,a < b
的结果为 1,因为 3 小于 5。 - 大于运算符(>):与小于运算符相反,判断左边操作数是否大于右边操作数。如
b > a
结果为 1,而a > b
结果为 0。 - 等于运算符(==):检查左右两个操作数是否相等,相等时返回 1,不相等返回 0。需要注意的是,不要将等于运算符
==
与赋值运算符=
混淆。例如,if (a == 3)
是正确的条件判断,而if (a = 3)
则是将 3 赋值给a
,并且整个表达式的值恒为 3(非零,即被视为真),这可能导致逻辑错误。 - 不等于运算符(!=):判断左右操作数是否不相等,不相等时返回 1,相等返回 0。例如,
a!= b
结果为 1,因为 3 不等于 5。
这些关系运算符常用于条件判断语句,如 if
、while
、for
等,以控制程序的执行流程。例如,if (score >= 60)
判断成绩是否大于等于 60 分,以决定是否及格。
逻辑运算符(&&、||、!)的短路特性与逻辑运算流程
- 逻辑与运算符(&&):逻辑与运算符连接两个表达式,只有当两个表达式的值都为真时,整个逻辑与表达式的值才为真,否则为假。其具有短路特性,即当计算到第一个表达式的值为假时,就不再计算第二个表达式,因为无论第二个表达式的值如何,整个逻辑与表达式都必然为假。例如,
int x = 5, y = 10;
,(x > 10) && (y > 5)
中,由于x > 10
为假,所以不会再计算y > 5
,整个表达式的值直接为假。逻辑与运算符常用于多个条件必须同时满足的场景,如判断一个数是否在某个区间内:if (num >= 1 && num <= 10)
。 - 逻辑或运算符(||):逻辑或运算符连接的两个表达式,只要其中一个表达式的值为真,整个逻辑或表达式的值就为真,只有当两个表达式都为假时,才为假。它也有短路特性,当计算到第一个表达式的值为真时,就不再计算第二个表达式,因为此时整个逻辑或表达式已经确定为真。例如,
(x < 10) || (y < 5)
,因为x < 10
为真,所以不会再计算y < 5
,整个表达式的值为真。逻辑或运算符常用于多个条件中只要满足一个即可的情况,如判断一个字符是否是数字或字母:if (isdigit(c) || isalpha(c))
(其中isdigit
和isalpha
是判断字符类型的函数)。 - 逻辑非运算符(!):逻辑非运算符是单目运算符,它对一个表达式取反。如果原表达式的值为真,则
!
运算后的值为假;如果原表达式的值为假,则!
运算后的值为真。例如,int flag = 0;
,!flag
的值为 1,因为flag
为假,取反后为真。逻辑非运算符常用于对条件进行反转判断,如if (!(a > 5))
等价于if (a <= 5)
。
关系运算符与逻辑运算符常常结合使用,构建复杂的条件判断逻辑,以实现程序根据不同情况执行不同的操作。例如,if ((age >= 18) && (gender == 'M'))
可以判断一个人是否是成年男性。
3. 位运算符
位与(&)、位或(|)、位异或(^)、左移(<<)、右移(>>)运算符的位操作原理
- 位与运算符(&):位与运算符对两个操作数的对应二进制位进行与操作。只有当两个二进制位都为 1 时,结果位才为 1,否则为 0。例如,对于二进制数 1010(十进制为 10)和 1100(十进制为 12),进行位与运算:
1010
& 11001000
结果为 1000(十进制为 8)。位与运算符常用于屏蔽某些二进制位,例如,要获取一个整数的低 4 位,可以将该整数与 0x0F(二进制为 00001111)进行位与运算。
- 位或运算符(|):位或运算符对两个操作数的对应二进制位进行或操作。只要有一个二进制位为 1,结果位就为 1。例如,对于上述的 1010 和 1100 进行位或运算:
1010
| 11001110
结果为 1110(十进制为 14)。位或运算符可用于设置某些二进制位,如将一个整数的低 4 位设置为 1,可以将该整数与 0x0F 进行位或运算。
- 位异或运算符(^):位异或运算符对两个操作数的对应二进制位进行异或操作。当两个二进制位不同时,结果位为 1,相同时为 0。例如,1010 和 1100 进行位异或运算:
1010
^ 11000110
结果为 0110(十进制为 6)。位异或运算符有一些特殊应用,如交换两个变量的值而不使用临时变量:a = a ^ b; b = a ^ b; a = a ^ b;
。
- 左移运算符(<<):左移运算符将一个操作数的二进制位向左移动指定的位数。左移后,右边空出的位用 0 填充。例如,将二进制数 1010(十进制为 10)左移 2 位:
1010 << 2 = 101000
结果为 101000(十进制为 40)。左移一位相当于乘以 2,左移 n 位相当于乘以 2 的 n 次方。但需要注意,如果左移后超出了数据类型所能表示的范围,会导致数据溢出。
- 右移运算符(>>):右移运算符将一个操作数的二进制位向右移动指定的位数。对于无符号数,右移后左边空出的位用 0 填充;对于有符号数,如果是正数,左边空出的位用 0 填充,如果是负数,则根据编译器的不同,可能用 0 填充(逻辑右移)或用 1 填充(算术右移)。例如,将二进制数 1010(十进制为 10)右移 1 位:
1010 >> 1 = 0101
结果为 0101(十进制为 5)。右移一位相当于除以 2(取整),右移 n 位相当于除以 2 的 n 次方。
位运算符在底层数据处理与优化中的应用实例
- 数据压缩与存储优化:在一些数据存储场景中,如果数据具有特定的位模式,可以利用位运算符进行压缩存储。例如,一个布尔数组,如果每个元素只占用 1 位而不是 1 个字节,可以大大节省存储空间。可以使用位或运算符将多个布尔值组合成一个字节或整数进行存储,然后通过位与运算符和位移操作来读取和修改特定位置的布尔值。
- 权限管理系统:在操作系统或软件的权限管理中,常常使用位来表示不同的权限。例如,用一个整数的不同二进制位表示读、写、执行等权限。通过位与、位或、位异或运算符可以方便地进行权限的设置、检查和修改。比如,定义一个权限值
PERMISSION_READ = 0x01
(二进制为 00000001),PERMISSION_WRITE = 0x02
(二进制为 00000010),PERMISSION_EXECUTE = 0x04
(二进制为 00000100)。如果一个用户的权限值为0x07
(二进制为 00000111,表示具有读、写、执行权限),要检查是否具有写权限,可以使用(userPermission & PERMISSION_WRITE) == PERMISSION_WRITE
进行判断;要赋予或撤销某个权限,可以使用位或和位异或运算符。 - 图像处理中的颜色通道处理:在图像处理中,图像的颜色信息通常用 RGB(红、绿、蓝)三个通道表示,每个通道占用 8 位(一个字节)。可以利用位运算符对颜色通道进行分离、合并和调整。例如,要提取图像像素颜色值中的红色通道,可以将像素值与 0xFF0000(二进制为 111111110000000000000000)进行位与运算,然后通过右移 16 位得到红色通道的值。同样,可以对绿色通道和蓝色通道进行类似操作,或者通过位或运算将修改后的通道值合并回像素颜色值。
- 加密算法中的位变换:一些简单的加密算法会利用位运算符对数据进行位变换,增加数据的安全性。例如,通过多次位异或运算对数据进行混淆,使得原始数据难以被直接识别。虽然这种简单的加密方式相对较弱,但在一些特定场景或作为更复杂加密算法的基础部分仍然有应用。
4. 赋值运算符与其他运算符
赋值运算符(=、+=、-= 等)的复合赋值形式与运算顺序
- 基本赋值运算符(=):基本赋值运算符用于将一个值赋给一个变量。例如,
int a = 5;
将 5 赋值给变量a
。赋值表达式的值就是所赋的值,所以可以进行连续赋值,如int b, c; b = c = 10;
先将 10 赋值给c
,然后将c
的值(即 10)赋值给b
。 - 复合赋值运算符(+=、-=、*=、/=、%= 等):复合赋值运算符是一种简化的赋值形式,它将算术运算和赋值操作结合在一起。例如,
a += 3
等价于a = a + 3
,b *= 2
等价于b = b * 2
。这种形式在代码编写中可以使表达更简洁,并且在一些情况下可能具有更高的执行效率。以a += 3
为例,编译器可能会直接对a
进行加 3 的操作并赋值,而不需要先计算a + 3
再赋值,减少了临时变量的生成和计算步骤。复合赋值运算符的运算顺序是先进行右边的算术运算,然后将结果赋值给左边的变量。例如,int x = 5; x *= 2 + 3;
先计算2 + 3 = 5
,然后x = x * 5
,最终x
的值为 25。
sizeof 运算符计算数据类型或变量所占字节数的用法
sizeof
运算符用于获取数据类型或变量在内存中所占的字节数。它是一个编译时运算符,在编译阶段就确定了结果,而不是在运行时计算。例如,sizeof(int)
返回int
类型在当前系统环境下所占的字节数,通常为 4 字节。对于变量,sizeof
也可以直接作用于变量名,如int num; sizeof(num)
同样返回int
类型所占字节数。sizeof
运算符在处理动态内存分配、数组操作、数据结构对齐等方面有重要应用。例如,在动态分配内存时,需要知道数据类型的大小来确定分配的字节数:int *arr = (int *)malloc(sizeof(int) * 100);
这里使用sizeof(int)
计算每个整型元素的大小,然后乘以元素个数 100 来分配足够的内存空间。在处理数组时,
四、控制结构
1. 顺序结构
顺序结构在程序执行中的自然流程体现
- 在 C 语言程序中,顺序结构是最基本的执行流程。它遵循代码书写的先后顺序,从上至下依次执行每条语句。就如同人们日常做事按照步骤依次进行一样,计算机在执行顺序结构的程序时,会先执行第一条语句,完成后接着执行第二条语句,依此类推,直至程序结束。例如,在一个简单的计算程序中:
#include <stdio.h>int main() {int num1 = 5;int num2 = 10;int sum = num1 + num2;printf("两数之和为:%d\n", sum);return 0;
}
程序首先声明并初始化两个整型变量 num1
和 num2
,接着进行加法运算,将结果存储在变量 sum
中,最后使用 printf
函数输出结果。整个过程按照语句的书写顺序依次执行,没有任何跳跃或分支。这种顺序结构清晰明了,适用于简单的、线性的程序逻辑,能够完成一些基本的、不需要复杂判断或重复操作的任务,如简单的数据初始化、一次性的计算和输出等。
简单顺序结构程序的编写与分析
- 编写简单顺序结构程序时,关键在于按照逻辑顺序组织语句。首先要明确程序的目的,确定需要哪些变量来存储数据,然后依次进行变量声明、初始化以及相关的操作。以计算圆的面积为例:
#include <stdio.h>
#define PI 3.14159int main() {float radius = 2.5;float area = PI * radius * radius;printf("半径为%.2f 的圆的面积为%.2f\n", radius, area);return 0;
}
在这个程序中,先定义了一个宏 PI
表示圆周率,然后在 main
函数中声明并初始化了表示半径的变量 radius
,接着根据圆的面积公式计算出面积并存储在变量 area
中,最后输出结果。分析这个程序可以看出,顺序结构使得代码的执行流程直观易懂,每一行代码都在为最终的输出结果做铺垫。然而,顺序结构的局限性在于它只能处理简单的、固定步骤的任务,一旦遇到需要根据不同条件执行不同操作或者重复执行某些操作的情况,就需要引入其他控制结构。
2. 选择结构
if - else 语句的单分支、双分支及多分支嵌套使用方法
- 单分支 if 语句:单分支 if 语句用于在满足特定条件时执行一段代码。其语法结构为
if (条件表达式) { 代码块 }
。当条件表达式的值为真(非零)时,就会执行花括号内的代码块;若条件为假(零),则跳过该代码块。例如,判断一个数是否为正数:
#include <stdio.h>int main() {int num = 10;if (num > 0) {printf("%d 是正数\n", num);}return 0;
}
- 双分支 if - else 语句:双分支 if - else 语句在条件为真时执行一个代码块,条件为假时执行另一个代码块。语法为
if (条件表达式) { 代码块 1 } else { 代码块 2 }
。例如,判断一个数是奇数还是偶数:
#include <stdio.h>int main() {int num = 7;if (num % 2 == 0) {printf("%d 是偶数\n", num);} else {printf("%d 是奇数\n", num);}return 0;
}
- 多分支嵌套 if - else 语句:当有多个条件需要判断时,可以使用嵌套的 if - else 语句。例如,根据学生的成绩划分等级:
#include <stdio.h>int main() {int score = 85;if (score >= 90) {printf("优秀\n");} else if (score >= 80) {printf("良好\n");} else if (score >= 60) {printf("及格\n");} else {printf("不及格\n");}return 0;
}
通过嵌套的方式,可以逐步细化条件判断,使程序能够根据不同的情况执行相应的操作。但过多的嵌套会使代码结构变得复杂,降低可读性和可维护性。
switch - case 语句的结构与 break 语句的作用
- switch - case 语句结构:
switch - case
语句用于多分支选择,它根据一个表达式的值与多个case
常量进行匹配,一旦匹配成功,就执行相应case
后面的代码块。其语法结构为:
switch (表达式) {case 常量表达式 1:代码块 1;break;case 常量表达式 2:代码块 2;break;//...default:代码块 n;break;
}
例如,根据用户输入的数字选择对应的星期:
#include <stdio.h>int main() {int day;printf("请输入数字(1-7)表示星期:");scanf("%d", &day);switch (day) {case 1:printf("星期一\n");break;case 2:printf("星期二\n");break;case 3:printf("星期三\n");break;case 4:printf("星期四\n");break;case 5:printf("星期五\n");break;case 6:printf("星期六\n");break;case 7:printf("星期日\n");break;default:printf("输入错误\n");break;}return 0;
}
- break 语句的作用:在
switch - case
语句中,break
语句起着至关重要的作用。当执行到break
语句时,程序会跳出switch
结构,继续执行后续的代码。如果没有break
语句,在匹配成功的case
后面的代码块执行完后,程序会继续执行下一个case
的代码块,而不管其条件是否匹配,这可能导致错误的结果。例如,如果在上述代码中去掉break
语句,当输入1
时,会依次输出 “星期一”“星期二”“星期三” 等所有后续case
的结果。所以,break
语句用于确保每个case
分支的独立性和准确性,使程序按照预期的逻辑进行多分支选择。
选择结构在处理不同条件分支逻辑中的应用案例
- 在实际编程中,选择结构广泛应用于各种条件判断场景。例如,在一个简单的游戏程序中,根据玩家的得分和生命值来决定游戏的状态:
#include <stdio.h>int main() {int score = 100;int health = 50;if (score >= 100 && health > 0) {printf("恭喜你,通关了!\n");} else if (score < 100 && health > 0) {printf("继续努力,你还未通关\n");} else {printf("游戏结束,你失败了\n");}return 0;
}
又比如,在一个图形绘制程序中,根据用户选择的图形类型(如圆形、矩形、三角形)来调用不同的绘制函数:
#include <stdio.h>// 假设已经定义了绘制圆形、矩形、三角形的函数 drawCircle、drawRectangle、drawTriangleint main() {int shapeChoice;printf("请选择要绘制的图形(1-圆形,2-矩形,3-三角形):");scanf("%d", &shapeChoice);switch (shapeChoice) {case 1:drawCircle();break;case 2:drawRectangle();break;case 3:drawTriangle();break;default:printf("无效的图形选择\n");break;}return 0;
}
这些案例展示了选择结构如何根据不同的条件来控制程序的执行路径,使程序能够根据具体情况做出相应的反应,从而实现更加灵活和智能的功能。
3. 循环结构
for 循环的初始化、条件判断、循环体与迭代部分的详细讲解
- 初始化部分:
for
循环的初始化部分用于在循环开始前设置初始条件,通常是声明并初始化循环变量。例如,for (int i = 0; i < 10; i++)
中的int i = 0
就是初始化部分,它定义了一个整型变量i
并初始化为 0。这个变量将在后续的循环过程中起到计数或索引的作用。初始化部分只在循环开始时执行一次。 - 条件判断部分:条件判断部分紧跟在初始化部分之后,它决定了循环是否继续执行。在上述例子中,
i < 10
就是条件判断部分。每次循环开始前,都会对这个条件表达式进行求值,如果结果为真(非零),则继续执行循环体;如果为假(零),则循环结束。这意味着只要循环变量i
的值小于 10,循环就会持续进行。 - 循环体部分:循环体是
for
循环中被重复执行的代码块。在for (int i = 0; i < 10; i++) { printf("%d ", i); }
中,printf("%d ", i);
就是循环体。它包含了在每次循环中需要执行的操作,可以是任何合法的 C 语言语句,如变量操作、函数调用等。循环体中的代码会根据条件判断部分的结果被多次执行。 - 迭代部分:迭代部分位于循环体之后,它用于在每次循环结束后更新循环变量或执行其他相关操作。在
for (int i = 0; i < 10; i++)
中,i++
就是迭代部分,它使循环变量i
的值在每次循环结束后自增 1。通过不断地更新循环变量,使得条件判断部分的结果最终会变为假,从而结束循环。迭代部分的操作对于控制循环的次数和流程至关重要。
while 循环与 do - while 循环的执行流程与区别
- while 循环执行流程:
while
循环首先判断条件表达式的值,如果为真(非零),则执行循环体中的代码;然后再次判断条件表达式,若仍为真,则继续执行循环体,如此反复,直到条件表达式的值为假(零)时,循环结束。例如:
#include <stdio.h>int main() {int i = 0;while (i < 10) {printf("%d ", i);i++;}return 0;
}
在这个例子中,先判断 i < 10
是否成立,由于 i
初始为 0,条件成立,执行循环体输出 0
并将 i
自增 1;然后再次判断条件,直到 i
等于 10 时,条件不成立,循环结束。
- do - while 循环执行流程:
do - while
循环与while
循环不同,它先执行一次循环体,然后再判断条件表达式的值。如果条件为真,则继续执行循环体;如果为假,则循环结束。例如:
#include <stdio.h>int main() {int i = 0;do {printf("%d ", i);i++;} while (i < 10);return 0;
}
这里先执行循环体输出 0
并自增 i
,然后判断 i < 10
,后续流程与 while
循环类似。
- 两者区别:
while
循环是先判断条件再执行循环体,所以循环体可能一次都不执行;而do - while
循环是先执行循环体再判断条件,因此循环体至少会执行一次。这使得它们适用于不同的场景。例如,在需要先获取用户输入,然后根据输入判断是否继续执行循环的情况下,如果使用while
循环,可能在用户输入不符合条件时循环根本不会开始;而使用do - while
循环则可以先获取一次用户输入并进行处理,然后再根据条件决定是否继续循环。
循环嵌套的应用与控制(外层循环与内层循环的关系)
- 循环嵌套的应用:循环嵌套是指在一个循环体内包含另一个循环。它在处理多维数据结构(如二维数组)、实现复杂的算法(如矩阵乘法)以及生成特定图案(如九九乘法表)等方面有广泛应用。例如,打印九九乘法表:
#include <stdio.h>int main() {for (int i = 1; i <= 9; i++) {for (int j = 1; j <= i; j++) {printf("%d x %d = %d\t", j, i, i * j);}printf("\n");}return 0;
}
这里外层循环控制行数,内层循环控制每行的列数,通过两层循环的嵌套实现了九九乘法表的打印。
- 外层循环与内层循环的关系:外层循环每执行一次,内层循环会完整地执行一轮。在上述九九乘法表的例子中,外层循环变量
i
从 1 变化到 9,对于i
的每一个值,内层循环变量j
都会从 1 变化到i
,从而实现了乘法表的逐行打印。在处理二维数组时,外层循环可以用于遍历数组的行,内层循环用于遍历每行中的列元素。例如:
#include <stdio.h>int main() {int matrix[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {printf("%d ", matrix[i][j]);}printf("\n");}return 0;
}
外层循环变量 i
控制行索引,每变化一次,内层循环变量 j
就会遍历该行的所有列元素,从而实现了对整个二维数组的遍历。
循环结构在遍历数据、重复执行任务等方面的经典应用
- 遍历数组:循环结构常用于遍历数组中的元素,无论是一维数组还是多维数组。例如,计算一维数组中所有元素的和:
#include <stdio.h>
#include <stdlib.h>int main() {int arr[] = {1, 2, 3, 4, 5};int sum = 0;int size = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < size; i++) {sum += arr[i];}printf("数组元素之和为:%d\n", sum);return 0;
}
这里使用 for
循环遍历数组 arr
,将每个元素累加到变量 sum
中,最终得到数组元素的总和。
- 重复执行任务:在一些需要重复执行某个操作直到满足特定条件的场景中,循环结构也发挥着重要作用。例如,猜数字游戏中,玩家不断猜测一个随机生成的数字,直到猜对为止:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>int main() {srand((unsigned int)time(NULL));int targetNumber = rand() % 100 + 1;int guess;do {printf("请输入你的猜测(1-100):");scanf("%d", &guess);if (guess > targetNumber) {printf("太大了\n");} else if (guess < targetNumber) {printf("太小了\n");}} while (guess!= targetNumber);printf("恭喜你,猜对了!\n");return 0;
}
在这个游戏中,使用 do - while
循环让玩家不断猜测,直到猜对为止,每次猜测后根据比较结果给出提示,使游戏能够持续进行。
今天的C语言复习就到这了,其他的内容下篇见。
相关文章:
【C语言篇】C 语言总复习(上):点亮编程思维,穿越代码的浩瀚星河
我的个人主页 我的专栏:C语言,希望能帮助到大家!!!点赞❤ 收藏❤ 在计算机科学的广袤宇宙中,C语言犹如一颗璀璨的恒星,散发着持久而耀眼的光芒。它作为一种基础且强大的编程语言,承载…...
多线程——04
本节目标 1. wait 和 notify 方法 2. 代码案例 1. wait 和 notify 方法 1. 方法使用 多个线程的执行顺序本身是随机的(抢占式执行) wait —— 让指定线程进入阻塞状态 notify —— 唤醒对应的阻塞状态的线程 注意: wait, notify, notifyAl…...
使用ECS和OSS搭建个人网盘
在linux服务器 一、下载cloudreve安装包。 执行如下命令,下载cloudreve安装包。 wget https://labfileapp.oss-cn-hangzhou.aliyuncs.com/cloudreve_3.3.1_linux_amd64.tar.gz 下载完毕后,执行如下命令,解压cloudreve安装包。 tar -zxvf c…...
Android 单元测试断言校验方法 org.junit.Assert
判断布尔值 assertTrue assertFalse 判断对象非空 assertNull(object); 案例: PersistableBundle result Util.getCarrierConfig(mockContext, subId);assertNull(result); 判断是否相等 assertEquals("mocked_string", result.toString()); package or…...
SpringSecurity学习
介绍 SpringSecurity是一个作用于身份认证和权限控制的框架,其针对的主要就是网站的安全问题 页面代码 要使用SpringSecurity的前提是有一个可以正常访问业务逻辑的代码,再使用SpringSecurity实现权限控制和身份验证。 后端代码 package com.learn.…...
Eureka和Zookeeper、Nacos的区别
目录 一、Eureka与Zookeeper的区别 适用场景: 架构设计: 功能特性: 社区生态: 二、Eureka与Nacos的区别 接口方式: 实例类型: 健康检测: 服务发现: 一致性与可用性&#…...
基于gitlab API刷新MR的commit的指定status
场景介绍 自己部署的gitlab Jenkins,并已经设置好联动(如何设置可以在网上很容易搜到)每个MergeRequest都可以触发多个Jenkins pipeline,pipeline结束后会将状态更新到gitlab这个MR上希望可以跳过pipeline运行,直接将指定的MR的指定pipeline状态刷新为…...
SpringBoot | 拦截器 | 统一数据返回格式 | 统一异常处理 | 适配器模式
拦截器 拦截器是Spring框架提供的核心功能之一, 主要用来拦截用户的请求, 在指定方法前后, 根据业务需要执行预先设定的代码. 也就是说, 允许开发人员提前预定义一些逻辑, 在用户的请求响应前后执行. 也可以在用户请求前阻止其执行. 在拦截器当中,开发人员可以在…...
Oracle清除水位
– 清除水位 ALTER TABLE 数据库名.表名 ENABLE ROW MOVEMENT; ALTER TABLE 数据库名.表名 SHRINK SPACE CASCADE; ALTER TABLE 数据库名.表名 DISABLE ROW MOVEMENT; – 回收统计信息 BEGIN DBMS_STATS.GATHER_TABLE_STATS(OWNNAME > ‘数据库名’, TABNAME > ‘表名’…...
软件工程——期末复习(2)
Part1:软件工程基本概念 软件程序文档数据 在软件工程中,软件通常被定为程序、文档和数据的集合。程序是按事先设计的功能和性能要求编写的指令序列;程序是完成指定功能的一段特定语言代码。文档是描述程序操作和使用的文档,是与…...
RAID1技术是什么?它的发展和工作原理如何?
RIAD1是一种先进的数据存储与冗余技术,设计用于解决现代分布式系统中常见的数据安全、数据一致性和高可用性等问题。随着云计算和大规模分布式存储系统的兴起,如何保障数据在高效传输与存储过程中仍然能具备足够的安全性和可靠性,成为了各大企…...
【Apache Paimon】-- 8 -- flink 创建 paimon connecor 的两种方式
目录 1、使用 catalog 创建非临时表 2、使用 with 创建 temporary 表 3、对比 4、参考 1、使用 catalog 创建非临时表 CREATE CATALOG my_catalog WITH (type = paimon,warehouse = hdfs:///path/to/warehouse );USE CATALOG my_catalog; CREATE TABLE `<your-paimon-…...
js进阶-关于运算符++
一、运算符与表达式 运算符按参与的运算单元数量分为:一元运算符、二元运算符和三元运算符;表达式是运算单元和运算符一起构成的;每个表达式都有一个运算后的返回值。 二、关于运算符 1.概述 运算符分为两部分,第一部分是返回运…...
三维地图,智慧城市,商业智能BI,数据可视化大屏(Cesiumjs/UE)
绘图工具 三维地图:Cesiumjs 建模方式:激光点云建模、航拍倾斜摄影建模、GIS建模、BIM建模、手工建模 建模工具:C4D Blender GeoBuilding ArcGIS Cesiumjs <!DOCTYPE html> <html lang"en"> <head><meta …...
通过EPEL 仓库,在 CentOS 7 上安装 OpenResty
通过EPEL 仓库,在 CentOS 7 上安装 OpenResty 通过EPEL 仓库,在 CentOS 7 上安装 OpenResty步骤 1: 安装 EPEL 仓库步骤 2: 安装 OpenResty步骤 3: 启动 OpenResty步骤 4: 设置开机自启步骤 5: 验证安装说明 通过EPEL 仓库,在 CentOS 7 上安装…...
每日一题 LCR 054. 把二叉搜索树转换为累加树
LCR 054. 把二叉搜索树转换为累加树 使用后序遍历即可 class Solution { public:TreeNode* convertBST(TreeNode* root) {int temp 0;dfs(root,temp);return root;}void dfs(TreeNode* root,int &temp){if(!root){return ;}dfs(root->right,temp);temp root->val;…...
【贪心算法】贪心算法五
贪心算法五 1.跳跃游戏 II2.跳跃游戏3.加油站3.单调递增的数字 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃😃 1.跳跃游戏 II 题目链接&…...
vue2播放视频和预览文件的组件以及使用方法
##文件预览组件 按照组件 解决展示pdf的问题 npm install pdfh5 npm install canvas2.8.0 --ignore-scripts npm install --save dommatrix npm install --save web-streams-polyfill解决excel和docx预览的问题 npm install vue-office/docx vue-demi0.14.6 npm inst…...
记录一下,解决js内存溢出npm ERR! code ELIFECYCLEnpm ERR! errno 134 以及 errno 9009
项目是个老项目,依赖包也比较大,咱就按正常流程走一遍来详细解决这个问题,先看一下node版本,我用的是nvm管理的,详细可以看我的其他文章 友情提醒:如果项目比较老,包又大,又有一些需…...
【批处理脚本】更改Windows系统中的 hosts 解析文件
概述 作用 修改 Windows 系统中的 hosts 文件,可以实现 插入 或 删除 条目。该脚本允许用户以管理员权限执行,将特定的域名解析到指定的 IP 地址 应用场景 非常适用于需要频繁或批量修改 hosts 文件的场景: 屏蔽网站、域名重定向、DNS 污染防…...
AIGC 与艺术创作:变革与机遇
在当今数字化时代,人工智能生成内容(AIGC)正以惊人的速度重塑着艺术创作的格局,为艺术家们带来了令人振奋的新机遇。 一.AIGC 的崛起与艺术领域的变革 随着人工智能技术的不断进步,AIGC 逐渐在艺术领域崭露头角。它依…...
String IP和Int IP的互相转换
android中,wifiManager.connectionInfo.ipAddress 可以获取到wifi的ip地址,但这是一个int值,如何转换为常见的如192.168.1.129这种形式,以及这种形式如何转换回int值的形式。 这里ip分为4段,每一段的值都是0 ~ 255&am…...
【大数据学习 | 面经】yarn的资源申请和分配的单位-Container
在yarn中,资源的申请和分配是以container为单位进行的,而不是直接以application和task为单位。 每个提交到yarn上的应用程序(application)都有一个对应的ApplicationMaster(AM)。这个AM负责与ResourceMana…...
php基础:文件处理2
1.文件属性 当我们在程序中操作文件时,可能会使用到文件的一些常见属性,比如文件的大小、类型、修改时间、访问时间以及权限等等。PHP 中提供了非常全面的用来获取这些属性的内置函数,如下表所示。 2.目录操作 新建目录:mkdir(路…...
gradle下载慢解决方案2024 /12 /1android studio (Windows环境)
gradle下载慢解决方案2024 /12 /1 默认环境配置好了,环境配置和程序安装请出门右转 打开软件,点击右上角设置,找到如下设置页 选择本地安装并制定好你已经安装好的 gradle 应用保存即可 全局插件环境配置(新版本可以直接在设置中添加了) 找对应位置添加国内源并把前面的内置源…...
使用Java将PDF文件解析成Excel文件
安装pom依赖 <!-- 解析pdf--><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.27</version> <!-- 请检查并使用最新版本 --></dependency>测试读取pdf文件…...
怎么区分直线模组中的导程和行程?
直线模组是一种直线传动装置,安装方便,精度高,其使用范围广。直线模组中的导程和行程是两个不同的概念,它们分别描述了直线模组的不同性能参数。 直线模组的行程指的是模组能够正常移动的最大距离,即滑块预期移动的有效…...
算法日记 42 day 图论
今天来看看广度优先搜索,并且写几个题。刷到这里我才想起来,当时第一次面试的时候问的就是这个题,当时大概知道一点思路,但不清楚是图论方面的,更别说写出来了。 广度优先搜索(BFS) 不同于深度…...
STM32 GPIO 8种工作模式的应用场景
目录 一、四种输入模式1、模拟输入:2、浮空输入:3、上拉输入:4、下拉输入: 二、四种输出模式1、推挽输出:2、开漏输出:3、复用推挽输出:4、复用开漏输出: 一、四种输入模式 1、模拟…...
JVM 类加载器有哪些?双亲委派机制的作用是什么?如何自定义类加载器?
类加载器分类 大家好,我是码哥,可以叫我靓仔,《Redis 高手心法》畅销书作者。 先回顾下,在 Java 中,类的初始化分为几个阶段: 加载、链接(包括验证、准备和解析)和 初始化。 而 类加载器&#x…...
揭秘:短视频矩阵源码功能开发分析!!!
一、短视频矩阵系统源码概述 短视频矩阵系统源码旨在为内容创作者及企业提供一种高效的工具,以实现对多个短视频账户的一站式管理。该系统支持同时管理、发布、监控和优化多达1000个短视频账户,显著提升了操作效率。 二、主要功能 1. 多账号管理 该系…...
leetcode - 2337. Move Pieces to Obtain a String
Description You are given two strings start and target, both of length n. Each string consists only of the characters ‘L’, ‘R’, and ‘_’ where: The characters ‘L’ and ‘R’ represent pieces, where a piece ‘L’ can move to the left only if there i…...
第九篇:k8s 通过helm发布应用
什么是helm? Helm 是 Kubernetes 的包管理器。Helm 是查找、分享和使用软件构建 Kubernetes 的最优方式。 在红帽系的Linux中我们使用yum来管理RPM包,类似的,在K8s中我们可以使用helm来管理资源对象(Deployment、Service、Ingress…...
MySQL:锁机制
锁是计算机协调多个进程或线程并发访问某一资源的机制(避免争抢)。 在数据库中,除传统的计算资源(如 CPU、RAM、I/O 等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效…...
在lio_sam中融入GPS
文章目录 概要GPS里程计GPS因子反算后的GPS里程计概要 在LIO(激光惯性里程计)系统中,将GPS信息融合到里程计中,借助GTSAM(Georgia Tech Smoothing and Mapping)库进行因子图优化,可以有效提升全局定位精度。 GPS里程计 利用GeographicLib第三方库将经纬度投影到局部笛…...
快速构建NLP理论知识体系
NLP理论知识体系 一句话解释NLPNLP模型及原理简述1、Rag 一句话解释NLP 如果我们要实现机器翻译、情感分析、问答系统、文本摘要、聊天机器人、构造智能化的辅助文件填写模板,NLP可以通过现成的模型对输入的语音、文字、图片进行处理(分词、标词性、去停…...
长期稳定境外号码解决方案:内地用户如何打电话、接收短信和验证码
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 三大方案对比 📒📝 免费且稳定的境外号码📝 长期稳定且符合本地政策📝 适合低频使用者⚓️ 相关链接 ⚓️📖 介绍 📖 许多用户在日常生活中需要拨打境外电话、接收短信或验证码,尤其是跨境电商从业者,更是对境外号…...
SpringCloud 与 SpringBoot版本对应关系,以及maven,jdk
目录 SpringCloud 与 SpringBoot各版本的对应关系 方式一 Learn 方式二 OverView SpringBoot与JDK、maven 容器等对应关系 SpringCloud 与 SpringBoot各版本的对应关系 SpringCloudSpringBootFinchley2.0.xFinchley.SR1Spring Boot >=2.0.3.RELEASE and <=2.0.9RELEAS…...
FSMC实验
FSMC ,即灵活的静态存储控制器,能够与同步或异步存储器和 16 位 PC 存储器卡连接, STM32 的 FSMC 接口支持包括 SRAM 、 NAND FLASH 、 NOR FLASH 和 PSRAM 等存储器。 从上图我们可以看出, STM32 的 FSMC 将外部设…...
K8s命令大全(Complete List of K8s Commands)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…...
C++学习笔记
小甲鱼学习课程 02 #include <iostream> 头文件 iostream iostream 翻译为IO流 输入输出流 using namespace std; 引入命名空间 c标准库所使用的所有标识符都是在同一个特殊的名字空间std中来定义的; 偷懒使用这句话; std::cout <…...
伺服控制电机多大功率合适?
随着现代工业自动化的发展,伺服控制电机在各个行业的应用愈加广泛。伺服电机以其精确的控制、高效的运行和高响应速度,成为许多机械设备中不可或缺的组成部分。然而,在选择伺服电机时,确定其功率大小是一个关键环节。那么…...
《OpenCV 基础全攻略:从入门到精通》
《OpenCV 基础全攻略:从入门到精通》 一、OpenCV 简介(一)特点(二)优势(三)应用场景 二、安装与配置三、OpenCV 函数详解1. 图像读取函数 cv2.imread ()2. 图像显示函数 cv2.imshow ()3. 图像保…...
spring ai如何使用function call调用第三方模型
这里写自定义目录标题 背景什么是function call怎么用function call?总结 背景 一直困惑于ai是如何使用插件或者其他一些功能的,后来发现,很多大模型都支持function call功能,如何让大模型能够联网查询呢,function ca…...
2024 数学建模国一经验分享
2024 数学建模国一经验分享 背景:武汉某211,专业:计算机科学 心血来潮,就从学习和组队两个方面指点下后来者,帮新人避坑吧 2024年我在数学建模比赛中获得了国一(教练说论文的分数是湖北省B组第一࿰…...
javaweb-Mybaits
1.Mybaits入门 (1)介绍 (2) 2.Mybaits VS JDBC 3.数据库连接池 (1)SpringBoot默认连接池为hikari,切换为Druid有两种方式 方式一:加依赖 方式二:直接修改配置文件 …...
108.【C语言】数据结构之二叉树查找值为x的节点
目录 1.题目 代码模板 2.分析 分类讨论各种情况 大概的框架 关键部分(继续递归)的详解 递归调用展开图 3.测试结果 其他写法 4.结论 5.注意事项 不推荐的写法 1.题目 查找值为x的节点并返回节点的地址 代码模板 typedef int BTDataType; typedef struct BinaryT…...
Ant-Design-Vue 全屏下拉日期框无法显示,能显示后小屏又位置错乱
问题1:在全屏后 日期选择器的下拉框无法显示。 解决:在Ant-Design-Vue的文档中,很多含下拉框的组件都有一个属性 getPopupContainer可以用来指定弹出层的挂载节点。 在该组件上加上 getPopupContainer 属性,给挂载到最外层盒子上。 <temp…...
sec啥意思
sec的基本含义是秒,是时间的基本单位之一;在数学中表示正割函数;在计算机科学中有时指安全;在法语中意为干的等。 sec的多重含义与应用 sec在数学中的定义 在数学领域,尤其是三角学中,sec代表正割函数&a…...
云计算vspere 安装过程
1 材料的准备 1 安装虚拟机 vmware workstation 2 安装esxi 主机 3 在esxi 主机上安装windows 2018 dns 服务器 4 在虚拟机上安装windows 2018 服务器 6 安装vcenter 5 登入界面测试 这里讲一下,由于部署vspere 需要在windows 2012 服务器上部…...