Skip to content

C语言

C语言是一种通用的、过程式编程语言,广泛应用于系统编程和嵌入式系统开发。以下是C语言的一些基础知识:

1. 基本语法

1.1. Hello, World!

这是一个典型的“Hello, World!”程序,展示了C语言的基本结构:

#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}

1.2. 注释

  • 单行注释:使用 //
  • 多行注释:使用 /* ... */
// 这是单行注释
/*
这是多行注释
*/

2. 数据类型

C语言提供了多种数据类型,包括基本数据类型和用户自定义数据类型。

2.1. 基本数据类型

  • 整数类型:int, short, long, long long
  • 浮点类型:float, double
  • 字符类型:char
  • 布尔类型:_Bool(在 stdbool.h 中定义为 bool
int a = 10;
float b = 3.14;
char c = 'A';

3. 变量和常量

3.1. 变量声明和初始化

int a = 5;
float b = 4.5;
char c = 'x';

3.2. 常量

使用 const 关键字定义常量,值不能被修改。

const int DAYS_IN_WEEK = 7;

4. 运算符

C语言支持多种运算符:

  • 算术运算符:+, -, *, /, %
  • 关系运算符:==, !=, >, <, >=, <=
  • 逻辑运算符:&&, ||, !
  • 位运算符:&, |, ^, ~, <<, >>
  • 赋值运算符:=, +=, -=, *=, /=, %=

5. 控制结构

5.1. 条件语句

if (a > b) {
// 执行代码
} else if (a == b) {
// 执行代码
} else {
// 执行代码
}

5.2. 循环语句

  • for 循环
for (int i = 0; i < 10; i++) {
// 执行代码
}
  • while 循环
int i = 0;
while (i < 10) {
// 执行代码
i++;
}
  • do-while 循环
int i = 0;
do {
// 执行代码
i++;
} while (i < 10);

6. 数组和指针

6.1. 数组

int numbers[5] = {1, 2, 3, 4, 5};

6.2. 指针

int a = 10;
int *p = &a; // p 是指向 a 的指针

7. 函数

7.1. 函数定义和调用

#include <stdio.h>
int add(int x, int y) {
return x + y;
}
int main() {
int sum = add(3, 5);
printf("Sum: %d\n", sum);
return 0;
}

7.2. 函数声明

在函数定义之前进行声明,以便在其他函数中调用。

int add(int, int);
int main() {
int sum = add(3, 5);
return 0;
}
int add(int x, int y) {
return x + y;
}

8. 结构体

结构体用于定义复杂数据类型。

struct Point {
int x;
int y;
};
int main() {
struct Point p;
p.x = 10;
p.y = 20;
return 0;
}

在C语言中,联合体(union)和枚举(enum)是两种非常有用的自定义数据类型,它们各自有不同的用途和特点。

9. 联合体(Union)

联合体允许存储不同类型的数据,但在同一时间只能存储其中的一种类型。它的大小由其中最大的成员决定。

语法

union Data {
int i;
float f;
char str[20];
};

示例

#include <stdio.h>
#include <string.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i : %d\n", data.i);
data.f = 220.5;
printf("data.f : %f\n", data.f);
strcpy(data.str, "C Programming");
printf("data.str : %s\n", data.str);
return 0;
}

在这个例子中,data联合体可以存储一个整数、一个浮点数或者一个字符串,但是在同一时间只能存储其中之一。每次赋值会覆盖之前的值。

10. 枚举(Enum)

枚举是一种用户定义的数据类型,它由一组具名的整型常量组成。这些常量代表了一系列相关的值,通常用于表示状态、选项或标志。

语法

enum Day {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};

示例

#include <stdio.h>
enum Day {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};
int main() {
enum Day today;
today = Wednesday;
printf("Day %d\n", today);
return 0;
}

在这个例子中,Day枚举类型定义了一周的七天,每个枚举常量默认从0开始递增。Wednesday对应的值为3。

联合体和枚举的结合使用

联合体和枚举可以结合使用,例如定义一个可以存储不同类型数据的结构体,并用枚举来指示当前存储的数据类型。

示例
#include <stdio.h>
#include <string.h>
enum DataType {
INT,
FLOAT,
STRING
};
union Data {
int i;
float f;
char str[20];
};
struct Container {
enum DataType type;
union Data data;
};
int main() {
struct Container c;
c.type = INT;
c.data.i = 42;
if (c.type == INT) {
printf("Integer: %d\n", c.data.i);
}
c.type = FLOAT;
c.data.f = 3.14;
if (c.type == FLOAT) {
printf("Float: %f\n", c.data.f);
}
c.type = STRING;
strcpy(c.data.str, "Hello, World!");
if (c.type == STRING) {
printf("String: %s\n", c.data.str);
}
return 0;
}

技巧

实现offsetof

offsetof可以获取字段相对于结构体的偏移

#define offsetof(type, member) ((const unsigned long)&((type*)0)->member)

通过字段指针获取整个结构体的指针

offsetof 宏定义在 <stddef.h>

#include <stdio.h>
#include <stddef.h>
struct Person {
char name[20];
int age;
};
// 通过字段指针获取整个结构体的指针
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
int main() {
struct Person p = {"John", 30};
int *age_ptr = &p.age;
// 使用 container_of 宏获取指向整个结构体的指针
struct Person *p_ptr = container_of(age_ptr, struct Person, age);
// 验证结果
printf("Name: %s, Age: %d\n", p_ptr->name, p_ptr->age);
return 0;
}