Bootstrap

单片机(STM32)开发中常用的C语言基础语法(二)

9、goto语句与标签

goto语句用于无条件地转移到程序中的另一部分。它通常用于跳出循环或提前退出函数。然而,使用goto语句需要谨慎,因为过度使用它可能会导致代码难以理解和维护。

标签(Label)是一个代码标识符,后面跟一个冒号,它标识一个语句的位置。标签可以与goto语句一起使用,使程序跳转到该标签所在的语句。

#include <stdio.h>  
  
int main() {  
    int i;  
  
    for (i = 0; i < 10; i++) {  
        printf("%d ", i);  
        if (i == 5) {  
            goto end_of_loop;  //goto语句
        }  
    }  
  
    end_of_loop:  //标签语法
        printf("End of loop\n");  
  
        return 0;  
}

标签可以被多次引用,这意味着可以在代码中定义相同的标签多次。但是,一个标签只能被一个语句块内的 goto 语句引用,也就是说,goto语句只能跳转到最近的被引用的标签所在的位置。这个规则是为了确保代码的可读性和可维护性。如果一个标签被多个goto语句引用,就会导致代码的流程变得不清晰,而且也不容易理解。因此,一个标签只能被一个语句块内的goto语句引用,这样可以保证代码的流程更加清晰和易于理解。

10、sscanf函数

sscanf 是一个标准C库函数,用于从字符串中读取格式化输入。它的名字代表“string scan”,意即从字符串中扫描格式化的数据。sscanf 的工作方式与 scanf 类似,但它不是从标准输入(通常是键盘)读取数据,而是从一个给定的字符串中读取。

int sscanf(const char *str, const char *format, ...);
  • str:指向要读取的字符串的指针。

  • format:一个格式字符串,指定了要读取的数据的类型和格式。

  • ...:表示可变数量的指针,这些指针指向将要存储读取数据的变量。

sscanf 函数的返回值是成功读取并赋值的输入项数,如果输入结束或发生输入失败,则可能小于提供的指针数量。

下面是一个简单的 sscanf 示例:

#include <stdio.h>  
int main() {  
    const char *input = "42 3.14 Hello";  
    int integer;  
    float floating;  
    char str[20];  
  
    // 从input字符串中读取一个整数,一个浮点数和一个字符串  
    int itemsRead = sscanf(input, "%d %f %19s", &integer, &floating, str);  
  
    // 打印读取到的值和读取的项数  
    printf("Read %d items:\n", itemsRead);  
    printf("Integer: %d\n", integer);  
    printf("Floating: %f\n", floating);  
    printf("String: %s\n", str);  
  
    return 0;  
}

在这个例子中,sscanf 函数尝试从 input 字符串中读取一个整数、一个浮点数和一个字符串,并将这些值存储在相应的变量中。格式字符串 "%d %f %19s" 指定了要读取的数据类型和格式:一个整数(%d)、一个浮点数(%f)和一个最多包含19个字符的字符串(%19s)。注意,字符串的读取长度被限制为19个字符,以避免缓冲区溢出。

11、左移右移操作符<< >>

  1. 左移运算符 (<<)

    • 将左操作数的所有位向左移动指定的位数,右侧空出的位用0填充。

    • 有一个8位的二进制数 00001010 (十进制中的10)。如果我们将其左移1位,结果为 00010100 (十进制中的20)。

    • 左移操作相当于乘以2的某个幂。例如,左移1位相当于乘以2,左移2位相当于乘以4,以此类推。

  2. 右移运算符 (>>)

    • 将左操作数的所有位向右移动指定的位数。

    • 对于无符号整数,右侧溢出的位被丢弃,左侧空出的位用0填充。

    • 对于有符号整数,右移的处理方式依赖于具体的编译器或机器。在许多环境中,算术右移会保留符号位(即最左边的位),这意味着负数的右移会在左侧填充1,而正数或0的右移会在左侧填充0。但在某些环境中,也可能采用逻辑右移,即无论符号如何,都在左侧填充0。

    • 有一个8位的二进制数 00001010 (十进制中的10)。如果我们将其右移1位,结果为 00000101 (十进制中的5)。

    • 右移操作可以被视为整除2的某个幂。例如,右移1位相当于除以2,右移2位相当于除以4(忽略余数),以此类推。

12、strcmp函数

strcmp 函数是 C 语言标准库中 <string.h> 头文件提供的一个函数,用于比较两个字符串,其语法如下:

int strcmp(const char *str1, const char *str2);
  • str1:要比较的第一个字符串。指向第一个要比较的 null 结尾字符串的指针

  • str2:要比较的第二个字符串。指向第二个要比较的 null 结尾字符串的指针

