c语言常用函数

Size of()计算数组的大小

sizeof 可以直接用于计算整个数组占用的总字节数。要获取数组中的元素个数,可以使用:

sizeof(array) / sizeof(array[0])

这可以通过 sizeof 返回数组的总大小,然后除以单个元素的大小来得到元素的个数。

示例:
#include <stdio.h>

int main() {
    int arr[10]; // 定义一个包含10个整数的数组

    // 计算数组的总大小(字节数)
    printf("Size of arr: %zu bytes\n", sizeof(arr));  // 输出数组占用的字节数

    // 计算数组中元素的个数
    printf("Number of elements in arr: %zu\n", sizeof(arr) / sizeof(arr[0]));  // 输出数组元素的个数

    return 0;
}
输出:
Size of arr: 40 bytes      // 在32位系统上,int 通常占用4字节
Number of elements in arr: 10

计算结构体的大小

对于结构体,sizeof 返回的是结构体在内存中占用的总字节数。结构体的大小不仅取决于其成员的大小,还可能受到内存对齐的影响。

示例:
#include <stdio.h>

struct Example {
    int a;        // 占用4字节
    double b;     // 占用8字节
    char c;       // 占用1字节
};

int main() {
    // 计算结构体的大小
    printf("Size of struct Example: %zu bytes\n", sizeof(struct Example));

    return 0;
}
输出:
Size of struct Example: 16 bytes   // 由于内存对齐,结构体可能占用比其成员之和更多的内存

内存对齐对结构体大小的影响

内存对齐是 C 语言中的一个优化机制,它可能会导致结构体的实际大小大于其所有成员的大小之和。比如,编译器通常会在结构体中插入一些“填充字节”(padding bytes),使得结构体的成员在内存中的地址满足对齐要求。

示例:
#include <stdio.h>

struct AlignmentExample {
    char c;     // 占用1字节
    int i;      // 占用4字节
};

int main() {
    printf("Size of struct AlignmentExample: %zu bytes\n", sizeof(struct AlignmentExample));
    return 0;
}
输出:
Size of struct AlignmentExample: 8 bytes
  • 虽然 char c 占用 1 字节,int i 占用 4 字节,但由于内存对齐,结构体的大小是 8 字节,而不是 5 字节。
  • 这是因为编译器会在 charint 之间插入 3 个字节的填充,以确保 int 变量按照 4 字节边界对齐。

数组和结构体的组合

如果你在结构体中定义了数组,你可以使用 sizeof 计算整个结构体的大小。

示例:
#include <stdio.h>

struct ArrayInStruct {
    int arr[10];    // 定义一个包含10个整数的数组
    char c;         // 定义一个字符
};

int main() {
    // 计算包含数组的结构体的大小
    printf("Size of struct ArrayInStruct: %zu bytes\n", sizeof(struct ArrayInStruct));

    return 0;
}
输出:
Size of struct ArrayInStruct: 44 bytes   // 假设 int 占用 4 字节,char 占用 1 字节
  • 数组 arr[10] 占用了 10 × 4 = 40 字节(假设 int 占 4 字节)。
  • char c 占 1 字节。
  • 由于内存对齐规则,编译器可能会为结构体添加额外的字节,使结构体的总大小为 44 字节(而不是 41 字节)。
在 C 语言中,sizeof(struct tm)sizeof(t) 可以是等价的:
  • sizeof(struct tm):直接获取 struct tm 这个结构体类型在内存中占用的字节数。
  • sizeof(t):获取变量 t 在内存中占用的字节数。此时,t 必须是 struct tm 类型的变量,才能返回结构体的大小。
示例:
#include <stdio.h>
#include <time.h>

int main() {
    struct tm t;  // 定义一个 struct tm 类型的变量

    // 计算 struct tm 类型的大小
    printf("Size of struct tm: %zu bytes\n", sizeof(struct tm));

    // 计算变量 t 的大小
    printf("Size of t: %zu bytes\n", sizeof(t));

    return 0;
}
输出(在典型的系统上可能是):
Size of struct tm: 44 bytes
Size of t: 44 bytes
结论:
  • sizeof(struct tm) 是计算 struct tm 结构体类型本身在内存中的大小。
  • sizeof(t) 是计算 t 变量的大小。因为 tstruct tm 类型的变量,sizeof(t) 返回的大小与 sizeof(struct tm) 相同。

