首页 > 后端开发 > C++ > 正文

C++结构体如何定义和使用 struct关键字基本语法解析

P粉602998670
发布: 2025-08-22 12:57:01
原创
692人浏览过
C++结构体是自定义数据类型,用struct定义,成员默认public,可包含数据和函数,支持初始化、成员访问及内存对齐,与class主要区别在于默认访问权限。

c++结构体如何定义和使用 struct关键字基本语法解析

结构体(

struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
)在C++里,说白了,就是一种把不同类型的数据打包在一起的自定义数据类型。它让你能用一个名字来管理一组相关的信息,比如一个人的姓名、年龄、身高,或者一个点的X、Y坐标。定义它,就是告诉编译器你要打包哪些数据;使用它,就是创建这种打包好的数据,然后去访问里面的具体成员。这玩意儿是C++面向对象编程的基石之一,虽然看起来简单,但用起来门道还不少。

解决方案

要定义和使用C++结构体,核心就是

struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
关键字和成员访问操作符。

1. 结构体的定义

定义结构体,你得先用

struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
关键字,后面跟着你给这个结构体起的名字(也就是类型名),然后是一对花括号
{}
登录后复制
,里面列出这个结构体包含的所有数据成员,每个成员后面跟一个分号。

立即学习C++免费学习笔记(深入)”;

// 定义一个表示二维点的结构体
struct Point {
    int x;      // x坐标
    int y;      // y坐标
};

// 定义一个表示学生信息的结构体
struct Student {
    std::string name; // 姓名
    int age;          // 年龄
    double score;     // 成绩
};
登录后复制

这里需要注意,结构体定义完,别忘了最后那个分号

;
登录后复制
,很多人容易漏掉。这个分号是告诉编译器,结构体定义到此结束。结构体可以定义在全局作用域,也可以在函数内部,甚至在另一个结构体或类的内部。

2. 结构体变量的声明与初始化

定义好结构体类型后,你就可以像使用

int
登录后复制
登录后复制
登录后复制
char
登录后复制
这些内置类型一样,去声明它的变量了。

// 声明一个Point类型的变量p1
Point p1;

// 声明一个Student类型的变量s1
Student s1;
登录后复制

声明变量后,你可以对它们的成员进行赋值。

p1.x = 10;
p1.y = 20;

s1.name = "张三";
s1.age = 18;
s1.score = 95.5;
登录后复制

C++也支持在声明时进行初始化,这叫聚合初始化(aggregate initialization),尤其对于简单的结构体很方便:

Point p2 = {30, 40}; // 按照成员声明的顺序进行初始化

Student s2 = {"李四", 20, 88.0}; // 同样按顺序
登录后复制

如果成员是复杂的类型,比如另一个结构体,也可以嵌套初始化:

struct Line {
    Point start;
    Point end;
};

Line l1 = {{1, 2}, {5, 6}}; // 嵌套初始化
登录后复制

3. 结构体成员的访问

访问结构体变量的成员,使用“点操作符”(

.
登录后复制
)。

std::cout << "p1的x坐标是: " << p1.x << std::endl;
std::cout << "s1的姓名是: " << s1.name << std::endl;
登录后复制

当你有结构体指针时,你需要使用“箭头操作符”(

->
登录后复制
)来访问成员。

Point* ptr_p1 = &p1; // 获取p1的地址,赋值给指针
std::cout << "通过指针访问,p1的y坐标是: " << ptr_p1->y << std::endl;

// 等价于 (*ptr_p1).y,但箭头操作符更简洁
登录后复制

C++结构体与类的主要区别是什么?

说实话,C++里的

struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
class
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
在功能上几乎是一模一样的,它们都能包含数据成员、成员函数、构造函数、析构函数,甚至能继承和实现多态。这常常让初学者感到困惑,甚至一些有经验的开发者也会在选择时纠结。

