【C语言学习初篇】

C语言学习初篇

  • C语言篇
    • 1.结构体基础知识
    • 2.c语言内存模型
    • 3.C基本数据类型
    • 4.C语言中静态局部变量和局部变量的区别是什么
    • 5.C语言关于字符串的处理函数
      • 5.1 字符串赋值 strcpy
      • 5.2 字符串比较 strcmp
      • 5.3 字符串反转 strrev
      • 5.4 字符串初始化 memset
      • 5.5 字符串拼接 strcat
      • 5.6 字符串截取 strncpy
      • 5.7 计算字符串长度 strlen函数
      • 5.8 查找指定字符 strchr函数
      • 5.9 查找子串 strstr函数
      • 5.10 格式化数据写入 sprintf函数
    • 6.C语言中野指针,空指针和悬浮指针的区别和示例
      • 野指针
      • 空指针
      • 悬浮指针
    • 7.日志设计
      • 定义日志级别
      • 定义日志格式
      • 实现日志输出函数
      • 配置日志级别
      • 实现日志轮换

C语言篇

知之愈明,行之愈笃

1.结构体基础知识

a.认识结构体

结构体是一些值的集合,这些值称为成员变量.结构体的每个成员可以是不同类型的变量.

//声明一个结构体类型
//声明一个学生类型(Stu就是你对这个结构体的称呼)
struct Stu
{
	char name[20];
	char tele[12];  //成员变量
	char sex[10];
	int age;
}s4,s5,s6;//全局变量
struct Stu s3;//全局变量
int main()
{
	//创建结构体变量(局部的)
	struct Stu s1;
	struct Stu s2; 
}

b.结构体自引用

//结构体的自引用
typedef struct Node
{
	int data;
	//struct Node n; sizeof(struct Node)无限包含下去错误
	struct Node* next;//找到同类型的其它变量
}Node;//你只用关心这里就好了,我们给结构体创建了另一个名字

c.结构体嵌套使用

struct T
{
	double weight;
	int  age;
};
struct S
{
	char c;
	int a;
	double d;
	char arr[20];
	struct T st;//嵌套了上一个结构体
};
 
int main()
{
	struct S s = { 'c',100,3.14,"hello disposable",{55.6,20} };
	printf("%c %d %lf %s %lf %d\n", s.c, s.a, s.d, s.arr,s.st.weight,s.st.age);
}

2.c语言内存模型

C语言的内存模型是基于物理内存的抽象模型,它将物理内存划分为不同的区域,每个区域有不同的作用和特点。C语言的内存模型主要包括以下几个部分:

代码区:存放程序的指令代码,通常是只读的。
数据区:存放程序中已经初始化的全局变量和静态变量。
BSS段:存放未初始化的全局变量和静态变量,通常被初始化为0。
堆区:动态分配内存的区域,由程序员手动管理。
栈区:存放函数的局部变量和函数调用的上下文信息,由编译器自动管理。
在C语言中,变量的存储位置和生命周期由其作用域和存储类别决定。作用域指的是变量在程序中的可见范围,存储类别指的是变量在内存中的存储方式和生命周期。C语言中的存储类别包括自动存储类别、静态存储类别、寄存器存储类别和外部存储类别。

自动存储类别的变量通常存储在栈区,它们的生命周期与函数调用的生命周期相同。静态存储类别的变量通常存储在数据区或BSS段,它们的生命周期与程序的运行周期相同。寄存器存储类别的变量通常存储在CPU的寄存器中,它们的生命周期与函数调用的生命周期相同。外部存储类别的变量通常存储在数据区或BSS段,它们的生命周期与程序的运行周期相同,但它们可以被其他文件访问。

当我们定义一个变量时,它的存储位置和生命周期由其作用域和存储类别决定。下面是一些不同类型变量存储在不同区域的例子:

全局变量:全局变量通常存储在数据区或BSS段中,它们的生命周期与程序的运行周期相同。例如:

int global_var = 10; // 存储在数据区
static int static_global_var = 20; // 存储在BSS段

局部变量:局部变量通常存储在栈区中,它们的生命周期与函数调用的生命周期相同。例如:

void func() {
    int local_var = 30; // 存储在栈区
    static int static_local_var = 40; // 存储在数据区
}

动态分配内存:动态分配内存通常存储在堆区中,由程序员手动管理。例如:

