第2章.STM32开发C语言常用知识点

目录

0. 《STM32单片机自学教程》专栏总纲 

2.1. STM32嵌入式开发C语言编程的不同

2.2. C语言常用知识点

2.2.1 位操作

2.2.2 define 宏定义

2.2.3 条件编译

2.2.3.1 #ifdef

2.2.3.2 #ifndef

2.2.3.3 #if !defined

2.2.4 extern 变量声明

2.2.5  typedef 类型别名

2.2.6 结构体

2.2.6.1 结构体的声明和定义

2.2.6.2 引用结构体成员变量 

2.2.6.3 结构体的作用

2.2.6.4 结构体成员的内存分布与对齐

2.2.7 关键字

2.2.7.1 volatile 

2.2.7.2 const

2.2.7.3 static

2.2.8 指针        

参考资料:

0. 《STM32单片机自学教程》专栏总纲 

        本文作为专栏《STM32单片机自学教程》专栏其中的一部分,返回专栏总纲,阅读所有文章,点击Link:  

STM32单片机自学教程-[目录总纲]_stm32 学习-CSDN博客

2.1. STM32嵌入式开发C语言编程的不同

         STM32开发中的C语言编程与通用计算机编程之间存在一些显著的区别,这些区别主要源于两者不同的应用场景和硬件环境。如下图2.1-1,区别主要体现在以下五个方面: 

图2.1-1 STM32嵌入式开发C语言编程和通用编程的区别点

  1. 硬件相关性
    • STM32开发中的C语言编程直接关联到特定的硬件,如微控制器、IO端口、中断、DMA等。开发者需要直接操作这些硬件资源,因此必须了解相关的硬件手册和寄存器配置。
    • 通用计算机编程则更多关注于软件设计和算法实现,与硬件的关联度较低。开发者通常不需要直接操作硬件寄存器,而是通过操作系统提供的API进行编程。
  2. 资源限制
    • STM32等嵌入式系统通常具有有限的内存、存储空间和计算能力。因此,在STM32开发中,C语言编程需要特别注意内存管理、代码优化和性能调优。
    • 通用计算机则具有较大的内存和存储空间,以及强大的计算能力。开发者在编写通用计算机程序时,通常不需要过分关注这些资源限制。
  3. 实时性要求
    • STM32等嵌入式系统通常需要满足严格的实时性要求,即系统需要在规定的时间内响应外部事件。因此,在STM32开发中,C语言编程需要特别注意时间管理和代码执行效率。
    • 通用计算机编程则通常不需要满足如此严格的实时性要求。
  4. 开发工具和环境
    • STM32开发通常使用专门的嵌入式开发环境和工具链,如Keil MDK、IAR Embedded Workbench、STM32CubeIDE等。这些工具提供了针对STM32硬件的特定支持和优化。
    • 通用计算机编程则可以使用各种通用的集成开发环境(IDE),如Visual Studio、Eclipse、Dev-C++等。
  5. 调试和测试
    • STM32开发中的调试和测试通常需要借助专门的调试器、仿真器和测试工具,以模拟硬件环境和验证程序功能。
    • 通用计算机编程则可以使用各种调试器和测试框架,以方便地进行程序调试和测试。 

        总之,STM32开发中的C语言编程与通用计算机编程在硬件相关性、资源限制、实时性要求、开发工具和环境以及编程语言特性等方面存在显著的区别。这些区别要求开发者在编写STM32程序时,需要更加注重底层编程和硬件操作,并充分考虑到嵌入式系统的特殊性和限制。 

2.2. C语言常用知识点

        我们这里就列举部分 STM32 学习中会遇见的 C  语言基础知识点。

2.2.1 位操作

        C 语言位操作就是对基本类型变量可以在位级别进行操作。C 语言支持如下表6种位操作:

表2.1-1-C语言支持的位操作 

运算符含义
&按位与
|按位或
^按位异或
~按位取反
<<左移
>>右移

        这些按位与或,取反,异或,右移,左移这些我们就不多做详细讲解,毕竟这里不是给大家普及C语言的基本知识,不清楚的大家可以再复习一下。下面我们着重讲解位操作在嵌入式开发中的一些实用技巧。 