它们最主要的、也是唯一的语法层面的区别,在于默认的成员访问权限

  • struct
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    的成员默认是
    public
    登录后复制
    的。
    这意味着如果你不在结构体里明确写
    private:
    登录后复制
    protected:
    登录后复制
    登录后复制
    ,那么所有成员(数据和函数)在结构体外部都是可以直接访问的。
  • class
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    的成员默认是
    private
    登录后复制
    的。
    这表示如果你不明确声明
    public:
    登录后复制
    protected:
    登录后复制
    登录后复制
    ,所有成员都是私有的,只能在类内部或通过友元访问。

从历史角度看,

struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
是为了兼容C语言而保留的,C语言中没有
class
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
的概念,
struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
就是纯粹的数据聚合。所以,在C++中,当你定义一个
struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
时,通常意味着你更倾向于把它当作一个“纯粹的数据容器”,里面的数据成员可以直接访问,或者它是一个“普通旧数据类型”(POD,Plain Old Data)。而当你使用
class
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
时,则通常表示你正在设计一个更复杂的对象,它可能封装了数据和行为,并希望通过接口来控制对内部数据的访问,遵循面向对象的“信息隐藏”原则。

我个人在实践中,如果只是想简单地把一些数据打包,且这些数据对外是透明的,我更倾向于用

struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
,感觉更轻量、更直接。但如果涉及到行为、复杂的生命周期管理或者需要严格的封装,那
class
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
无疑是更合适的选择。当然,这更多是一种编程习惯和风格约定,而非强制性的语法要求。

如何在结构体中定义成员函数和构造函数?

很多人可能觉得

struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
就只能放数据,这其实是个误区。在C++里,
struct
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
完全可以拥有成员函数,包括构造函数、析构函数,甚至重载运算符。这进一步模糊了它和
class
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
的界限。

1. 成员函数

在结构体内部定义成员函数,就和在类里定义成员函数一样。这些函数可以操作结构体内部的数据成员。

struct Point {
    int x;
    int y;

    // 成员函数:打印点坐标
    void print() {
        std::cout << "(" << x << ", " << y << ")" << std::endl;
    }

    // 成员函数:移动点
    void move(int dx, int dy) {
        x += dx;
        y += dy;
    }
};

// 使用示例
Point p;
p.x = 1;
p.y = 2;
p.print(); // 输出 (1, 2)
p.move(5, -3);
p.print(); // 输出 (6, -1)
登录后复制

2. 构造函数

构造函数是一种特殊的成员函数,它在创建结构体对象时自动调用,用于初始化对象的状态。构造函数的名称必须和结构体名称相同,并且没有返回类型。

struct Point {
    int x;
    int y;

    // 默认构造函数:不带参数
    Point() {
        x = 0;
        y = 0;
        std::cout << "默认构造函数被调用" << std::endl;
    }

    // 带参数的构造函数:初始化x和y
    Point(int initial_x, int initial_y) {
        x = initial_x;
        y = initial_y;
        std::cout << "带参数构造函数被调用" << std::endl;
    }

    void print() {
        std::cout << "(" << x << ", " << y << ")" << std::endl;
    }
};

// 使用示例
Point p1;             // 调用默认构造函数,p1为(0,0)
p1.print();

Point p2(10, 20);     // 调用带参数构造函数,p2为(10,20)
p2.print();
登录后复制

通过构造函数,你可以确保结构体对象在创建时就处于一个有效的、有意义的状态,避免了手动逐个初始化成员的麻烦和潜在错误。

C++结构体的内存布局和对齐规则是怎样的?

这块内容有点技术深度,但对于理解结构体的底层工作原理,以及避免一些潜在的性能问题或跨平台兼容性问题,是相当重要的。C++标准并没有强制规定结构体成员在内存中必须是紧密排列的,编译器在处理结构体时,为了优化内存访问效率,往往会引入“内存对齐”(Memory Alignment)。

1. 内存对齐(Padding)

