在C语言中,函数是程序的基本构建块,它封装了特定功能的代码。一个标准的C函数定义包含以下要素:
c复制返回值类型 函数名(参数列表) {
// 函数体
return 返回值; // 如果返回值类型不是void
}
以加法函数为例:
c复制int add(int a, int b) {
return a + b;
}
注意:C语言是强类型语言,函数声明时必须明确指定参数和返回值的类型。这与Python等动态类型语言有本质区别。
C语言采用值传递(pass by value)方式:
c复制void swap(int a, int b) { // 无效的交换
int temp = a;
a = b;
b = temp;
}
void real_swap(int *a, int *b) { // 有效的交换
int temp = *a;
*a = *b;
*b = temp;
}
C语言函数调用有几个重要特性:
c复制// 错误示例:函数嵌套
void outer() {
void inner() { // 编译错误
// ...
}
}
// 错误示例:函数重载
void print(int a) {...}
void print(double a) {...} // 会与上一个函数冲突
c复制void print1() {
for(int i=0; i<10; i++) {
for(int j=0; j<8; j++) {
printf("*");
}
printf("\n");
}
}
编程技巧:外层循环控制行数,内层循环控制列数。这种嵌套循环结构是处理二维图形的经典模式。
c复制int print2() {
print1(); // 复用已有函数
return 10 * 8; // 返回面积
}
代码优化建议:
改进版:
c复制#define ROWS 10
#define COLS 8
int print2_improved() {
if(ROWS <=0 || COLS <=0) {
printf("Invalid dimensions!\n");
return -1; // 错误码
}
print1();
return ROWS * COLS;
}
c复制int print3(int rows, int cols) {
if(rows <=0 || cols <=0) {
printf("Dimensions must be positive!\n");
return -1;
}
for(int i=0; i<rows; i++) {
for(int j=0; j<cols; j++) {
printf("*");
}
printf("\n");
}
return rows * cols;
}
实际调用示例:
c复制int main() {
int area = print3(5, 12); // 打印5行12列的矩形
printf("Area: %d\n", area);
return 0;
}
c复制double max(double x, double y) {
return (x > y) ? x : y; // 三目运算符简洁实现
}
c复制double triMax(double x, double y, double z) {
double temp = max(x, y); // 函数复用
return max(temp, z);
}
性能考虑:这种实现方式会进行两次比较。也可以直接使用嵌套比较:
c复制double triMax_alt(double x, double y, double z) { return (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z); }后者在某些编译器优化下可能效率略高,但可读性较差。
exit()函数用于立即终止程序执行:
c复制#include <stdlib.h>
void check_positive(int num) {
if(num <= 0) {
printf("Error: Number must be positive!\n");
exit(EXIT_FAILURE); // 相当于return 1;
}
}
int main() {
check_positive(-5); // 程序将在此终止
printf("This won't be printed\n");
return EXIT_SUCCESS; // 相当于return 0;
}
exit()与return的区别:
| 特性 | exit() | return |
|---|---|---|
| 作用范围 | 整个程序 | 当前函数 |
| 清理操作 | 会调用atexit注册的函数 | 不会 |
| 头文件 | 需要<stdlib.h> | 不需要 |
| 返回值 | 返回给操作系统 | 返回给调用者 |
标准三步法:
c复制struct Student {
int id;
char name[20];
int age;
};
int main() {
struct Student stu1;
stu1.id = 1001;
strcpy(stu1.name, "Alice"); // 字符串赋值需要用strcpy
stu1.age = 20;
printf("ID: %d, Name: %s, Age: %d\n",
stu1.id, stu1.name, stu1.age);
return 0;
}
C语言提供了多种初始化方式:
c复制// 方式1:顺序初始化
struct Point {
int x;
int y;
} p1 = {10, 20}; // x=10, y=20
// 方式2:指定成员初始化(C99)
struct Point p2 = {.y = 30, .x = 40};
// 方式3:复合字面量(C99)
struct Point p3 = (struct Point){50, 60};
// 方式4:先声明后整体赋值(C99)
struct Point p4;
p4 = (struct Point){70, 80};
typedef可以简化结构体类型的使用:
c复制// 传统方式
struct Employee {
char *name;
int age;
};
struct Employee emp1;
// 使用typedef
typedef struct {
char *name;
int age;
} Employee;
Employee emp2; // 不再需要struct关键字
高级用法:结构体指针别名
c复制typedef struct Node {
int data;
struct Node *next;
} Node, *PNode; // Node是结构体类型,PNode是指向该结构体的指针类型
PNode create_node(int value) {
PNode new_node = (PNode)malloc(sizeof(Node));
new_node->data = value;
new_node->next = NULL;
return new_node;
}
c复制#define MAX_STUDENTS 50
typedef struct {
int id;
char name[50];
float gpa;
} Student;
void input_students(Student class[], int count) {
for(int i=0; i<count; i++) {
printf("Enter student %d details:\n", i+1);
printf("ID: "); scanf("%d", &class[i].id);
printf("Name: "); scanf("%s", class[i].name);
printf("GPA: "); scanf("%f", &class[i].gpa);
}
}
void print_students(Student class[], int count) {
printf("\nStudent List:\n");
for(int i=0; i<count; i++) {
printf("ID: %d, Name: %s, GPA: %.2f\n",
class[i].id, class[i].name, class[i].gpa);
}
}
结构体作为函数参数和返回值:
c复制typedef struct {
double real;
double imag;
} Complex;
Complex add_complex(Complex a, Complex b) {
Complex result;
result.real = a.real + b.real;
result.imag = a.imag + b.imag;
return result;
}
// 更高效的指针版本
void add_complex_ptr(const Complex *a, const Complex *b, Complex *result) {
result->real = a->real + b->real;
result->imag = a->imag + b->imag;
}
性能提示:大型结构体应通过指针传递,避免值传递时的拷贝开销。
隐式函数声明:
c复制int main() {
printf("%f", sqrt(4.0)); // 警告:隐式声明sqrt
return 0;
}
修正:包含math.h头文件
参数类型不匹配:
c复制void foo(int a) {...}
int main() {
foo(3.14); // 自动截断为3,可能非预期
return 0;
}
字符串赋值错误:
c复制struct Person {
char name[20];
} p;
p.name = "Alice"; // 错误!数组不能直接赋值
strcpy(p.name, "Alice"); // 正确
结构体比较:
c复制struct Point {int x,y;} p1={1,2}, p2={1,2};
if(p1 == p2) {...} // 错误!不能直接比较结构体
// 正确做法:逐个成员比较或使用memcmp
if(p1.x==p2.x && p1.y==p2.y) {...}
忘记结构体大小:
c复制typedef struct {
char a;
int b;
char c;
} BadStruct; // 可能有填充字节,sizeof不是1+4+1=6
使用gdb调试结构体:
bash复制gcc -g program.c -o program
gdb ./program
(gdb) break main
(gdb) run
(gdb) print variable_name
(gdb) print *pointer_name
打印结构体内容:
c复制void print_point(struct Point p) {
printf("Point: (%d, %d)\n", p.x, p.y);
}
使用offsetof宏检查结构体布局:
c复制#include <stddef.h>
printf("name offset: %zu\n", offsetof(struct Student, name));
结构体对齐优化:
c复制// 优化前:可能占用更多空间
struct Inefficient {
char c;
int i;
char d;
}; // 可能占12字节(取决于架构)
// 优化后:相同大小成员放在一起
struct Efficient {
int i;
char c;
char d;
}; // 可能只占8字节
函数调用优化:
static inline缓存友好设计:
c复制// 不好的设计:指针数组导致内存不连续
struct Node **node_array;
// 更好的设计:结构体数组
struct Node *node_array;
在实际工程中,这些基础概念会组合使用形成更复杂的数据结构和算法。理解函数和结构体的本质,是掌握C语言系统编程的关键第一步。