跳转至

struct 结构体

方法速览

任务 方法 口诀
定义 struct 名 { 成员列表 };——末尾分号必带 大括号后分号
访问 变量用 .,指针用 -> 变量点、指针箭
求大小 成员依次排 + 对齐填充,补齐到最大对齐值 相加再对齐
排序 sort(a, a+n, cmp),cmp 写“小于”的规则 cmp 管小于,返回真假

定义与成员访问

1
2
3
4
5
struct Point {
    int x, y;          // 数据成员
};                     // ← 这个分号漏了必编译错
Point p = {3, 4};      // 按成员顺序初始化
Point* q = &p;
  • 初始化{3, 4} 按声明顺序给 xy;也可逐个赋值 p.x = 3; p.y = 4;
  • 访问:变量 p.x;指针 q->x(等价 (*q).x);
  • 嵌套a.in.x 一路点到底;自引用必须用指针:struct Node { int d; Node* next; };——不能直接放 Node next;(无限套娃);
  • typedeftypedef struct { int x, y; } P; 之后直接 P p;
  • 结构体可以整体赋值(逐成员拷贝)、可以作函数参数(值传递,拷贝一份,函数内修改不影响外面)。

1
2
3
4
5
struct Node { int data; Node* next; };
Node a = {1, nullptr}, b;
b = a;            // 整体拷贝:b.data = 1, b.next 同 a.next
Node* p = &b;
p->data = 5;      // 等价 b.data = 5

配合排序

sort 默认升序;自定义顺序靠比较器 cmp(a, b):返回真表示 a 排在 b 前面(即 a “小于” b)。

1
2
3
4
5
6
7
struct P { int x, y; };

bool cmp(const P& a, const P& b) {
    if (a.x != b.x) return a.x < b.x;   // 第一关键字 x 升序
    return a.y > b.y;                   // 第二关键字 y 降序
}
// 用法:sort(v.begin(), v.end(), cmp);

三条规矩:

  • cmp 描述的是严格小于,相等必须返回 false(返回 true 会违反严格弱序,可能崩);
  • 多关键字:先比第一关键字,不同就出结果;相同再比第二关键字;
  • 降序就把 < 换成 >(或 sort(..., greater<int>()))。

{(3,2), (1,5), (3,1), (1,2)} 按“x 升、x 同 y 降”排 → \(\{(1,5), (1,2), (3,2), (3,1)\}\)

与 union 对比

对比项 struct union
内存 每个成员各占一块 所有成员共享一块
大小 成员相加 + 对齐填充(≥ 成员和 最大成员(对齐补齐)
有效成员 全部同时有效 同一时刻只有一个
场景 打包多种数据(点、学生记录) 互斥格式、拆字节、省空间

struct { int i; double d; } = 16 字节;union { int i; double d; } = 8 字节。

练习题目

练习 1 · 定义的末尾分号

题目

下列结构体定义正确的是( )

  • A. struct P { int x, y; };
  • B. struct P { int x, y }
  • C. struct P { int x, y; }
  • D. struct P ( int x, y );
答案与解析

答案:A

} 后的分号是定义的一部分,漏了编译错误;成员列表用 {} 不用 ()

练习 2 · 变量访问用点

题目

struct P { int x, y; }; P p; 给 x 赋 3 的正确写法是( )

  • A. p.x = 3;
  • B. p:x = 3;
  • C. p->x = 3;
  • D. x.p = 3;
答案与解析

答案:A

p 是变量用 .-> 是指针专属。

练习 3 · 指针访问用箭头

题目

P* q = &p; 通过 q 给 y 赋 4,正确写法是( )

  • A. q.y = 4;
  • B. *q.y = 4;
  • C. y->q = 4;
  • D. q->y = 4;
答案与解析

答案:D

指针用 ->(*q).y 也对,但 *q.y. 优先级高于 * 会解析成 *(q.y) 出错。

练习 4 · 两种访问的等价

题目

p->next 完全等价的表达式是( )

  • A. p.next
  • B. (*p).next
  • C. &p.next
  • D. p.(*next)
答案与解析

答案:B

-> 就是“解引用再点”的缩写:(*p).next

练习 5 · 初始化

题目

struct S { int a; char b; double c; }; S s = {1, 'x', 2.5}; 之后( )

  • A. 编译错误,必须逐个赋值
  • B. s.a='x', s.b=1, s.c=2.5
  • C. s.a=1, s.b='x', s.c=2.5
  • D. s.c 未初始化
答案与解析

答案:C

花括号按成员声明顺序依次初始化。

练习 6 · typedef struct

题目

typedef struct { int x, y; } P; 定义变量应写( )

  • A. struct P p;
  • B. p P;
  • C. P struct p;
  • D. P p;
答案与解析

答案:D

typedef 之后 P 就是完整类型名,前面的 struct 不再需要。

