高效c语言1快速入门

Posted pythontesting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高效c语言1快速入门相关的知识,希望对你有一定的参考价值。

本章将开发你的第一个C语言程序:传统的 "Hello, world!"程序。然后讨论一些编辑器和编译器的选项,并阐述移植性问题。

Hello, world!

#include <stdio.h>
#include <stdlib.h>

int main(void) 
puts("Hello, world!");
return EXIT_SUCCESS;

在Linux和其他类似Unix的操作系统上,你可以用cc命令调用系统编译器:

$cc hello.c
$ls
a.out hello.c
$./a.out
Hello, world!
% cc -o hello hello.c
% ./hello
Hello, world!

cc命令有许多标志和编译器选项。例如,-o文件标志让你给可执行文件起名字,而不是a.out。

hello.c程序的前两行使用了#include预处理器指令,它的行为就像你在完全相同的位置用指定文件的内容替换它一样。我们包括<stdio.h>和<stdlib.h>头文件来访问这些头文件中声明的函数,然后我们可以在程序中调用这些函数。puts函数在<stdio.h>中声明,而EXIT_SUCCESS宏在<stdlib.h>中定义。正如文件名所示,<stdio.h>包含了C语言标准I/O函数的声明,而<stdlib.h>则包含了一般实用函数的声明。

C定义了两种可能的执行环境:独立的和托管的。独立环境可能不提供操作系统,通常用于嵌入式编程。这些执行环境提供了一套最小的库函数,程序启动时调用的函数的名称和类型是执行环境定义的。

我们定义main返回int类型的值,并将void放在括号内,表示该函数不接受参数。int类型是有符号的整数类型,可以用来表示正、负整数值以及零。与其他程序性语言类似,C语言程序由可以接受参数和返回值的过程(称为函数)组成。每个函数都可重用,你可以根据需要在程序中频繁调用。在本例中,主函数返回的值表示程序是否成功终止。

puts("Hello, world!")打印出"Hello, world!"。 puts函数是标准库函数,它将字符串参数写入stdout(通常代表控制台或终端窗口),并在输出中附加换行符。如果不需要换行可以使用fputs。

return语句退出程序,向主机环境或调用脚本返回一个整数值。EXIT_SUCCESS是类似对象的宏,通常扩展为0,通常定义为:#define EXIT_SUCCESS 0。

检查函数的返回值

函数通常会返回一个计算结果的值,或者表示函数是否成功完成了它的任务。例如,我们在 "Hello, world!"程序中使用的puts函数需要打印字符串并返回int类型的值。如果发生写入错误,puts函数返回宏EOF的值(负整数);否则,它返回非负的整数值。

尽管对于我们的简单程序来说,puts函数不太可能失败并返回EOF,但这是可能的。因为对puts的调用可能会失败并返回EOF,这意味着你的第一个C程序有bug,或者,可以按以下方法改进。

#include <stdio.h>
#include <stdlib.h>
int main(void) 
  if (puts("Hello, world!") == EOF) 
    return EXIT_FAILURE;
    // code here never executes
  
  return EXIT_SUCCESS;
  // code here never executes

