🤖 作者:包瑞清(richie bao): lastmod: 2024-12-13T19:46:54+08:00

在 C、C++ 和 C# 的结构体(struct),及 C/C++ 联合体(union)是用来组合不同类型数据的工具,由用户定义,从而形成一个单一复合数据类型,其目的是让相关数据能够作为一个整体进行处理,每个成员可以有不同的数据类型。C/C++ 结构体和联合体的主要区别如表。

比较内容 结构体 联合体
内存分配 结构体中的每个成员都有自己的内存空间,结构体的总大小等于所有成员大小的总和 联合体中的所有成员共享同一块内存空间,因为联合体每次只能存储其中一个成员 ,联合体的大小等于最大成员的大小
访问方式 结构体的每个成员都可以独立存储和访问,多个成员可以同时有效 联合体的所有成员共用同一块内存,每次只能存储一个成员,因此在同一时刻只能访问一个成员
用途 通常用于存储不同类型的多个数据元素,例如表示一个学生的信息(姓名、年龄、成绩等) 通常用于节省内存空间,特别是在需要存储不同类型数据但只在某一时刻需要其中之一的场景中

8.1 结构体

结构体 C C++ C#

结构体的基本语法

C 结构体的基本语法为:

  // 定义结构体
struct StructureName{
    dataType member1;
    dataType member2;
    // 其他成员
};
  

示例:

  #include <stdio.h>

struct Person {
	char name[50];
	int age;
	float height;
};

int main() {
    // 声明结构体变量并初始化
	struct Person person1 = {"Alice", 30, 173};

    // 访问结构体成员
	printf("Name: %s\n", person1.name);
	printf("Age: %d\n", person1.age);
	printf("Height: %.2f\n", person1.height);

    // 修改结构体成员
	person1.age = 27;
	printf("Updated Age: %d\n", person1.age);

	return 0;
}
  
  🡮
Name: Alice
Age: 30
Height: 173.00
Updated Age: 27
  

C++ 结构体与类的功能类似,但默认成员权限为public。除了包含 C 的结构体功能外,还支持构造函数、方法及其他面向对象的特征。

C++ 结构体的基本语法为:

  struct StructureName{
    dataType member1;
    dataType member2;
    // 其他成员
};
  

示例:

  #include <iostream>

// 定义结构体
struct Person {
	std::string name;
	int age;
	double height;
};

int main() {
    // 定义结构体变量并初始化
	Person person1 = { "Alice", 30,173 };

    // 访问结构体成员
	std::cout << "Name: " << person1.name << std::endl;
	std::cout << "Age: " << person1.age << std::endl;
	std::cout << "Height: " << person1.height << std::endl;

    // 修改结构体成员
	person1.age = 27;
	std::cout << "Updated Age: " << person1.age << std::endl;

	return 0;
}
  
  🡮
Name: Alice
Age: 30
Height: 173
Updated Age: 27
  

在 C# 中,结构体(struct)是一种值类型数据结构,用于封装多个相关的数据。与类(class)不同,结构体存储于堆栈(stack),具有更高的性能,尤其在处理小型数据时。

C# 结构体使用struct关键字定义,可以用包括字段、属性、方法、事件、索引器和构造函数等。由于结构体是值类型,赋值操作会复制值,而不是引用。

  struct StructureName{
    public Datatype Member1;
    public Datatype Member2;

    public StructureName(DataType member1, Datatype member2){
        Member1 = member1;
        Member2 = member2;
    }

    public void Display(){
        Console.WriteLine($"Member1: {Member1}, Member2: {<Member2}");
    }
}
  

示例:

  using System;

struct Point
{
    public int X;
    public int Y;

    // 带参数的构造函数
    public Point(int x, int y)
    {
        X = x; 
        Y = y;
    }

    // 显示点的信息
    public void Display()
    {
        Console.WriteLine($"Point: ({X},{Y})");
    }
}

class Program
{
    static void Main()
    {
        // 使用构造函数初始化结构体
        Point p1=new Point(10,20);

        // 逐一赋值初始化
        Point p2;
        p2.X = 30;
        p2.Y = 40;

        // 调用方法
        p1.Display();
        p2.Display();
    }
}
  
  🡮
