gRPC之Protobuf语法

1、Protobuf语法

gRPC推荐使用proto3,这里只介绍常用语法,更多高级使用方法请参考官方文档:

https://developers.google.com/protocol-buffers/doc

1.1 Message定义

一个message类型定义描述了一个请求或响应的消息格式,可以包含多种类型字段。

例如定义一个搜索请求的消息格式SearchRequest,每个请求包含查询字符串、页码、每页数目。每个字段声明

以分号结尾。

syntax = "proto3";
message SearchRequest {string query = 1;int32  page_number = 2;int32  result_per_page = 3;
}

首行要求明确声明使用的protobuf版本为proto3,如果不声明,编译器默认使用proto2。本声明必须在文件的

首行。

一个.proto文件中可以定义多个message,一般用于同时定义多个相关的message,例如在同一个.proto文件中

同时定义搜索请求和响应消息:

syntax = "proto3";
message SearchRequest {string query = 1;int32  page_number = 2;int32  result_per_page = 3;
}
message SearchResponse {...
}

1.2 字段类型声明

所有的字段需要前置声明数据类型,上面的示例指定了两个数值类型和一个字符串类型。除了基本的标量类型还有

复合类型,如枚举、map、数组、甚至其它message类型等,后面会依次说明。

1.3 分配Tags

消息的定义中,每个字段都有一个唯一的数值标签。这些标签用于标识该字段在消息中的二进制格式,使用中的类

型不应该随意改动。其中,[1-15]内的标识在编码时占用一个字节,包含标识和字段类型。[16-2047]之间的标识

符占用2个字节。建议为频繁出现的消息元素分配[1-15]间的标签。如果考虑到以后可能或扩展频繁元素,可以预

留一些。

最小的标识符可以从1开始,最大到229 - 1,或536,870,911。不可以使用[19000-19999]之间的标识符,

Protobuf协议实现中预留了这些标识符。在.proto文件中使用这些预留标识号,编译时会报错。

1.4 字段规则

  • 单数形态:一个message内同名单数形态的字段不能超过一个

  • repeated:前置repeated关键词,声明该字段为数组类型

  • proto3不支持proto2中的requiredoptional关键字

repeated是protobuf中的一种限定修饰符,从字面意思看有重复的意思,实际上它就是用来指定某一个字段可

以存放同一个类型的多个数据(当然也可以是0个或者1个),相当于C++中的vector或者Java中的List。

1.5 添加注释

.proto文件中添加注释,支持C/C++风格双斜线//单行注释。

syntax = "proto3";              // 协议版本声明
// SearchRequest 搜索请求消息
message SearchRequest {string query = 1;           // 查询字符串int32  page_number = 2;     // 页码int32  result_per_page = 3; // 每页条数
}

1.6 保留字段名与Tag

可以使用reserved关键字指定保留字段名和标签。

message Foo {reserved 2, 15, 9 to 11;reserved "foo", "bar";
}

注意,不能在一个reserved声明中混合字段名和标签。

1.7 .proto文件编译结果

当使用protocol buffer编译器运行.proto文件时,编译器将生成所选语言的代码,用于使用在.proto文件中定

义的消息类型、服务接口约定等。不同语言生成的代码格式不同:

  • C++:每个.proto文件生成一个.h文件和一个.cc文件,每个消息类型对应一个类。

  • Java:生成一个.java文件,同样每个消息对应一个类,同时还有一个特殊的Builder类用于创建消息接口

  • Python: 姿势不太一样,每个.proto文件中的消息类型生成一个含有静态描述符的模块,该模块与一个元类

    metaclass在运行时创建需要的Python数据访问类。

  • Go:生成一个.pb.go文件,每个消息类型对应一个结构体。

  • Ruby:生成一个.rb文件的Ruby模块,包含所有消息类型。

  • JavaNano:类似Java,但不包含Builder类。

  • Objective-C:每个.proto文件生成一个pbobjc.h和一个pbobjc.m文件。

  • C#:生成.cs文件包含,每个消息类型对应一个类。

各种语言的更多的使用方法请参考官方API文档:

https://developers.google.com/protocol-buffers/docs/reference/overview

1.8 基本数据类型

