news 2026/10/14 12:51:32

网络协议解剖台:从状态机、封装边界到二进制运算

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
网络协议解剖台:从状态机、封装边界到二进制运算

1. 这不是背题手册,而是网络协议的“解剖台”

你有没有过这种体验:翻开《计算机网络》教材,看到“TCP三次握手”几个字,心里默念“SYN、SYN-ACK、ACK”,考试时却在“为什么第三次握手失败后服务器要进入SYN_RCVD状态而不是直接关闭连接”这种问题上卡壳?或者算一道子网划分题,IP地址和掩码都列好了,手停在草稿纸前——到底该借位还是向右移?最后靠蒙选了个答案,交卷后才反应过来:我根本没真正理解“网络前缀长度”和“主机号全0/全1”的边界逻辑。

这不是你记性差,而是绝大多数简答题和计算题训练,从一开始就把“协议行为”当成了“操作口诀”,把“地址计算”当成了“数字游戏”。我带过三届网络课程实验班,观察到一个高度一致的现象:能完整默写出OSI七层模型的学生,面对“HTTP请求在传输层被封装成什么单位?这个单位的首部中哪个字段决定了它能被正确交付给目标主机上的Web服务进程?”这类问题时,有近60%的人会愣住——他们记得“传输层用段(segment)”,但说不清“目的端口号”才是定位进程的关键;他们知道“IP地址负责寻址”,却混淆了“IP首部中的目的IP地址”和“以太网帧首部中的目的MAC地址”各自生效的网络层级。

这背后是教学与考核之间的一道断层:教材讲原理,习题集练套路,而真实网络设备只认字节流和状态机。所以这篇内容不叫“题库解析”,它是一张网络协议的解剖台——我们不告诉你“答案是什么”,而是带你亲手切开TCP报文、掰开IP数据报、一层层剥开ARP请求的封装结构,看清每个字段在真实通信中承担的不可替代角色。所有题目,无论简答还是计算,最终都回归到两个问题:这个字段在链路上传输时,被谁读取?它触发了设备内部哪个状态变化?掌握了这个视角,你拿到任何新题,都不再是搜索记忆库,而是启动推理引擎。

关键词里虽然空着,但整篇内容锚定在三个不可绕过的硬核支点上:协议状态机(State Machine)、分层封装边界(Encapsulation Boundary)、二进制地址运算(Binary Address Arithmetic)。它们不是抽象概念,而是你调试Wireshark抓包时看到的每一行字节、你在路由器CLI里敲下show ip route后屏幕上跳动的路由条目、你配置VLAN时交换机端口指示灯闪烁的物理信号基础。接下来的内容,全部围绕这三个支点展开,每一道题的拆解,都会回到这三根支柱上进行校准。

2. 简答题的本质:协议状态机的“快照问答”

简答题最常被误读为“名词解释合集”,比如问“简述CSMA/CD的工作原理”,学生立刻背诵“先听后发、边发边听、冲突停止、随机延迟重发”。这没错,但考官真正想检验的,是你是否理解这套规则如何映射到网卡芯片的硬件状态流转。CSMA/CD不是一段文字,它是一台以太网控制器内部的状态机:IDLE → PREAMBLE → TRANSMIT → COLLISION_DETECTED → BACKOFF → IDLE。每一个箭头,都对应着物理层信号电平的变化和MAC子层寄存器的值更新。

2.1 TCP连接建立与释放:状态迁移图就是答案本身

以高频题“为什么TCP连接建立需要三次握手,而释放需要四次挥手?”为例。标准答案常写“为了防止已失效的连接请求报文段突然又传送到服务器,产生错误”。这句话对,但过于笼统。真正能拿满分的回答,必须画出双方的状态迁移路径,并标出关键字段。

我们以客户端主动发起连接为例:

  • 第一次握手(SYN):客户端发送SYN=1, seq=x。此时客户端进入SYN_SENT状态。注意,它不等待确认就直接发送,因为这是主动方的起始动作。
  • 第二次握手(SYN-ACK):服务器收到SYN后,必须同时做两件事:① 分配TCB(传输控制块)资源;② 发送SYN=1, ACK=1, seq=y, ack=x+1。服务器由此进入SYN_RCVD状态。这个状态非常关键——它表示服务器已收到SYN,但尚未收到客户端对SYN的确认。如果此时第三次握手丢失,服务器会超时重传SYN-ACK,但不会关闭连接,因为它在等那个“x+1”的确认。
  • 第三次握手(ACK):客户端收到SYN-ACK后,发送ACK=1, seq=x+1, ack=y+1,然后进入ESTABLISHED状态。服务器收到此ACK后,也进入ESTABLISHED状态。

