编程C开发者

详细介绍一下C语言中结构数组的工作。声明、初始化、传递给函数和使用时会出现哪些细节,以及在实践中常见的错误?

用 Hintsage AI 助手通过面试

答案。

结构数组是C语言中存储和处理同类数据的一个流行方式,比如数据表、点的数组、员工等。

问题背景:

C语言早期版本支持数组和结构,以便于数据组织。然而,使用结构数组需要理解语言的特性、内存操作和数据传递的原则。

问题:

错误发生在结构数组初始化不当、内存混乱、将数组传递给函数(可以作为指针传递),以及通过错误索引访问结构的字段时。

解决方案:

  1. 声明结构数组的方式与基本类型数组相同,只需基于事先声明的结构类型。
  2. 数组初始化可以是完整的、部分的和逐个元素的,但需要严格遵循语法。
  3. 使用和传递——默认情况下,数组作为指针传递给函数,通过 .-> 访问字段。

代码示例:

#include <stdio.h> struct Point { int x; int y; }; void print_points(struct Point *arr, int size) { for(int i = 0; i < size; ++i) { printf("(%d, %d) ", arr[i].x, arr[i].y); } } int main() { struct Point points[3] = { {1,2}, {3,4}, {5,6} }; print_points(points, 3); return 0; }

关键特性:

  • 声明时需要预先定义结构类型。
  • 传递结构数组时,传递的是指向第一个元素的指针。
  • 可以通过列表或逐个元素进行初始化,并注意正确填充。

反向问题。

通过点和箭头访问结构在数组中的字段有什么区别?

arr[i].field 用于 arr[i] 是结构本身的情况。 ptr->field 用于 ptr 是指向结构的指针的情况。

struct Point *p = &points[0]; printf("%d", p->x); // 正确 // points[0].x — 也是正确

如果对结构数组进行部分初始化,其余字段的值是什么?

在静态分配的数组中,部分初始化未指定的字段将填充为零,但在自动(栈)变量中如果未初始化则不会。

struct Point arr[2] = { {10} }; // arr[0].x = 10, arr[0].y = 0, arr[1].x 和 arr[1].y = 0

在将结构数组传递给函数时,是它的副本被传递了吗?

不,传递的是指向数组第一个元素的指针,此时函数可以修改元素,因为它处理的是原始内存。

常见错误和反模式

  • 使用未初始化的结构字段。
  • 错误的索引(例如 points.x 而不是 points[i].x)。
  • 尝试从函数返回局部结构数组。

生活中的示例

负面案例

程序员声明了结构数组,但没有初始化字段,并将其传递给函数进行填充。在使用指针时错误使用了点运算符而不是箭头运算符。结果产生了类型错误和使用垃圾值。

优点:

  • 得到了可编译的代码,了解了编译器的错误。

缺点:

  • 在运行时出现了意想不到的错误,难以捕获。

正面案例

使用了显式的零初始化器 {0} 来初始化整个数组,函数接受指针和大小,严格按照类型访问字段(arr[i].x)。

优点:

  • 没有未初始化的值,代码可读性强。

缺点:

  • 在不需要的情况下初始化会耗费时间,但这弥补了可读性和安全性。