strcmp函数会比较两个字符串str1str2,如果两个字符串相等,则返回0;

如果str1小于str2,则返回一个负数;

如果str1大于str2,则返回一个正数。

以下是一个简单的示例代码,演示如何使用strcmp函数:

#include <stdio.h>
#include <string.h>

int main() {
    char str1[] = "hello";
    char str2[] = "world";
    int result = strcmp(str1, str2);
    if (result == 0) {
        printf("两个字符串相等\n");
    }
    else if (result < 0) {
        printf("第一个字符串小于第二个字符串\n");
    }
    else {
        printf("第一个字符串大于第二个字符串\n");
    }
    return 0;
}

在上面的示例代码中,我们比较了两个字符串str1str2,并根据strcmp函数的返回值输出不同的结果。

13、strlen/sizeof函数

在C语言中,strlensizeof 是两个不同的操作,它们用于不同的目的。

  1. strlen 函数用于计算字符串的长度,不包括 null 终止字符 '\0'。strlen 是 C 标准库中 <string.h> 头文件提供的函数。下面是它的语法:

#include <string.h>
size_t strlen(const char *str);

函数参数:

  • str: 指向以 null 结尾的字符串的指针。

返回值:

  • 返回 str 所指向的字符串的长度,不包括 null 终止字符。

用法示例:

#include <stdio.h>
#include <string.h>
int main() {
    const char *str = "Hello, World!";
    size_t length = strlen(str);
    printf("Length of the string is: %zu\n", length);
    return 0;
}

这段代码会输出字符串 "Hello, World!" 的长度,不包括结尾的 null 字符。

  1. sizeof 操作符用于计算变量或类型所占的字节数。它在编译时计算,而不是运行时。下面是它的用法:

size_t size = sizeof(object_or_type);

用法示例:

#include <stdio.h>
int main() {
    int arr[10];
    size_t sizeInBytes = sizeof(arr);
    printf("Size of the array is: %zu bytes\n", sizeInBytes);
    return 0;
}
//%zu 是一个格式说明符,用于 printf 或 scanf 等格式化输入输出函数中。它用来匹配 size_t 类型的数据。使用 %zu 格式说明符可以确保无论 size_t 是多大,都能正确地打印其值,而不会发生整型溢出或类型不匹配的问题。

这段代码会输出整型数组 arr 的大小(以字节为单位),因为 arr 包含 10 个整数,所以如果一个整数占 4 个字节,那么输出将会是 40 字节。

  • strlen 返回的是字符串的实际长度,直到遇到第一个 null 字符。

  • sizeof 返回的是数组分配的总字节数,对于字符串,这包括末尾的 null 字符。如果 sizeof 用于指针,它将返回指针本身的大小,而不是指针所指向的数据的大小。

14、memset函数

memset(rx_data, 0, sizeof(rx_data));memset常用于清空串口接收缓存,rx_data是自己定义用来存放串口数据的变量

在C语言中,memset函数的语法如下:

void *memset(void *ptr, int value, size_t num);
  • ptr:指向要设置数值的内存起始地址的指针。

  • value:要设置的值,通常是一个无符号字符(unsigned char),但传入int类型也可以,会被截断为无符号字符。

  • num:要设置的字节数,即连续内存块的大小。

memset函数通常用于将一块内存区域的值设置为指定的值,可以是0、-1或其他特定值。例如,将一个数组的所有元素初始化为0,可以使用memset函数:

int arr[10];
memset(arr, 0, sizeof(arr));
char rx[20];
memset(rx,0,strlen(rx));

这样就会把数组arr中的所有元素设置为0。

15、strtok函数

strtok 函数是 C 语言标准库中的一个用于分割字符串的函数。它可以用来将字符串分割成一系列的标记(token),分割是根据一组指定的分隔符来完成的。

函数原型如下:

#include <string.h>
char *strtok(char *str, const char *delim);

参数:

  • str: 要分割的原始字符串。第一次调用 strtok 时,str 应该指向要分割的字符串。此后,要继续分割同一个字符串,就应该传递 NULL 作为 str 参数,这样函数就会继续处理上次调用时剩下的部分。

  • delim: 包含所有分隔符的字符串。这些字符中的任何一个都可以作为标记的分隔符。

返回值:

  • 返回指向当前标记的指针,如果没有剩余的标记,则返回 NULL

注意: strtok 函数使用一个静态缓冲区来存储当前的位置,所以它不是线程安全的。在多线程环境中应当使用 strtok_r,这是它的线程安全版本。