int* ptr = (int*)malloc(sizeof(int)); // 存储在堆区

字符串常量:字符串常量通常存储在代码区中,它们是只读的。例如:

char* str = "Hello, world!"; // 存储在代码区

需要注意的是,这些变量的存储位置和生命周期可能会因为编译器、操作系统和硬件平台的不同而有所差异。

3.C基本数据类型

C语言中基本变量的类型及字节长度如下:

类型	字节长度
char	1
short	2
int	4
long	48
float	4
double	8

下面给出数组、字符数组、链表、队列、栈、图的数据结构用C语言定义的示例:


数组
数组是一种线性数据结构,它由一组相同类型的元素组成,这些元素在内存中是连续存储的。数组的定义方式如下:

// 定义一个包含5个int类型元素的数组
int arr[5];
字符数组
字符数组是一种特殊的数组,它的元素类型为char,用于存储字符串。字符数组的定义方式如下:

// 定义一个包含10个字符的字符数组
char str[10];
链表
链表是一种非线性数据结构,它由一组节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。链表的定义方式如下:

// 定义一个包含int类型数据的链表节点
struct ListNode {
    int val;
    struct ListNode *next;
};
队列
队列是一种先进先出(FIFO)的线性数据结构,它由一组元素组成,支持在队尾插入元素,在队头删除元素。队列的定义方式如下:

// 定义一个包含int类型数据的队列
struct Queue {
    int *data;
    int front;
    int rear;
    int size;
};
栈
栈是一种后进先出(LIFO)的线性数据结构,它由一组元素组成,支持在栈顶插入元素,在栈顶删除元素。栈的定义方式如下:

// 定义一个包含int类型数据的栈
struct Stack {
    int *data;
    int top;
    int size;
};
图
图是一种非线性数据结构,它由一组节点和一组边组成,每个节点包含一个数据元素和一组指向其他节点的边。图的定义方式如下:

// 定义一个包含int类型数据的图节点
struct GraphNode {
    int val;
    struct GraphNode **neighbors;
    int neighborsSize;
};

4.C语言中静态局部变量和局部变量的区别是什么

C语言中,静态局部变量和普通局部变量的区别主要有以下几点:

存储位置不同
静态局部变量存储在静态数据区,而普通局部变量存储在栈中。静态数据区是程序在编译时就分配好的一块内存,它在程序运行期间一直存在,直到程序结束才被释放。而栈是程序在运行时动态分配的一块内存,它的生命周期与函数调用的生命周期相同。

生命周期不同
静态局部变量的生命周期与程序的生命周期相同,即在程序运行期间一直存在,直到程序结束才被释放。而普通局部变量的生命周期与函数调用的生命周期相同,即在函数调用结束时被释放。

初值不同
静态局部变量在第一次使用之前会被自动初始化为0或者NULL,而普通局部变量不会被自动初始化,它们的初值是未定义的。

可见性不同
静态局部变量的作用域仅限于定义它的函数内部,但是它的值在函数调用结束后仍然保持不变,下次调用该函数时仍然可以使用。而普通局部变量的作用域也仅限于定义它的函数内部,但是它的值在函数调用结束后就被销毁了,下次调用该函数时需要重新初始化。

需要注意的是,静态局部变量和普通局部变量的使用场景不同。静态局部变量适用于需要在函数调用之间保持状态的情况,而普通局部变量适用于临时存储数据的情况。在使用静态局部变量时,需要注意它的生命周期和可见性,避免出现意外的错误。

5.C语言关于字符串的处理函数

C语言中关于字符串赋值、比较、反转、初始化、拼接、截取的函数如下:

5.1 字符串赋值 strcpy

字符串赋值可以使用strcpy函数,它的原型如下:

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

其中,dest是目标字符串的指针,src是源字符串的指针。该函数将源字符串复制到目标字符串中,并返回目标字符串的指针。

5.2 字符串比较 strcmp

字符串比较可以使用strcmp函数,它的原型如下:

int strcmp(const char *s1, const char *s2);

其中,s1和s2是要比较的两个字符串的指针。该函数比较两个字符串的大小,如果s1小于s2,则返回一个负数;如果s1等于s2,则返回0;如果s1大于s2,则返回一个正数。

5.3 字符串反转 strrev

字符串反转可以使用strrev函数,它的原型如下:

char *strrev(char *str);