注意(puts("Hello, world!")一定要有括号,否则编译会报错:

c$ cc hello2.c
hello2.c: In function ‘main’:
hello2.c:5:5: error: expected ‘(’ before ‘puts’
5 | if puts("Hello, world!") == EOF 
| ^~~~
| (

格式化的输出

puts函数是一种将字符串写入stdout的简单好方法,但最终你会需要使用printf函数来打印格式化的输出--例如,打印字符串以外的参数。printf函数接收定义输出格式的格式化字符串,然后是可变数量的参数,这些参数是你想打印的实际数值。例如,如果你想用printf函数来打印Hello, world!,你可以这样写。
printf("%s\\n", "Hello, world!")。

第一个参数是格式字符串"%s\\n"。%s是转换规范,指示printf函数读取第二个参数(字符串字面)并将其打印到stdout。\\n是一个字母转义序列,用于表示非图形字符,并告诉函数在该字符串后面包括新行。

注意不要将用户提供的数据作为第一个参数的一部分传递给printf函数,因为这样做会导致格式化输出的安全漏洞(Seacord 2013)。

编辑器和集成开发环境

可以使用各种编辑器和集成开发环境来开发你的C语言程序。图1-1显示了最常用的编辑器,根据2018年JetBrains的调查。

对于Microsoft Windows,Microsoft的Visual Studio IDE(https://visualstudio.microsoft.com/)是不错的选择。Visual Studio有三个版本。社区版、专业版和企业版。社区版的优点是免费,而其他版本的功能则需要付费。

对于Linux来说,Vim、Emacs、Visual Studio Code和Eclipse都可选择。Vim是许多开发者和高级用户的首选编辑器。它是一个基于vi编辑器的文本编辑器,由Bill Joy在1970年代为Unix的一个版本编写。它继承了vi的按键绑定,但也增加了原vi所缺少的功能和可扩展性。你可以选择安装Vim插件,如YouCompleteMe(https://github.com/Valloric/YouCompleteMe/)或deoplete(https://github.com/Shougo/deoplete.nvim/),为C语言编程提供本地语义完成。

GNU Emacs是可扩展的、可定制的、免费的文本编辑器。它的核心是Emacs Lisp的解释器,这是一种Lisp编程语言的方言,具有支持文本编辑的扩展功能--尽管我从未发现这是个问题。

Visual Studio Code(VS Code)是精简的代码编辑器,支持开发操作,如调试、任务运行和版本控制。它提供了开发人员所需的工具,以实现快速的代码构建--调试循环。VS Code可以在macOS、Linux和Windows上运行,对私人或商业使用都是免费的。

编译器

现在有很多C语言编译器,他么编译器实现了不同版本的C标准。许多用于嵌入式系统的编译器只支持C89/C90。用于Linux和Windows的流行编译器更努力地支持现代版本的C标准,直到并包括对C2x的支持。

GCC已经被采纳为Linux系统的标准编译器,尽管也有用于微软Windows、macOS和其他平台的版本。在Linux上安装GCC很容易。例如,下面的命令在Ubuntu上安装GCC 8。

$ sudo apt-get install gcc-9
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ sudo dnf install gcc # Fedora
$ sudo apt-get install clang

你可以用下面的命令测试你所使用的Clang的版本。
% clang --version
$ clang --version
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
  • 微软Visual Studio

Windows最流行的开发环境是Microsoft Visual Studio,它包括IDE和编译器。它与Visual C++ 2019捆绑在一起,其中包括C和C++编译器。
你可以在项目属性页上为Visual Studio设置选项。在C/C++下的高级选项卡上,确保你通过使用编译为C代码(/TC)选项而不是编译为C++代码(/TP)选项来编译为C代码。默认情况下,当你命名一个以.c为扩展名的文件时,它是用/TC编译的。如果文件被命名为.cpp、.cxx或其他一些扩展名,则用/TP编译。

参考资料

https://url97.ctfile.com/f/18113597-810419181-1f9306 下载密码 订阅号pythontesting 发送 密码 。

移植性

每个C语言编译器的实现都至少有一点不同。编译器不断发展,因此,例如,像GCC这样的编译器可能提供对C17的完全支持,但正在努力实现对C2x的支持,在这种情况下,它可能有一些C2x的功能实现,但没有其他。因此,编译器支持全部的C标准版本(包括中间的版本)。C语言实现的总体发展是缓慢的,许多编译器明显落后于C标准。

如果为C语言编写的程序只使用标准中规定的语言和库的那些功能,就可以认为是严格符合标准的。这些程序的目的是为了最大限度地提高可移植性。然而,由于实现行为的范围,现实世界中没有一个C语言程序是严格符合要求的,也不会是(可能也不应该是)。相反,C标准允许你编写符合要求的程序,这些程序可能依赖于非可移植的语言和库特性。

通常的做法是为一个参考实现编写代码,或者有时为几个实现编写代码,这取决于你打算在哪个平台上部署你的代码。C标准是确保这些实现不会有太大的差异,并允许你一次针对几个实现,而不必每次都学习一种新的语言。
在C标准文件的附件J中列举了五种可移植性问题。

  • 实现定义的行为

  • 未指定的行为

  • 未定义的行为

  • 针对本地的行为

  • 常见的扩展

  • 实现定义的行为
    实现定义的行为是指C语言标准中没有规定的程序行为,它可能在不同的实现中提供不同的结果,但在一个实现中具有一致的、有记录的行为。实现定义的行为的一个例子是一个字节中的位数。

实现定义的行为大多是无害的,但在移植到不同的实现时可能会导致缺陷。在可能的情况下,避免编写依赖于实现定义的行为的代码,这些行为在你可能用来编译你的代码的C实现中是不同的。C标准的附件J.3中列举了实现定义行为的完整列表。你可以通过使用static_assert声明来记录你对这些实现定义的行为的依赖。

  • 未指定的行为

未指定的行为是指标准提供了两个或多个选项的程序行为。该标准对在任何情况下选择哪个选项没有要求。每次执行一个给定的表达式可能会有不同的结果,或者产生与之前执行相同表达式不同的值。未指定行为的一个例子是函数参数存储布局,它在同一程序中的不同函数调用中可能会有所不同。避免编写依赖于C标准附件J.1中列举的非指定行为的代码。

  • 未定义的行为
    未定义的行为是指C标准没有定义的行为,或者说是 "在使用不可移植的或错误的程序结构或错误的数据时,标准没有规定的行为"。未定义行为的例子包括有符号的整数溢出和解读一个无效的指针值。具有未定义行为的代码往往是错误的,但比这更有细微差别。标准中对未定义行为的识别如下。

  • 当违反了 "应当 "或 "不应当 "的要求,并且该要求出现在约束条件之外时,该行为是未定义的

  • 当行为被明确规定为 "未定义行为 "时

  • 通过省略任何明确的行为定义
    前两种未定义行为经常被称为显式未定义行为,而第三种则被称为隐式未定义行为。这三者之间的重点没有区别,它们都描述了未定义的行为。C语言标准附件J.2 "未定义行为 "包含了C语言中显式未定义行为的列表。

开发者经常误认为未定义的行为是C标准中的错误或遗漏,但将行为归为未定义的决定是有意的,也是经过考虑的。C标准委员会将行为归类为未定义的行为是为了做到以下几点。

  • 给予实现者许可,使其不去捕捉难以诊断的程序错误
  • 避免定义晦涩难懂的案例,使之有利于一种实现策略而不是另一种策略
  • 识别可能的符合要求的语言扩展领域,在这些领域中,实现者可以通过提供官方未定义行为的定义来增强语言。
    这三个原因实际上是完全不同的,但都被认为是可移植性问题。编译器(实现)有做以下事情的余地。
  • 完全忽略未定义的行为,产生不可预测的结果
  • 以环境特征的文件方式行事(有或没有发出诊断书)。
  • 终止翻译或执行(发出诊断)。

这些选项都不是很好(尤其是第一个),所以最好避免未定义的行为,除非实现指定这些行为的定义是为了让你调用一个语言增强功能。

  • 特定于本地的行为和通用扩展

特定于本地的行为取决于每个实现所记录的国籍、文化和语言的本地惯例。通用扩展在许多系统中被广泛使用,但并不能移植到所有的实现中。

小结

在这章中,你学会了如何编写简单的C语言程序,编译它,并运行它。然后,我们看了几个编辑器和交互式开发环境,以及一些编译器,你可以用它们来开发Windows、Linux和macOS系统上的C语言程序。一般来说,你应该使用较新版本的编译器和其他工具,因为它们往往支持C编程语言的较新功能,并提供更好的诊断和优化。如果较新版本的编译器破坏了你现有的代码,或者你正准备部署你的代码,你可能不想使用较新版本的编译器,以避免在你已经测试过的应用程序中引入不必要的变化。在本章的最后,我们讨论了C语言程序的可移植性。

Go语言使用protobuf快速入门

前言

protobuf 即 Protocol Buffers,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。
protobuf 性能和效率大幅度优于 JSON、XML 等其他的结构化数据格式。
protobuf 是以二进制方式存储的,占用空间小,但也带来了可读性差的缺点。protobuf 在通信协议和数据存储等领域应用广泛。

Protobuf 在 .proto 定义需要处理的结构化数据,可以通过 protoc 工具,将 .proto 文件转换为 C、C++、Golang、Java、Python 等多种语言的代码,兼容性好,易于使用。

参考文献

本文文章持续更新于:https://github.com/mailjobblog/dev_go/tree/master/220115_protobuf
protobuf3 官方文档:https://link.jianshu.com/?t=https://developers.google.com/protocol-buffers/docs/proto3
Protocol Buffer 编码:https://developers.google.com/protocol-buffers/docs/encoding?hl=zh-cn#packed
proto service grpc 生成插件:https://github.com/protocolbuffers/protobuf/blob/master/docs/third_party.md
本文代码下载:https://github.com/mailjobblog/dev_go/tree/master/220115_protobuf

安装

安装 protoc
Protobuf Releases 下载最先版本的发布包安装。

brew intall protoc

安装 protoc-gen-go
我们需要在 Golang 中使用 protobuf,还需要安装 protoc-gen-go,这个工具用来将 .proto 文件转换为 Golang 代码。

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

Tips:
这儿有个小小的坑,github.com/golang/protobuf/protoc-gen-gogoogle.golang.org/protobuf/cmd/protoc-gen-go是不同的。
区别在于前者是旧版本,后者是google接管后的新版本,他们之间的API是不同的,也就是说用于生成的命令,以及生成的文件都是不一样的。

检查是否安装成功

$ protoc --version
libprotoc 3.19.3

$ protoc-gen-go --version
protoc-gen-go v1.27.1

protobuf生成代码

快速上手

接下来,我们创建一个非常简单的示例,student.proto

syntax = "proto3";
package main;

// this is a comment
message Student 
  string name = 1;
  bool male = 2;
  repeated int32 scores = 3;

在当前目录下执行代码生成命令

$ protoc --go_out=. *.proto

$ ls
student.pb.go  student.proto

执行此生成命令是将该目录下的所有的 .proto 文件转换为 Go 代码,我们可以看到该目录下多出了一个 Go 文件 student.pb.go。这个文件内部定义了一个结构体 Student,以及相关的方法:

type Student struct 
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name   string  `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Male   bool    `protobuf:"varint,2,opt,name=male,proto3" json:"male,omitempty"`
	Scores []int32 `protobuf:"varint,3,rep,packed,name=scores,proto3" json:"scores,omitempty"`

得到生成的 student.pb.go 文件后,可以在项目代码中直接使用了。
以下是一个例子,即证明被序列化的和反序列化后的实例,包含相同的数据。

func main() 
	test := &Student
		Name: "geektutu",
		Male:  true,
		Scores: []int3298, 85, 88,
	
	data, err := proto.Marshal(test)
	if err != nil 
		log.Fatal("marshaling error: ", err)
	
	newTest := &Student
	err = proto.Unmarshal(data, newTest)
	if err != nil 
		log.Fatal("unmarshaling error: ", err)
	
	// Now test and newTest contain the same data.
	if test.GetName() != newTest.GetName() 
		log.Fatalf("data mismatch %q != %q", test.GetName(), newTest.GetName())
	

枚举(Enumerations)

枚举类型适用于提供一组预定义的值,选择其中一个。例如我们将性别(gender)定义为枚举类型。

message StudentEnum 
  string name = 1;
  enum Gender 
    FEMALE = 0;
    MALE = 1;
  
  Gender gender = 2;
  repeated int32 scores = 3;

生成的Go代码主要信息如下:

type StudentEnum_Gender int32

const (
	StudentEnum_FEMALE StudentEnum_Gender = 0
	StudentEnum_MALE   StudentEnum_Gender = 1
)

type StudentEnum struct 
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields
    
    Name   string             `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
    Gender StudentEnum_Gender `protobuf:"varint,2,opt,name=gender,proto3,enum=main.StudentEnum_Gender" json:"gender,omitempty"`
    Scores []int32            `protobuf:"varint,3,rep,packed,name=scores,proto3" json:"scores,omitempty"`

枚举类型的第一个选项的标识符必须是0,这也是枚举类型的默认值。

别名(Alias)

允许为不同的枚举值赋予相同的标识符,称之为别名,需要打开allow_alias选项。

message StudentAlias 
  enum Status 
    option allow_alias = true;
    UNKOWN = 0;
    STARTED = 1;
    RUNNING = 1;
  

生成的Go语言主要代码如下:

type StudentAlias_Status int32

const (
	StudentAlias_UNKOWN  StudentAlias_Status = 0
	StudentAlias_STARTED StudentAlias_Status = 1
	StudentAlias_RUNNING StudentAlias_Status = 1
)

使用其他消息类型

Result是另一个消息类型,在 SearchReponse 作为一个消息字段类型使用。

message StudentResponse 
  repeated Result results = 1;


message Result 
  string url = 1;
  string title = 2;
  repeated string snippets = 3;

嵌套写也是支持的:

message Student2Response 
  message Result2 
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  
  repeated Result2 results2 = 1;

如果定义在其他文件中,可以导入其他消息类型来使用:

import "myproject/other_protos.proto";

任意类型(Any)

在使用 GRPC 时,常规的操作是将 message 定义好后进行数据传输,但总会遇到某些数据结构进行组合的操作,采用默认的定义 message 方式,造成代码量的激增。
为了解决这个问题 protobuf 提供类型 any 解决 GRPC 中泛型的处理方式

message StudentAny 
  string message = 1;
  repeated google.protobuf.Any details = 2;

生成的Go语言主要代码如下:

type StudentAny struct 
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Message string       `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
	Details []*anypb.Any `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty"`

oneof

如果你的消息中有很多可选字段, 并且同时至多一个字段会被设置, 你可以加强这个行为,使用oneof特性节省内存。
Oneof字段就像可选字段, 除了它们会共享内存, 至多一个字段会被设置。 设置其中一个字段会清除其它字段。

message StudentOneOf 
  oneof test_oneof 
    string name = 4;
    SubMessage sub_message = 9;
  

oneof的特性

  • 设置oneof会自动清楚其它oneof字段的值. 所以设置多次后,只有最后一次设置的字段有值
  • 如果解析器遇到同一个oneof中有多个成员,只有最后一个会被解析成消息
  • oneof不支持repeated

map

message StudentMap 
  map<string, int32> points = 1;

定义服务(Services)

如果消息类型是用来远程通信的(Remote Procedure Call, RPC),可以在 .proto 文件中定义 RPC 服务接口。
例如我们定义了一个名为 SearchService 的 RPC 服务,提供了 Search 接口,入参是 SearchRequest 类型,返回类型是 SearchResponse

service SearchService 
  rpc Search (SearchRequest) returns (SearchResponse);

官方仓库也提供了一个 插件列表,帮助开发基于 Protocol Buffer 的 RPC 服务。

生成go代码和grpc代码:

# 由proto生成go代码
protoc --go_out=. *.proto

# 由proto生成go的grpc代码
protoc --go-grpc_out=. *.proto

protoc 命令参数

protoc --proto_path=IMPORT_PATH --<lang>_out=DST_DIR path/to/file.proto

–proto_path=IMPORT_PATH: 可以在 .proto 文件中 import 其他的 .proto 文件,proto_path 即用来指定其他 .proto 文件的查找目录。如果没有引入其他的 .proto 文件,该参数可以省略。
–_out=DST_DIR: 指定生成代码的目标文件夹,例如 –go_out=. 即生成 GO 代码在当前文件夹,另外支持 cpp/java/python/ruby/objc/csharp/php 等语言

推荐风格

文件(Files)

  • 文件名使用小写下划线的命名风格,例如 lower_snake_case.proto
  • 每行不超过 80 字符
  • 使用 2 个空格缩进

包(Packages)

  • 包名应该和目录结构对应,例如文件在my/package/目录下,包名应为 my.package

消息和字段(Messages & Fields)

  • 消息名使用首字母大写驼峰风格(CamelCase),例如message StudentRequest …
  • 字段名使用小写下划线的风格,例如 string status_code = 1
  • 枚举类型,枚举名使用首字母大写驼峰风格,例如 enum FooBar,枚举值使用全大写下划线隔开的风格(CAPITALS_WITH_UNDERSCORES ),例如 FOO_DEFAULT=1

服务(Services)

  • RPC 服务名和方法名,均使用首字母大写驼峰风格,例如service FooService rpc GetSomething()

protobuf文件规范

syntax

protobuf 有2个版本,默认版本是 proto2,如果需要 proto3,则需要在非空非注释第一行使用 syntax = "proto3" 标明版本。

package

package,即包名声明符是可选的,用来防止不同的消息类型有命名冲突。

option go_package

option go_package="./proto/pb;pb";

这部分的内容是关于最后生成的go文件是处在哪个目录哪个包中,./proto/pb 代表在当前目录生成,pb 代表了生成的go文件的包名是 pb。

message

消息类型 使用 message 关键字定义,Student 是类型名,name, male, scores 是该类型的 3 个字段,类型分别为 string, bool 和 []int32。字段可以是标量类型,也可以是合成类型。
相当于Go语言中的 struct 结构体。一个 .proto 文件中可以写多个消息类型,即对应多个结构体(struct)。

修饰符

每个字段的修饰符默认是 singular,一般省略不写,repeated 表示字段可重复,即用来表示 Go 语言中的切片类型。

标识符

每个字符 = 后面的数字称为标识符,每个字段都需要提供一个唯一的标识符。标识符用来在消息的二进制格式中识别各个字段,一旦使用就不能够再改变,标识符的取值范围为 [1, 2^29 - 1] 。

文件注释

.proto 文件可以写注释,单行注释 //,多行注释 /* … */

标量类型(Scalar)

.proto TypeNotesC++ TypeJava TypePython Type[2]Go TypeRuby TypeC# TypePHP Type
doubledoubledoublefloatfloat64Floatdoublefloat
floatfloatfloatfloatfloat32Floatfloatfloat
int32使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
uint32使用变长编码uint32intint/longuint32Fixnum 或者 Bignum(根据需要)uintinteger
uint64使用变长编码uint64longint/longuint64Bignumulonginteger/string
sint32使用变长编码,这些编码在负值时比int32高效的多int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
sint64使用变长编码,有符号的整型值。编码时比通常的int64高效。int64longint/longint64Bignumlonginteger/string
fixed32总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。uint32intintuint32Fixnum 或者 Bignum(根据需要)uintinteger
fixed64总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。uint64longint/longuint64Bignumulonginteger/string
sfixed32总是4个字节int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
sfixed64总是8个字节int64longint/longint64Bignumlonginteger/string
boolboolbooleanboolboolTrueClass/FalseClassboolboolean
string一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。stringStringstr/unicodestringString (UTF-8)stringstring
bytes可能包含任意顺序的字节数据。stringByteStringstr[]byteString (ASCII-8BIT)ByteStringstring

标量类型如果没有被赋值,则不会被序列化,解析时,会赋予默认值

  • strings:空字符串
  • bytes:空序列
  • bools:false
  • 数值类型:0

常见问题

go_package报错

Please specify either:
• a "go_package" option in the .proto source file, or
• a "M" argument on the command line.

在go的1.14版本以后,proto文件中不添加go_package 会报错。
解决方法: option go_package = “./”
或者填写自己的包路径也行如option go_package = “http://github.com/package/name”

安装protoc-gen-go报错

can't load package: package google.golang.org/protobuf/cmd/protoc-gen-go: cannot find package "google.golang.org/protobuf/cmd/protoc-gen-go" in any of:
        C:\\Go\\src\\google.golang.org\\protobuf\\cmd\\protoc-gen-go (from $GOROOT)
        C:\\Users\\peikai\\go\\src\\google.golang.org\\protobuf\\cmd\\protoc-gen-go (from $GOPATH)

解决方法:
go get google.golang.org/protobuf/cmd/protoc-gen-go 然后再 install 安装。

以上是关于高效c语言1快速入门的主要内容,如果未能解决你的问题,请参考以下文章

PERL语言快速入门通道

C语言笔记初级篇第一章:C语言快速入门及注意事项

C语言笔记初级篇第一章:C语言快速入门及注意事项

C指针原理(36)-C快速入门

C/C++语言数据结构快速入门(代码解析+内容解析)顺序表

C/C++语言数据结构快速入门(代码解析+内容解析)顺序表