Point: (10,20)
Point: (30,40)
  

结构体的声明和定义方式

  • 先定义结构体再声明变量

可以在文件的全局作用域定义结构体,然后在需要的地方声明结构体变量。

  #include <stdio.h>

struct Point {
	int x;
	int y;
};


int main() {
	struct Point p1 = { 10,20 };
	printf("x = %d; y = %d\n", p1.x, p1.y);

	return 0;
}
  
  🡮
x = 10; y = 20
  
  • 在声明变量时定义结构体

如果结构体只在特定范围内使用,则可以直接在变量声明时定义。

声明结构体变量时初始化值。

  #include <stdio.h>

int main() {
	struct Point {
		int x;
		int y;
	}point1 = { 5,10 }, point2 = {15,20};

	printf("point1 = (%d, %d)\n", point1.x, point1.y);
	printf("point2 = (%d, %d)\n", point2.x, point2.y);

	return 0;
}
  
  🡮
point1 = (5, 10)
point2 = (15, 20)
  

声明结构体变量后赋值。

  #include <stdio.h>

int main() {
	struct Point {
		int x;
		int y;
	}point1, point2;

	point1.x = 5;
	point1.y = 10;
	point2.x = 15;
	point2.y = 20;

	printf("point1 = (%d, %d)\n", point1.x, point1.y);
	printf("point2 = (%d, %d)\n", point2.x, point2.y);

	return 0;
}
  
  🡮
point1 = (5, 10)
point2 = (15, 20)
  
  • 先定义结构体再声明变量

可以在文件的全局作用域定义结构体,然后在需要的地方声明结构体变量。

  #include <iostream>
#include <format>

struct Point {
	int x;
	int y;
};

int main() {
	struct Point p1 = { 10,20 };
	std::cout << std::format("x = {}; y = {}\n", p1.x, p1.y);

	return 0;
}
  
  🡮
x = 10; y = 20
  
  • 在声明变量时定义结构体

如果结构体只在特定范围内使用,则可以直接在变量声明时定义。

声明结构体变量时初始化值。

  #include <iostream>
#include <format>

int main() {
	struct Point {
		int x;
		int y;
	}point1 = { 5,10 }, point2 = { 15,20 };

	std::cout << std::format("point1 = ({},{})\n", point1.x, point1.y);
	std::cout << std::format("point2 = ({},{})\n", point2.x, point2.y);

	return 0;
}
  
  🡮
point1 = (5,10)
point2 = (15,20)
  

声明结构体变量后赋值。

  #include <iostream>
#include <format>

int main() {
	struct Point {
		int x;
		int y;
	}point1, point2;

	point1.x = 5;
	point1.y = 10;
	point2.x = 15;
	point2.y = 20;

	std::cout << std::format("point1 = ({},{})\n", point1.x, point1.y);
	std::cout << std::format("point2 = ({},{})\n", point2.x, point2.y);

	return 0;
}
  
  🡮
point1 = (5,10)
point2 = (15,20)
  

结构体的初始化

  #include <stdio.h>

struct MyStruct {
	int id;
	char name[50];
};

int main() {
    // 声明并初始化(列表初始化)
	struct MyStruct s1 = { 1, "Alice" };
	printf("Id: %d, Name: %s\n", s1.id, s1.name);

    // 声明后赋值(使用成员初始化)
	struct MyStruct s2;
	s2.id = 2;	
	snprintf(s2.name, sizeof(s2.name), "John Doe"); // strcpy(s2.name, "John Doe"); 
	printf("Id: %d, Name: %s\n", s2.id, s2.name);

	return 0;
}
  
  🡮
Id: 1, Name: Alice
Id: 2, Name: John Doe
  

🤖 代码解释

