go语言goto语句报错 go语言异常处理
Golang 中更好的错误处理:理论和实践技巧
云和安全管理服务专家新钛云服 张春翻译
冀州网站建设公司成都创新互联公司,冀州网站设计制作,有大型网站制作公司丰富经验。已为冀州上千提供企业网站建设服务。企业网站搭建\成都外贸网站建设公司要多少钱,请找那个售后服务好的冀州做网站的公司定做!
这种方法有几个缺点。首先,它可以对程序员隐藏错误处理路径,特别是在捕获异常不是强制性的情况下,例如在 Python 中。即使在具有必须处理的 Java 风格的检查异常的语言中,如果在与原始调用不同的级别上处理错误,也并不总是很明显错误是从哪里引发的。
我们都见过长长的代码块包装在一个 try-catch 块中。在这种情况下,catch 块实际上充当 goto 语句,这通常被认为是有害的(奇怪的是,C 中的关键字被认为可以接受的少数用例之一是错误后清理,因为该语言没有 Golang- 样式延迟语句)。
如果你确实从源头捕获异常,你会得到一个不太优雅的 Go 错误模式版本。这可能会解决混淆代码的问题,但会遇到另一个问题:性能。在诸如 Java 之类的语言中,抛出异常可能比函数的常规返回慢数百倍。
Java 中最大的性能成本是由打印异常的堆栈跟踪造成的,这是昂贵的,因为运行的程序必须检查编译它的源代码 。仅仅进入一个 try 块也不是空闲的,因为需要保存 CPU 内存寄存器的先前状态,因为它们可能需要在抛出异常的情况下恢复。
如果您将异常视为通常不会发生的异常情况,那么异常的缺点并不重要。这可能是传统的单体应用程序的情况,其中大部分代码库不必进行网络调用——一个操作格式良好的数据的函数不太可能遇到错误(除了错误的情况)。一旦您在代码中添加 I/O,无错误代码的梦想就会破灭:您可以忽略错误,但不能假装它们不存在!
try {
doSometing()
} catch (IOException e) {
// ignore it
}
与大多数其他编程语言不同,Golang 接受错误是不可避免的。 如果在单体架构时代还不是这样,那么在今天的模块化后端服务中,服务通常和外部 API 调用、数据库读取和写入以及与其他服务通信 。
以上所有方法都可能失败,解析或验证从它们接收到的数据(通常在无模式 JSON 中)也可能失败。Golang 使可以从这些调用返回的错误显式化,与普通返回值的等级相同。从函数调用返回多个值的能力支持这一点,这在大多数语言中通常是不可能的。Golang 的错误处理系统不仅仅是一种语言怪癖,它是一种将错误视为替代返回值的完全不同的方式!
重复 if err != nil
对 Go 错误处理的一个常见批评是被迫重复以下代码块:
res, err := doSomething()
if err != nil {
// Handle error
}
对于新用户来说,这可能会觉得没用而且浪费行数:在其他语言中需要 3 行的函数很可能会增长到 12 行 :
这么多行代码!这么低效!如果您认为上述内容不优雅或浪费代码,您可能忽略了我们检查代码中的错误的全部原因:我们需要能够以不同的方式处理它们!对 API 或数据库的调用可能会被重试。
有时事件的顺序很重要:调用外部 API 之前发生的错误可能不是什么大问题(因为数据从未通过发送),而 API 调用和写入本地数据库之间的错误可能需要立即注意,因为 这可能意味着系统最终处于不一致的状态。即使我们只想将错误传播给调用者,我们也可能希望用失败的解释来包装它们,或者为每个错误返回一个自定义错误类型。
并非所有错误都是相同的,并且向调用者返回适当的错误是 API 设计的重要部分,无论是对于内部包还是 REST API 。
不必担心在你的代码中重复 if err != nil ——这就是 Go 中的代码应该看起来的样子。
自定义错误类型和错误包装
从导出的方法返回错误时,请考虑指定自定义错误类型,而不是单独使用错误字符串。字符串在意外代码中是可以的,但在导出的函数中,它们成为函数公共 API 的一部分。更改错误字符串将是一项重大更改——如果没有明确的错误类型,需要检查返回错误类型的单元测试将不得不依赖原始字符串值!事实上,基于字符串的错误也使得在私有方法中测试不同的错误案例变得困难,因此您也应该考虑在包中使用它们。回到错误与异常的争论,返回错误也使代码比抛出异常更容易测试,因为错误只是要检查的返回值。不需要测试框架或在测试中捕获异常 。
可以在 database/sql 包中找到简单自定义错误类型的一个很好的示例。它定义了一个导出常量列表,表示包可以返回的错误类型,最著名的是 sql.ErrNoRows。虽然从 API 设计的角度来看,这种特定的错误类型有点问题(您可能会争辩说 API 应该返回一个空结构而不是错误),但任何需要检查空行的应用程序都可以导入该常量并在代码中使用它不必担心错误消息本身会改变和破坏代码。
对于更复杂的错误处理,您可以通过实现返回错误字符串的 Error() 方法来定义自定义错误类型。自定义错误可以包括元数据,例如错误代码或原始请求参数。如果您想表示错误类别,它们很有用。DigitalOcean 的本教程展示了如何使用自定义错误类型来表示可以重试的一类临时错误。
通常,错误会通过将低级错误与更高级别的解释包装起来,从而在程序的调用堆栈中传播。例如,数据库错误可能会以下列格式记录在 API 调用处理程序中:调用 CreateUser 端点时出错:查询数据库时出错:pq:检测到死锁。这很有用,因为它可以帮助我们跟踪错误在系统中传播的过程,向我们展示根本原因(数据库事务引擎中的死锁)以及它对更广泛系统的影响(调用者无法创建新用户)。
自 Go 1.13 以来,此模式具有特殊的语言支持,并带有错误包装。通过在创建字符串错误时使用 %w 动词,可以使用 Unwrap() 方法访问底层错误。除了比较错误相等性的函数 errors.Is() 和 errors.As() 外,程序还可以获取包装错误的原始类型或标识。这在某些情况下可能很有用,尽管我认为在确定如何处理所述错误时最好使用顶级错误的类型。
Panics
不要 panic()!长时间运行的应用程序应该优雅地处理错误而不是panic。即使在无法恢复的情况下(例如在启动时验证配置),最好记录一个错误并优雅地退出。panic比错误消息更难诊断,并且可能会跳过被推迟的重要关闭代码。
Logging
我还想简要介绍一下日志记录,因为它是处理错误的关键部分。通常你能做的最好的事情就是记录收到的错误并继续下一个请求。
除非您正在构建简单的命令行工具或个人项目,否则您的应用程序应该使用结构化的日志库,该库可以为日志添加时间戳,并提供对日志级别的控制。最后一部分特别重要,因为它将允许您突出显示应用程序记录的所有错误和警告。通过帮助将它们与信息级日志分开,这将为您节省无数时间。
微服务架构还应该在日志行中包含服务的名称以及机器实例的名称。默认情况下记录这些时,程序代码不必担心包含它们。您也可以在日志的结构化部分中记录其他字段,例如收到的错误(如果您不想将其嵌入日志消息本身)或有问题的请求或响应。只需确保您的日志没有泄露任何敏感数据,例如密码、API 密钥或用户的个人数据!
对于日志库,我过去使用过 logrus 和 zerolog,但您也可以选择其他结构化日志库。如果您想了解更多信息,互联网上有许多关于如何使用这些的指南。如果您将应用程序部署到云中,您可能需要日志库上的适配器来根据您的云平台的日志 API 格式化日志 - 没有它,云平台可能无法检测到日志级别等某些功能。
如果您在应用程序中使用调试级别日志(默认情况下通常不记录),请确保您的应用程序可以轻松更改日志级别,而无需更改代码。更改日志级别还可以暂时使信息级别甚至警告级别的日志静音,以防它们突然变得过于嘈杂并开始淹没错误。您可以使用在启动时检查以设置日志级别的环境变量来实现这一点。
原文:
c语言中 go to语句的使用方法
goto语句也称为无条件转移语句,其一般格式如下: goto 语句标号; 其中语句标号是按标识符规定书写的符号, 放在某一语句行的前面,标号后加冒号(:)。语句标号起标识语句的作用,与goto 语句配合使用。
如: label: i++;
loop: while(x7);
goto loop;
C语言不限制程序中使用标号的次数,但各标号不得重名。goto语句的语义是改变程序流向, 转去执行语句标号所标识的语句。
goto语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
扩展资料:
go to在C语言中的应用:
统计从键盘输入一行字符的个数。
#includestdio.h
int n=0;
int main(void) {
printf("input a string: ");
loop: if (getchar()!='\n') {
n++;
goto loop;
}
printf("output: %d\n",n);
}
例如输入:abcdefghijklmnopqrstuvwxyz
然后回车Enter
输出:26
本例用if语句和goto语句构成循环结构。当输入字符不为'\n'时即执行n++进行计数。
然后转移至if语句循环执行,直至输入字符为'\n'才停止循环。
参考资料:百度百科-go to 语句
为什么C语言中的go to 语句老师说要小心使用?注意的地方是什么?
goto语句称为无条件转移语句,通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。但是,在结构化程序设计中一般不主张使用goto语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。而且,goto语句一般可以用其他语句来代替。
当然,goto语句也有其优势,例如可以用goto语句一次性跳出多重循环,而不需要使用多个break语句一步一步地跳出,从而使代码更为简洁。
在C语言中使用goto语句的注意事项如下:
使用goto语句只能转移到到同一函数内,而不能从一个函数里转移到另外一个函数里。
使用goto语句在同一函数内进行转移时,转移的起点应是函数内一段小功能的结束处,goto的目的label处应是函数内另外一段小功能的开始处。
不能从一段复杂的执行状态中的位置goto到另外一个位置,比如,从多重嵌套的循环判断中跳出去就是不允许的。
应该避免向两个方向跳转。这样最容易导致“面条代码”。
所以说,goto语句要小心使用。
为何goto语句无法实现循环?
1楼答案完全错误,有没有用过goto?
建议楼主不要用goto语句,程序员最忌讳看goto程序,程序一复杂就很烦,这个程序还简单
先说错误原因:
else if (go==' '); //这里多了个分号";"所以一定执行goto end;
改进:去掉分号就可以了,像二楼那样改达不到程序目的
程序中很多错误,分析下:
#include stdio.h
#include conio.h
int zhong(int a, int b){return (a+b);}
int guo(int a, int b){return (a-b);}
int wo(int a, int b){return (a*b);}
int ni(int a, int b){return (a/b);}
int x,y,z,go; //go最好用char类型
main()
{
int zhong(int a, int b);
int guo(int a, int b);
int wo(int a, int b);
int ni(int a, int b);
get:
printf("please input one number!\n");
scanf("%c",go); //输入时必定包含'字符'和'\r'(回车),这里只读取字符,'\r'留在流中,下次运行时会读取'\r',建议:用getchar().
if(go=='+')
{
int zhong(int a, int b);
scanf("%d%d",x,y);
z=zhong(x,y);
if ((x||y)!=' ') //错误,必定成立,因为(x||y)的值为1或0,永远不等于' '(空格字符)
{
printf ("%d+%d=%d\n",x,y,z);
}
else if ((x||y)==' ') //与上同理
{
printf("end\n");
}
}
else if (go=='-')
{
int guo(int a, int b);
scanf("%d%d",x,y);
z=guo(x,y);
if((x||y)!=' ') //error
{
printf ("%d-%d=%d\n",x,y,z);
}
else if ((x||y)==' ') //error
{
printf("end\n");
}
}
else if (go=='*')
{
int wo(int a, int b);
scanf("%d%d",x,y);
z=wo(x,y);
if((x||y)!=' ') //error
{
printf ("%d*%d=%d\n",x,y,z);
}
else if ((x||y)==' ') //error
{
printf("end\n");}
}
else if (go=='/')
{
int ni(int a, int b);
scanf("%d%d",x,y);
z=ni(x,y);
if ((x||y)!=' ') //error
{
printf ("%d/%d=%d\n",x,y,z);
}
else if ((x||y)==' ') //error
{
printf("end\n");
}
}
else if (go==' '); //错误在这里,去掉";"就可以了
{
goto end;
}
goto get;
end:
printf("ByeBye!\n");
getch();
}
还有,除法会遇到圆整问题,要x,y,z使用double类型
goto的用法
臭名昭著的goto出场了。
goto的汉义为“转到”,在计算机语言里,它的完整名称为:“无条件跳转语句”。几乎所有高级语言都会劝你尽量不要使用它goto。因为它会破坏程序的模块性,严重降低一段程序的可读性。若是老外写的书,则比喻使用大量goto的代码:“像意大利面条”。嗯,其实北京的杂酱面也很缠绕……可惜没有走向世界。
goto的用法是,首先要在代码中某处加上一个位标(也称标号),然后在代码中的需处,加上goto,并写让要跳转到位标。比如你在第三行代码加一个位标:A : ,然后可以在第10行写上一个goto A,程序执行到该行时,就将跳到第三行。
加位标的方法是在一空行加上位标的名称,命名规则和变量一样,但最后要加上一冒号“:”。
例如:
int i = 1;
A :
cout i endl;
i++;
if(i = 10)
goto A;
... ...
goto 虽然号称“无条件跳转”,事实上倒是有些条件限制。主要是三条。
1、goto只能在当前的同一程序段内跳转;
2、goto 可以从循环内跳转到循环外的代码,但不能从循环外的代码跳到循环内;
3、在有goto的跳转范围内,不能再使用C++允许的临时变量声明。
好了,其实笔者写程序近10年,惟一用到goto的地方就是:将一段简单的程序故意用goto写得面目全非,以期能让破解程序的人因为眼晕而放弃功击……一句老话:如果没有什么特殊理由,不要在程序里使用goto。
可能你会觉得没有给出答案,但是看了以上的东西之后,我相信你会找到出错的地方,也许你已经找着了。呵呵
C语言中goto的用法要尽量详细
goto语句也称为无条件转移语句,其一般格式如下: goto 语句标号; 其中语句标号是按标识符规定书写的符号, 放在某一语句行的前面,标号后加冒号(:)。语句标号起标识语句的作用,与goto 语句配合使用。
如: label: i++;
loop: while(x7);
goto loop;
C语言不限制程序中使用标号的次数,但各标号不得重名。goto语句的语义是改变程序流向, 转去执行语句标号所标识的语句。
goto语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
扩展资料:
go to语句使用原则:
1、使用goto语句只能goto到同一函数内,而不能从一个函数里goto到另外一个函数里。
2、使用goto语句在同一函数内进行goto时,goto的起点应是函数内一段小功能的结束处,goto的目的label处应是函数内另外一段小功能的开始处。
3、不能从一段复杂的执行状态中的位置goto到另外一个位置,比如,从多重嵌套的循环判断中跳出去就是不允许的。
4、应该避免向两个方向跳转。这样最容易导致"面条代码"。
参考资料:百度百科-goto语句
标题名称:go语言goto语句报错 go语言异常处理
浏览路径:http://cdiso.cn/article/hepscj.html