strtok 的工作方式是,它在 str 指向的字符串中查找 delim 中的字符。当它找到一个这样的字符时,它会用 \0(空字符)替换掉,并返回指向当前标记起始位置的指针。在连续调用中,它会继续从上次停下来的地方开始查找下一个标记。

示例代码:

#include <stdio.h>
#include <string.h>
int main() {
    char str[] = "This is a sample string";
    char *token;//strtok的返回值是一个指针
    char *rest = str;
    // 获取第一个标记
    token = strtok(rest, " ");
    while (token != NULL) {
        printf("%s\n", token);
        // 为了获取后续的标记,rest 应该被设置为 NULL
        token = strtok(NULL, " ");
    }
    return 0;
}

在上面的例子中,strtok 被用来分割字符串 str,每当遇到空格 " " 时,就会分割出一个新的标记。每个标记在控制台上单独一行打印出来。第一次调用 strtok 时,rest 指向要分割的字符串;之后,为了继续分割同一个字符串,我们传递 NULLstrtok

strtok 函数找到一个标记时,它会在标记的末尾添加一个 \0(空字符)来终止这个标记。然后,strtok 返回一个指向这个标记起始位置的指针。这意味着,它返回的是一个指向原始字符串 str 内部的一个位置的指针,而不是创建一个新的字符串。

这里的“当前标记”是指 strtok 根据提供的分隔符 delimstr 或其后续调用中剩余部分中找到的第一个标记。标记是指原字符串中由分隔符分隔的子字符串

具体来说,假设你有以下字符串和调用:

char str[] = "hello,world,this,is,a,test";
char *token = strtok(str, ",");

在第一次调用 strtok 后,原始字符串 str 在内存中的表示会被修改。strtok 会在第一个分隔符(,)的位置放置一个 \0 字符,因此原始字符串 str 现在看起来像两个字符串:"hello" 和 "world,this,is,a,test"。

strtok 的返回值是指向原始字符串中第一个标记(在这个例子中是单词 "hello")的第一个字符的指针。因此,第一次调用 strtok 后,token 指向的是 "hello"。

继续调用 strtok(NULL, ",") 会继续从上次停止的位置开始查找下一个标记,并重复相同的过程(在下一个分隔符处放置 \0,并返回指向当前标记的指针)。这意味着,随着 strtok 的每次调用,原始字符串 str 会被进一步分割,strtok 返回的指针将指向这些新形成的标记。

这种方法的一个后果是 strtok 修改了原始字符串,所以如果你需要保留原始字符串未被修改的状态,你应该先对它进行复制。

16、size_t

size_t 是 C 和 C++ 编程语言中定义的一种数据类型。它是一个无符号整数类型,通常用于表示大小(如数组长度、字符串长度等)和基于内存的计算(如内存分配大小)。size_t 类型足够大,能够表示任何对象的大小,包括数组和字符串的最大可能大小。

这个数据类型在 <stddef.h>(在 C 语言中)和 <cstddef>(在 C++ 中)头文件中定义。由于它是无符号的,size_t 类型的值永远不会是负数。

size_t 的确切大小依赖于平台和编译器,但它必须至少能够表示编译器支持的最大对象大小。在许多 32 位系统上,size_t 是 32 位无符号整数;在 64 位系统上,它通常是 64 位无符号整数。

使用 size_t 而不是 int 或其他整数类型进行内存相关的计算和表示,有助于提高代码的可移植性和安全性,因为它能够适应不同平台上的地址空间大小变化。

下面是一些使用 size_t 的例子:

例子 1:使用 size_t 作为数组索引和循环计数器

#include <stdio.h>
int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    size_t numbers_count = sizeof(numbers) / sizeof(numbers[0]);
// 使用 sizeof 运算符计算数组总大小,然后除以单个元素的大小,得到数组元素的数量
    for (size_t i = 0; i < numbers_count; ++i) {
// 使用 size_t 类型的 i 作为循环计数器,从 0 遍历到数组的长度(不包括这个长度)
        printf("%d ", numbers[i]);
    }
    return 0;
}

例子 2:使用 size_t 接收 strlen 函数的返回值

#include <stdio.h>
#include <string.h>
int main() {
    char myString[] = "Hello, World!";
    size_t strLength = strlen(myString);
    printf("The length of '%s' is %zu.\n", myString, strLength);
//%zu 是一个格式说明符,用于 printf 或 scanf 等格式化输入输出函数中。它用来匹配 size_t 类型的数据。
    return 0;
}

在这些例子中,使用 size_t 来处理与大小相关的值,可以确保代码在不同的系统和编译器配置中具有良好的兼容性和正确性。

;