练习 7 · 嵌套结构体

题目

struct In { int v; }; struct Out { In a; int b; }; Out o; 访问 Out 里 In 的 v,写作( )

  • A. o->a.v
  • B. o.a.v
  • C. o.v.a
  • D. a.o.v
答案与解析

答案:B

o 是变量一路用 .;嵌套就从外往内逐层点。

练习 8 · 自引用必须用指针

题目

链表结点的正确定义是( )

  • A. struct Node { int d; Node next; };
  • B. struct Node { int d; Node* next; };
  • C. struct Node { int d; struct Node next; };
  • D. struct Node { int* d; int next; };
答案与解析

答案:B

成员里放自身类型会无限递归定义;放指针只占 8 字节,合法。

练习 9 · 结构体数组

题目

P a[3] = {{1,2},{3,4},{5,6}};a[1].y 的值是( )

  • A. \(2\)
  • B. \(3\)
  • C. \(4\)
  • D. \(6\)
答案与解析

答案:C

a[1] 是第 2 个元素 {3,4}.y 取 4。

练习 10 · 整体赋值

题目

P a = {1, 2}, b; b = a; b.x = 9; 之后 a.x 的值( )

  • A. \(1\)
  • B. 未定义
  • C. \(0\)
  • D. \(9\)
答案与解析

答案:A

b = a逐成员拷贝,之后改 b 不影响 a——结构体赋值是拷贝语义。

练习 11 · 值传递

题目

void f(P s) { s.x = 100; }
P p = {1, 2}; f(p);
调用后 p.x 的值( )

  • A. \(100\)
  • B. \(1\)
  • C. 编译错误
  • D. 未定义
答案与解析

答案:B

结构体值传递:函数里拿到的是拷贝,改的是副本。想在函数里改原件要传引用 f(P& s) 或指针。

练习 12 · sizeof(基础对齐)

题目

struct S { int i; char c; }; 的 sizeof 是( )

  • A. \(5\)
  • B. \(4\)
  • C. \(8\)
  • D. \(6\)
答案与解析

答案:C

\(4 + 1 = 5\),补齐到最大对齐值 4 的倍数 → 8。

干扰项思路:A 直接相加忘补齐。

练习 13 · sizeof(含 double)

题目

struct S { int i; char c; double d; }; 的 sizeof 是( )

  • A. \(13\)
  • B. \(8\)
  • C. \(24\)
  • D. \(16\)
答案与解析

答案:D

int 4 + char 1 + 填充 3(对齐 double)+ double 8 = 16。

练习 14 · sizeof(两个 char)

题目

struct S { char a; char b; int i; }; 的 sizeof 是( )

  • A. \(6\)
  • B. \(12\)
  • C. \(8\)
  • D. \(4\)
答案与解析

答案:C

两个 char 连排(1+1),再填充 2 字节对齐 int(4),共 8。

练习 15 · 含数组的 sizeof

题目

struct S { char name[10]; int age; }; 的 sizeof 是( )

  • A. \(14\)
  • B. \(16\)
  • C. \(11\)
  • D. \(10\)
答案与解析

答案:B

\(10 + 2\)(填充到 4 的倍数)\(+ 4 = 16\)

练习 16 · sort 默认升序

题目

int a[4] = {3, 1, 4, 2}; sort(a, a + 4); 之后数组是( )

  • A. {4, 3, 2, 1}
  • B. {3, 1, 4, 2}
  • C. {1, 2, 3, 4}
  • D. {1, 4, 2, 3}
答案与解析

答案:C

不带比较器默认升序。

练习 17 · 降序排序

题目

要把 int a[] 按从大到小排序,正确写法是( )

  • A. sort(a, a + n, greater<int>());
  • B. sort(a, a + n, less<int>()); 再反转
  • C. sort(a, a + n);
  • D. sort(a + n, a);
答案与解析

答案:A

greater<int>() 让“大的算小”→ 降序;less 是默认升序;D 区间端点写反。

练习 18 · cmp 的返回语义

题目

自定义 bool cmp(int a, int b) { return a > b; }sort(a, a+n, cmp),结果是( )

  • A. 升序
  • B. 编译错误
  • C. 只排序奇数
  • D. 降序
答案与解析

答案:D

cmp 返回真 = a 排 b 前面;a > b 为真即大者在前 → 降序。cmp 定义“谁在前”,不是“谁小”的字面意思

练习 19 · 多关键字排序

题目

1
2
3
4
bool cmp(const P& a, const P& b) {
    if (a.x != b.x) return a.x < b.x;
    return a.y > b.y;
}
{(3,2),(1,5),(3,1),(1,2)} 排序结果是( )

  • A. {(1,5), (1,2), (3,2), (3,1)}
  • B. {(1,2), (1,5), (3,1), (3,2)}
  • C. {(3,2), (3,1), (1,5), (1,2)}
  • D. {(1,2), (1,5), (3,2), (3,1)}