snprintf 是一个用于将格式化的数据写入字符串数组的函数。其作用与printf类似,但snprintf不会直接输出到标准输出,而是将结果写入指定的字符数组,并确保不会超过数组的大小,避免缓冲区溢出,其函数原型为int snprintf(char *str, size_t size, const char *format, ...);。参数str指向字符数组的指针,结果将被写入到该数组中;size为要写入的最大字符数(包括字符串的终止字符\0)。如果数组的大小不足以容纳所有数据,snprintf将确保不会超过此大小;format为格式化字符串,类似于printf的格式化方式,用于指定如何格式化后续的参数;...为格式化字符串中指定的额外参数,类似于printf中的参数。snprintf返回写入字符数组的字符数(不包括终止符\0)。如果返回值大于或等于size,则表示结果被截断,输出的数据没有完全写入字符数组。

strcpy是一个用于将一个字符串复制到另一个字符串的标准库函数,通常用于字符串的复制,但需要注意避免发生缓冲区溢出,其函数原型为char *strcpy(char *dest, const char *src);。参数dest为目标字符串指针,指向一个足够大的字符数组,存储复制后的字符串;src源字符串指针,为复制的字符串。strcpy返回dest,即目标字符串的指针。

C++ 结构体和 C 类似,但支持更多的初始化方法,如构造函数。

  #include <iostream>
#include <string>
#include <format>

struct MyStruct {
	int id;
	std::string name;

    // 构造函数
	MyStruct():id(0),name("Nobody"){} // 默认构造函数
	MyStruct(int i,std::string n):id(i),name(n){} // 自定义构造函数
};


int main() {
    // 使用构造函数初始化
	MyStruct s1(1, "Alice");

    // 列表初始化
	MyStruct s2 = { 2,"John Doe" };

    // 默认初始化后赋值(需要在结构体中配置默认构造函数)
	MyStruct s3;
	s3.id = 3;
	s3.name = "Lily";

	for (auto s : { s1,s2,s3 }) {
		std::cout << std::format("Id: {}, Name: {}\n", s.id, s.name);
	}	

	return 0;
}
  
  🡮
Id: 1, Name: Alice
Id: 2, Name: John Doe
Id: 3, Name: Lily
  

🤖 代码解释

  MyStruct():id(0),name("Nobody"){}
  

为定义的默认构造函数。这个构造函数没有参数;初始化列表id(0),name("Nobody")用于初始化id0nameNobody。当使用MyStruct时未提供任何参数,如MyStruct s3;,这个默认构造函数将被调用。

  MyStruct(int i,std::string n):id(i),name(n){} 
  

为定义的构造函数。这个构造函数接受两个参数,int istd::string n。初始化列表id(i),name(n)用于将传入的参数in分别赋值给idname成员变量。当创建MyStruct对象时提供有具体参数,例如MyStruct s1(1, "Alice");,这个构造函数将被调用。

在 C# 中,结构体(struct)默认是值类型(value type),并且编译器会自动生成一个默认构造函数。这个默认构造函数会将所有字段初始化为其默认值,如int类型初始化为0string类型初始化为null等。C# 不允许显示定义无参数的默认构造函数(从 C# 10开始例外,但仅适用于 .NET6 或更高版本)。

  using System;

struct MyStruct
{
    public int Id;
    public string Name;