.protoC++JavaPythonGoRubyC#
doubledoubledoublefloatfloat64Floatdouble
floatfloatfloatfloatfloat32Floatfloat
int32int32intintint32Fixnum or Bignumint
int64int64longint/long[3]int64Bignumlong
uint32uint32int[1]int/long[3]uint32Fixnum or Bignumuint
uint64uint64long[1]int/long[3]uint64Bignumulong
sint32int32intintint32Fixnum or Bignumint
sint64int64longint/long[3]int64Bignumlong
fixed32uint32int[1]intuint32Fixnum or Bignumuint
fixed64uint64long[1]int/long[3]uint64Bignumulong
sfixed32int32intintint32Fixnum or Bignumint
sfixed64int64longint/long[3]int64Bignumlong
boolboolbooleanbooleanboolTrueClass/FalseClassbool
stringstringStringstr/unicode[4]stringString(UTF-8)string
bytesstringByteStringstr[]byteString(ASCII-8BIT)ByteString

关于这些类型在序列化时的编码规则请参考Protocol Buffer Encoding

https://developers.google.com/protocol-buffers/docs/encoding

1.9 默认值

  • 字符串类型默认为空字符串

  • 字节类型默认为空字节

  • 布尔类型默认false

  • 数值类型默认为0值

  • enums类型默认为第一个定义的枚举值,必须是0

针对不同语言的默认值的具体行为参考 generated code guide

https://developers.google.com/protocol-buffers/docs/reference/overview

1.10 枚举(Enum)

当定义一个message时,想要一个字段只能是一个预定义好的值列表内的一个值,就需要用到enum类型了。

示例:这里定义一个名为Corpus的enum类型值,并且指定一个字段为Corpus类型。

message SearchRequest {string query = 1;int32 page_number = 2;int32 result_per_page = 3;// 定义enum类型enum Corpus {UNIVERSAL = 0;WEB = 1;IMAGES = 2;LOCAL = 3;NEWS = 4;PRODUCTS = 5;VIDEO = 6;}Corpus corpus = 4; // 使用Corpus作为字段类型
}

注意:每个enum定义的第一个元素值必须是0。

还可以通过给不同的enum元素赋相同的值来定义别名,要求设置allow_alias选项的值为true,否则会报错。

// 正确示例
enum EnumAllowingAlias {option allow_alias = true; // 开启allow_alias选项UNKNOWN = 0;STARTED = 1;RUNNING = 1;  // RUNNING和STARTED互为别名
}
// 错误示例
enum EnumNotAllowingAlias {UNKNOWN = 0;STARTED = 1;RUNNING = 1;  // 未开启allow_alias选项,编译会报错
}

enum类型值同样支持文件级定义和message内定义,引用方式与message嵌套一致。

1.11 使用其它Message

可以使用其它message类型作为字段类型。

例如,在SearchResponse中包含Result类型的消息,可以在相同的.proto文件中定义Result消息类型,然后

SearchResponse中指定字段类型为Result:

message SearchResponse {repeated Result results = 1;
}
message Result {string url = 1;string title = 2;repeated string snippets = 3;
}

1.12 导入定义(import)

上面的例子中,Result类型和SearchResponse类型定义在同一个.proto文件中,我们还可以使用import语句

导入使用其它描述文件中声明的类型:

import "others.proto";

默认情况,只能使用直接导入的.proto文件内的定义。但是有时候需要移动.proto文件到其它位置,为了避免

更新所有相关文件,可以在原位置放置一个模型.proto文件,使用public关键字,转发所有对新文件内容的引

用,例如:

// new.proto
// 所有新的定义在这里
// old.proto
// 客户端导入的原来的proto文件
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// 这里可以使用old.proto和new.proto文件中的定义,但是不能使用other.proto文件中的定义。

protocol编译器会在编译命令中 -I/--proto_path参数指定的目录中查找导入的文件,如果没有指定该参数,

默认在当前目录中查找。

1.13 Message嵌套

上面已经介绍过可以嵌套使用另一个message作为字段类型,其实还可以在一个message内部定义另一个

message类型,作为子级message。

示例:Result类型在SearchResponse类型中定义并直接使用,作为results字段的类型。