1.在不改变其他位的值的状况下对某几个位进行设值

        这个场景在单片机开发中经常使用,方法就是先对需要设置的位用"&"操作符进行清零操作,然后用"|"操作符设值。比如我要改变 GPIOA 的状态,可以先对寄存器的值进行&清零操作:

GPIOA->CRL &= 0XFFFFFF0F;    /* 将第 4~7位清 0 */ 
/*然后再与需要设置的值进行|或运算:*/ 
GPIOA->CRL |= 0X00000040;    /* 设置相应位的值(4),不改变其他位的值 */ 

2.移位操作提高代码的可读性

         移位操作在单片机开发中非常重要,下面是 delay_init 函数的一行代码:

SysTick->CTRL |= 1 << 1;

        这个操作就是将 CTRL 寄存器的第 1 位(从 0 开始算起)设置为 1,为什么要通过左移而不是直接设置一个固定的值呢?其实这是为了提高代码的可读性以及可重用性。这行代码可以很直观明了的知道,是将第 1 位设置为 1。如果写成:

SysTick->CTRL |= 0X0002;

         这个虽然也能实现同样的效果,但是可读性稍差,而且修改也比较麻烦。

3.~按位取反操作使用技巧

        按位取反在设置寄存器的时候经常被使用,常用于清除某一个/某几个位。下面是 delay_us函数的一行代码:

SysTick->CTRL &= ~(1 << 0) ;    /* 关闭 SYSTICK */ 

        该代码可以解读为  仅设置 CTRL 寄存器的第 0 位(最低位)为 0,其他位的值保持不变。同样我们也不使用按位取反,将代码写成:  

SysTick->CTRL &= 0XFFFFFFFE;        /* 关闭 SYSTICK */ 

        可见前者的可读性,及可维护性都要比后者好很多。

4.^按位异或操作使用技巧

        该功能非常适合用于控制某个位翻转,常见的应用场景就是控制 LED 闪烁,如:

GPIOB->ODR ^= 1 << 5;

        执行一次该代码,就会使 PB5 的输出状态翻转一次,如果我们的 LED 接在 PB5 上,就可以看到 LED 闪烁了。 

2.2.2 define 宏定义

        define 是 C 语言中的预处理命令,它用于宏定义(定义的是常量),可以提高源代码的可读性,为编程提供方便。常见的格式:  

 #define         标识符       字符串

         "标识符"为所定义的宏名;"字符串"可以是常数、表达式、格式串等。例如:

#define PIE 3.14159f

         PIE在后续出现的地方都代表3.14159。后续如果想修改π的值,可以直接在宏定义的地方修改,不用再在程序出现的每一个地方再去修改,而且非常直观,代码可读性强。

2.2.3 条件编译