答案与解析

答案:A

x 升序先分桶:x=1 在前;x 相同比 y 降序 → (1,5) 在 (1,2) 前,(3,2) 在 (3,1) 前。

练习 20 · cmp 不能“相等返回真”

题目

bool cmp(int a, int b) { return a >= b; } 的问题是( )

  • A. 没有问题,等价降序
  • B. 相等时返回真违反严格弱序,可能运行期崩溃
  • C. 会把数组变成升序
  • D. 编译错误
答案与解析

答案:B

比较器要求“a 等于 b 时必须返回 false”;>= 让相等元素也满足“前于”,std::sort 的快速排序分支可能越界崩溃。降序写 a > b 就够了。

练习 21 · 字符串成员排序

题目

struct S { string name; }; 按 name 字典序升序,cmp 应写( )

  • A. return a.name < b.name;
  • B. return a.name > b.name;
  • C. return a < b;
  • D. return a.name.size() < b.name.size();
答案与解析

答案:A

string 的 < 就是字典序;B 是降序;D 按长度排不是字典序。

练习 22 · 成员默认权限

题目

struct S { int x; }; 中 x 的默认访问权限是( )

  • A. private
  • B. protected
  • C. public
  • D. 没有权限概念
答案与解析

答案:C

struct 成员默认 public;class 默认 private——这是两者在 C++ 里几乎唯一的差别。

练习 23 · 结构体含函数成员

题目

关于 C++ 的 struct,正确的是( )

  • A. 只能放数据成员,不能放函数
  • B. struct 不是 C++ 关键字
  • C. 可以放函数,但不能有构造函数
  • D. 可以有成员函数、构造函数,与 class 基本等价(仅默认权限不同)
答案与解析

答案:D

C++ 的 struct 可含成员函数与构造函数,功能上与 class 等价,只差默认访问权限。

练习 24 · 与 union 的大小对比

题目

struct S { int i; char c; };union U { int i; char c; }; 的 sizeof 分别是( )

  • A. \(5\)\(4\)
  • B. \(8\)\(4\)
  • C. \(8\)\(5\)
  • D. \(4\)\(4\)
答案与解析

答案:B

struct:\(4+1\) 补齐到 8;union:\(\max(4,1) = 4\)

练习 25 · 选型

题目

“学生记录:姓名 string、年龄 int、成绩数组”,应选用( )

  • A. union——省空间
  • B. enum
  • C. struct——各字段同时有效
  • D. 指针数组
答案与解析

答案:C

多个字段要同时保存,必须 struct;union 适合互斥字段。

练习 26 · 综合输出

题目

1
2
3
struct P { int x, y; };
void g(P* s) { s->x = s->x + s->y; }
P p = {3, 4}; g(&p); cout << p.x;
输出( )

  • A. \(3\)
  • B. \(7\)
  • C. \(4\)
  • D. 未定义
答案与解析

答案:B

指针传参改的是原件\(3 + 4 = 7\)。对比练习 11 的值传递——指针(或引用)才能把修改带出来。

历年真题

2023 年 · 第 4 题

题目

假设有一个链表的节点定义如下:

1
2
3
4
struct Node {
    int data;
    Node* next;
};

现在有一个指向链表头部的指针:Node* head。如果想要在链表中插入一个新节点,其成员 data 的值为 \(42\),并使新节点成为链表的第一个节点,下面哪个操作是正确的?( )

A.

1
2
3
4
Node* newNode = new Node;
newNode->data = 42;
newNode->next = head;
head = newNode;
B.

1
2
3
4
Node* newNode = new Node;
head->data = 42;
newNode->next = head;
head = newNode;
C.

1
2
3
Node* newNode = new Node;
newNode->data = 42;
head->next = newNode;
D.

1
2
3
Node* newNode = new Node;
newNode->data = 42;
newNode->next = head;
答案与解析

答案:A

头插三步:新结点接到旧头(newNode->next = head)→ 头指针改指新结点(head = newNode)。newNode 是指针,所以成员访问全程 ->

干扰项思路:B 给 head->data 赋值改的是旧首结点且 newNode 未接 data;C 是尾接不是头插;D 忘了 head = newNode,链表头还是旧结点。

易错小结

  • } 后的分号是 struct 定义的一部分,漏了必错;
  • 变量 .、指针 ->p->x 等价 (*p).x,但 *p.x 因优先级出错;
  • 结构体赋值/传参都是拷贝:函数里想改原件传 P&P*
  • sizeof = 成员相加 + 对齐填充再补齐到最大对齐值(int+char=8、int+char+double=16);
  • cmp 描述“谁排前面”:升序 a.x < b.x、降序 >相等必须返回 false>= 会崩);
  • 自引用成员必须是指针 Node* next