message SearchResponse {message Result {string url = 1;string title = 2;repeated string snippets = 3;}repeated Result results = 1;
}

内部声明的message类型名称只可在内部直接使用,在外部引用需要前置父级message名称,如Parent.Type

message SomeOtherMessage {SearchResponse.Result result = 1;
}

支持多层嵌套:

message Outer {                // Level 0message MiddleAA {         // Level 1message Inner {        // Level 2int64 ival = 1;bool  booly = 2;}}message MiddleBB {         // Level 1message Inner {        // Level 2int32 ival = 1;bool  booly = 2;}}
}

1.14 Map类型

proto3支持map类型声明:

map<key_type, value_type> map_field = N;
message Project {...}
map<string, Project> projects = 1;
  • key_type类型可以是内置的标量类型(除浮点类型和bytes)

  • value_type可以是除map以外的任意类型

  • map字段不支持repeated属性

  • 不要依赖map类型的字段顺序

1.15 包(Packages)

.proto文件中使用package声明包名,避免命名冲突。

syntax = "proto3";
package foo.bar;
message Open {...}

在其他的消息格式定义中可以使用包名+消息名的方式来使用类型,如:

message Foo {...foo.bar.Open open = 1;...
}

在不同的语言中,包名定义对编译后生成的代码的影响不同:

  • C++ 中:对应C++命名空间,例如Open会在命名空间foo::bar中。

  • Java 中:package会作为Java包名,除非指定了option jave_package选项。

  • Python 中:package被忽略。

  • Go 中:默认使用package名作为包名,除非指定了option go_package选项。

  • JavaNano 中:同Java。

  • C# 中:package会转换为驼峰式命名空间,如Foo.Bar,除非指定了option csharp_namespace选项。

1.16 定义服务(Service)

如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol编译

器会根据所选择的不同语言生成服务接口代码。例如,想要定义一个RPC服务并具有一个方法,该方法接收

SearchRequest并返回一个SearchResponse,此时可以在.proto文件中进行如下定义:

service SearchService {rpc Search (SearchRequest) returns (SearchResponse) {}
}

生成的接口代码作为客户端与服务端的约定,服务端必须实现定义的所有接口方法,客户端直接调用同名方法向服

务端发起请求。

1.17 选项(Options)

在定义.proto文件时可以标注一系列的options。Options并不改变整个文件声明的含义,但却可以影响特定环境

下处理方式。完整的可用选项可以查看google/protobuf/descriptor.proto

一些选项是文件级别的,意味着它可以作用于顶层作用域,不包含在任何消息内部、enum或服务定义中。一些选

项是消息级别的,可以用在消息定义的内部。

以下是一些常用的选项:

  • java_package (file option):指定生成java类所在的包,如果在.proto文件中没有明确的声明

    java_package,会使用默认包名。不需要生成java代码时不起作用。

  • java_outer_classname (file option):指定生成Java类的名称,如果在.proto文件中没有明确声明

    java_outer_classname,生成的class名称将会根据.proto文件的名称采用驼峰式的命名方式进行生成。如

    (foo_bar.proto生成的java类名为FooBar.java),不需要生成java代码时不起任何作用。

  • objc_class_prefix (file option): 指定Objective-C类前缀,会前置在所有类和枚举类型名之前。没有默认

    值,应该使用3-5个大写字母。注意所有2个字母的前缀是Apple保留的。

1.18 基本规范

  • 描述文件以.proto做为文件后缀,除结构定义外的语句以分号结尾

    结构定义包括:message、service、enum

    rpc方法定义结尾的分号可有可无

  • Message命名采用驼峰命名方式,字段命名采用小写字母加下划线分隔方式

    message SongServerRequest {required string song_name = 1;
    }
    
  • Enums类型名采用驼峰命名方式,字段命名采用大写字母加下划线分隔方式

    enum Foo {FIRST_VALUE = 1;SECOND_VALUE = 2;
    }
    
  • Service名称与RPC方法名统一采用驼峰式命名

1.19 编译

通过定义好的.proto文件生成Java, Python, C++, Go, Ruby, JavaNano, Objective-C, or C# 代码,需要安装编译