现在看释放过程(四次挥手):

  • 第一次挥手(FIN):客户端发送FIN=1, seq=u,进入FIN_WAIT_1状态。它告诉服务器:“我数据发完了,准备关了。”
  • 第二次挥手(ACK):服务器收到FIN后,发送ACK=1, seq=v, ack=u+1,进入CLOSE_WAIT状态。注意,此时服务器可能还有数据没发完,所以不能立刻发FIN,只能先确认客户端的关闭请求。
  • 第三次挥手(FIN):服务器数据发完后,发送FIN=1, ACK=1, seq=w, ack=u+1,进入LAST_ACK状态。
  • 第四次挥手(ACK):客户端收到FIN后,发送ACK=1, seq=u+1, ack=w+1,进入TIME_WAIT状态,等待2MSL(最大报文段生存时间)后才彻底关闭。

提示:TIME_WAIT状态的存在,是为了确保网络中残留的旧连接报文段彻底消失,避免它们干扰新建立的同四元组连接。这是很多学生忽略的深层原因——它不是为了“等服务器确认”,而是为了清空网络中的历史幽灵报文。

实操验证:你可以用netstat -an | grep :80(Linux)或Get-NetTCPConnection | Where-Object {$_.State -eq "TimeWait"}(PowerShell)在本机观察大量TIME_WAIT连接。它们不是故障,而是TCP协议健壮性的体现。

2.2 滑动窗口机制:不是“大小”,而是“动态范围”

另一道经典题:“TCP滑动窗口的作用是什么?”常见回答是“流量控制”“提高信道利用率”。这依然停留在功能层面。真正的核心,在于理解窗口是一个左闭右开的字节序号区间,它由接收方通过Window Size字段动态通告,发送方据此调整unacknowledged和unsent数据的边界。

假设接收方通告窗口大小为5000字节,当前期望接收的序号是rcv_nxt = 10000,那么它的接收窗口就是[10000, 14999]。发送方必须保证:

  • 所有已发送但未确认的数据(unacknowledged)的序号必须 ≤rcv_nxt + window_size - 1
  • 所有尚未发送的数据(unsent)的序号必须 ≥rcv_nxt + window_size

当接收方成功接收并缓存了序号10000~12000的数据后,它会发送ACK=12001,并通告新的窗口大小(可能仍是5000,也可能因缓冲区满而变为0)。此时窗口滑动到[12001, 17000]。

注意:窗口大小字段只有16位,最大65535字节。现代高速网络中这显然不够,因此引入了窗口扩大选项(Window Scale Option),它在SYN报文中协商一个缩放因子(如2^3=8),实际窗口大小 = 报文中的16位值 × 缩放因子。这是很多学生在分析高吞吐量抓包时困惑的根源——Wireshark显示Window size value: 65535,但实际有效窗口可能是524280字节。

2.3 ARP协议:链路层的“地址翻译官”,不是“广播找人”

“ARP协议的作用是什么?”标准答案是“解决IP地址到MAC地址的映射”。但更本质的描述是:ARP是网络层与数据链路层之间的地址解析中介,它通过广播请求和单播响应,在本地链路上建立IP-MAC绑定关系,并将结果缓存在ARP缓存表中供后续使用。

关键细节在于“本地链路”和“缓存”。ARP请求(ARP Request)是以太网帧的目的MAC地址为FF:FF:FF:FF:FF:FF(广播地址),源MAC为发送方自身,而IP首部中的目的IP地址才是查询目标。交换机收到广播帧后,会将其泛洪到所有端口(除接收端口外),但路由器不会转发ARP请求,因为路由器是网络层设备,它只处理IP包,不处理ARP帧。这就是为什么ARP只在同一个子网内有效。

ARP缓存表(arp -a命令查看)有超时机制(通常2分钟未使用则删除),这是为了应对主机更换网卡或IP地址变更的情况。如果缓存长期不更新,会导致“IP地址已变,但MAC地址仍指向旧主机”的通信故障。

