跳转至

Chapter2 C编程

菜鸟教程 - C 教程

1. 数据类型、变量

  • 变量未初始化初值

    C 语言中变量的默认值取决于其类型和作用域。

    全局变量和静态变量的默认值为 0,

    字符型变量的默认值为 \0,

    指针变量的默认值为 NULL,

    局部变量(在函数内部定义的非静态变量)不会自动初始化为默认值,它们的初始值是未定义的(包含垃圾值)。因此,在使用局部变量之前,应该显式地为其赋予一个初始值。

数据类型转化

隐式类型转换:隐式类型转换是在表达式中自动发生的,无需进行任何明确的指令或函数调用。它通常是将一种较小的类型自动转换为较大的类型,例如,将int类型转换为long类型或float类型转换为double类型。隐式类型转换也可能会导致数据精度丢失或数据截断。

int i = 10;
float f = 3.14;
double d = i + f; // 隐式将int类型转换为double类型

显式类型转换:显式类型转换需要使用强制类型转换运算符(type casting operator),它可以将一个数据类型的值强制转换为另一种数据类型的值。强制类型转换可以使程序员在必要时对数据类型进行更精确的控制,但也可能会导致数据丢失或截断。

double d = 3.14159;
int i = (int)d; // 显式将double类型转换为int类型

2. 运算符

三元运算符

(a>0)?b:c

判断是否表达式a>0是否为真,true取b,false取c

用于替代简单的if-else判断

3. C指针

定义指针

指针解址符—— *

指针取址符—— &

// 函数定义,通过指针修改原始变量的值
void change_value(int *ptr) {
   *ptr = 20;
}

void main() {
   int num = 10;
   // 调用函数,传递变量num的地址
   change_value(&num);
}

结构体指针

typedef struct {
    Elemtype member1;
    Elemtype member2;
    ...
}struct_type;

struct_type* type_ptr;
  • 访问结构体成员

    结构体变量用 . 来访问结构体的成员

    typedef struct {
        Elemtype member1;
        Elemtype member2;
    ...
    }struct_type;
    
    struct_type s1;
    
    s1.member1;
    s1.member2;
    
  • 访问结构体指针的结构体成员

    指向结构体的结构体指针,要用->来访问指向的结构体成员

    typedef struct {
        Elemtype member1;
        Elemtype member2;
    ...
    }struct_type;
    
    struct_type s1;
    struct_type* struct_ptr = s1; 
    
    struct_ptr->var1; 
    struct_ptr->var2;
    
  • 访问结构体指针的结构体成员,该结构体嵌套的内部结构体

    一个指针指向结构体1,而结构体1成员有一个结构体2,通过该指针访问结构体2的成员

    如:结构体1->结构体1内的结构体2->结构体2的成员,第二个->换成.

    typedef struct {
        char member1;
        int member2;
        another_struct member3;
    ...
    }struct_type;
    
    struct_type s1;
    struct_type* struct_ptr = s1; 
    
    struct_ptr->member1; 
    struct_ptr->member3.member;
    

函数指针与回调函数

  • 函数指针:本质上是一个指针,但指向的是一个函数首地址(指针不能偏移),可以用函数指针来传递函数,常用于回调函数中

    C语言:函数指针与指针函数

    // 声明一个指向同样参数、返回值的函数指针类型
    void Function(int num,int (*func)(void)){ 
        num = func(); 
    }
    
  • 回调函数callback:通过函数指针实现的一种回调机制;即当某条件(事件)触发后,执行相对应登记(注册)的动作(函数)。

    你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。

    菜鸟教程 - C 语言回调函数详解

    博客园 - 回调函数实战

// 事件类型枚举
typedef enum { 
    CLICK,
    MIDDLE_CLICK,
    RIGHT_CLICK,
    KEY_PRESS,
    KEY_RELEASE,
    MOUSE_MOVE,
    SCROLL,
}EventType;

// 事件结构体
typedef struct {
    EventType type;
    int x, y;
    char key;
}Event;

// 定义回调函数类型
//EventCallback 函数指针数据类型,Event 函数变量
typedef void (*EventCallback)(const Event*);

// 事件处理器结构体
typedef struct {
    EventCallback callbacks[10];  // 假设最多10种事件类型,对应最多10个处理函数
}EventHandler;

// 注册事件回调
void registerCallback(EventHandler* handler, EventType type, EventCallback callback) {
    handler->callbacks[type] = callback; //函数指针赋值
}

// 事件分发器
void dispatchEvent(EventHandler* handler, const Event* event) {
    if (handler->callbacks[event->type] != NULL) {
        handler->callbacks[event->type](event);
    }
}

// 点击事件处理函数
void onClickCallback(const Event* event) {
    printf("点击事件触发了!坐标: (%d, %d)\n", event->x, event->y);
}
// 按键事件处理函数
void onKeyPressCallback(const Event* event) {
    printf("按键事件触发了!按下的键是: %c\n", event->key);
}

void main() {
    // 创建并初始化事件处理器
    EventHandler handler = {NULL};

    // 注册回调函数
    registerCallback(&handler, CLICK, onClickCallback);

    // 模拟点击事件
    Event clickEvent = {CLICK, 100, 200};

    // 事件触发
    dispatchEvent(&handler, &clickEvent);

}

4. C函数

定义函数

函数由一个函数头和一个函数主体组成。

  • 返回类型return_type:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。

  • 函数名称function_name:这是函数的实际名称。函数名和参数列表一起构成了函数签名。

  • 参数parameter:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。

  • 函数主体body:函数主体包含一组定义函数执行任务的语句。

return_type function_name( parameter list )
{
   body of the function
}

指针函数

指针函数: 本质上是一个函数,函数的返回值是一个指针

//1 (void):这个函数不接收任何参数
//2 (*func_ptr):func_ptr 是一个指针
//3 void:这个函数没有返回值
//4 typedef:给前面这一整串起别名

// 1. 定义类型别名:func_ptr = 无参无返回值函数指针
typedef void (*func_ptr)(void);

// 2. 写一个匹配的函数
void my_func(void) {
    // 无参、无返回值 ✔️
}

// 3. 使用这个类型定义指针变量
int main() {
    func_ptr p;  // 定义一个函数指针变量 p
    p = my_func; // 把函数地址赋值给指针

    p(); // 调用函数 → 等价 my_func()
}

5. C动态内存分配

void *calloc(int num, int size);
//在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。
//所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是 0。
void *malloc(int num);//num通常使用sizeof(var)定大小
//在堆区分配一块指定大小的内存空间,用来存放数据。
//这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
void free(void *address);
//该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
void *realloc(void *address, int newsize);
//该函数重新分配内存,把内存扩展到 newsize。