因此,tstruct tm 类型的变量时sizeof(struct tm)sizeof(t) 是等价的,都会返回相同的值。

strcmp 是 C 标准库中的一个字符串比较函数,用于比较两个字符串的大小。这个函数在 <string.h> 头文件中定义。其基本用法是比较两个以 null 结尾的字符串,返回一个整数值来表示两个字符串的相对大小。

strcmp 函数

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

参数

  • str1:第一个要比较的字符串。
  • str2:第二个要比较的字符串。

返回值

  • 返回 0:如果 str1str2 内容相同(完全相等)。
  • 返回负数:如果 str1 在字典顺序中小于 str2
  • 返回正数:如果 str1 在字典顺序中大于 str2

比较规则

  • strcmp 函数是按字符逐个比较 str1str2 的字符(基于 ASCII 值进行比较),直到找到第一个不同的字符或者遇到字符串结尾 \0
  • 如果字符串长度不同,比较到短的字符串结尾处,较长字符串的字符将被视为更大。
示例:
#include <stdio.h>
#include <string.h>

int main() {
    const char *str1 = "apple";
    const char *str2 = "apple";
    const char *str3 = "banana";
    const char *str4 = "apricot";

    // 比较相等的字符串
    if (strcmp(str1, str2) == 0) {
        printf("str1 and str2 are equal.\n");
    }

    // 比较不同的字符串
    if (strcmp(str1, str3) < 0) {
        printf("str1 is less than str3.\n");
    }

    // 比较不同的字符串
    if (strcmp(str1, str4) > 0) {
        printf("str1 is greater than str4.\n");
    }

    return 0;
}
输出:
str1 and str2 are equal.
str1 is less than str3.
str1 is greater than str4.

解释:

  1. strcmp(str1, str2)str1str2 是相同的字符串,因此返回 0。
  2. strcmp(str1, str3)"apple" 小于 "banana",因为 "a" 的 ASCII 值小于 "b",所以返回负数。
  3. strcmp(str1, str4)"apple" 大于 "apricot",因为比较到第三个字符时,'p' 的 ASCII 值大于 'r',所以返回正数。

常见的用法场景

  1. 字符串比较strcmp 最常用于两个字符串的比较,比如在排序算法中根据字母顺序排序,或者在查找特定的字符串时进行匹配。
  2. 判断字符串相等:在条件语句中,使用 strcmp(str1, str2) == 0 来判断两个字符串是否相等。
  3. 字典序排序strcmp 可用于排序函数,比如按字母顺序排列字符串数组。

#注意事项

  1. 大小写敏感strcmp 是大小写敏感的。例如,"Apple""apple" 被认为是不相等的。如果需要忽略大小写的比较,可以使用 strcasecmp(POSIX 标准)。
  2. 传递的参数必须是以 '\0' 结尾的字符串strcmp 假设每个字符串都是以 null 终止的,因此必须确保传递的是正确的 C 字符串。

sscanf 是 C 语言标准库中的一个函数,用于从字符串中读取格式化的数据,并将其存储在指定的变量中。它的功能与 scanf 类似,但 sscanf 从字符串中读取数据,而不是从标准输入(键盘)读取。

strncmp 是 C 语言标准库中的一个字符串比较函数,用于比较两个字符串的前 n 个字符。与 strcmp 类似,strncmp 可以比较两个字符串,但它只比较指定数量的字符(即前 n 个字符),不会比较整个字符串。

strncmp 函数

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

参数解释

  • str1:指向要比较的第一个字符串。
  • str2:指向要比较的第二个字符串。
  • n:指定比较的字符数,最多比较前 n 个字符。

返回值

  • 返回 0:如果 str1str2 的前 n 个字符相等。
  • 返回负值:如果 str1 在字典顺序中小于 str2 的前 n 个字符。
  • 返回正值:如果 str1 在字典顺序中大于 str2 的前 n 个字符。
