(声明:我已经看到了这个问题,我不是重新问了-我很感兴趣,为什么代码工作,而不是在如何它的工作原理.)
所以,这里的这个实现苹果的(当然,FreeBSD的)strlen()
.它使用一个众所周知的优化技巧,即它一次检查4或8个字节,而不是与0进行逐字节比较:
size_t strlen(const char *str) { const char *p; const unsigned long *lp; /* Skip the first few bytes until we have an aligned p */ for (p = str; (uintptr_t)p & LONGPTR_MASK; p++) if (*p == '\0') return (p - str); /* Scan the rest of the string using word sized operation */ for (lp = (const unsigned long *)p; ; lp++) if ((*lp - mask01) & mask80) { p = (const char *)(lp); testbyte(0); testbyte(1); testbyte(2); testbyte(3); #if (LONG_BIT >= 64) testbyte(4); testbyte(5); testbyte(6); testbyte(7); #endif } /* NOTREACHED */ return (0); }
现在我的问题是:也许我错过了明显的,但这不能读过字符串的结尾吗?如果我们有一个长度不能被字大小整除的字符串怎么办?想象一下以下场景:
|<---------------- all your memories are belong to us --------------->|<-- not our memory --> +-------------+-------------+-------------+-------------+-------------+ - - | 'A' | 'B' | 'C' | 'D' | 0 | +-------------+-------------+-------------+-------------+-------------+ - - ^ ^^ | || +------------------------------------------------------++-------------- - - long word #1 long word #2
当读取第二个长字时,程序访问实际上不应该访问的字节...这不是错误的吗?我非常有信心Apple和BSD的人都知道他们在做什么,所以有人可以解释为什么这是正确的吗?
我注意到的一件事是啤酒男孩认为这是未定义的行为,我也相信它确实是,但他被告知它不是,因为"我们用字符串大小与初始for循环对齐"(未显示)这里).但是,我根本没有看到为什么如果数组不够长并且我们正在读取它的末尾,那么对齐将是任何相关的.
虽然这在技术上是未定义的行为,但实际上没有本机架构以比字大小更精细的粒度检查越界内存访问.因此,虽然通过终结器的垃圾可能最终被读取,但结果不会是崩溃.