蒟蒻最近发现前面设 memset(dist, 0x3f, sizeof dist); 与后面判断if (dist[n] == 0x3f3f3f3f)是不冲突。 因为:memset 是按字节填充的,不是按 int 赋值。

写:

memset(dist, 0x3f, sizeof dist);

其中:

  • 0x3f 是十六进制数,等于十进制的 63,二进制是 0011 1111。
  • memset 会把 dist 这块内存的每一个字节都设置成 0x3f。
  • 在常见的 4 字节 int 环境下,一个 int 占 4 个字节,所以它会被填成:
0x3f 0x3f 0x3f 0x3f

也就是:

0x3f3f3f3f

所以执行完:

memset(dist, 0x3f, sizeof dist);

之后,dist 中每个 int 的值实际上就是:

0x3f3f3f3f

因此后面判断:

if (dist[n] == 0x3f3f3f3f);

是完全正确的,不冲突。

可以简单理解为:

memset(dist, 0x3f, sizeof dist);

等价于把每个 int 初始化为:

0x3f3f3f3f

而不是:

0x0000003f

另外,这里用 0x3f3f3f3f 作为无穷大也是常见技巧。因为:

0x3f3f3f3f = 1061109567

两个 0x3f3f3f3f 相加:

0x3f3f3f3f + 0x3f3f3f3f = 0x7e7e7e7e

仍然小于 INT_MAX,不会导致有符号整数溢出。所以它在最短路算法里很适合当 INF。

所以:

  • memset(dist, 0x3f, sizeof dist); 之后,每个 int 是 0x3f3f3f3f。
  • dist[n] == 0x3f3f3f3f 判断的是同一个值。
  • 两者不冲突。

1 comments

  • @ 2026-9-24 16:44:08

    这位同学对 memset 的按字节填充机制和 0x3f3f3f3f 作为 INF 的优越性理解得很到位,总结基本正确。作为讨论帖,我来做几点补充,帮助大家把这块知识点彻底夯实。

    一、结论确认

    你的核心结论没问题:

    • memset 按字节操作,memset(dist, 0x3f, sizeof dist) 会把每个字节写成 0x3f;
    • 4 字节 int 上,每个元素被填成 0x3f3f3f3f;
    • 因此 dist[n] == 0x3f3f3f3f 与初始化值是同一个数,不冲突。

    二、几个值得深挖的点

    1. 为什么 memset 只能安全地填 0x00、0x3f、0xff 这类"重复字节"?

    关键不是"值是多少",而是每个字节是否相同。

    • 想填 0:每个字节 0x00,可以 → memset(a, 0, ...);
    • 想填 -1:补码是全 1,每个字节 0xff,可以 → memset(a, -1, ...);
    • 想填 0x3f3f3f3f:每个字节 0x3f,可以。

    反过来,如果你想填 1,memset(a, 1, ...) 得到的是 0x01010101 = 16843009,而不是 1。这是最容易踩的坑,务必记住。

    2. 关于 0x3f3f3f3f 作为 INF 的"不溢出"论证

    你的加法推导方向是对的,但表述可以更严谨一点���

    • 在 32 位有符号 int 下,INT_MAX = 0x7fffffff;
    • 0x3f3f3f3f + 0x3f3f3f3f = 0x7e7e7e7e < 0x7fffffff,确实不会溢出。

    但要注意:这只保证"两个 INF 相加不溢出"。如果一条边权本身很大,或者路径上做了多次加法(比如 Floyd 里 d[i][k] + d[k][j] 两端都是 INF),结论依然成立,因为 INF + INF 是上界。而在松弛时通常还会配合 if (d[u] == INF) continue; 来避免无意义的加法。

    3. 一个隐藏的坑:判断"不可达"用 == 还是 >=?

    在纯 Dijkstra 且不修改 INF 值的情况下,dist[n] == 0x3f3f3f3f 是安全的。

    但在 Floyd 或某些实现里,如果 INF 参与了加法,dist 可能变成 0x7e7e7e7e 之类的值,此时 == 0x3f3f3f3f 就会判错。所以更稳妥的写法往往是:

    if (dist[n] >= 0x3f3f3f3f / 2)  // 视为不可达
    

    这是一个值得大家养成习惯的写法。

    4. 跨平台/类型问题

    • 如果 dist 是 long long,memset(dist, 0x3f, ...) 得到的是 0x3f3f3f3f3f3f3f3f,此时判断值也要相应改成 0x3f3f3f3f3f3f3f3f,不能再用 0x3f3f3f3f;
    • 如果 dist 是 int 数组,但你在 64 位平台上,int 仍是 4 字节,不受指针宽度影响,这点可以放心。

    三、留给大家思考的小问题

    1. 如果我想把数组全部初始化成 -1,memset(a, -1, sizeof a) 之后,每个 int 的十六进制表示是什么?为什么?
    2. 在 long long 数组上,INF 应该设成什么值,才能保证"两两相加不溢出"?请给出一个十六进制常量。
    3. 为什么很多模板里 INF 用 0x3f3f3f3f 而不用 0x7fffffff?除了"加法不溢出",还有没有别的原因?

    把这三个问题想清楚,memset 和 INF 这块基本就不会再出错了。欢迎继续跟帖讨论。

    • 1