其中,str是要反转的字符串的指针。该函数将字符串中的字符顺序反转,并返回反转后的字符串的指针。

5.4 字符串初始化 memset

字符串初始化可以使用memset函数,它的原型如下:

void *memset(void *s, int c, size_t n);

其中,s是要初始化的字符串的指针,c是要初始化的字符,n是要初始化的字符数。该函数将字符串中的每个字符都设置为指定的字符。

5.5 字符串拼接 strcat

字符串拼接可以使用strcat函数,它的原型如下:

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

其中,dest是目标字符串的指针,src是要拼接的字符串的指针。该函数将源字符串拼接到目标字符串的末尾,并返回目标字符串的指针。

5.6 字符串截取 strncpy

字符串截取可以使用strncpy函数,它的原型如下:

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

其中,dest是目标字符串的指针,src是源字符串的指针,n是要截取的字符数。该函数将源字符串中的前n个字符复制到目标字符串中,并返回目标字符串的指针。

需要注意的是,上述函数的使用需要注意字符串的长度和内存分配,避免出现缓冲区溢出等问题。在使用字符串函数时,需要仔细阅读函数的文档,了解函数的使用方法和注意事项。

5.7 计算字符串长度 strlen函数

strlen函数用于计算字符串的长度,它的原型如下:

size_t strlen(const char *s);

其中,s是要计算长度的字符串的指针。该函数返回字符串的长度,不包括字符串末尾的空字符。

示例:

#include 
#include 

int main() {
    char str[] = "hello world";
    size_t len = strlen(str);
    printf("字符串的长度为:%zu\n", len);
    return 0;
}
//输出结果:
//字符串的长度为:11

5.8 查找指定字符 strchr函数

strchr函数用于在字符串中查找指定字符的位置,它的原型如下:

char *strchr(const char *s, int c);

其中,s是要查找的字符串的指针,c是要查找的字符。该函数返回指向第一个匹配字符的指针,如果未找到匹配字符,则返回NULL。

示例:

#include 
#include 

int main() {
    char str[] = "hello world";
    char *p = strchr(str, 'o');
    if (p != NULL) {
        printf("找到了字符'o',位置为:%ld\n", p - str);
    } else {
        printf("未找到字符'o'\n");
    }
    return 0;
}
输出结果:

找到了字符'o',位置为:4

5.9 查找子串 strstr函数

strstr函数用于在字符串中查找指定子串的位置,它的原型如下:

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

其中,haystack是要查找的字符串的指针,needle是要查找的子串的指针。该函数返回指向第一个匹配子串的指针,如果未找到匹配子串,则返回NULL。

示例:

#include 
#include 

int main() {
    char str[] = "hello world";
    char *p = strstr(str, "world");
    if (p != NULL) {
        printf("找到了子串'world',位置为:%ld\n", p - str);
    } else {
        printf("未找到子串'world'\n");
    }
    return 0;
}
输出结果:

找到了子串'world',位置为:6

5.10 格式化数据写入 sprintf函数

sprintf函数用于将格式化的数据写入字符串中,它的原型如下:

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

其中,str是要写入的字符串的指针,format是格式化字符串,…是可变参数列表。该函数将格式化的数据写入字符串中,并返回写入的字符数。

示例:

#include 
#include 

int main() {
    char str[100];
    int num = 123;
    float f = 3.14;
    sprintf(str, "num=%d, f=%.2f", num, f);
    printf("%s\n", str);
    return 0;
}
输出结果:
num=123, f=3.14

需要注意的是,在使用sprintf函数时,需要确保目标字符串的长度足够大,以避免缓冲区溢出等问题。

以上字符串处理函数的头文件为

C语言中,动态申请内存、释放内存、保证内存安全的函数如下:

动态申请内存
动态申请内存可以使用malloc函数,它的原型如下:

void *malloc(size_t size);
其中,size是要申请的内存大小,单位是字节。该函数返回一个指向申请到的内存块的指针,如果申请失败,则返回NULL

示例:

#include 
#include 

int main() {
    int *p = (int *)malloc(sizeof(int));
    if (p != NULL) {
        *p = 123;
        printf("申请到的内存地址为:%p,值为:%d\n", p, *p);
        free(p);
    } else {
        printf("申请内存失败\n");
    }
    return 0;
}
输出结果:

申请到的内存地址为:0x7f8c7ac02a40,值为:123

释放内存
释放内存可以使用free函数,它的原型如下:

