如何根据scanf获取的用户输入访问结构体不同成员?
解决C语言中根据字符串输入访问结构体成员的问题
在C语言中,由于没有反射机制,无法直接通过字符串名称访问结构体成员,但可以通过分支判断或者查找表+指针运算两种方式实现需求。以下是具体实现方案:
方案一:分支判断(简单直接,适合少量成员)
如果结构体成员数量不多,直接通过strcmp判断输入字符串,针对每个成员编写对应的逻辑即可。这种方式代码直观,易于理解。
完整代码示例
#include <stdio.h> #include <string.h> struct class { char name[50]; int score; char grade[2]; } data[100]; int data_count = 0; // 记录已填充的学生数量 // 统计指定成员的唯一值数量 int count_unique(struct class* data, int count, const char* member_name) { if (strcmp(member_name, "name") == 0) { int unique = 0; for (int i = 0; i < count; i++) { int exists = 0; // 检查当前name是否已出现过 for (int j = 0; j < i; j++) { if (strcmp(data[i].name, data[j].name) == 0) { exists = 1; break; } } if (!exists) unique++; } return unique; } else if (strcmp(member_name, "score") == 0) { int unique = 0; for (int i = 0; i < count; i++) { int exists = 0; for (int j = 0; j < i; j++) { if (data[i].score == data[j].score) { exists = 1; break; } } if (!exists) unique++; } return unique; } else if (strcmp(member_name, "grade") == 0) { int unique = 0; for (int i = 0; i < count; i++) { int exists = 0; for (int j = 0; j < i; j++) { if (strcmp(data[i].grade, data[j].grade) == 0) { exists = 1; break; } } if (!exists) unique++; } return unique; } else { printf("无效的成员名称:%s\n", member_name); return -1; } } int main() { // 示例数据填充 strcpy(data[0].name, "Alice"); data[0].score = 90; strcpy(data[0].grade, "A"); strcpy(data[1].name, "Bob"); data[1].score = 85; strcpy(data[1].grade, "B"); strcpy(data[2].name, "Alice"); data[2].score = 90; strcpy(data[2].grade, "A"); data_count = 3; char input[10]; printf("请输入要统计的成员名称(name/score/grade):"); scanf("%s", input); getchar(); // 消耗输入后的换行符 int result = count_unique(data, data_count, input); if (result != -1) { printf("唯一的%s数量:%d\n", input, result); } return 0; }
方案二:查找表+指针运算(可扩展,避免代码重复)
如果结构体成员较多,使用查找表映射字符串到成员的偏移量和比较函数,通过指针运算动态访问成员,避免重复代码。核心用到offsetof宏获取成员在结构体中的字节偏移。
完整代码示例
#include <stdio.h> #include <string.h> #include <stddef.h> // 用于offsetof宏 #include <stdlib.h> // 用于malloc和qsort struct class { char name[50]; int score; char grade[2]; } data[100]; int data_count = 0; // 成员比较函数 int compare_names(const void* a, const void* b) { return strcmp(*(const char**)a, *(const char**)b); } int compare_scores(const void* a, const void* b) { return *(const int*)a - *(const int*)b; } int compare_grades(const void* a, const void* b) { return strcmp(*(const char**)a, *(const char**)b); } // 成员信息结构体:映射名称、偏移量、比较函数、元素大小 typedef struct { const char* name; size_t offset; int (*comparator)(const void*, const void*); size_t elem_size; } MemberInfo; MemberInfo member_map[] = { {"name", offsetof(struct class, name), compare_names, sizeof(char*)}, {"score", offsetof(struct class, score), compare_scores, sizeof(int)}, {"grade", offsetof(struct class, grade), compare_grades, sizeof(char*)}, {NULL, 0, NULL, 0} // 哨兵元素,用于遍历结束判断 }; // 通用统计唯一值函数 int count_unique(struct class* data, int count, const char* member_name) { // 查找对应的成员信息 MemberInfo* info = NULL; for (int i = 0; member_map[i].name != NULL; i++) { if (strcmp(member_map[i].name, member_name) == 0) { info = &member_map[i]; break; } } if (!info) { printf("无效的成员名称:%s\n", member_name); return -1; } // 分配内存存储成员指针/值 void* elements = malloc(count * info->elem_size); if (!elements) { perror("内存分配失败"); return -1; } // 填充成员数据 for (int i = 0; i < count; i++) { void* member_addr = (char*)&data[i] + info->offset; if (info->elem_size == sizeof(char*)) { // 对于字符数组成员,存储其指针 *(char**)((char*)elements + i * sizeof(char*)) = member_addr; } else if (info->elem_size == sizeof(int)) { // 对于整数成员,存储其值 *(int*)((char*)elements + i * sizeof(int)) = *(int*)member_addr; } } // 排序以便统计唯一值 qsort(elements, count, info->elem_size, info->comparator); // 统计唯一值数量 int unique_count = 0; if (count > 0) { unique_count = 1; for (int i = 1; i < count; i++) { void* curr = (char*)elements + i * info->elem_size; void* prev = (char*)elements + (i-1)*info->elem_size; if (info->comparator(curr, prev) != 0) { unique_count++; } } } free(elements); return unique_count; } int main() { // 示例数据填充 strcpy(data[0].name, "Alice"); data[0].score = 90; strcpy(data[0].grade, "A"); strcpy(data[1].name, "Bob"); data[1].score = 85; strcpy(data[1].grade, "B"); strcpy(data[2].name, "Alice"); data[2].score = 90; strcpy(data[2].grade, "A"); data_count = 3; char input[10]; printf("请输入要统计的成员名称(name/score/grade):"); scanf("%s", input); getchar(); int result = count_unique(data, data_count, input); if (result != -1) { printf("唯一的%s数量:%d\n", input, result); } return 0; }
两种方案对比
- 分支判断:代码简单直接,适合成员数量少的场景,但存在重复代码,新增成员时需要修改函数逻辑。
- 查找表+指针运算:代码更通用,新增成员只需在查找表中添加条目,无需修改统计逻辑,但对指针和内存管理要求稍高。
内容的提问来源于stack exchange,提问作者dru
相关产品推荐
相关产品推荐