    // 构造函数
    public MyStruct(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

class Program
{
    static void Main()
    {
        // 使用构造函数初始化
        MyStruct s1=new MyStruct(1, "Alice");

        // 声明后对字段赋值初始化
        MyStruct s2;
        s2.Id = 2;
        s2.Name = "John Doe";

        // 对字段使用默认值。调用自动生成的默认构造函数
        MyStruct s3 = new MyStruct();

        Console.WriteLine($"Id: {s1.Id}, Name: {s1.Name}");
        Console.WriteLine($"Id: {s2.Id}, Name: {s2.Name}");
        Console.WriteLine($"Id: {s3.Id}, Name: {s3.Name}");
        Console.WriteLine($"Id: {s3.Id}, Name: {s3.Name ?? "Nobody"}");
    }
}
  
  🡮
Id: 1, Name: Alice
Id: 2, Name: John Doe
Id: 0, Name:
Id: 0, Name: Nobody
  

定义无参数(默认)构造函数

  using System;

struct MyStruct
{
    public int Id;
    public string Name;

    // 显示定义无参数构造函数
    public MyStruct()
    {
        Id = -1;
        Name = "Unknown";
    }
    public MyStruct(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

class Program
{
    static void Main()
    {
        MyStruct s1=new MyStruct(1, "Alice");

        MyStruct s2;
        s2.Id = 2;
        s2.Name = "John Doe";

        MyStruct s3 = new MyStruct(); // 调用显示定义的无参数构造函数

        Console.WriteLine($"Id: {s1.Id}, Name: {s1.Name}");
        Console.WriteLine($"Id: {s2.Id}, Name: {s2.Name}");
        Console.WriteLine($"Id: {s3.Id}, Name: {s3.Name}");
        Console.WriteLine($"Id: {s3.Id}, Name: {s3.Name ?? "Nobody"}");
    }
}
  
  🡮
Id: 1, Name: Alice
Id: 2, Name: John Doe
Id: -1, Name: Unknown
Id: -1, Name: Unknown
  

🤖 代码解释

  Console.WriteLine($"Id: {s3.Id}, Name: {s3.Name ?? "Nobody"}");
  

??为空合并运算符(Null-Coalescing Operator),如果s3.Namenull,运算结果将为Nobody;如不不为null,运算结果将为s3.Name的默认值。

访问结构体

C、C++ 和 C# 访问结构体成员方式略有不同,如表,

特性 C C++ C#
成员访问操作符 .-> .-> .
是否支持方法
是否支持构造函数
是否支持指针 否(除非启用不安全代码)
默认权限 public public

如果为结构体变量,可以用.访问其成员,如rect.width;如果为结构体指针,需要用->来间接访问其成员,如ptr->width

  #include <stdio.h>

// 定义结构体
struct Rectangle {
	int width;
	int height;
};


int main() {
	struct Rectangle rect = { 10,20 }; // 声明并初始化结构体变量
	struct Rectangle* ptr = &rect; // 定义指向结构体的指针

    // 使用`.`访问结构体成员
	printf("Width: %d, Height: %d\n", rect.width, rect.height);
    // 使用`->`通过指针访问结构体成员
	printf("Width: %d, Height: %d\n", ptr->width, ptr->height);

	return 0;
}
  
  🡮
Width: 10, Height: 20
Width: 10, Height: 20
  

如果为结构体变量,可以用.访问其成员,如rect.width(成员变量)和rect.display()(成员函数);如果为结构体指针,需要用->来间接访问其成员,如ptr->width(成员变量)和ptr->display()(成员函数)。

  #include <iostream>
#include <format>

// 定义结构体
struct Rectangle {
	int width;
	int height;

    // 构造函数
	Rectangle(int w, int h):width(w),height(h){}

    // 成员函数
	void display() {
		std::cout << std::format("Width: {}; Height: {}\n", width, height);
	}
};

int main() {
	Rectangle rect(10, 20); // 调用构造函数创建结构体
	Rectangle* ptr = &rect; // 定义指向结构体的指针
    
    // 使用`.`访问成员方法
	rect.display();
    // 使用`->`通过指针访问成员方法
	ptr->display();

    // 使用`.`访问成员变量
	std::cout << std::format("Width: {}, Height: {}\n", rect.width, rect.height);
    // 使用`->`通过指针访问成员变量
	std::cout << std::format("Width: {}, Height: {}\n", ptr->width, ptr->height);

	return 0;
}
  
  🡮
Width: 10; Height: 20
Width: 10; Height: 20
Width: 10, Height: 20
Width: 10, Height: 20
  

C# 结构体为值类型,不能像 C/C++ 直接使用指针(除非启用不安全代码),其成员访问同一使用.操作符。

  using System;

struct Rectangle
{
    public int Width;
    public int Height;

    // 构造函数
    public Rectangle(int width,int height)
    {
        Width = width;
        Height = height;
    }

    // 成员方法
    public void Display()
    {
        Console.WriteLine($"Width: {Width}, Height: {Height}");
    }
}


class Program
{
    static void Main()
    {
        Rectangle rect = new Rectangle(10,20); // 创建结构体实例

        // 使用`.`访问成员方法
        rect.Display();

        // 使用`.`访问成员变量
        Console.WriteLine($"Width: {rect.Width}, Height: {rect.Height}");
    }
}
  
  🡮
Width: 10, Height: 20
Width: 10, Height: 20
  

结构体数组

在 C 中,结构体数组是由结构体类型的多个实例组成的数组。通过下标访问数组中的结构体元素,然后用.->访问结构体成员。

  #include <stdio.h>

// 定义结构体
struct Point {
	int x;
	int y;
};

int main() {
    // 定于结构体数组并初始化
	struct Point points[3] = {
		{1,2},
		{3,4},
		{5,6}
	};

    // 遍历结构体数组
	for (int i = 0;i < 3;i++) {
		printf("Point %d: x = %d, y = %d\n", i + 1, points[i].x, points[i].y);
	}

	return 0;
}
  
  🡮
Point 1: x = 1, y = 2
Point 2: x = 3, y = 4
Point 3: x = 5, y = 6
  

C++ 的结构体数组与 C 基本相同,但可以使用 C++ 的特性,如果构造函数和方法,通过初始化列表来赋值。

  #include <iostream>
#include <format>

// 定义结构体
struct Point {
	int x;
	int y;

    // 构造函数
	Point(int a=0,int b=0):x(a),y(b){}

    // 成员函数
	void display()const {
		std::cout << std::format("X = {}, y = {}\n", x, y);
	}
};


int main() {
    // 定义结构体数组并初始化
	Point points[3] = { {1,2},{3,4},{5,6} };

    // 遍历结构体数组
	for (int i = 0;i < 3;i++) {
		std::cout << std::format("Point {}: ", i + 1);
		points[i].display();
	}

	return 0;
}
  
  🡮
Point 1: X = 1, y = 2
Point 2: X = 3, y = 4
Point 3: X = 5, y = 6
  

C# 中,结构体数组中的每个元素是结构体的一个实例。

  using System;

// 定义结构体
struct Point
{
    public int X;
    public int Y;

    // 构造函数
    public Point(int x, int y)
    {
        X = x; Y = y;
    }

    // 成员方法
    public void Display()
    {
        Console.WriteLine($"X = {X}, Y = {Y}");
    }

}


class Program
{
    static void Main()
    {
        // 定义结构体数组并初始化
        Point[] points = new Point[3]{
            new Point(1,2),
            new Point(3,4),
            new Point(4,5)
         };

        // 遍历结构体数组
        for (int i = 0; i < points.Length; i++)
        {
            Console.Write($"Point {i + 1}: ");
            points[i].Display();
        }


    }
}
  
  🡮
Point 1: X = 1, Y = 2
Point 2: X = 3, Y = 4
Point 3: X = 4, Y = 5
  

结构体嵌套

在 C、C++ 和 C# 中,结构体可以嵌套,即在一个结构体内部定义另一个结构体。

示例中struct Address被嵌套在struct Person中,可以使用person.address.streetptr->address.city访问嵌套结构体的成员。

  #include <stdio.h>

// 定义嵌套结构体
struct Address {
	char street[100];
	char city[50];
};

struct Person {
	char name[50];
	int age;
	struct Address address; // 嵌套结构体
};


int main() {
    // 初始化嵌套结构体
	struct Person person = { "John Doe",25,{"Burma Rd", "New York"} };
	struct Person *ptr = &person;
    
    // 访问嵌套结构体,用`.`或`->`操作符
	printf("Name: %s\n", person.name);
	printf("Age: %d\n", ptr->age);
	printf("Street: %s\n", person.address.street);
	printf("City: %s\n", ptr->address.city);

	return 0;
}
  
  🡮
Name: John Doe
Age: 25
Street: Burma Rd
City: New York
  

C++ 嵌套结构体与 C 类似,示例中struct Address被嵌套在struct Person中,可以使用person.address.streetptr->address.city访问嵌套结构体的成员。

  #include <iostream>
#include <format>

struct Address {
	std::string street;
	std::string city;

	Address(std::string s, std::string c):street(s),city(c){}
};

struct Person {
	std::string name;
	int age;
	Address address;

	void display() const {
		std::cout << std::format("Name: {}\n", name);
		std::cout << std::format("Age: {}\n", age);
		std::cout << std::format("Street: {}\n", address.street);
		std::cout << std::format("City: {}\n", address.city);
	}
};

int main() {
	Address addr("Burma Rd", "New York");
	Person person("John Doe", 25, addr);
	Person* ptr = &person;

	person.display();
	std::cout << std::format("Street: {}\n", person.address.street);
	std::cout << std::format("City: {}\n", ptr->address.city);

	return 0;
}
  
  🡮
Name: John Doe
Age: 25
Street: Burma Rd
City: New York
Street: Burma Rd
City: New York
  

示例中struct Address被嵌套在struct Person中,可以使用person.Address.Street访问嵌套结构体的成员。

  using System;
using System.Net.Sockets;

struct Address
{
    public string Street;
    public string City;

    public Address(string street, string city)
    {
        Street = street;
        City = city;
    }
}

struct Person
{
    public string Name;
    public int Age;
    public Address Address;

    public Person(string name, int age, Address address)
    {
        Name = name;
        Age = age;
        Address = address;
    }
    
    public void Display()
    {
        Console.WriteLine($"Name: {Name}");
        Console.WriteLine($"Age: {Age}");
        Console.WriteLine($"Street: {Address.Street}");
        Console.WriteLine($"City: {Address.City}");
    }
}


class Program
{
    static void Main()
    {
        Address address = new Address("Burma Rd","New York");
        Person person = new Person("John Doe", 25, address);
        person.Display();

        Console.WriteLine($"Street: {person.Address.Street}");
    }
}
  
  🡮
Name: John Doe
Age: 25
Street: Burma Rd
City: New York
Street: Burma Rd
  

结构体作为函数参数和返回值

特性 C C++ C#
参数传递 按值、按指针 按值、按引用(&)、按指针 按值、按引用(ref)、按输出(out
返回值 返回副本 返回副本、引用、指针 返回副本、支持out关键字
  • 结构体作为参数传递

C 中的结构体可以按值传递(将整个结构体赋值一份传递给函数)和按指针传递(传递结构体的地址,函数可以直接操作原始数据)。

  #include <stdio.h>

// 定义结构体
struct Point {
	int x;
	int y;
};

// 按值传递
void printPointByValue(struct Point p) {
	printf("Point by value: (%d, %d)\n", p.x, p.y);
}

// 按指针传递
void modifyPointByPointer(struct Point* p) {
	p->x = 100; // 修改原始数据
	p->y = 200;
}


int main() {
	struct Point p1 = { 10,20 };

    // 按值传递
	printPointByValue(p1);

    // 按指针传递
	modifyPointByPointer(&p1);
	printf("Point after modify: (%d,%d)", p1.x, p1.y);

	return 0;
}
  
  🡮
Point by value: (10, 20)
Point after modify: (100,200)
  
  • 结构体作为返回值

C 中可以直接返回一个结构体值,但返回值通常是结构体的副本。

  #include <stdio.h>

// 定义结构体
struct Point {
	int x;
	int y;
};

// 返回结构体
struct Point createPoint(int x, int y) {
	struct Point p = { x,y };
	return p; // 返回结构体副本
}

int main() {
	struct Point p1 = createPoint(10, 20);
	printf("Created Point: (%d, %d)\n", p1.x, p1.y);

	return 0;
}
  
  🡮
Created Point: (10, 20)
  
  • 结构体作为参数传递

C++ 中的结构体作为参数传递包括按值传递(传递副本);按引用传递(使用引用&避开副本开销,并允许修改原始数据);按指针传递,与 C 类似(传递结构体的地址,函数可以直接操作原始数据)。

  #include <iostream>
#include <format>

// 定义结构体
struct Point {
	int x;
	int y;

    // 构造函数
	Point(int x_val, int y_val):x(x_val),y(y_val){}
};

// 按值传递
void printPoint(Point p) {
	std::cout << std::format("Point by value: ({}, {})\n", p.x, p.y);
}

// 按引用传递
void modifyPointByRef(Point& p) {
	p.x = 100;
	p.y = 200;
}

// 按指针传递
void modifyPointByPointer(Point* p) {
	p->x = 300;
	p->y = 400;
}

int main() {
	Point p1(10, 20);

    // 按值传递
	printPoint(p1);

    // 按引用传递
	modifyPointByRef(p1);
	std::cout << std::format("After modify by reference: ({}, {})\n", p1.x, p1.y);

    // 按指针传递
	modifyPointByPointer(&p1);
	std::cout << std::format("After modify by pointer: ({}, {})\n", p1.x, p1.y);

	return 0;
}
  
  🡮
Point by value: (10, 20)
After modify by reference: (100, 200)
After modify by pointer: (300, 400)
  
  • 结构体作为返回值

C++ 允许直接返回结构体值,也可以返回引用或指针。

  #include <iostream>
#include <format>

struct Point {
	int x;
	int y;

	Point(int x_val, int y_val):x(x_val),y(y_val){}
};

// 返回结构体
Point createPoint(int x, int y) {
	return Point(x, y); // 返回副本
}

// 返回引用(不推荐局部遍历引用)
Point& riskyReferenceReturn() {
	static Point p(0, 0); // 静态变量
	return p;
}

int main() {
	Point p1 = createPoint(10, 20);
	std::cout << std::format("Created Point: ({}, {})\n", p1.x, p1.y);

	Point p2 = riskyReferenceReturn();
	std::cout << std::format("Risky reference Point: ({}, {})\n", p2.x, p2.y);

	return 0;
}
  
  🡮
Created Point: (10, 20)
Risky reference Point: (0, 0)
  
  • 结构体作为参数传递

C# 结构体作为参数传递包括按值传递(传递副本,原始数据不会被修改);按引用传递(通过ref关键字传递引用,允许修改原始数据);按输出传递(使用out关键字返回计算后的值)。

  using System;

struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y) { 
        X= x; 
        Y = y;
    }
}

class Program
{
    static void Main()
    {
        Point p1=new Point(10,20);

        PrintPoint(p1);

        ModifyPoint(ref p1);
        Console.WriteLine($"After modify by ref: ({p1.X}, {p1.Y})");

        Point result;
        createPointByOut(out result, 30, 50);
        Console.WriteLine($"Result: ({result.X}, {result.Y})");

    }

    // 按值传递
    static void PrintPoint(Point p)
    {
        Console.WriteLine($"Point by value: ({p.X}, {p.Y})");
    }

    // 按引用传递
    static void ModifyPoint(ref Point p)
    {
        p.X = 100;
        p.Y = 200;
    }

    // 按输出 out 关键字传递
    static void createPointByOut(out Point p, int x, int y)
    {
        p = new Point(x, y);
    }

}
  
  🡮
Point by value: (10, 20)
After modify by ref: (100, 200)
Result: (30, 50)
  
  • 结构体作为返回值

C# 支持返回结构体值,并可通过out参数返回多个值。

  using System;

struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y) { 
        X= x; 
        Y = y;
    }
}

class Program
{
    static void Main()
    {
        Point p1 = CreatePoint(10, 20);
        Console.WriteLine($"Created Point: ({p1.X}, {p1.Y})");

    }

