作为一门接近硬件底层的编程语言,C语言对字符串和结构体的处理方式与其他高级语言有着显著区别。本文将深入解析strtok字符串切割、sprintf/sscanf格式化处理以及结构体的核心用法,这些都是C语言开发中的必备技能。
strtok是C标准库中最常用的字符串切割函数,其函数原型为:
c复制char *strtok(char *str, const char *delim);
这个函数的工作原理是通过修改原始字符串,将分隔符替换为'\0'字符来实现切割。使用时需要注意:
重要提示:strtok会直接修改原始字符串内容,因此不能用于字符串常量。如果需要对常量字符串进行切割,需要先复制到可修改的内存中。
下面是一个完整的strtok使用示例,展示了如何将包含多个分隔符的字符串切割并存储:
c复制#include <stdio.h>
#include <string.h>
void tokenize_string() {
char text[] = "name:age:city:occupation";
char *tokens[10] = {NULL}; // 存储切割结果
int i = 0;
// 第一次切割
tokens[i] = strtok(text, ":");
// 后续切割
while(tokens[i] != NULL) {
i++;
tokens[i] = strtok(NULL, ":");
}
// 输出结果
for(int j=0; j<i; j++) {
printf("Token %d: %s\n", j, tokens[j]);
}
}
int main() {
tokenize_string();
return 0;
}
这段代码的输出将是:
code复制Token 0: name
Token 1: age
Token 2: city
Token 3: occupation
实际开发中,建议封装自己的字符串切割函数,避免直接使用strtok带来的副作用。
sprintf函数原型:
c复制int sprintf(char *str, const char *format, ...);
它可以将各种类型的数据格式化为字符串,存储到指定的缓冲区中。常见用途包括:
c复制char buffer[100];
int year = 2023, month = 12, day = 31;
int len = sprintf(buffer, "%d-%02d-%02d", year, month, day);
// buffer内容为"2023-12-31",len为10
sscanf函数原型:
c复制int sscanf(const char *str, const char *format, ...);
它可以从字符串中按照指定格式提取数据,是字符串解析的利器。
c复制char date[] = "2023-12-31";
int y, m, d;
sscanf(date, "%d-%d-%d", &y, &m, &d);
// y=2023, m=12, d=31
c复制sscanf("123 abc 456", "%*d %s %d", str, &num); // 跳过第一个数字
c复制char str[10];
sscanf("1234567890", "%5s", str); // 只取前5个字符
c复制char letters[20];
sscanf("abc123def", "%[a-z]", letters); // letters="abc"
解析电子邮件地址:
c复制char email[] = "user@example.com";
char name[50], domain[50];
sscanf(email, "%[^@]@%[^.]", name, domain);
// name="user", domain="example"
const修饰的变量成为只读变量,必须在定义时初始化:
c复制const int MAX_SIZE = 100;
// MAX_SIZE = 200; // 编译错误
const与指针结合使用时,位置不同含义不同:
c复制int a = 10;
const int *p = &a;
// *p = 20; // 错误:不能通过p修改a的值
a = 20; // 合法:可以直接修改a
p = &b; // 合法:可以改变p的指向
c复制int a = 10, b = 20;
int * const p = &a;
*p = 30; // 合法:可以修改指向的值
// p = &b; // 错误:不能改变p的指向
c复制const int * const p = &a;
// *p = 30; // 错误
// p = &b; // 错误
实际开发中,合理使用const可以提高代码安全性和可读性,特别是在函数参数传递时。
结构体定义语法:
c复制struct 结构体名 {
类型 成员1;
类型 成员2;
// ...
};
示例:
c复制struct Student {
int id;
char name[20];
float score;
};
内存布局特点:
c复制struct Student s1 = {1001, "张三", 90.5};
c复制struct Student s2 = {.name="李四", .id=1002};
c复制struct Student s3;
s3.id = 1003;
strcpy(s3.name, "王五");
s3.score = 85.0;
结构体变量可以直接相互赋值(浅拷贝):
c复制struct Student s4 = s1; // 复制s1的所有成员值到s4
对于包含指针成员的结构体,需要注意深拷贝问题。
定义和初始化结构体数组:
c复制struct Student class[3] = {
{1001, "张三", 90.5},
{1002, "李四", 85.0},
{1003, "王五", 92.0}
};
遍历结构体数组:
c复制for(int i=0; i<3; i++) {
printf("学号:%d,姓名:%s,成绩:%.1f\n",
class[i].id, class[i].name, class[i].score);
}
c复制typedef struct {
int x;
int y;
} Point;
Point p1; // 无需写struct关键字
c复制struct Date {
int year, month, day;
};
struct Employee {
int id;
char name[20];
struct Date hire_date;
};
c复制void printStudent(struct Student s) {
// 传值调用,会复制整个结构体
}
void modifyStudent(struct Student *ps) {
// 传指针,可以修改原结构体
}
下面是一个完整的学生信息管理示例,综合运用了字符串处理和结构体:
c复制#include <stdio.h>
#include <string.h>
#define MAX_STUDENTS 50
typedef struct {
int id;
char name[20];
float score;
} Student;
void inputStudents(Student arr[], int n) {
for(int i=0; i<n; i++) {
printf("输入第%d个学生信息(学号 姓名 成绩): ", i+1);
scanf("%d %s %f", &arr[i].id, arr[i].name, &arr[i].score);
}
}
void printStudents(Student arr[], int n) {
printf("\n学生列表:\n");
printf("学号\t姓名\t成绩\n");
for(int i=0; i<n; i++) {
printf("%d\t%s\t%.1f\n", arr[i].id, arr[i].name, arr[i].score);
}
}
float averageScore(Student arr[], int n) {
float sum = 0;
for(int i=0; i<n; i++) {
sum += arr[i].score;
}
return sum / n;
}
int main() {
Student class[MAX_STUDENTS];
int count;
printf("输入学生人数:");
scanf("%d", &count);
inputStudents(class, count);
printStudents(class, count);
float avg = averageScore(class, count);
printf("\n平均成绩:%.2f\n", avg);
return 0;
}
这个示例展示了:
问题:多次调用strtok时出现异常
原因:没有正确处理NULL参数或跨线程调用
解决:
问题:sscanf返回值与预期不符
原因:格式字符串与实际输入不匹配
解决:
c复制if(sscanf(input, "%d", &num) != 1) {
printf("输入格式错误\n");
}
问题:结构体大小与预期不符
原因:编译器进行了内存对齐优化
解决:
问题:结构体赋值后指针成员指向同一内存
解决:
字符串处理:
结构体使用:
内存管理:
高级数据结构:
文件IO与序列化:
面向对象思想:
嵌入式开发:
掌握这些C语言核心概念和技术,将为你的系统级编程和嵌入式开发打下坚实基础。在实际项目中,建议结合具体需求选择最合适的实现方式,并注重代码的可维护性和安全性。