protoc。参考Github项目google/protobuf(https://github.com/google/protobuf)安装编译器。

运行命令:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --javanano_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto

这里只做参考就好,具体语言的编译实例请参考详细文档。

  • [Go generated code reference]

    https://developers.google.com/protocol-buffers/docs/reference/go-generated

  • [C++ generated code reference]

    https://developers.google.com/protocol-buffers/docs/reference/cpp-generated

  • [Java generated code reference]

    https://developers.google.com/protocol-buffers/docs/reference/java-generated

  • [Python generated code reference]

    https://developers.google.com/protocol-buffers/docs/reference/python-generated

  • [Objective-C generated code reference]

    https://developers.google.com/protocol-buffers/docs/reference/objective-c-generated

  • [C# generated code reference]

    https://developers.google.com/protocol-buffers/docs/reference/csharp-generated

  • [JavaScript Generated Code Guide]

    https://developers.google.com/protocol-buffers/docs/reference/javascript-generated

  • [PHP Generated Code Guide]

    https://developers.google.com/protocol-buffers/docs/reference/php-generated

照着官方文档一步步操作不一定成功哦!

1.20 更多

  • [Any类型]

    https://developers.google.com/protocol-buffers/docs/proto3#any

  • [Oneof]

    https://developers.google.com/protocol-buffers/docs/proto3#oneof

  • [自定义Options]

    https://developers.google.com/protocol-buffers/docs/proto.html#customoptions

这些用法在实践中很少使用,这里不做介绍,有需要请参考官方文档:

https://developers.google.com/protocol-buffers/

1.21 Protobuf⇢Go的转换说明

这里使用一个测试文件对照说明常用结构的protobufgo的转换。

示例test.proto

syntax = "proto3";
package test;
option go_package = "./test";message Test {int32 age = 1;int64 count = 2;double money = 3;float score = 4;string name = 5;bool fat = 6;bytes char = 7;enum Status {OK = 0;FAIL = 1;}Status status = 8;message Child {string sex = 1;}Child child = 9;map<string, string> dict = 10;
}service TestService {rpc Test(Request) returns (Response) {};
}message Request {string name = 1;
}message Response {string message = 1;
}
$ protoc -I . --go_out=plugins=grpc:. test.proto
# 树结构
$ tree demo
demo
├── go.mod
├── test
│   └── test.pb.go
└── test.proto1 directory, 3 files

1.21.1 Package

在proto文件中使用package关键字声明包名,默认转换成go中的包名与此一致,如果需要指定不一样的包名,可

以使用go_package选项:

package test;
option go_package = "./test";

1.21.2 Message

proto中的message对应go中的struct,全部使用驼峰命名规则。嵌套定义的messageenum转换为go之后,

名称变为Parent_Child结构。

// Status 枚举状态
type Test_Status int32const (Test_OK   Test_Status = 0Test_FAIL Test_Status = 1
)// Child 子结构
type Test_Child struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsSex string `protobuf:"bytes,1,opt,name=sex,proto3" json:"sex,omitempty"`
}// 整体结构
type Test struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsAge    int32             `protobuf:"varint,1,opt,name=age,proto3" json:"age,omitempty"`Count  int64             `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`Money  float64           `protobuf:"fixed64,3,opt,name=money,proto3" json:"money,omitempty"`Score  float32           `protobuf:"fixed32,4,opt,name=score,proto3" json:"score,omitempty"`Name   string            `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"`Fat    bool              `protobuf:"varint,6,opt,name=fat,proto3" json:"fat,omitempty"`Char   []byte            `protobuf:"bytes,7,opt,name=char,proto3" json:"char,omitempty"`Status Test_Status       `protobuf:"varint,8,opt,name=status,proto3,enum=test.Test_Status" json:"status,omitempty"`Child  *Test_Child       `protobuf:"bytes,9,opt,name=child,proto3" json:"child,omitempty"`Dict   map[string]string `protobuf:"bytes,10,rep,name=dict,proto3" json:"dict,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}

除了会生成对应的结构外,还会有些工具方法,如字段的getter:

func (x *Test) GetAge() int32 {if x != nil {return x.Age}return 0
}func (x *Test) GetCount() int64 {if x != nil {return x.Count}return 0
}

枚举类型会生成对应名称的常量,同时会有两个map方便使用:

// Enum value maps for Test_Status.
var (Test_Status_name = map[int32]string{0: "OK",1: "FAIL",}Test_Status_value = map[string]int32{"OK":   0,"FAIL": 1,}
)

1.21.3 Service

TestService有一个方法Test,接收一个Request参数,返回Response

// 客户端接口
type TestServiceClient interface {Test(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}// 服务端接口
type TestServiceServer interface {Test(context.Context, *Request) (*Response, error)
}

生成的go代码中包含该Service定义的接口,客户端接口已经自动实现了,直接供客户端使用者调用,服务端接口

需要由服务提供方实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/80900.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DC系列靶机5通关教程

信息收集 主机扫描 sudo arp-scan -l端口扫描 nmap -p- -A 192.168.16.172漏洞发现 浏览器访问靶机IP 在Contact找到类似提交数据的地方 点击submit&#xff0c;数字发生变化。不断刷新的话&#xff0c;数字依然会发生变化 使用bp抓包发送重发器查看数据包 再次点击发送查看…

k8s优雅停服

在应用程序的整个生命周期中&#xff0c;正在运行的 pod 会由于多种原因而终止。在某些情况下&#xff0c;Kubernetes 会因用户输入&#xff08;例如更新或删除 Deployment 时&#xff09;而终止 pod。在其他情况下&#xff0c;Kubernetes 需要释放给定节点上的资源时会终止 po…

浅谈C++|类的成员

一.类对象作为类成员 类可以作为另一个类的成员 代码&#xff1a; #include <iostream> using namespace std; class phone { public:string shouji;phone(string shouji1) :shouji(shouji1) {cout << "phone的构造函数调用" << endl;}~phone() …

Mac版本破解Typora,解决Mac安装软件的“已损坏,无法打开。 您应该将它移到废纸篓”问题

一、修改配置文件 首先去官网选择mac版本下载安装 typora下载 然后打开typora包内容找到 /Applications/Typora.app/Contents/Resources/TypeMark/ 编辑器打开上面文件夹&#xff0c;这里我拉到vscode 找到page-dist/static/js/Licen..如下图 输入 hasActivated"…

周易算卦流程c++实现

代码 #include<iostream> using namespace std; #include<vector> #include<cstdlib> #include<ctime> #include<Windows.h>int huaYiXiangLiang(int all, int& left) {Sleep(3000);srand(time(0));left rand() % all 1;while (true) {if…

实时语音通讯技术:多人通话和语音识别

实时语音通讯技术是一种基于网络传输的语音通讯技术&#xff0c;可以实现语音通话、语音聊天、语音会议等功能。随着互联网的发展&#xff0c;实时语音通讯技术越来越受到人们的关注和应用。本文将重点介绍实时语音通讯技术中的多人通话和语音识别两个方面。 多人通话 多人通…

Dell 服务器远程安装操作系统

登录Dell服务器管理页面 1、网络连接服务器的idrac的管理口 2、确保本地电脑与服务器的管理口可以正常通信。通过浏览器访问服务器idrac口的ip地址。建议使用IE浏览器。 默认IP地址: 192.168.0.120 子网掩码: 255.255.255.0 默认网关: 192.168.0.1 默认用户名: …

LeetCode算法心得——和可被 K 整除的子数组(前缀和+HashMap)

大家好&#xff0c;我是晴天学长&#xff0c;同余定理的应用&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。 1) .和可被 K 整除的子数组 题目描述 给定一个整数数组 A&#xff0c;返回其中元素之和可被 K 整除的&#xff08;连续、非空&#xff0…

Linux网络基础

一.协议的概念 1.1协议的概念 什么是协议 从应用的角度出发&#xff0c;协议可理解为“规则”&#xff0c;是数据传输和数据的解释的规则。假设&#xff0c;A、B双方欲传输文件。规定: 第一次&#xff0c;传输文件名&#xff0c;接收方接收到文件名&#xff0c;应答OK给传输方…

合宙Air724UG LuatOS-Air LVGL API控件-截屏(Screenshots)

截屏&#xff08;Screenshots&#xff09; 分 享导出pdf 截屏功能&#xff0c;core版本号要>3211 示例代码 -- 创建图片控件img lvgl.img_create(lvgl.scr_act(), nil)-- 设置图片显示的图像lvgl.img_set_src(img, "/lua/test.png")-- 图片居中lvgl.obj_align(…

QT基础教学(QMainWindow)

文章目录 前言一、QMainWindow介绍二、代码示例三、QMainWindow高级用法总结 前言 之前我们都是在QWidget中来进行学习的&#xff0c;那么今天我将为大家讲解一下QMainWindow。 一、QMainWindow介绍 QMainWindow是Qt框架提供的一个重要的窗口类&#xff0c;用于创建具有标准…

c语言输出杨辉三角

#include<stdio.h> int main() {int x 0; //表示杨辉三角的的大小int y 1;printf("请输入x的值: ");scanf("%d", &x);for (int i 0; i < x; i) {for (int j 0; j < i; j) {if (j 0 || i 0) {y 1;}else {y y * (i - j 1) / j;}pri…

c#动态保留小数位数的数值格式化方法实例----从小数点后非零数字保留两位进行四舍五入

c#动态保留小数位数的数值格式化方法实例----从小数点后非零数字保留两位进行四舍五入 1、功能介绍2、代码案例3、输出结果4、封装扩展方法5、控制台调用 6、其他方法地址 1、功能介绍 1. 输入的数字是整数&#xff0c;则直接返回整数部分的字符串表示。 2. 如果输入的数字是…

2023-09-13力扣每日一题

链接&#xff1a; 2596. 检查骑士巡视方案 题意 骑士走日字&#xff0c;就是absx2 absy1或absx1 absy2 解&#xff1a; 一共就那么点格子&#xff0c;暴力搜八个方向也行 先用数组存每一步对应的位置&#xff0c;然后判断相邻两个位置是否符合日字 我直接用absxabsy3就过…

SpringBoot集成Apache RocketMQ详解

文章目录 0. 前言1. Spring Boot 集成Apache RocketMQ详细步骤1.1.添加依赖1.2.配置RocketMQ1.3.创建消息生产者&#xff08;Producer&#xff09;1.4.创建消息消费者&#xff08;Consumer&#xff09; 2. 测试验证3. 常见报错4. 参考文档5. 源码地址 0. 前言 上个章节我们学习…

宝塔安装python和openssl

宝塔安装python和openssl OpenSSL Centos7 openssl 升级 1.1.1k.tar.gz centos7系统安装Vicuna&#xff08;小羊驼&#xff09;聊天机器人 CentOS中输入yum报错&#xff1a;sudo: unable to execute /bin/yum: No such file or directory opensslrpm安装指南-让你的网站更加…

4G版本云音响设置教程腾讯云平台版本

文章目录 4G本云音响设置教程介绍一、申请设备三元素1.腾讯云物联网平台2.创建产品3.设置产品参数4.添加设备5.获取三元素 二、设置设备三元素1.打开MQTTConfigTools2.计算MQTT参数3.使用USB连接设备4.设置参数 三、腾讯云物联网套件协议使用说明1.推送协议信息2.topic规则说明…

JDK16特性

文章目录 一、JAVA16概述二、语法层面变化1.密封类&#xff08;第二次预览&#xff09;2.instanceof 的模式匹配3.记录类习惯4基于值的类的警告 三、API层面变化1 Vector API&#xff08;孵化器&#xff09; 四、其他变化1.启用 C14 语言功能2.从 Mercurial 迁移到 Git3.ZGC&am…

Microsoft Excel 101 简介

什么是 Microsoft Excel&#xff1f; Microsoft Excel 是一个电子表格程序&#xff0c;用于记录和分析数值数据。 将电子表格想像成构成表格的列和行的集合。 字母通常分配给列&#xff0c;数字通常分配给行。 列和行相交的点称为像元。 单元格的地址由代表列的字母和代表行的…

【C++】LeetCode 160 相交链表

今天再写一道算法题&#xff08;这两周都写算法题有点摆烂&#xff09; 题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1…