简单来说,内存对齐就是编译器在结构体成员之间插入一些空白字节(padding),以确保每个成员都从一个内存地址开始,而这个地址是其自身大小(或其类型对齐要求)的整数倍。这样做的好处是,CPU访问对齐的数据通常会更快,因为很多硬件设计要求数据从特定的地址边界开始读取。

举个例子:

struct MyStruct {
    char c1;   // 1 byte
    int i;     // 4 bytes
    char c2;   // 1 byte
};
登录后复制

理想情况下,你可能觉得这个结构体的大小是 1 + 4 + 1 = 6 字节。但实际上,它很可能不是6字节。假设在一个32位或64位系统上,

int
登录后复制
登录后复制
登录后复制
类型通常需要4字节对齐。

  • c1
    登录后复制
    登录后复制
    放在地址0。
  • i
    登录后复制
    登录后复制
    登录后复制
    需要4字节对齐,所以编译器会在
    c1
    登录后复制
    登录后复制
    后面插入3个填充字节(padding),使
    i
    登录后复制
    登录后复制
    登录后复制
    从地址4开始。
  • i
    登录后复制
    登录后复制
    登录后复制
    占用地址4, 5, 6, 7。
  • c2
    登录后复制
    登录后复制
    放在地址8。

此时,结构体的大小看起来是 1 (c1) + 3 (padding) + 4 (i) + 1 (c2) = 9 字节。但通常,结构体的总大小也会被对齐到其最大成员的对齐边界。在这个例子中,

int
登录后复制
登录后复制
登录后复制
是4字节,所以整个结构体的大小会是4的倍数。9不是4的倍数,所以编译器可能会在
c2
登录后复制
登录后复制
后面再插入3个填充字节,使得整个结构体的大小变为12字节。

你可以使用

sizeof
登录后复制
运算符来查看结构体在内存中实际占用的字节数:

std::cout << "MyStruct的大小是: " << sizeof(MyStruct) << " 字节" << std::endl;
// 输出通常会是 12
登录后复制

2. 为什么内存对齐很重要?

  • 性能优化: CPU访问对齐的数据通常比访问未对齐的数据更快。在某些架构上,访问未对齐数据甚至可能导致性能急剧下降,或者直接引发硬件异常。
  • 跨平台兼容性: 不同的编译器、不同的硬件架构可能有不同的默认对齐规则。如果你的代码需要跨平台,了解并考虑对齐问题是必要的。尤其是在进行网络通信或文件IO时,如果直接将结构体内存写入或读取,对齐问题可能导致数据错位。
  • 与C语言接口: C++结构体经常需要与C语言库进行交互。C语言也有类似的对齐规则,保持一致性是确保接口正确工作的关键。

3. 控制对齐

在某些特殊情况下,你可能需要精确控制结构体的对齐方式,例如为了节省内存或者与外部二进制数据格式匹配。C++11引入了

alignas
登录后复制
关键字,GCC和MSVC等编译器也提供了
#pragma pack
登录后复制
指令(非标准但常用)。

// 使用alignas强制对齐
struct alignas(8) AlignedStruct { // 整个结构体8字节对齐
    int i;
    char c;
};

// 使用#pragma pack (非标准,慎用)
#pragma pack(push, 1) // 设置对齐字节为1,即不进行填充
struct PackedStruct {
    char c1;
    int i;
    char c2;
};
#pragma pack(pop) // 恢复默认对齐

std::cout << "AlignedStruct的大小是: " << sizeof(AlignedStruct) << " 字节" << std::endl; // 可能是8或16
std::cout << "PackedStruct的大小是: " << sizeof(PackedStruct) << " 字节" << std::endl; // 应该是6
登录后复制

但我个人经验是,除非你真的清楚自己在做什么,并且有非常明确的性能或兼容性需求,否则最好不要轻易去修改默认的对齐规则。让编译器自己处理通常是最安全和高效的做法。过度干预可能导致代码的可移植性变差,甚至引入难以调试的bug。

以上就是C++结构体如何定义和使用 struct关键字基本语法解析的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 //m.sbmmt.com/ All Rights Reserved | php.cn | 湘ICP备2023035733号