一次完整的ARP交互流程:

  1. 主机A(192.168.1.10)要发包给主机B(192.168.1.20),查本地ARP缓存,无B的MAC记录;
  2. A构造ARP请求帧:以太网目的MAC=FF:FF:FF:FF:FF:FF,源MAC=AA:AA:AA:AA:AA:AA,ARP操作码=1(请求),发送方IP=192.168.1.10,发送方MAC=AA:AA:AA:AA:AA:AA,目标IP=192.168.1.20,目标MAC=00:00:00:00:00:00;
  3. B收到后,发现目标IP匹配自己,于是构造ARP响应帧:以太网目的MAC=AA:AA:AA:AA:AA:AA(单播),源MAC=BB:BB:BB:BB:BB:BB,ARP操作码=2(响应),发送方IP=192.168.1.20,发送方MAC=BB:BB:BB:BB:BB:BB,目标IP=192.168.1.10,目标MAC=AA:AA:AA:AA:AA:AA;
  4. A收到响应,将192.168.1.20 -> BB:BB:BB:BB:BB:BB写入ARP缓存。

这个过程清晰地展示了分层封装边界:ARP帧本身没有IP首部,它是直接封装在以太网帧中的;而IP数据报则是封装在以太网帧的Payload字段里的。混淆这两者,是理解网络分层的最大障碍。

3. 计算题的核心:二进制地址运算的“手工推演”

计算题的失分,90%源于对二进制运算的“肌肉记忆”缺失。子网划分、CIDR聚合、IP地址合法性判断,表面是数学题,实质是位运算题。计算器可以帮你算出结果,但无法帮你理解“为什么这个地址不能作为主机地址”。

3.1 子网划分:从“借位”到“网络前缀长度”的思维转换

题目:“将网络192.168.1.0/24划分为4个子网,求每个子网的网络地址、广播地址、可用主机地址范围及子网掩码。”

传统做法是“借2位”,得到子网掩码255.255.255.192(/26),然后列四个子网。但这只是结果,不是过程。我们必须回到二进制层面,逐位推演。

原始网络:192.168.1.0/24

  • 二进制:11000000.10101000.00000001.00000000
  • 前24位是网络前缀,后8位是主机号。

要分4个子网,需2位(2²=4),所以新的网络前缀长度是24+2=26位。

  • 新掩码:前26位全1,后6位全0 →11111111.11111111.11111111.11000000=255.255.255.192

现在,主机号部分只剩下6位(原8位,借走2位),所以每个子网的主机数是2⁶=64,但全0(网络地址)和全1(广播地址)不可用,故可用主机数为62。

第一个子网(子网号00):

  • 网络地址:11000000.10101000.00000001.00000000 =192.168.1.0
  • 广播地址:11000000.10101000.00000001.00111111 =192.168.1.63
  • 可用主机:192.168.1.1~192.168.1.62

第二个子网(子网号01):

  • 网络地址:11000000.10101000.00000001.01000000 =192.168.1.64
  • 广播地址:11000000.10101000.00000001.01111111 =192.168.1.127
  • 可用主机:192.168.1.65~192.168.1.126

依此类推。关键在于,子网号是插入在网络前缀和主机号之间的,它决定了网络地址的“步长”。步长 = 2^(主机位数) = 2⁶ = 64。所以每个子网的网络地址,都是前一个加64。

实操技巧:在考试中快速验算,只需记住“步长”即可。例如,/26的步长是64,/27是32,/28是16,/29是8,/30是4。看到192.168.1.128/26,立刻知道它属于第二个子网(64~127),而128是第三个子网的起始地址。

3.2 CIDR地址聚合:寻找最长公共前缀的“位对齐”

题目:“将以下四个网络聚合为一个CIDR地址:192.168.128.0/24, 192.168.129.0/24, 192.168.130.0/24, 192.168.131.0/24。”

聚合的本质,是找到这些地址的最长公共前缀(Longest Common Prefix)。我们把它们的二进制写出来,对齐:

192.168.128.0 → 11000000.10101000.10000000.00000000 192.168.129.0 → 11000000.10101000.10000001.00000000 192.168.130.0 → 11000000.10101000.10000010.00000000 192.168.131.0 → 11000000.10101000.10000011.00000000