void free(void *ptr);

其中,ptr是要释放的内存块的指针。该函数将指定的内存块释放回系统,以便其他程序可以使用。

示例:

#include 
#include 

int main() {
    int *p = (int *)malloc(sizeof(int));
    if (p != NULL) {
        *p = 123;
        printf("申请到的内存地址为:%p,值为:%d\n", p, *p);
        free(p);
        printf("内存已释放\n");
    } else {
        printf("申请内存失败\n");
    }
    return 0;
}
输出结果:

申请到的内存地址为:0x7f8c7ac02a40,值为:123
内存已释放

需要注意的是,在使用free函数释放内存时,需要确保指定的内存块是通过malloc函数申请的,否则可能会导致程序崩溃或者内存泄漏等问题。

保证内存安全
为了保证内存安全,C语言提供了一些函数,可以检查内存访问是否越界,避免出现缓冲区溢出等问题。常用的内存安全函数如下:

memset函数:用于将一段内存块清零,可以避免出现敏感信息泄露的问题。
memcpy函数:用于将一段内存块复制到另一段内存块中,可以避免出现内存访问越界的问题。
memmove函数:与memcpy函数类似,但是可以处理内存块重叠的情况。
calloc函数:与malloc函数类似,但是会将申请到的内存块清零,可以避免出现敏感信息泄露的问题。

当然,下面给出一些常用的C语言保证内存安全函数的用法示例:

memset函数
memset函数用于将一段内存块清零,它的原型如下:

void *memset(void *s, int c, size_t n);
其中,s是要清零的内存块的指针,c是要设置的值,n是要清零的字节数。该函数将指定的内存块中的每个字节都设置为指定的值。

示例:

#include 
#include 

int main() {
    char str[100] = "hello world";
    printf("清零前的字符串为:%s\n", str);
    memset(str, 0, sizeof(str));
    printf("清零后的字符串为:%s\n", str);
    return 0;
}
输出结果:

清零前的字符串为:hello world
清零后的字符串为:
memcpy函数
memcpy函数用于将一段内存块复制到另一段内存块中,它的原型如下:

void *memcpy(void *dest, const void *src, size_t n);
其中,dest是目标内存块的指针,src是源内存块的指针,n是要复制的字节数。该函数将源内存块中的内容复制到目标内存块中。

示例:

#include 
#include 

int main() {
    char src[100] = "hello world";
    char dest[100];
    printf("复制前的字符串为:%s\n", src);
    memcpy(dest, src, sizeof(src));
    printf("复制后的字符串为:%s\n", dest);
    return 0;
}
输出结果:

复制前的字符串为:hello world
复制后的字符串为:hello world
memmove函数
memmove函数与memcpy函数类似,但是可以处理内存块重叠的情况,它的原型如下:

void *memmove(void *dest, const void *src, size_t n);
其中,dest是目标内存块的指针,src是源内存块的指针,n是要复制的字节数。该函数将源内存块中的内容复制到目标内存块中,可以处理内存块重叠的情况。

示例:

#include 
#include 

int main() {
    char str[100] = "hello world";
    printf("反转前的字符串为:%s\n", str);
    memmove(str + 3, str, 5);
    printf("反转后的字符串为:%s\n", str);
    return 0;
}
输出结果:

反转前的字符串为:hello world
反转后的字符串为:helhelrld
calloc函数
calloc函数与malloc函数类似,但是会将申请到的内存块清零,它的原型如下:

void *calloc(size_t nmemb, size_t size);
其中,nmemb是要申请的内存块数量,size是每个内存块的大小,单位是字节。该函数返回一个指向申请到的内存块的指针,如果申请失败,则返回NULL。

示例:

#include 
#include 

int main() {
    int *p = (int *)calloc(5, sizeof(int));
    if (p != NULL) {
        for (int i = 0; i < 5; i++) {
            printf("p[%d]=%d\n", i, p[i]);
        }
        free(p);
    } else {
        printf("申请内存失败\n");
    }
    return 0;
}
输出结果:

p[0]=0
p[1]=0
p[2]=0
p[3]=0
p[4]=0

6.C语言中野指针,空指针和悬浮指针的区别和示例

C语言中,野指针、空指针和悬浮指针是指针的一些特殊情况,它们的区别和示例如下:

野指针