    static Point CreatePoint(int x, int y)
    {
        return new Point(x, y);
    }
}
  
  🡮
Created Point: (10, 20)
  

[C#] 结构体与接口

C# 中,结构体可以实现接口(interface),允许为值类型定义行为,并保持其轻量级的特性;结构体可以实现一个或多个接口,并需要为接口中定义的所有成员提供实现;当将一个结构体赋值给接口变量时,会发生装箱(boxing),导致性能开销。为了避免装箱,可以使用泛型约束(where T : IShape)避免装箱。

  using System;

interface IShape
{
    double GetArea();
    void Display()=>Console.WriteLine("Default Display Implementation!");
}

struct Rectangle : IShape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public Rectangle(double width, double height)
    {
        Width = width; Height = height;
    }

    // 实现接口方法
    public double GetArea()
    {
        return Width * Height;
    }

    public void Display()
    {
        Console.WriteLine($"Rectangle: Width = {Width}, Height = {Height}, Area = {GetArea()}");
    }

}

class Program
{
    static void Main()
    {
        // 使用结构体
        Rectangle rectangle = new Rectangle(10,20);
        rectangle.Display();

        // 将结构体赋值给接口
        IShape shape = rectangle;
        shape.Display(); // 掉用接口的方法
        DisplayShape(shape); // 使用泛型约束,避免装箱

        Console.WriteLine($"Area via interface: {shape.GetArea()}");  
    }