前三段(24位)完全相同:11000000.10101000.100000。看第四段的前两位:

  • 128→10000000
  • 129→10000001
  • 130→10000010
  • 131→10000011

它们的前6位都是100000,第7位开始出现差异(0,0,1,1)。所以,公共前缀长度是24 + 6 = 30位?不对!因为前三段是24位,第四段的前6位是额外的6位,总共30位。但我们需要覆盖全部四个网络,而30位前缀只能覆盖4个地址(2²=4),这恰好是我们的需求。

验证:192.168.128.0/30的范围是192.168.128.0~192.168.128.3,显然不对。错误在于,我们聚合的是整个/24网络,不是单个IP。所以应该看第四段的网络号部分。

128,129,130,131的二进制是:

  • 128:10000000
  • 129:10000001
  • 130:10000010
  • 131:10000011

它们的共同前缀是前6位100000,即十进制的128。而100000是6位,所以网络前缀是24+6=30位?还是不对。正确方法是:这四个网络的起始地址,其二进制表示中,从高位开始,有多少位是完全相同的。

将四个地址的完整32位对齐:

128: 11000000.10101000.10000000.00000000 129: 11000000.10101000.10000001.00000000 130: 11000000.10101000.10000010.00000000 131: 11000000.10101000.10000011.00000000

从第1位开始比对,直到第26位(即第三段的最后2位+第四段的前2位):

  • 第25位:全是0(128=10000000, 129=10000001, 130=10000010, 131=10000011,第25位是第三段的倒数第二位,都是0)
  • 第26位:全是0(同上,第三段的最后一位是0)

第27位:128和129是0,130和131是1,出现分歧。所以最长公共前缀是26位。

验证:192.168.128.0/26的范围是192.168.128.0~192.168.191.255?不对,/26的步长是64,所以192.168.128.0/26覆盖128.0~128.63,128.64~128.127是下一个/26。显然,128,129,130,131都在128.0/24内,但我们需要一个能包含它们四个/24的更大网络。

正确做法:这四个/24网络,其网络号分别是128,129,130,131。将它们视为整数,求它们的按位与(AND),再求按位或(OR),然后计算公共前缀长度。

  • AND(128,129,130,131) = 128 (10000000)
  • OR(128,129,130,131) = 131 (10000011)
  • XOR = 128 ^ 131 =00000011= 3
  • 3的二进制是11,占2位,所以主机位至少2位,网络位=32-2=30?还是不对。

标准算法:找出最小地址和最大地址,计算它们的XOR,然后统计XOR结果中最高位1的位置。
最小:128 (10000000)
最大:131 (10000011)
XOR =00000011= 3
3的二进制最高位是第2位(从0开始计数),所以需要32-2=30位前缀?但192.168.128.0/30显然太小。

错误根源:我们聚合的是网络地址,不是IP地址。每个网络地址是X.0/24,所以我们要比较的是128.0,129.0,130.0,131.0的二进制。

128.0=10000000.00000000
129.0=10000001.00000000
130.0=10000010.00000000
131.0=10000011.00000000

对齐后,前6位100000相同,第7位开始不同。所以网络前缀是24+6=30位?但192.168.128.0/30的网络地址是192.168.128.0,广播是192.168.128.3,只覆盖4个IP,不是4个/24网络。

终于找到正解:这四个/24网络,其第三段(128-131)的二进制是10000000到10000011,共4个连续数字。4=2²,所以需要2位来表示,因此聚合后的网络前缀是24-2=22位?不对,是24位减去用于表示子网的位数。

标准答案是192.168.128.0/22。验证:/22的子网掩码是255.255.252.0,即前22位网络位。192.168.128.0/22的范围是192.168.128.0~192.168.131.255,完美覆盖128,129,130,131这四个/24网络。

计算过程:

  • /22的步长是2^(32-22)=2^10=1024个IP地址,即4个/24网络(每个/24有256个IP,4×256=1024)。
  • 起始地址必须是步长的整数倍。128 ÷ 4 = 32,是整数,所以192.168.128.0/22是合法聚合。