野指针是指没有被初始化或者已经被释放的指针,它指向的内存地址是不确定的,可能是一个随机的值,也可能是一个已经被释放的内存地址。使用野指针会导致程序崩溃或者出现其他不可预测的错误。

示例:

#include 

int main() {
    int *p;
    printf("野指针的值为:%p\n", p);
    *p = 123;
    return 0;
}
输出结果:

野指针的值为:0x7f8c7ac02a40
Segmentation fault (core dumped)

在上面的示例中,指针p没有被初始化,它指向的内存地址是不确定的,因此在对它进行解引用操作时,会导致程序崩溃。

空指针

空指针是指没有指向任何内存地址的指针,它的值为NULL。空指针可以用来表示指针不指向任何有效的内存地址,可以避免野指针的问题。

示例:

#include 

int main() {
    int *p = NULL;
    if (p != NULL) {
        *p = 123;
    } else {
        printf("指针为空\n");
    }
    return 0;
}
输出结果:

指针为空

在上面的示例中,指针p被初始化为NULL,它不指向任何有效的内存地址,因此在对它进行解引用操作时,不会导致程序崩溃。

悬浮指针

悬浮指针是指指向已经被释放的内存地址的指针,它指向的内存地址已经不再属于当前程序的内存空间,使用悬浮指针会导致程序崩溃或者出现其他不可预测的错误。

示例:

#include 
#include 

int main() {
    int *p = (int *)malloc(sizeof(int));
    if (p != NULL) {
        *p = 123;
        printf("申请到的内存地址为:%p,值为:%d\n", p, *p);
        free(p);
        printf("内存已释放\n");
        printf("悬浮指针的值为:%p\n", p);
        *p = 456;
    } else {
        printf("申请内存失败\n");
    }
    return 0;
}
输出结果:

申请到的内存地址为:0x7f8c7ac02a40,值为:123
内存已释放
悬浮指针的值为:0x7f8c7ac02a40
Segmentation fault (core dumped)

在上面的示例中,指针p被初始化为申请到的内存地址,然后又被释放了。在释放后,指针p变成了悬浮指针,它指向的内存地址已经不再属于当前程序的内存空间,因此在对它进行解引用操作时,会导致程序崩溃。

7.日志设计

在大型C语言项目开发中,设计调试debug日志表框架是非常重要的,可以帮助开发人员快速定位和解决问题。下面是一些设计调试debug日志表框架的建议:

定义日志级别

在设计调试debug日志表框架时,需要定义不同的日志级别,以便开发人员可以根据需要选择输出哪些日志信息。常见的日志级别包括:

  • DEBUG:用于输出调试信息,通常只在开发阶段使用。
  • INFO:用于输出一般信息,例如程序启动、配置文件加载等。
  • WARNING:用于输出警告信息,例如文件读写失败、网络连接断开等。
  • ERROR:用于输出错误信息,例如内存分配失败、函数调用失败等。
  • FATAL:用于输出致命错误信息,例如程序崩溃、系统错误等。

定义日志格式

在设计调试debug日志表框架时,需要定义日志的格式,以便开发人员可以快速定位和解决问题。常见的日志格式包括:

  • 时间戳:记录日志的时间,以便开发人员可以知道问题发生的时间。
  • 日志级别:记录日志的级别,以便开发人员可以根据需要选择输出哪些日志信息。
  • 文件名和行号:记录日志所在的文件名和行号,以便开发人员可以快速定位问题。
  • 日志内容:记录日志的具体内容,例如函数调用参数、返回值等。

实现日志输出函数

在设计调试debug日志表框架时,需要实现日志输出函数,以便开发人员可以调用该函数输出日志信息。常见的日志输出函数包括:

  • printf函数:可以使用printf函数输出日志信息,但是需要手动添加时间戳、日志级别、文件名和行号等信息。
  • syslog函数:可以使用syslog函数输出日志信息,它可以自动添加时间戳、日志级别等信息,但是需要在系统中配置syslog服务。

自定义函数:可以根据项目的需要自定义日志输出函数,例如使用第三方日志库、使用自定义的日志格式等。

配置日志级别

在设计调试debug日志表框架时,需要提供配置日志级别的接口,以便开发人员可以根据需要选择输出哪些日志信息。常见的配置日志级别的接口包括:

命令行参数:可以在程序启动时通过命令行参数指定日志级别。
配置文件:可以在配置文件中指定日志级别,例如使用INI文件、XML文件等。
环境变量:可以在系统中设置环境变量,以便指定日志级别。