示例 1:比较两个字符串的前几个字符
#include <stdio.h>
#include <string.h>

int main() {
    const char *str1 = "applepie";
    const char *str2 = "applejuice";

    // 比较前 5 个字符
    if (strncmp(str1, str2, 5) == 0) {
        printf("The first 5 characters are equal.\n");
    } else {
        printf("The first 5 characters are not equal.\n");
    }

    return 0;
}
输出:
The first 5 characters are equal.

解释:

  • strncmp(str1, str2, 5) 比较 str1str2 的前 5 个字符,它们都是 "apple",因此返回 0,表示它们的前 5 个字符相等。
示例 2:使用 strncmp 检查前缀
#include <stdio.h>
#include <string.h>

int main() {
    const char *str1 = "banana";
    const char *prefix = "ban";

    // 比较字符串是否以 "ban" 开头
    if (strncmp(str1, prefix, 3) == 0) {
        printf("str1 starts with 'ban'.\n");
    } else {
        printf("str1 does not start with 'ban'.\n");
    }

    return 0;
}
输出:
str1 starts with 'ban'.

解释:

  • strncmp(str1, prefix, 3) 用于比较 str1 的前 3 个字符是否等于 "ban",因为 str1"banana",前 3 个字符确实是 "ban",所以返回 0,表示匹配。
常见用法场景

(1) 限制字符串比较的长度

strncmp 可以用来只比较两个字符串的前几个字符,而不是整个字符串,尤其适用于处理部分匹配或前缀匹配。

if (strncmp(data, "CNBR", 4) == 0 || strncmp(data, "VNBR", 4) == 0)
{
    printf("Valid parking type.\n");
}

在这个例子中,strncmp 用于比较字符串的前 4 个字符,检查停车类型是否是 "CNBR""VNBR"

(2) 防止越界

strncmp 允许你只比较部分字符串,避免了当字符串很长时,逐字符比较整个字符串的效率问题。同时它可以防止在未指定长度的情况下发生越界访问。

(3) 前缀匹配

strncmp 可以用来检查字符串是否以某个特定的前缀开头,如网址、文件路径等前缀匹配。

注意事项
  1. 大小写敏感strncmp 是区分大小写的。如果需要忽略大小写,可以使用 strncasecmp(POSIX 标准)。

  2. 比较的字符数:如果字符串的长度小于 nstrncmp 只会比较到字符串的末尾('\0')为止,不会越界。

  3. 返回值的使用strncmp 返回的正值或负值取决于字符串在字典顺序中的先后顺序。通常用 strncmp(str1, str2, n) == 0 来判断它们是否相等。

总结

  • strncmp 用于比较两个字符串的前 n 个字符,常用于部分匹配或前缀比较。
  • 返回值与 strcmp 类似:相等返回 0,str1 小于 str2 返回负值,str1 大于 str2 返回正值。
  • 可以避免比较整个字符串,提高效率,同时防止内存越界问题。

sscanf 函数

int sscanf(const char *str, const char *format, ...);

参数解释

  • str:指向要解析的字符串。
  • format:格式化字符串,定义了要读取的数据格式(类似于 printfscanf 的格式化符号)。
  • ...:后面的参数是指向要存储解析结果的变量的指针。

返回值

  • 成功时,sscanf 返回成功读取并赋值的项数。
  • 如果没有成功读取任何数据,返回 0
  • 如果遇到错误,返回 EOF
示例 1:从字符串中读取整数和字符串
#include <stdio.h>

int main() {
    const char *data = "12345 Hello World";
    int number;
    char str1[10], str2[10];

    // 使用 sscanf 从字符串 data 中读取一个整数和两个字符串
    sscanf(data, "%d %s %s", &number, str1, str2);

    printf("Number: %d\n", number);
    printf("String 1: %s\n", str1);
    printf("String 2: %s\n", str2);

    return 0;
}
输出:
Number: 12345
String 1: Hello
String 2: World