    static void DisplayShape<T>(T shape) where T : IShape
    {
        shape.Display();
    }
}
  
  🡮
Rectangle: Width = 10, Height = 20, Area = 200
Rectangle: Width = 10, Height = 20, Area = 200
Rectangle: Width = 10, Height = 20, Area = 200
Area via interface: 200
  

🤖 代码解释

  static void DisplayShape<T>(T shape) where T : IShape
{
    shape.Display();
}
  
  1. static表示该方法是静态方法,不依赖于类的实例,可以直接通过类名调用。
  2. void为方法的返回类型,表示此方法不返回任何值。
  3. DisplayShape<T>是泛型方法定义,<T>表示该方法是一个泛型方法,可以接收任何类型的参数,但前提是该类型满足后续的约束条件。
  4. (T shape)是方法参数,T是类型参数,表示调用方法时传入的具体类型。shape是参数名,表示传入该方法的实际对象。
  5. where T : IShape为泛型约束。where T : IShape限制了类型参数T,表示T必须实现接口IShape。这样,shape就可以安全地调用IShape接口中的成员,例如Display()方法。
  6. shape.Display();中,shape是传入的泛型类型参数,一个实现了IShape接口的对象。通过IShape接口调用其定义的Display方法。因为类型参数T被约束为实现了IShape,所以编译器知道T类型中肯定有Display()方法。

8.2 联合体

联合体(union)是 C/C++ 中的一种特殊数据类型,允许在同一内存空间中存储不同的数据类型。联合体的定义如,

