控制结构 |
说明 |
Py |
C |
C++ |
C# |
|
条件语句
|
if 条件语句
条件语句在编程语言中用于根据某个条件的真假来决定程序执行的路径,其通常会判断布尔表达式的值来选择不同的代码块。在 Python 语言中,条件语句关键字包括if 、elif 和else ;而在 C 系列语言中为if 、else if 和else 。其中的差异在于elif 和else if 。通过关键字的组合可以构建不同分支方式,主要有if 结构、if-else if (elif) 结构和if-else if(elif)-else 结构等,基本语法如表。
|
x = 10
if x > 0:
print(f"{x}: Positive")
x = -10
if x > 0:
print(f"{x}: Positive")
else:
print(f"{x}: Negative")
x = 0
if x > 0:
print(f"{x}: Positive")
elif x == 0:
print(f"{x}: Zero")
else:
print(f"{x}: Negative")
|
#include <stdio.h>
int main() {
int x = 10;
if (x > 0) {
printf("%d: Positive", x);
}
return 0;
}
#include <stdio.h>
int main() {
int x = -10;
if (x > 0) {
printf("%d: Positive", x);
}
else {
printf("%d: Negative", x);
}
return 0;
}
#include <stdio.h>
int main() {
int x = 0;
if (x > 0) {
printf("%d: Positive", x);
}
else if (x == 0) {
printf("%d: Zero", x);
}
else {
printf("%d: Negative", x);
}
return 0;
}
|
#include <iostream>
#include <format>
int main() {
int x = 10;
if (x > 0) {
std::cout << std::format("{}: Positive", x);
}
return 0;
}
#include <iostream>
#include <format>
int main() {
int x = -10;
if (x > 0) {
std::cout << std::format("{}: Positive", x);
}
else {
std::cout << std::format("{}: Negative", x);
}
return 0;
}
#include <iostream>
#include <format>
int main() {
int x = 0;
if (x > 0) {
std::cout << std::format("{}: Positive", x);
}
else if(x==0){
std::cout << std::format("{}: Zero", x);
}
else {
std::cout << std::format("{}: Negative", x);
}
return 0;
}
|
using System;
class Program
{
static void Main()
{
int x = 10;
if (x > 0)
{
Console.WriteLine($"{x}: Positive");
}
}
}
using System;
class Program
{
static void Main()
{
int x = -10;
if (x > 0)
{
Console.WriteLine($"{x}: Positive");
}
else
{
Console.WriteLine($"{x}: Negative");
}
}
}
using System;
class Program
{
static void Main()
{
int x = 0;
if (x > 0)
{
Console.WriteLine($"{x}: Positive");
}
else if (x == 0)
{
Console.WriteLine($"{x}: Zero");
}
else
{
Console.WriteLine($"{x}: Negative");
}
}
}
|
|
条件运算符(三元运算符)
条件运算符用于根据条件选择一个值,是if-else if (elif) 结构的简化版本,可以用于较为简单的判断条件,使得语句更为简洁。Python 和 C 系列语言条件运算符的基本语法如表。
|
a, b = 5, 10
max_value = a if a > b else b
print(f"Max value: {max_value}")
|
#include <stdio.h>
int main() {
int a = 5, b = 10;
int max = (a > b) ? a : b;
printf("Max value: %d", max);
return 0;
}
|
#include <iostream>
#include <format>
int main() {
int a = 5, b = 10;
int max = (a > b) ? a : b;
std::cout << std::format("Max value: {}", max);
return 0;
}
|
using System;
class Program
{
static void Main()
{
int a = 5, b = 10;
int max = (a > b) ? a : b;
Console.WriteLine($"Max value: {max}");
}
}
|
|
switch 和match
Python 中的match-case 和 C 系列语言中的switch-case 用于根据表达式的值跳转到不同的case 。当有多个条件判断时,switch /match 语句使得代码更加简洁。每个case 对应一个常量值,程序根据表达式的值匹配相应的case ,如果没有匹配的case ,则执行default /_ 分支。其语法如表。
各类语言的case 标签所能支持的类型不同,Python 使用match 语句支持多种任意类型的模式匹配,包括复杂数据结构;C/C++ 的switch 语句支持整型(int )、字符型(char )、枚举类型(enum )等基本类型。C++ 并拓展了constexpr 的常量类型;C# 支持整数、字符、字符串、枚举类型,且支持模式匹配和switch 表达式,如表。
语言 |
支持的 case 标签类型 |
Py |
任意类型(常量、对象、序列、字典、类实例等) |
C |
整数类型、字符常量、枚举类型 |
C++ |
整数类型、字符常量、枚举类型、constexpr 常量 |
C# |
整数类型、字符常量、字符串、枚举类型、模式匹配 |
|
day = 3
match day:
case 1:
print("Monday")
case 2:
print("Tuesday")
case 3:
print("Wednesday")
case _:
print("Invalid day")
+ 匹配——解构
match 可以匹配复杂的结构,如元组、列表和字典等。
解构元组:
person = ("Alice", 30)
match person:
case ("John", age):
print(f"Name is John, Age is {age}")
case ("Alice", age):
print(f"Name is Alice, Age is {age}")
case _:
print("Unkwon person")
🡮
Name is Alice, Age is 30
解构字典:
person={"name":"Alice","age":30}
match person:
case {"name":name,"age":age}:
print(f"Name: {name},Age: {age}")
case _:
print("Unknown person")
+ 匹配——类型
match 可以匹配数据类型。
def handle_value(value):
match value:
case int():
print(f"Integer: {value}")
case str():
print(f"String: {value}")
case list():
print(f"List: {value}")
case _:
print("Unkown type")
handle_value(98)
handle_value("World!")
handle_value([10,20])
🡮
Integer: 98
String: World!
List: [10, 20]
|
#include <stdio.h>
int main() {
int day = 3;
switch (day) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednsday\n");
break;
default:
printf("Invalid day\n");
break;
}
return 0;
}
+ switch 的穿透现象
如果没有使用 break , switch 会发生“穿透”(fall-through)现象,继续执行后面的case ,直至遇到break ,或者执行到switch 的末尾。
#include <stdio.h>
int main() {
int day = 2;
switch (day) {
case 1:
printf("Monday\n");
case 2:
printf("Tuesday\n");
case 3:
printf("Wednsday\n");
break;
default:
printf("Invalid day\n");
break;
}
return 0;
}
+ switch 的枚举类型(enum )
#include <stdio.h>
enum Day{Monday=1,Tuesday,Wednesday};
int main() {
int day = 3;
switch (day) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
default:
printf("Invalid day\n");
break;
}
return 0;
}
|
#include <iostream>
#include <format>
int main() {
int day = 3;
switch (day) {
case 1:
std::cout << "Monday" << std::endl;
break;
case 2:
std::cout << "Tuesday" << std::endl;
break;
case 3:
std::cout << "Wednesday" << std::endl;
break;
default:
std::cout << "Invalid day" << std::endl;
break;
}
return 0;
}
+ switch 的穿透现象
#include <iostream>
#include <format>
int main() {
int day = 2;
switch (day) {
case 1:
std::cout << "Monday" << std::endl;
case 2:
std::cout << "Tuesday" << std::endl;
case 3:
std::cout << "Wednesday" << std::endl;
break;
default:
std::cout << "Invalid day" << std::endl;
break;
}
return 0;
}
+ switch 的枚举类型(enum )
#include <iostream>
enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
int main() {
Day day = Wednesday;
switch (day) {
case Monday:
std::cout << "Start of the week\n";
break;
case Wednesday:
std::cout << "Midweek\n";
break;
case Friday:
std::cout << "Almost weekend\nj";
break;
default:
std::cout << "Weekend\n";
break;
}
return 0;
}
+ switch 的constexpr 类型
在 C++11 及更高版本中,constexpr 引入了编译期常量的概念,为在编译过程中得到计数结果的常量表达式。switch 的case 标签必须是编译期常量,而constexpr 提供了的方法确保在编译期求值。
#include <iostream>
constexpr int getDayValue(int day) {
return day + 1;
}
int main() {
constexpr int day = getDayValue(2);
switch (day) {
case 1:
std::cout << "Monday\n";
break;
case 2:
std::cout << "Tuesday\n";
break;
case 3:
std::cout << "Wednesday\n";
break;
default:
std::cout << "Invalid day\n";
break;
}
return 0;
}
|
using System;
class Program
{
static void Main()
{
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
default:
Console.WriteLine("Invalid day");
break;
}
}
}
+ switch 的枚举类型(enum )
using System;
enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
class Program
{
static void Main()
{
Day day=Day.Wednesday;
switch (day)
{
case Day.Monday:
Console.WriteLine("Start of the week");
break;
case Day.Wednesday:
Console.WriteLine("Midweek");
break;
case Day.Friday:
Console.WriteLine("Almost weekend");
break;
default:
Console.WriteLine("Weekend");
break;
}
}
}
+ switch 的表达式
C# 8.0 引入了 switch 表达式,使得switch 可以作为表达式而不仅仅是语句。switch 表达式更加简洁,且每个case 使用=> 表达式形式。
using System;
class Program
{
static void Main()
{
int day = 3;
string dayName = day switch
{
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
_ => "Invalid day"
};
Console.WriteLine(dayName);
}
}
+ switch 的模式匹配
在 C# 9.0 中,switch 语句支持模式匹配,可以根据条件进行匹配,甚至可以将case 条件与类型检查结合使用。
using System;
class Program
{
static void Main()
{
object value = 3.14;
string result = value switch
{
int i when i > 0 => $"Positive integer: {i}",
int i => $"Negative integer: {i}",
double d => $"Double: {d}",
string s => $"String: {s}",
_ => "Unknown type"
};
Console.WriteLine(result);
}
}
|
循环语句
|
for 循环
Python、C、C++ 和 C# 中,for 循环语句的基本功能类似,用于多次重复的执行代码块。不同语言在语法和一些特性上有所差异,特别是在迭代方式,数据类型和简洁性上,表中列出了各自实现的基本语法。
类型 |
Py |
C |
C++ |
C# |
基本形式(返回“索引值”形式) |
for variable in range(start, stop, step): |
for (initialization; condition; increment) { } |
for (initialization; condition; increment) { } |
for (initialization; condition; increment) { } |
返回元素形式 |
for item in collection: (for-in)结构 |
/ |
for (auto element : container) { } |
foreach (var item in collection) { } |
|
numbers = [10,20,30,40,50]
for i in numbers:
print(f"{i}, ",end="")
for i in range(1,6,1):
print(f"{i}, ", end="")
lst = [10,20,30,40,50]
for i in range(len(lst)):
print(f"Element {i}: {lst[i]}; ",end="")
🡮
Element 0: 10; Element 1: 20; Element 2: 30; Element 3: 40; Element 4: 50;
配合字典的keys() 、values() 和items() 方法循环字典。如果直接循环列表,则为循环其键。
score = {"Henry": 99, "Oliver": 86, "Emily": 91, "Lily": 76}
# 循环键
for k in score:
print(f"{k}:{score[k]}; ", end="")
print("\n")
for k in score.keys():
print(f"{k}:{score[k]}; ", end="")
print("\n")
# 循环值
for v in score.values():
print(f"{v}; ", end="")
print("\n")
# 循环键值对
for k, v in score.items():
print(f"{k}:{v}; ", end="")
🡮
Henry:99; Oliver:86; Emily:91; Lily:76;
Henry:99; Oliver:86; Emily:91; Lily:76;
99; 86; 91; 76;
Henry:99; Oliver:86; Emily:91; Lily:76; P
Python 中常用while 循环来实现。下述示例使用了iter(int,1) 的方法实现。
for _ in iter(int,1):
print("Infinite loop!")
break
for i in range(1,4,1):
for j in range(1,4,1):
print(f"{i} x {j} = {i*j}\t", end="")
print("\n")
🡮
1 x 1 = 1 1 x 2 = 2 1 x 3 = 3
2 x 1 = 2 2 x 2 = 4 2 x 3 = 6
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
continue 是在满足条件下跳过当前迭代循环体内的其余部分,进入下一次迭代;而break 语句是完全退出循环。下述结果中没有数字5,和到数字8(不含自身)终止说明了这一点。
for i in range(1, 11, 1):
if i == 5:
continue
if i == 8:
break
print(f"{i}, ", end="")
Python 的for 循环支持else 子句,else 部分会在循环正常完成后执行;如果循环被break 终止,则else 部分将不会执行。
for i in range(5):
if i == 3:
break
print(f"{i}, ", end="")
else:
print("Loop ends normally.")
|
#include <stdio.h>
int main() {
for (int i = 1; i <= 5; i++) {
printf("%d, ", i);
}
return 0;
}
#include <stdio.h>
int main() {
int arr[] = { 10,20,30,40,50 };
int length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < length; i++) {
printf("Element %d: %d; ", i, arr[i]);
}
return 0;
}
🡮
Element 0: 10; Element 1: 20; Element 2: 30; Element 3: 40; Element 4: 50;
如果省略了条件部分,那么for 循环会一直持续下去,因此通常需要使用break 语句来退出循环。
#include <stdio.h>
int main() {
for (;;) {
printf("This will print forever!");
break;
}
return 0;
}
🡮
This will print forever!
#include <stdio.h>
int main() {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
printf("%d x %d = %d\t", i, j, i * j);
}
printf("\n");
}
return 0;
}
🡮
1 x 1 = 1 1 x 2 = 2 1 x 3 = 3
2 x 1 = 2 2 x 2 = 4 2 x 3 = 6
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
for 循环中,continue 语句用于跳过当前循环迭代的剩余部分,直接进入下一次迭代。可以用来在满足特定条件下跳过某些代码,从而灵活控制循环流程。例如下述示例过滤特定条件的元素,当为小于0的整数值时,跳过printf() 代码行而直接进入下一次迭代,即仅打印输出大于和等于0的整数。
#include <stdio.h>
int main() {
int numbers[] = { 7,0,-3,6,-12,5,9 };
int length = sizeof(numbers) / sizeof(numbers[0]);
for (int i = 0; i < length; i++) {
if (numbers[i] < 0) {
continue; // 跳过非正数
}
printf("%d, ", numbers[i]);
}
return 0;
}
continue 是在满足条件下跳过当前迭代循环体内的其余部分,进入下一次迭代;而break 语句是完全退出循环。下述结果中没有数字5,和到数字8(不含自身)终止说明了这一点。
#include <stdio.h>
int main() {
for (int i = 1; i <= 10; i++) {
if (i == 5) {
continue;
}
if (i == 8) {
break;
}
printf("%d, ", i);
}
return 0;
}
|
#include <iostream>
#include <format>
int main() {
for (int i = 1; i <= 5; i++) {
std::cout << std::format("{}, ", i);
}
return 0;
}
#include <iostream>
#include <format>
int main() {
int arr[] = { 10,20,30,40,50 };
int length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < length; i++) {
std::cout << std::format("Element {}: {}", i, arr[i]);
}
return 0;
}
🡮
Element 0: 10Element 1: 20Element 2: 30Element 3: 40Element 4: 50
C++ 11 引入了 Range-Based for 循环,用于遍历容器(如数组、std::vector 等),该语句使循环代码更简洁。但是需要注意直接对容器内的元素循环时,并不返回元素对应的索引。同时因为为可循环访问的任何内容,可以优先考虑使用auto 关键字,或者直接指定容器元素的数据类型。
#include <iostream>
#include <format>
#include <vector>
int main() {
std::vector<int> numbers = { 10,20,30,40,50 };
for (auto num : numbers) {
std::cout << std::format("{}, ", num);
}
return 0;
}
可以在 Range-Based for 循环中使用引用类型修改容器内的元素。
引用(Reference),使用符号 & 来声明,为一种轻量的别名机制。引用可以通过为变量创建别名,使得对引用的操作直接影响原始变量
#include <iostream>
#include <format>
#include <vector>
int main() {
std::vector<int> numbers = { 10,20,30,40,50 };
for (auto &num : numbers) {
num *= 2;
}
for (auto num : numbers) {
std::cout << std::format("{}, ", num);
}
return 0;
}
如果省略了条件部分,那么for 循环会一直持续下去,因此通常需要使用break 语句来退出循环。
#include <iostream>
#include <format>
int main() {
for (;;) {
std::cout << "This will print forever!" << std::endl;
break; // 使用 break 语句退出循环
}
return 0;
}
🡮
This will print forever!
#include <iostream>
#include <format>
int main() {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
std::cout << std::format("{} x {} = {}\t", i, j, i * j);
}
std::cout << "\n";
}
return 0;
}
🡮
1 x 1 = 1 1 x 2 = 2 1 x 3 = 3
2 x 1 = 2 2 x 2 = 4 2 x 3 = 6
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
continue 是在满足条件下跳过当前迭代循环体内的其余部分,进入下一次迭代;而break 语句是完全退出循环。下述结果中没有数字5,和到数字8(不含自身)终止说明了这一点。
#include <iostream>
#include <format>
int main() {
for (int i = 1; i <= 10; i++) {
if (i == 5) {
continue;
}
if (i == 8) {
break;
}
std::cout << std::format("{}, ", i);
}
return 0;
}
|
using System;
class Program
{
static void Main()
{
for (int i = 1; i <= 5; i++)
{
Console.Write($"{i}, ");
}
}
}
using System;
class Program
{
static void Main()
{
int[] arr = new int[] { 10, 20, 30, 40, 50 };
for (int i = 0; i < arr.Length; i++)
{
Console.Write($"Element {i}: {arr[i]}");
}
}
}
🡮
Element 0: 10Element 1: 20Element 2: 30Element 3: 40Element 4: 50
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = [ 10, 20, 30, 40, 50 ];
foreach (var num in numbers)
{
Console.Write($"{num}, ");
}
}
}
如果省略了条件部分,那么for 循环会一直持续下去,因此通常需要使用break 语句来退出循环。
using System;
class Program
{
static void Main()
{
for (; ; )
{
Console.WriteLine("Infinite loop!");
break;
}
}
}
using System;
class Program
{
static void Main()
{
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= 3; j++)
{
Console.Write($"{i} x {j} = {i * j}\t");
}
Console.WriteLine();
}
}
}
🡮
1 x 1 = 1 1 x 2 = 2 1 x 3 = 3
2 x 1 = 2 2 x 2 = 4 2 x 3 = 6
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
continue 是在满足条件下跳过当前迭代循环体内的其余部分,进入下一次迭代;而break 语句是完全退出循环。下述结果中没有数字5,和到数字8(不含自身)终止说明了这一点。
using System;
using System.Runtime.CompilerServices;
class Program
{
static void Main()
{
for (int i = 1; i <= 10; i++)
{
if (i == 5)
{
continue;
}
if (i == 8)
{
break;
}
Console.Write($"{i}, ");
}
}
}
|
|
while 和do-while 循环
while 循环用于条件为真时重复执行代码块,适合在循环次数不确定的情况下使用,当满足某个条件时才停止循环。while 循环通常包括三种情况,条件控制循环,无限循环和do-while 的后测试循环。语法如表。
语言 |
基本语法(条件控制循环) |
无限循环 |
do-while (后测试循环) |
Py |
while condition: |
while True: |
不支持do-while ,但可以通过while True 和break 实现 |
C |
while (condition) { } |
while (1) { } |
do { } while (condition); |
C++ |
while (condition) { } |
while (true) { } |
do { } while (condition); |
C# |
while (condition) { } |
while (true) { } |
do { } while (condition); |
如果将条件设置为永远为真,则可以创建一个无限循环。在使用无限while 循环时,要明确添加break 语句,当满足某一指定条件时跳出循环,避免程序无限运行。C/C++和C#支持do-while 循环,确保循环体至少执行一次,Python不支持do-while ,但可以通过while True 和break 实现。
|
i = 1
while i <= 5:
print(f"{i}, ", end="")
i += 1
i = 0
while True: # 无限循环
print(f"{i}, ", end="")
i += 1
if i == 5:
break # 当 i 等于 5 时退出循环
counter = 1
while True:
print(f"{counter}, ", end="")
counter += 1
if counter > 5:
break
|
#include <stdio.h>
int main() {
int i = 1;
while (i <= 5) {
printf("%d, ", i);
i++;
}
return 0;
}
#include <stdio.h>
int main() {
int i = 0;
while (1) { // 无限循环
printf("%d, ", i);
i++;
if (i == 5) {
break; // 当 i 等于 5 时退出循环
}
}
return 0;
}
#include <stdio.h>
int main() {
int counter = 1;
do {
printf("%d, ", counter);
counter++;
} while (counter <= 5);
return 0;
}
|
#include <iostream>
#include <format>
int main() {
int i = 1;
while (i <= 5) {
std::cout << std::format("{}, ", i);
i++;
}
return 0;
}
#include <iostream>
#include <format>
int main() {
int i = 0;
while (true) { // 无限循环
std::cout << std::format("{}, ", i);
i++;
if (i == 5) {
break; // 当 i 等于 5 时退出循环
}
}
return 0;
}
#include <iostream>
#include <format>
int main() {
int counter = 1;
do {
std::cout << std::format("{}, ", counter);
counter++;
} while (counter <= 5);
return 0;
}
|
using System;
class Program
{
static void Main()
{
int i = 1;
while (i <= 5)
{
Console.Write($"{i}, ");
i++;
}
}
}
using System;
class Program
{
static void Main()
{
int i = 0;
while (true) // 无限循环
{
Console.Write($"{i}, ");
i++;
if (i == 5)
{
break; // 当 i 等于 5 时退出循环
}
}
}
}
using System;
class Program
{
static void Main()
{
int counter = 1;
do
{
Console.Write($"{counter}, ");
counter++;
} while (counter <= 5);
}
}
|
异常处理
|
异常(Exception)是指程序在运行过程中出现的错误或意外情况,包括运行时错误(如除零错误、数组越界、空指针等),资源问题(如文件未找到、网络或数据库连接失败等),数据问题(如无效的输入数据、数据格式错误等)等由程序执行时错误条件引发的情况。异常处理是编程语言中重要的机制,用于捕获、报告和处理这些错误(如重试、记录日志、向用户显示错误信息等),以确保程序的稳定性和可维护性。异常处理机制通常包括抛出异常(throwing exception),捕获异常(catching exception)和处理异常(handling exception)。即当程序检查到错误条件时,抛出异常(通常在try 块中);进而程序的控制流转移到异常处理块(通常为except 或catch 块);在异常处理块中处理捕获到的异常,可以采取恢复或报告错误信息等方式。
特性 |
Python |
C |
C++ |
C# |
异常处理机制 |
内置try 、except 、else 和finally 关键字 |
无内置异常处理机制,但可以通过函数返回值,或setjmp/longjmp 等内置函数或宏实现 |
内置try 、catch 和throw 关键字 |
内置try 、catch 、throw 和finally 关键字 |
自定义异常 |
通过继承Exception 类实现 |
通过返回值或错误代码 |
通过继承std::exception 实现 |
通过继承Exception 类实现 |
多重捕获异常 |
支持多个except 块 |
手动判断不同的错误码 |
支持多个catch 块 |
支持多个catch 块 |
清理机制 |
finally |
无内置清理机制,需要手动清理 |
RAII 机制 |
finally |
异常传播 |
异常会传播到调用栈上 |
手动处理错误码 |
异常会传播到调用栈上 |
异常会传播到调用栈上 |
性能 |
异常处理较慢,性能开销较大 |
无异常处理机制,性能较高 |
异常处理较慢,但支持优化 |
异常处理有性能开销,但优化较好 |
|
Python 使用try 、except 、else 和finally 关键字进行异常处理。其语法如下,
try:
# 尝试执行的代码(可能包含引发异常的代码)
except 异常类型:
# 发生异常时执行的代码(当 try 代码块中的代码引发异常,跳转到 except 当前代码块执行相应的处理)(可包含多个 except 代码块,处理不同的异常)
else:
# 没有异常时执行的代码(可选)
finally:
# 无论是否发生异常都会执行的代码,通常用于清理资源(可选)
Python 内置的异常层次结构如附1。
try:
x = int(input("Enter a number: "))
y = 10 / x
except ValueError:
print("Invalid input. Please enter a valid integer.")
except ZeroDivisionError:
print("Cannot divide by zero!")
else:
print("Division successful. Result:", y)
finally:
print("Execution finished.")
🡮
Enter a number: five
Invalid input. Please enter a valid integer.
Execution finished.
Enter a number: 0
Cannot divide by zero!
Execution finished.
Enter a number: 5
Division successful. Result: 2.0
Execution finished.
在基本的异常处理示例中,使用了两个except 块来捕捉异常,可以使用except(ExceptionType-1,ExceptionType-2,...,ExceptionType-n ) 将多个except 块合并到一个exxcept 下来捕获异常。
try:
x = int(input("Enter a number: "))
y = 10 / x
print("Division successful. Result:", y)
except (ValueError, ZeroDivisionError) as e:
print(f"An error occure: {e}.")
🡮
Enter a number: five
An error occure: invalid literal for int() with base 10: 'five'.
Enter a number: 0
An error occure: division by zero.
Enter a number: 5
Division successful. Result: 2.0
raise 可以强制触发指定的异常。raise 唯一的参数就是触发的异常。
x=-5
if x<0:
raise ValueError("Only non-negative numbers are allowed.")
🡮
[Exception Thrown]
Only non-negative numbers are allowed.
通过raise 触发异常,并在except 下打印该异常实例参数。
try:
raise Exception("spam", "eggs")
except Exception as inst:
print(type(inst)) # 为异常实例
print(inst.args) # 存储在 inst.args 中的参数
print(inst) # __str__ 许直接打印参数,但可能在异常子类中被覆盖
x, y = inst.args # 序列解包参数
print("x =", x)
print("y =", y)
🡮
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
如果只想判断是否触发了异常,但并不打算处理该异常,则可以使用更简单的 raise 语句重新触发异常。
try:
raise NameError("HiThere")
except NameError:
print("An exception flew by!")
raise
可以通过继承Exception 类自定义异常。通常异常命名以Error 结尾,类似标准异常的命名。
class CustomError(Exception):
pass
try:
raise CustomError("A custom error occurred.")
except CustomError as e:
print(e)
🡮
A custom error occurred.
可以定义__str__() 类,附加状态信息或者方法。
class AlreadyGotOneError(Exception):
def __str__(self):
return "So you got an exception..."
pass # 自定义异常
def grail():
raise AlreadyGotOneError # 引发自定义异常
try:
grail()
except AlreadyGotOneError as ago_e:
print(ago_e)
print("got exception!")
🡮
So you got an exception...
got exception!
|
在 C 语言中没有原生的异常处理机制,但可以通过函数返回值或 C 语言标准库中提供的方法实现。
通过函数返回值来表示错误或异常,例如下述示例中函数返回 0 表示成功运行,而返回非零值表示发生了异常。
#include <stdio.h>
int divide(int a, int b, int* result) {
if (b == 0) {
return -1; // Return error code for division by zero
}
*result = a / b;
return 0; // Success
}
int main() {
int a = 10, b = 0, result;
if (divide(a, b, &result) != 0) {
printf("Error: Division by zero~\n");
}
else {
printf("Result: %d\n", result);
}
return 0;
}
🡮
Error: Division by zero~
setjmp 和longjmp 是 C 语言标准库中提供的一对函数,用于在程序中实现非局部跳转,允许在某个函数中保存当前执行的状态(即程序计数器和栈指针等信息),然后从另一个函数中恢复并跳转回这个保存的状态。主要用于错误或异常处理。setjmp 用于保存当前的执行状态。在调用setjmp 时,会记录当前的堆栈信息,并将其存储到jmp_buf 类型的结构中。jmp_buf 是一个数据结构,用于保存堆栈信息和程序计数器。如果直接调用setjmp ,则返回值 0。如果通过longjmp 跳转回setjmp 所在的位置,则返回一个非零值。longjmp 即用于恢复并跳转到之前使用setjmp 保存的执行状态,从而实现非局部跳转。
#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
void functionError() {
printf("An error occurred!\n");
longjmp(buf, 1); // 跳转回设置点(保存状态)
}
int main() {
if (setjmp(buf)) { // 初次调用 setjmp 返回 0
// longjmp 后执行
printf("Recovered from functionError\n");
}
else {
functionError(); // 调用会触发错误的函数
printf("This line will not be executed\n");
}
return 0;
}
🡮
An error occurred!
Recovered from functionError
另外, <errno.h> 是 C 语言标准库中的头文件,定义了与错误代码相关的宏和变量,用于表示程序运行时的错误状态,其包括了一个全局的整数变量errno ,这个变量表示最近一次函数调用的错误代码,常和库函数fopen 、malloc 等配合使用。而<assert.h> 中assert 宏用于检查表达式是否为真,如果为假则输出错误信息并终止程序,适合在调试阶段使用。
|
C++ 具有内置的异常处理,通过try 、catch 和throw 关键字结构化的方法实现异常处理机制。
可能抛出异常的代码放置于try 代码块中;catch 块则捕获并处理这个异常;throw 则用于在发生错误时抛出异常。C++ 标准库提供了一系列异常类,大多数都继承自std::exception ,常用的有:
std::exception :多有异常的基类。
std::runtime_error :表示运行时错误。
std::logic_error :表示逻辑错误。
std::out_of_range :表示超出范围的错误。
std::invalid_argument :表示无效参数的错误。
#include <iostream>
#include <format>
#include <stdexcept> // 用于标准异常
int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Division by zero"); // 抛出异常
}
return a / b;
}
int main() {
try {
int result = divide(10, 0);
std::cout << std::format("Result: {}\n", result);
}
catch (const std::runtime_error& e) {
std::cerr << std::format("Error: {}\n", e.what()); // 捕获并处理异常
}
return 0;
}
🡮
Error: Division by zero
std::cerr 是一个用于显示错误消息的标准输出流,是 iostream 库的一部分,通常用于向控制台输出错误或调试信息。std::cerr 与通常缓冲的 std::cout 不同,其没有缓冲,意味着每个 std::cerr 输出都会立即出现在控制台中,而无需等待换行符或手动刷新缓冲区。
可以为不同的异常类型定义多个 catch 块。C++ 会从上到下检查每个catch 块,并匹配相应的异常类型进行处理。
#include <iostream>
#include <format>
#include <stdexcept>
int divide(int a, int b) {
if (b == 0) throw std::runtime_error("Division by zero");
if (a < 0 || b < 0) throw std::invalid_argument("Negative values not allowed");
return a / b;
}
int main() {
try {
int result = divide(-10, 2);
std::cout << std::format("Result: {}\n", result);
}
catch (const std::runtime_error& e) {
std::cerr << std::format("Error: {}\n", e.what());
}
catch (const std::invalid_argument& e) {
std::cerr << std::format("Invalid argument: {}\n", e.what());
}
return 0;
}
🡮
Invalid argument: Negative values not allowed
可以使用catch(...) 捕获所有异常。
#include <iostream>
#include <stdexcept>
int main() {
try {
throw std::runtime_error("Any exception"); ;
}
catch (...) {
std::cerr << "An unexpected error occurred.\n";
}
return 0;
}
🡮
An unexpected error occurred.
自定义异常类通常继承自std::exception ,并重载what() 方法提供错误信息。
#include <iostream>
#include <format>
#include <exception>
class MyException : public std::exception {
public:
const char* what() const noexcept override {
return "My custom exception";
}
};
int main() {
try {
throw MyException();
}
catch (const MyException& e) {
std::cerr << std::format("Caught exception: {}\n",e.what());
}
return 0;
}
🡮
Caught exception: My custom exception
noexcept 用于声明一个函数不会抛出异常。一方面通过明确标记可以防止无意抛出异常,并提醒调用者该函数不会发生异常,使得代码更具可预测性;同时,标准库中的某些容器可以利用noexcept 的信息在内存重新分配时避免不必要的拷贝等操作,从而使得编译器可以对其进行更好的优化。
#include <iostream>
#include <format>
#include <exception>
void safeFunction() noexcept {
}
void riskyFunction() {
throw std::runtime_error("Something went wrong");
}
int main() {
try {
std::cout << std::format("safeFunction is noexcept: {}\n", noexcept(safeFunction()));
std::cout << std::format("riskyFunction is noexcept: {}\n", noexcept(riskyFunction()));
safeFunction();
}
catch (...) {
std::cout << "Exception caught\n";
}
return 0;
}
🡮
safeFunction is noexcept: true
riskyFunction is noexcept: false
|
C# 使用try 、catch 、finally 和throw 等关键字进行异常处理。其中try 包括可能触发异常的代码;catch 捕获并处理try 块内抛出的异常;无论是否抛出异常,finally 为在try 和catch 之后要执行的代码;throw 用于引发异常,要么是新的异常,要么是重新抛出原来的异常。
C# 提供了丰富的内置异常类,常用的包括:
Exception :所有异常的基类。
ArgumentNullException :传递给方法的参数为null 。
InvalidOperationException :对象当前无效。
NullReferenceException :尝试访问空引用时触发。
IndexOutOfRangeException :索引超出数组或集合的范围。
FormatException : 格式错误,例如无法将字符串解析为数值。
using System;
class Program
{
static int Divide(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException();
}
return a / b;
}
static void Main()
{
try
{
int result = Divide(10, 0);
Console.WriteLine($"Result: {result}");
}
catch(DivideByZeroException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}finally
{
Console.WriteLine("Execution completed");
}
}
}
🡮
Error: Attempted to divide by zero.
Execution completed
在try-catch 结构中可以使用多个catch 来捕获多个异常。如果一个异常发生时,C# 会从上到下依次检查,并被第一个匹配的catch 块捕获。
using System;
class Program
{
static void Main()
{
try
{
Console.Write("Enter a number: ");
int x = int.Parse(Console.ReadLine());
double y = 10 / x;
Console.WriteLine($"Division successful. Result: {y}");
}catch(FormatException ex)
{
Console.WriteLine($"An error occurred: Invalid number format. {ex.Message}");
}catch(DivideByZeroException ex)
{
Console.WriteLine($"An error occured: Division by zero is not allowed. {ex.Message}");
}
}
}
🡮
Enter a number: five
An error occurred: Invalid number format. The input string 'five' was not in a correct format.
Enter a number: 0
An error occured: Division by zero is not allowed. Attempted to divide by zero.
Enter a number: 5
Division successful. Result: 2
int.Parse() 是一个用于将数字的字符串表示形式转换为整数的方法。
throw 用于显示抛出异常。在catch 块中,可以使用throw 重新抛出异常,以便在上层处理。
using System;
class Program
{
static void ValiddateAge(int age)
{
if (age < 18)
{
throw new ArgumentException("Age must be 18 or oder.");
}
}
static void Main()
{
try
{
ValiddateAge(10);
}catch (ArgumentException ex)
{
Console.WriteLine($"Caught exception: {ex.Message}");
throw; // 重新抛出异常
}
}
}
🡮
Caught exception: Age must be 18 or oder.
可以通过继承系统内置的Exception ,并定义不同的构造函数来创建自定义异常。自定义异常可以帮助更具体地描述代码中出现的错误,并便于捕获和处理特定类型的异常。
using System;
public class InvalidAgeException : Exception
{
// 默认构造函数
public InvalidAgeException(): base("Invalid age provided.")
{
}
// 接受自定义消息的构造函数
public InvalidAgeException(string message) : base(message)
{
}
// 接受自定义消息和内部异常的构造函数
public InvalidAgeException(string message, Exception innerException) : base(message, innerException)
{
}
}
public class Person
{
public int Age
{
get;
set;
}
public void SetAge(int age)
{
if (age<0 || age > 150)
{
throw new InvalidAgeException("Age must between 0 and 150.");
}
Age = age;
}
}
class Program
{
static void Main()
{
var person = new Person();
try
{
person.SetAge(300);
}catch(InvalidAgeException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
🡮
Error: Age must between 0 and 150.
base 关键字用于访问基类的成员(如方法、属性、构造函数等),通常在派生类(子类)中使用,以引用和调用基类的功能。
上述示例中使用了Exception 的innerException 属性,用于保存导致当前异常的原始异常,特别适用于当一个异常由另一个异常触发时的情景。当捕获到一个异常,但将其包装成一个新的、更具描述性或有意义的异常重新抛出,其原始异常作为innerException 传递,保留了异常路径,可以错误/异常跟踪。
using System;
class Program
{
static void Main()
{
try
{
try
{
throw new FileNotFoundException("File not found.");
}
catch (Exception ex)
{
throw new Exception("Failed to process file.", ex);
}
}catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("Inner Exception: " + ex.InnerException.Message);
}
}
}
}
🡮
Exception: Failed to process file.
Inner Exception: File not found.
when 关键字允许在捕获异常时指定额外的条件,并仅当条件为真时才会处理异常。
using System;
using System.IO;
class Program
{
static void Main()
{
try
{
File.ReadAllText("importantfile.txt");
}catch(FileNotFoundException ex) when (Path.GetFileName(ex.FileName) == "importantfile.txt")
{
Console.WriteLine("Important file is missing. Please check!");
}catch(FileNotFoundException ex)
{
Console.WriteLine($"File not found: {ex.FileName}");
}
}
}
🡮
Important file is missing. Please check!
|