解释:

  • sscanf(data, "%d %s %s", &number, str1, str2) 从字符串 data 中读取一个整数 12345,并将其存储在变量 number 中;然后读取两个字符串 "Hello""World",分别存储在 str1str2 中。
示例 2:从字符串中读取带格式的日期
#include <stdio.h>

int main() {
    const char *date = "2023-09-30";
    int year, month, day;

    // 使用 sscanf 解析日期字符串
    sscanf(date, "%4d-%2d-%2d", &year, &month, &day);

    printf("Year: %d, Month: %d, Day: %d\n", year, month, day);

    return 0;
}
输出:
Year: 2023, Month: 9, Day: 30

解释:

  • sscanf(date, "%4d-%2d-%2d", &year, &month, &day):从字符串 date 中提取出一个年份(4 位数)、一个月份(2 位数)和一个日期(2 位数),并将它们分别存储在 yearmonthday 中。
示例 3:从字符串中读取多个格式化的整数
#include <stdio.h>

int main() {
    const char *time_str = "12:34:56";
    int hours, minutes, seconds;

    // 使用 sscanf 解析时间字符串
    sscanf(time_str, "%2d:%2d:%2d", &hours, &minutes, &seconds);

    printf("Hours: %d, Minutes: %d, Seconds: %d\n", hours, minutes, seconds);

    return 0;
}
输出:
Hours: 12, Minutes: 34, Seconds: 56

解释:

  • sscanf(time_str, "%2d:%2d:%2d", &hours, &minutes, &seconds):从 time_str 中提取小时、分钟和秒数,并分别存储在 hoursminutesseconds 变量中。
示例 4:处理字符串中的多个变量
#include <stdio.h>

int main() {
    const char *data = "123 45.67 ABC";
    int num;
    float f;
    char str[10];

    // 解析整数、浮点数和字符串
    sscanf(data, "%d %f %s", &num, &f, str);

    printf("Number: %d, Float: %.2f, String: %s\n", num, f, str);

    return 0;
}
输出:
Number: 123, Float: 45.67, String: ABC

解释:

  • sscanf(data, "%d %f %s", &num, &f, str):从字符串 data 中解析出一个整数 123、一个浮点数 45.67 和一个字符串 "ABC",分别存储在 numfstr 中。
常见注意事项
  1. 字符串格式必须匹配sscanf 读取的格式必须与输入字符串严格匹配,否则会导致数据读取失败或不正确的行为。

    • 例如,如果你使用 %d-%d-%d 读取字符串 "2023/09/30",会失败,因为 /- 不匹配。
  2. 读取的变量必须是指针sscanf 函数需要将数据写入传入的变量,因此这些变量必须通过指针传递(例如 &number,而不是 number)。

  3. 返回值sscanf 函数返回成功读取的字段数。检查返回值是确保正确读取数据的一个好办法。

    • 例如:如果期望从字符串中读取三个值,但 sscanf 返回的值不是 3,则表示数据读取不完整或有误。

将字符串转换为时间戳相关逻辑函数

首先,时间戳通过 convert_to_timestamp 函数将格式化的时间字符串(例如 YYYYMMDDHHMM)转换为 time_t 类型的时间戳,以便后续计算。time_t 是 C 语言中用于表示时间点的标准类型。