2.2.3.1 #ifdef

        嵌入式程序开发过程中,经常会遇到一种情况,当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。条件编译命令最常见的形式为:

   #ifdef 标识符  
      程序段 1  
   #else  
      程序段 2  
   #endif  

        它的作用是:当标识符已经被定义过(一般是用#define 命令定义),则对程序段 1 进行编译,否则编译程序段 2。  其中#else 部分也可以没有,即:

    #ifdef  
           程序段 1  
    #endif 
2.2.3.2 #ifndef
#ifndef SOME_MACRO  
// 如果 SOME_MACRO 没有被定义,则编译以下代码  
#endif
2.2.3.3 #if !defined
#if !defined(SOME_MACRO)  
// 如果 SOME_MACRO 没有被定义,则编译以下代码  
#endif

          这也是检查是否没有定义某个宏的方法,但它使用了!defined操作符.

        在这个例子中!defined(SOME_MACRO) 是一个条件表达式,当 SOME_MACRO 没有被定义时,该表达式的值为真(非零),从而编译 #if 和对应 #endif 之间的代码。下面是STM32里的一段代码:

    #if !defined  (HSE_VALUE)  
      #define HSE_VALUE            24000000U  
    #endif  

        如果没有定义HSE_VALUE这个宏,则定义HSE_VALUE宏,并且HSE_VALUE的值为24000000U。24000000U中的U表示无符号整型,常见的,UL表示无符号长整型,F表示浮点型。这里加了U以后,系统编译时就不进行类型检查,直接以U的形式把值赋给某个对应的内存,如果超出定义变量的范围,则截取。

2.2.4 extern 变量声明

        C 语言中 extern 可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于extern声明变量可以多次,但定义只有一次。在我们的代码中你会看到看到这样的语句: 

extern uint16_t speed_x; 

        这个语句是申明 speed_x变量在其他文件中已经定义了,在这里要使用到。所以,你肯定可以找到在某个地方有变量定义的语句:

uint16_t speed_x;

2.2.5  typedef 类型别名

        typedef 用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。

        例如C99标准中引入的头文件<stdint.h>,定义了一组具有固定宽度的整数类型,包括有符号和无符号的8位、16位、32位和64位整数。这些类型分别命名为int8_t、int16_t、int32_t、int64_t(以及对应的无符号类型uint8_t、uint16_t、uint32_t、uint64_t)。在STM32F10x的标准库函数stm32f10x.h中又对这些数据类型进行了重新定义,代码如下:

typedef int32_t  s32;
typedef int16_t s16;
typedef int8_t  s8;
typedef uint32_t  u32;
typedef uint16_t u16;
typedef uint8_t  u8;

        typedef在 MDK 用得最多的就是定义结构体的类型别名和枚举类型了。

struct _GPIO 
{ 
__IO uint32_t CRL; 
__IO uint32_t CRH; 
… 
}; 

         定义了一个结构体 GPIO,这样我们定义结构体变量的方式为:

struct  _GPIO  gpiox;       /* 定义结构体变量 gpiox */ 

         但是这样很繁琐,MDK中有很多这样的结构体变量需要定义。这里我们可以为结体定义一
个别名GPIO_TypeDef,这样我们就可以在其他地方通过别名GPIO_TypeDef来定义结构体变量了,方法如下: 

typedef struct 
{ 
__IO uint32_t CRL; 
__IO uint32_t CRH; 
… 
} GPIO_TypeDef; 

        Typedef为结构体定义一个别名GPIO_TypeDef,这样我们可以通过GPIO_TypeDef来定义结构体变量:  

GPIO_TypeDef gpiox;

         这里的 GPIO_TypeDef 就跟 struct  _GPIO 是等同的作用了,但是 GPIO_TypeDef 使用起来方便很多。

2.2.6 结构体

        在C语言中,结构体(struct)是一种用户自定义的数据类型,它允许你将不同类型的数据项组合成一个单独的数据结构。结构体可以用来表示一个具有复杂属性的实体,比如一个人(具有姓名、年龄、性别等属性)或者一本书(具有书名、作者、出版日期等属性)。

2.2.6.1 结构体的声明和定义
/*声明结构体类型: */
    struct 结构体名 
    { 
        成员列表; 
    }变量名列表; 

         你可以在声明结构体的时候直接创建结构体变量,也可以先定义结构体类型,然后再创建变量,如下面几种方式都是可以的:

// 直接定义并创建结构体变量  
struct {  
    int age;  
    char name[50];  
} person1;  

// 直接定义并创建结构体变量  
struct Person{  
    int age;  
    char name[50];  
} person2; 
  
// 先定义结构体类型,再创建变量  
struct Person {  
    int age;  
    char name[50];  
};  
struct Person person3;
2.2.6.2 引用结构体成员变量 

        要访问结构体变量的成员,你需要使用.运算符(对于结构体变量)或->运算符(对于指向结构体的指针)。

/*接前面章节2.6.1的示例代码*/
// 访问结构体变量的成员  
person1.age = 25;    
  
// 如果有一个指向结构体的指针  
struct Person *ptr = &person2;  
ptr->age = 30; 
2.2.6.3 结构体的作用

        下面我们将简单的通过一个实例描述一下结构体的作用。 

        在我们单片机程序开发过程中,经常会遇到要初始化一个外设比如串口,它的初始化状态是由几个属性来决定的,比如串口号,波特率,极性,以及模式。对于这种情况,在我们没有学习结构体的时候,我们一般的方法是:

 void usart_init(uint8_t usartx, uiut32_t BaudRate, uint32_t Parity,  
uint32_t Mode); 

        这种方式是有效的同时在一定场合是可取的。但是试想,如果有一天,我们希望往这个函数里面再传入一个/几个参数,那么势必我们需要修改这个函数的定义,重新加入新的入口参数,随着开发不断的增多,那么是不是我们就要不断的修改函数的定义呢?这是不是给我们开发带来很多的麻烦?那又怎样解决这种情况呢?
        我们使用结构体参数,就可以在不改变入口参数的情况下,只需要改变结构体的成员变量就可以达到改变入口参数的目的。
        结构体就是将多个变量组合为一个有机的整体,上面的函数usartx,BaudRate,Parity,Mode等这些参数,他们对于串口而言,是一个有机整体,都是来设置串口参数的,所以我们可以将他们通过定义一个结构体来组合在一个。MDK中是这样定义的:

typedef struct 
{  
uint32_t BaudRate; 
uint32_t WordLength; 
uint32_t StopBits; 
uint32_t Parity; 
uint32_t Mode; 
uint32_t HwFlowCtl; 
uint32_t OverSampling;    
} UART_InitTypeDef; 

        这样,我们在初始化串口的时候入口参数就可以是 USART_InitTypeDef 类型的变量或者指针变量了,于是我们可以改为:

void usart_init(UART_InitTypeDef *huart); 

        这样,任何时候,我们只需要修改结构体成员变量,往结构体中间加入新的成员变量,而不需要修改函数定义就可以达到修改入口参数同样的目的了。这样的好处是不用修改任何函数定义就可以达到增加变量的目的。
        在以后的开发过程中,如果你的变量定义过多,如果某几个变量是用来描述某一个对象,你可以考虑将这些变量定义在结构体中,这样也许可以提高你的代码的可读性。使用结构体组合参数,可以提高代码的可读性,不会觉得变量定义混乱。

2.2.6.4 结构体成员的内存分布与对齐

        首先一些基本知识点:
(1)声明一个结构体类型的时候是没有为它分配任何存储空间的,只有在定义结构体变量的时候,才会为变量分配存储空间。
(2)结构体中可以有不同的数据类型成员,成员在定义时依次存储在内存连续的空间中,结构体变量的首地址就是第一个成员的地址,内存偏移量就是各个成员相对于第一个成员地址的差(即,把低位内存分配给最先定义的变量)。
(3)理论上,结构体所占用的存储空间是各个成员变量所占的存储空间之和,但是为了提高CPU的访问效率,采用了内存对齐方式:
        ①结构体的每一个成员起始地址必须是自身类型大小的整数倍,若不足,则不足部分用数据填充至所占内存的整数倍。
        ②结构体大小必须是结构体占用最大字节数成员的整数倍,这样在处理数组时可以保证每一项都边界对齐根据上面的说明,我们举例子分析如下:

    struct test 
        { 
            char a; 
            int b; 
            float c; 
            double d; 
        }mytest; 

        这个结构体所占用的内存怎么算呢?理论结果为17,实际上并不是17,而是24。为什么会这样呢?这个就是前面我们说的内存对齐。
        char型变量占1个字节,所以它的起始地址是0。int类型占用4个字节,它的起始地址要求是4的整数倍数,那么内存地址1、2、3就需要被填充(被填充的内存不适于变量),b从4开始。float类型也是占用4个字节,起始地址要求是4的倍数,所以c的起始地址就是8。double类型变量占用8个字节,起始地址为16,12~15被填充。这里,第一个成员a的地址首地止,第二个成员b的偏移量为4,第三个成员c的偏移量是8,以此类推,是如下图2.2-1所示:

图2.2-1 结构地地址内存分配  

2.2.7 关键字

        在STM32的一些库函数头文件中,经常会看到如下代码, 表示将 volatile 或者 volatile  const 来代替某一个符号。

#define   __I     volatile 
#define   __O     volatile    
#define   __IO    volatile            
#define   __IM     volatile const    
#define   __OM     volatile            
#define   __IOM    volatile 
2.2.7.1 volatile 

         volatile 表示强制编译器减少优化,告诉编译器必须每次去内存中取变量值。     

        程序运行时数据是存储在主内存(物理内存)中的,每个线程先从主内存拷贝变量到对应的寄存器中。对没有加volatile的变量进行读写时,为了提高读取速度,编译器进行优化时,会先把主内存中的变量读取到一个寄存器中,以后,再读取此变量的值时,就直接从该寄存器中读取,而不是直接从内存中读取了,这样的读写速度比较快。如果其它程序改变了内存中变量的值,上面已经保存到寄存器中的值不会跟着改变,从而造成应用程序读取的值和实际的变量值不一致。加了修饰关键字volatile以后的变量,表示不想被编译器优化掉,每次都要从内存中读取该变量的数据,不会用寄存器里的值,这样确保了数据的准确性,但影响了效率。 

2.2.7.2 const

        const称为常量限定符,用来限定特定变量为只读属性,如果修改此变量,则编译器会报错。const修饰的变量存储在只读数据段,在程序结束时释放,而const局部变量存储在栈中,代码块结束时释放。用const定义变量时就要初始化该变量:       

 const int a = 1; 
2.2.7.3 static

        static关键字修饰的变量称为静态变量,如果该变量在声明时未赋初始值,则编译器自动初始化为0,静态变量存储在全局区(静态区)。
        在函数内被static声明的变量,仅能在本函数中使用,也叫静态局部变量。
        在文件内(函数体外)被static声明的变量,仅能被本文件内的函数访问,不能被其他文件中的函数访问,也叫静态全局变量。
        静态全局变量和普通的全局变量不同,静态全局变量仅限于本文件中使用,在其它文件中可以定义一个与静态全局变量名字相同的变量。普通的全局变量可以通过extern外部声明后被其他文件使用,也就是整个工程可见,而且其他文件不能再定义一个与普通全局变量名字相同的变量了。

        用static修饰的函数和用static修饰的变量类似。 下面是用法举例说明:

1.局部静态变量
        当在函数内部声明一个变量为static时,该变量的存储期将变为整个程序的执行期,而不是只在函数调用被时存在。这意味着局部变量只会被初始化一次,并且会保留其值,直到程序结束。这在需要跨函数调用保留某些信息时非常有用。

void func() {  
    static int count = 0; // 只在程序开始时初始化一次  
    count++;  
    printf("%d\n", count);  
}

        每次调用func()时,count的值都会递增。

2.全局静态变量
        在文件级别(即不在任何函数内部)声明的static变量只能在该文件内部可见。这意味着它们只能被定义它们的文件内的函数访问,而不能被其他文件访问。这提供了一种封装机制,允许你在一个文件中定义和使用变量,而不用担心与其他文件冲突。

// file1.c  
static int file_scope_var = 42; // 只能在file1.c中访问  

// file2.c  
extern int file_scope_var; // 错误:无法在其他文件中访问file_scope_var

3.静态函数:
        当在文件级别使用static关键字声明一个函数时,该函数将具有内部链接,即它只能在其定义的文件内被调用。这提供了另一种封装机制,允许你隐藏函数的实现细节,只暴露需要被其他文件使用的函数。

// file1.c  
static void internal_function() {  
    // ...  
}  

// file2.c  
extern void internal_function(); // 错误:无法在其他文件中调用internal_function

4.静态初始化

        尽管这不是static的直接用途,但它在静态初始化中扮演了重要角色。当全局变量或静态变量被声明并赋予初值时,编译器会确保在程序开始执行之前进行初始化。这通常是在main()函数之前发生的。 

2.2.8 指针        

        在STM32开发中,指针的作用十分重要。首先,指针是C语言的一个重要组成部分,它允许我们通过内存地址直接访问和操作数据。在STM32这样的嵌入式系统开发中,指针的使用与底层硬件的联系尤为密切。

        具体来说,STM32库开发中,我们对寄存器进行了封装,将寄存器放入到结构体(如GPIOX)当中。通过指针,我们可以指向这些结构体的地址,从而访问和操作寄存器,完成对寄存器的配置。这种方式可以减少开发时的代码量,提高开发效率。

        同时,指针移位操作在STM32开发中也是常见的。通过指针移位,我们可以方便地访问连续的内存区域,比如数组或结构体中的连续元素。在C语言中,我们可以通过指针算术运算(如加法、减法)来实现指针的移位。需要注意的是,在进行指针移位操作时,应确保指针类型和指向的数据类型一致,并遵循C语言指针算术运算的规则。

        此外,指针还可以用于访问和操作内存映射的硬件寄存器。在STM32中,许多硬件资源都是通过内存映射的方式暴露给软件的。通过指针,我们可以直接访问这些硬件寄存器的地址,从而实现对硬件的控制和配置。

        总的来说,指针在STM32开发中具有重要的作用,它允许我们通过内存地址直接访问和操作数据,实现对硬件的底层控制和优化。然而,由于指针直接操作内存地址,因此在使用时也需要格外小心,以避免出现内存泄漏、野指针等问题。

        指针的具体使用方法,这里就不再赘述。

参考资料:


        【1】哔站江协科技STM32入门教程

        【2】《STM32单片机原理与项目实战》刘龙、高照玲、田华著

        【3】《ARM Cortex-M3嵌入式原理及应用》黄可亚著

        【4】《STM32嵌入式微控制器快速上手》陈志旺著

        【5】《STM32单片机应用与全案例实践》沈红卫等著

        【6】《野火STM32开发指南》

        【7】《正点原子STM32开发指南》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信用户可以通过哪些渠道查找和关注到公众号

什么是公众号 公众号&#xff0c;作为微信生态中不可或缺的一部分&#xff0c;已经成为连接用户与品牌、企业与个人的重要桥梁。它不仅是一个信息发布平台&#xff0c;更是一个集互动、服务、营销于一体的综合性工具。公众号通过发布高质量的内容&#xff0c;吸引用户关注&…

Leetcode—976. 三角形的最大周长【简单】(ranges::sort函数)

2024每日刷题&#xff08;122&#xff09; Leetcode—976. 三角形的最大周长 实现代码 class Solution { public:int largestPerimeter(vector<int>& nums) {ranges::sort(nums);for(int i nums.size() - 1; i > 1; i--) {if(nums[i - 1] nums[i - 2] > nu…

项目经理【过程】原则

系列文章目录 【引论一】项目管理的意义 【引论二】项目管理的逻辑 【环境】概述 【环境】原则 【环境】任务 【环境】绩效 【人】概述 【人】原则 【人】任务 【人】绩效 【过程】概念 【过程】原则 一、质量管理水平、质量管理的发展 1.1 质量管理水平 1.2 质量管理的发展 …

亲测快捷高效的编写测试用例方法

前言 测试用例是任何测试周期的第一步&#xff0c;对任何项目都非常重要。如果在此步骤中出现任何问题&#xff0c;则在整个软件测试过程中都会扩大影响。如果测试人员在创建测试用例模板时使用正确的过程和准则&#xff0c;则可以避免这种情况。 在本篇文章中将分享一些简单而…

【大学物理】双语合集听课笔记

7.5 angular momentu(角动量)_哔哩哔哩_bilibili 6.4Energy in Rotation Motion 有质量有速度的物体有动能&#xff0c;是不是很有道理 international system&#xff08;from French systeme international&#xff0c;acronym&#xff0c;SI&#xff09;of ineria kg*m^2 转…

[笔试训练](十六)

目录 046:字符串替换 047:神奇数 048:DNA序列 046:字符串替换 字符串替换_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 简单模拟题~ class StringFormat { public:string formatString(string str, int n, vector<char> arg, int m) {strin…

PPP点对点协议

概述 Point-to-Point Protocol&#xff0c;点到点协议&#xff0c;工作于数据链路层&#xff0c;在链路层上传输网络层协议前验证链路的对端&#xff0c;主要用于在全双工的同异步链路上进行点到点的数据传输。 PPP主要是用来通过拨号或专线方式在两个网络节点之间建立连接、…

牛客 二叉树 NB1 牛群的最大高度

原题链接 就不采用, 递归的方式来做了, 自己弄个栈来做 用栈来保存路径, curr 表示当前的节点, pre 保留往回走时的上一步 如果是 用递归来做 它的栈链路是这样的, 可以做下参考 黄色表示返回 用栈模拟的话, 不可能模拟得一摸一样, 递归的话一个栈会经过3次, 第三次后就不…

MATLAB实现遗传算法优化第三类生产线平衡问题

第三类生产线平衡问题的数学模型 假设&#xff1a; 工作站数量&#xff08;m&#xff09;和生产线节拍&#xff08;CT&#xff09;是预设并固定的。每个任务&#xff08;或作业元素&#xff09;只能分配到一个工作站中。任务的执行顺序是预先确定的&#xff0c;且不可更改。每…

ICode国际青少年编程竞赛- Python-2级训练场-列表入门

ICode国际青少年编程竞赛- Python-2级训练场-列表入门 1、 Dev.step(3)2、 Flyer.step(1) Dev.step(-2)3、 Flyer.step(1) Spaceship.step(7)4、 Flyer.step(5) Dev.turnRight() Dev.step(5) Dev.turnLeft() Dev.step(3) Dev.turnLeft() Dev.step(7) Dev.turnLeft() Dev.…

二叉树习题汇总

片头 嗨&#xff01;大家好&#xff0c;今天我们来练习几道二叉树的题目来巩固知识点&#xff0c;准备好了吗&#xff1f;Ready Go ! ! ! 第一题&#xff1a;二叉树的最大深度 解答这道题&#xff0c;我们采用分治思想 1. 递归子问题&#xff1a;左子树的高度和右子树的高度 …

Tkinter组件:Checkbutton

Tkinter组件&#xff1a;Checkbutton Checkbutton&#xff08;多选按钮&#xff09;组件用于实现确定是否选择的按钮。Checkbutton 组件可以包含文本或图像&#xff0c;你可以将一个 Python 的函数或方法与之相关联&#xff0c;当按钮被按下时&#xff0c;对应的函数或方法将被…

【汇编语言小练习输入两个数字然后输出它们的和】

这个程序是用汇编语言编写一个简单的程序&#xff0c;它将从键盘输入两个数字&#xff0c;然后输出它们的和。 .MODEL SMALL .STACK 100H.DATAINPUT_MSG1 DB Enter the first number: $INPUT_MSG2 DB 13, 10, Enter the second number: $RESULT_MSG DB 13, 10, The sum is: $N…

【抽样调查】分层抽样上

碎碎念&#xff1a;在大一大二时听课有的时候会发现听不太懂&#xff0c;那时候只觉得是我自己的基础不好的原因&#xff0c;但现在我发现“听不懂”是能够针对性解决的。比如抽样调查这门课&#xff0c;分析过后我发现我听不懂的原因之一是“没有框架”&#xff0c;一大堆知识…

2024年第六届世界软件工程研讨会(WSSE 2024)即将召开!

2024年第六届世界软件工程研讨会&#xff08;WSSE 2024&#xff09;将于2024年9月13-15日在日本京都举行。软件工程领域的发展离不开各位专家学者和业界精英的共同努力和贡献。WSSE 2024将就软件工程领域的最新研究成果、实践经验和发展趋势进行深入交流和探讨&#xff0c;汇聚…

Ubuntu将软件图标添加到应用列表

一.简介snap snap和yum&#xff0c;apt一样都是安装包工具&#xff0c;但是snap里的软件源是自动更新到最新版本&#xff0c;最好用 比如Ubuntu的软件商城就是使用的snap软件包 二. Ubuntu软件商城更新 1.ps -ef | grep snap-store 查询并kill snap-store的所有进程 2.sudo …

【Linux进程间通信(六)】深入理解 System V IPC

&#xff08;一&#xff09;引入 &#xff08;二&#xff09;IPC 命名空间 &#xff08;三&#xff09;ipc_ips结构体 &#xff08;四&#xff09;ipc_id_ary结构体 &#xff08;五&#xff09;kern_ipc_perm结构体 &#xff08;六&#xff09;操作系统对IPC资源是如何管理…

县供电公司员工向媒体投稿发文章用亲身经历告诉你并不难

在县供电公司的日子里,我肩负着一项至关重要的使命——信息宣传工作。这不仅仅是一份职责,更是连接公司与外界的桥梁,通过新闻稿件传递我们的声音,展示我们的成果。然而,回忆起刚刚踏入这个领域的时光,那段经历至今让我感慨万千。 初涉投稿,步履维艰 刚接手这项工作时,我的投稿…

探索DeepSeek平台:新一代MoE模型的深度体验

简介 DeepSeek是一个创新的人工智能平台&#xff0c;它最近推出了其最新版本的模型——DeepSeek-V2 MoE&#xff08;Mixture of Experts&#xff09;。这个平台不仅提供了一个交互式的聊天界面&#xff0c;还提供了API接口&#xff0c;让用户可以更深入地体验和利用这一先进的…

全体模型师请做好日入过万的准备!3D模型库海量资源,老子云平台免费用

在数字化的大背景下&#xff0c;3D转型已然成为了多行业关注的重点战略版块。无论是科技、金融、互联网、化工、建筑等等各个行业都在加速布局&#xff0c;3D手段会成为下一个重要的技术风口。也正因如此&#xff0c;3D市场潜能巨大&#xff0c;并且3D需求每年都在暴涨&#xf…
最新文章