关键心得:CIDR聚合不是纯数学,而是网络规划实践。它要求起始地址必须能被聚合后的步长整除。考试时,先算需要覆盖的网络数量N,确定所需位数k(2^k ≥ N),然后用原始前缀长度减去k,得到新前缀长度,再用原始地址向下取整到新步长的倍数,即为聚合网络地址。

3.3 IP地址合法性判断:全0与全1的“禁区”逻辑

题目:“判断以下IP地址是否可以作为主机地址:192.168.1.0, 192.168.1.255, 10.0.0.128/25。”

这题直击二进制本质。一个IP地址能否作为主机地址,取决于它在所属子网中的位置。

  • 192.168.1.0/24:子网掩码255.255.255.0,主机位是最后8位。0的二进制是00000000,全0,所以是网络地址,不可用作主机地址。
  • 192.168.1.255/24:255的二进制是11111111,全1,所以是广播地址,不可用作主机地址。
  • 10.0.0.128/25:/25的掩码是255.255.255.128,即前25位网络位,后7位主机位。128的二进制是10000000。在/25子网中,主机位是最后7位,所以10.0.0.128的最后7位是0000000(因为128=10000000,最后7位是0000000),即全0,所以它是该子网的网络地址,不可用。

验证:10.0.0.0/25的范围是10.0.0.0~10.0.0.127,网络地址10.0.0.0,广播10.0.0.127;10.0.0.128/25的范围是10.0.0.128~10.0.0.255,网络地址10.0.0.128,广播10.0.0.255。所以10.0.0.128确实是网络地址。

经验技巧:对于任意X.Y.Z.W/P,判断W是否为网络地址,只需计算该子网的步长S=2^(32-P),然后看W mod S是否等于0。若等于0,则为网络地址;若等于S-1,则为广播地址。例如,10.0.0.128/25,S=2^7=128,128 mod 128 = 0,所以是网络地址。

4. 高频陷阱题:那些“看起来像对”的错误答案

有些题目,标准答案看似简单,但隐藏着极易踩中的逻辑陷阱。这些题才是区分“死记硬背”和“真正理解”的试金石。

4.1 “MTU”与“MSS”的混淆:物理限制 vs 协议协商

题目:“TCP的MSS(Maximum Segment Size)是如何确定的?”

常见错误答案:“MSS等于MTU(Maximum Transmission Unit)。”
这是大错特错。MTU是数据链路层(如以太网)对单个帧的有效载荷(Payload)的最大字节数限制,典型值为1500字节。而MSS是TCP层协商的,一个TCP段(Segment)中数据部分的最大长度,它必须小于MTU,因为TCP段还要加上IP首部(通常20字节)和TCP首部(通常20字节)。

所以,MSS = MTU - IP首部长度 - TCP首部长度。在标准以太网中,MSS = 1500 - 20 - 20 = 1460字节。

更复杂的是,MSS是在TCP三次握手的SYN报文中通过MSS Option字段协商的。客户端在SYN中通告自己的MSS,服务器在SYN-ACK中通告自己的MSS。双方后续发送的TCP段,数据长度都不能超过对方通告的MSS值。

重要提示:MSS只影响TCP数据部分,不影响TCP首部。一个1460字节的TCP数据段,加上20字节TCP首部和20字节IP首部,总长度为1500字节,刚好填满以太网MTU。如果应用层数据超过1460字节,TCP会自动分段(Segmentation),这是传输层的职责,与IP分片(Fragmentation)完全不同。IP分片发生在网络层,当IP数据报长度超过下一跳链路的MTU时,由路由器或发送主机的IP模块执行,而TCP分段是端到端的、有状态的。

4.2 “路由表查找”与“最长前缀匹配”的执行顺序

题目:“路由器收到一个目的IP为192.168.1.100的数据包,其路由表中有以下条目:0.0.0.0/0(默认路由)、192.168.1.0/24、192.168.0.0/16。路由器会选择哪条路由?”

很多学生会选192.168.0.0/16,理由是“它更宽泛”。这是对“最长前缀匹配(Longest Prefix Match)”原则的根本误解。

路由选择的唯一原则是:匹配位数最多的路由条目。192.168.1.0/24的前缀长度是24位,192.168.0.0/16是16位,0.0.0.0/0是0位。24 > 16 > 0,所以选择192.168.1.0/24。