实现日志轮换

在设计调试debug日志表框架时,需要考虑日志文件的大小和数量,以免占用过多的磁盘空间。常见的日志轮换方式包括:

  • 按文件大小轮换:当日志文件达到一定大小时,自动创建新的日志文件。
  • 按时间轮换:当一定时间内没有写入日志时,自动创建新的日志文件。
  • 压缩日志文件:当日志文件达到一定大小时,自动压缩日志文件,以节省磁盘空间。

总之,在设计调试debug日志表框架时,需要考虑项目的实际需求和开发人员的使用习惯,以便提高开发效率和代码质量

设计一个日志系统时,需要考虑以下几个方面:

日志级别: 定义不同的日志级别,例如 DEBUG、INFO、WARNING、ERROR和FATAL。

日志格式: 定义日志的格式,例如时间戳、日志级别、文件名和行号、日志内容等。

日志输出: 实现日志输出函数,例如使用printf函数输出日志信息。

日志轮换: 实现日志轮换功能,例如按文件大小轮换、按时间轮换、压缩日志文件等。

下面是一个简单的日志系统设计示例,包括日志级别、日志格式、日志输出和日志轮换功能。

#include 
#include 
#include 
#include 

#define LOG_LEVEL_DEBUG     0
#define LOG_LEVEL_INFO      1
#define LOG_LEVEL_WARNING   2
#define LOG_LEVEL_ERROR     3
#define LOG_LEVEL_FATAL     4

#define LOG_FILE_SIZE       (1024 * 1024 * 10)   // 10MB
#define LOG_FILE_COUNT      10

static const char *log_level_str[] = {
    "DEBUG",
    "INFO",
    "WARNING",
    "ERROR",
    "FATAL"
};

static FILE *log_file = NULL;
static int log_level = LOG_LEVEL_DEBUG;
static int log_file_size = LOG_FILE_SIZE;
static int log_file_count = LOG_FILE_COUNT;

static void log_rotate()
{
    char old_file_name[256];
    char new_file_name[256];

    fclose(log_file);

    for (int i = log_file_count - 1; i > 0; i--) {
        snprintf(old_file_name, sizeof(old_file_name), "log.%d", i - 1);
        snprintf(new_file_name, sizeof(new_file_name), "log.%d", i);
        rename(old_file_name, new_file_name);
    }

    snprintf(new_file_name, sizeof(new_file_name), "log.0");
    log_file = fopen(new_file_name, "w");
}

void log_init(int level, const char *file_name)
{
    log_level = level;
    log_file = fopen(file_name, "w");
}

void log_set_level(int level)
{
    log_level = level;
}

void log_set_file_size(int size)
{
    log_file_size = size;
}

void log_set_file_count(int count)
{
    log_file_count = count;
}

void log_write(int level, const char *file, int line, const char *fmt, ...)
{
    if (level < log_level) {
        return;
    }

    time_t now = time(NULL);
    struct tm *tm = localtime(&now);
    char time_str[64];
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm);

    va_list args;
    va_start(args, fmt);
    char buf[1024];
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    fprintf(log_file, "[%s] [%s:%d] %s\n", log_level_str[level], file, line, buf);
    fflush(log_file);

    long file_size = ftell(log_file);
    if (file_size >= log_file_size) {
        log_rotate();
    }
}

#define LOG(level, fmt, ...) \
    log_write(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__)

int main()
{
    log_init(LOG_LEVEL_DEBUG, "log.0");

    LOG(LOG_LEVEL_DEBUG, "debug message");
    LOG(LOG_LEVEL_INFO, "info message");
    LOG(LOG_LEVEL_WARNING, "warning message");
    LOG(LOG_LEVEL_ERROR, "error message");
    LOG(LOG_LEVEL_FATAL, "fatal message");

    return 0;
}

在上面的示例中,定义了日志级别、日志格式、日志输出和日志轮换功能。日志级别使用了宏定义,方便开发人员根据需要选择输出哪些日志信息。日志格式包括时间戳、日志级别、文件名和行号、日志内容等。日志输出使用了log_write函数,它可以根据日志级别输出不同的日志信息。日志轮换使用了log_rotate函数,它可以按文件大小轮换日志文件。

你可能感兴趣的:(c语言,学习,算法)