  union UnionName{
    int intValue;
    float floatValue;
    char charArray[4];
}
  
联合体 C C++
  #include <stdio.h>

// 定义联合体
union Data {
	int i;
	float f;
	char str[20];
};


int main() {
	union Data data;

    // 赋值整型
	data.i = 10;
	printf("data.i = %d\n", data.i);

    // 赋值浮点型,会覆盖前面的整型值
	data.f = 365.5;
	printf("data.f = %.2f\n", data.f);

    // 赋值字符串,再次覆盖
	snprintf(data.str, 20,"Hello Union!");
	printf("data.str = %s\n", data.str);

    // 浮点型成员被覆盖后,输出为错误信息
	printf("data.f = %.2f\n", data.f);

	return 0;
}
  
  🡮
data.i = 10
data.f = 365.50
data.str = Hello Union!
data.f = 1143139122437582505939828736.00
  
  #include <iostream>
#include <format>

// 定义联合体
union Data {
	int i;
	float f;
	char str[20];

    // C++ 中可以添加构造函数
	Data() : i(7){} // 初始化成员 i 值为7
};


int main() {
	Data data;

    // 打印整型成员,值为默认初始值
	std::cout << std::format("data.i = {}\n",data.i);

    // 赋值浮点型,会覆盖前面的整型值
	data.f = 365.5;
	std::cout << std::format("data.f = {}\n", data.f);

    // 赋值字符串,再次覆盖
	snprintf(data.str, 20,"Hello Union!");
	std::cout << std::format("data.str = {}\n", data.str);

    // 浮点型成员被覆盖后,输出为错误信息
	std::cout << std::format("data.f = {}\n", data.f);

	return 0;
}
  
  🡮
data.i = 7
data.f = 365.5
data.str = Hello Union!
data.f = 1.1431391e+27