相关代码:
time_t convert_to_timestamp(const char *time_str)
{
    struct tm t;
    memset(&t, 0, sizeof(struct tm));
    sscanf(time_str, "%4d%2d%2d%2d%2d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min);
    t.tm_year -= 1900; // 年份是从1900年开始计算的
    t.tm_mon -= 1;     // 月份是0-11的范围
    return mktime(&t); // 将 tm 结构转换为 time_t 类型
}

作用

  • 解析格式化的时间字符串并存储为 struct tm 结构。
  • 通过 mktime 函数将 struct tm 转换为 time_t 类型的时间戳。
  • 时间戳表示车辆进入或离开停车场的时间点,后续计算中将使用这个时间戳进行时间差的计算。
计算停车时长

当车辆离开停车场时,停车时长是通过计算车辆离开时间的时间戳与进入时间的时间戳之间的差异来得出的。这是通过 calculate_parking_duration 函数实现的。

相关代码:
uint32_t calculate_parking_duration(time_t entry_time, time_t exit_time)
{
    double duration_seconds = difftime(exit_time, entry_time);
    uint32_t duration_hours = (uint32_t)((duration_seconds + 3599) / 3600); // 四舍五入
    return duration_hours;
}

作用

  • 计算 entry_timeexit_time 之间的时间差(秒数),并将其转换为小时数。
  • 使用 difftime 函数计算秒数差异,再通过除以 3600 将秒数转换为小时。
  • 这个停车时长(小时)用于后续的停车费用计算。
格式化时间

为了让时间更具可读性,代码中还提供了 format_time 函数,该函数将 time_t 类型的时间戳转换为易读的日期和时间格式,并输出给用户。

相关代码:
void format_time(time_t time, char *formatted_time)
{
    struct tm *tm_info = localtime(&time); // 将 time_t 转换为本地时间的 struct tm
    strftime(formatted_time, 100, "%Y年%m月%d日%H小时%M分钟", tm_info);
}

作用

  • 通过 localtime 函数将 time_t 转换为本地时间的 struct tm 结构。

  • 通过 strftime 将其格式化为类似 "2023年09月30日12小时30分钟" 的可读字符串。

利用指针获得数组索引:
  1. 计算要删除的记录的索引:
int index = record - vehicle_records;
  • record 是指向 vehicle_records 数组中某个元素的指针。
  • vehicle_records 是一个数组的起始地址。
  • record - vehicle_records 是一种指针运算,用于计算 record 在数组中的位置,实际上是计算 recordvehicle_records 之间的偏移量
    • 例如,如果 record 指向 vehicle_records 数组的第 2 个元素,那么 index 就会等于 1(因为数组索引是从 0 开始的)。
    • 这相当于确定数组中要删除的记录的索引 index
  1. 将数组中后面的元素向前移动:
for (int i = index; i < vehicle_count - 1; i++)
{
    vehicle_records[i] = vehicle_records[i + 1];
}
  • 这个 for 循环的作用是将 vehicle_records 数组中,从 index 位置开始的所有后续元素向前移动一位
  • i = index:从要删除的记录开始。
  • i < vehicle_count - 1:直到数组的最后一个有效记录。vehicle_count - 1 是当前最后一辆车的索引,因为 vehicle_count 是车辆总数。
  • vehicle_records[i] = vehicle_records[i + 1]:将下一个元素 vehicle_records[i + 1] 移动到当前索引 i,从而覆盖被删除的元素。
  • 这个过程实现了删除记录后,数组元素的紧凑排列。
  1. 减少车辆记录数量:
vehicle_count--;
  • vehicle_count--:将车辆数量减 1,表示停车场中少了一辆车,数组中有效的车辆记录数减少。
总结:
  • 这段代码的目的是在 vehicle_records 数组中删除一条记录,删除后通过将后续记录向前移动来保持数组的紧凑性。
  • 删除操作的关键步骤是通过指针运算确定要删除的元素的索引,并通过循环将数组中该元素之后的每个元素向前移动一位,最后更新数组中车辆记录的总数量。
假设的例子:

如果 vehicle_records 数组中有 5 辆车的记录,数组元素如下:

索引记录(车辆 ID)
0ABCD
1EFGH
2IJKL
3MNOP
4QRST

假设我们要删除 EFGH 的记录(record 指向 vehicle_records[1]),那么删除的步骤是:

  1. index = record - vehicle_records 得到 index = 1
  2. 将数组中 index = 1 之后的所有元素向前移动:
    • vehicle_records[1] = vehicle_records[2] -> EFGHIJKL 覆盖。
    • vehicle_records[2] = vehicle_records[3] -> IJKLMNOP 覆盖。
    • vehicle_records[3] = vehicle_records[4] -> MNOPQRST 覆盖。
  3. 数组最后一位 (vehicle_records[4]) 现在可能仍然保存着 QRST,但是由于 vehicle_count 减少了 1,后续不会再访问到这条记录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值