计算过程:将目的IP192.168.1.100与各路由条目的网络地址做按位与(AND),看结果是否等于该路由条目的网络地址。

  • 192.168.1.100 & 255.255.255.0=192.168.1.0==192.168.1.0→ 匹配,匹配长度24位
  • 192.168.1.100 & 255.255.0.0=192.168.0.0==192.168.0.0→ 匹配,匹配长度16位
  • 192.168.1.100 & 0.0.0.0=0.0.0.0==0.0.0.0→ 匹配,匹配长度0位

选择匹配长度最大的,即24位。

实操验证:在Linux中,ip route get 192.168.1.100会明确显示它匹配192.168.1.0/24。这个原则是互联网路由可扩展性的基石——它允许ISP发布聚合路由(如192.168.0.0/16),同时允许企业发布更精确的明细路由(如192.168.1.0/24),两者共存且优先级明确。

4.3 “ICMP重定向”:不是错误,而是路由器的善意提醒

题目:“当主机发送数据包给同一子网内的另一台主机,但该主机不在其ARP缓存中时,会发生什么?”

标准答案是“主机会发送ARP请求”。但更深入的问题是:“如果主机错误地将数据包发给了一个非默认网关的路由器,而该路由器发现目标主机其实和源主机在同一子网,它会怎么做?”

答案是:该路由器会向源主机发送一个ICMP重定向(ICMP Redirect)报文,类型为5,代码为1(对主机的重定向),并在报文中指明“你应该直接把包发给这个IP地址(即目标主机的IP)”。

这是一个常被忽略的优化机制。它避免了数据包在局域网内绕远路:主机→路由器→主机,而是直接主机→主机。

ICMP重定向报文的结构:

  • IP首部:源IP=路由器IP,目的IP=源主机IP
  • ICMP首部:类型
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 12:51:27

VLAN原理与三层交换实战:从广播域隔离到SVI路由

1. 项目概述:为什么VLAN不是“配完就完”的配置题,而是网络逻辑的第一次真正建模你打开思科模拟器,拖出几台交换机、几台PC,连上线,敲下switchport mode access和switchport access vlan 10——看起来一切顺利&#xf…

作者头像 李华
网站建设 2026/10/14 12:51:17

RASteer:扩散模型激活层的概念擦除与保留感知技术

1. 这不是“删图”而是“概念重写”:RASteer到底在动扩散模型的哪根神经?最近在几个AI模型安全研讨会上,我反复听到同行问:“你们说的‘擦除’,到底是把生成结果里某个东西P掉,还是让模型压根就学不会这个概…

作者头像 李华
网站建设 2026/10/14 12:51:08

C语言贪吃蛇开发:核心知识点与实战技巧

1. 项目概述与核心目标 这个C语言贪吃蛇项目的前置知识准备,主要面向有一定C语言基础但尚未接触过游戏开发的编程学习者。作为经典的控制台小游戏,贪吃蛇的实现涉及数据结构、控制台操作、键盘输入处理等多个核心知识点。在真正动手编码之前,…

作者头像 李华
网站建设 2026/10/14 12:50:55

SpringBoot高校运动会管理系统设计与实践

1. 项目背景与核心价值高校运动会作为校园体育文化的重要组成部分,每年都需要处理大量赛事报名、成绩记录、排名统计等工作。传统纸质化管理方式存在信息滞后、统计效率低、数据易丢失等问题。这个基于SpringBoot的校运动会管理系统,正是为了解决这些痛点…

作者头像 李华
网站建设 2026/10/14 12:50:46

react-native-video实战指南:从基础配置到性能优化与踩坑解决

做RN开发,只要涉及音视频播放,几乎绕不开react-native-video这个库。我最早接触它还是0.x版本,那时候Android和iOS两边还要写不少平台差异代码,现在API已经统一得很舒服了。不过正因为这个库功能多、配置项复杂,网上能…

作者头像 李华
网站建设 2026/10/14 12:50:46

Python魔术方法:让对象打印告别天书,实现可读输出

1. 从一行 print 说起&#xff1a;为什么你的对象输出是“天书”很多刚上手 Python 的朋友都踩过同一个坑&#xff1a;辛辛苦苦定义了一个类&#xff0c;实例化之后想打印出来看看里面到底装了什么&#xff0c;结果终端里蹦出来一行<__main__.Point object at 0x7f8b1c0a3d9…

作者头像 李华