5

我正在阅读 K&R 的书,其中一个练习的答案让我感到困扰。

在解决方案手册中,练习 1-22 声明了一个 char 数组:

#define MAXCOL 10
char line[MAXCOL];

所以我的理解是,在 C 数组中,从 0 ... n-1 开始。如果是这种情况,那么上述声明应该为长度为 10 的 char 数组分配内存,该数组从 0 开始并以 9 结束。根据我的理解,更重要的是 line[10] 超出范围?示例程序中的一个函数最终被传递一个等于 10 的整数值 pos,然后进行以下比较:

int findblnk(int pos) {
while(pos > 0 && line[pos] != ' ')
        --pos;
    if (pos == 0)                   //no blanks in line ?
        return MAXCOL;
    else                        //at least one blank
        return pos+1;               //position after blank
}

如果 pos 为 10 并且 line[] 的长度仅为 10,那么 line[pos] 不是超出数组的范围吗?

在 C 中以这种方式进行比较是否可以,或者这可能会导致分段错误?我确信解决方案手册是正确的,这真的让我很困惑。如有必要,我也可以发布整个程序。谢谢!

感谢您快速且非常有帮助的回复,我想这绝对是一个错误。它通过以下分支调用:

else if (++pos >= MAXCOL) {
            pos = findblnk(pos);
            printl(pos);
            pos = newpos(pos);
        }

如上所述,MAXCOL 定义为 10。所以对于这个分支 findblnk(pos) pos 将至少传递 10。

您认为 K&R 的解决方案手册值得一读还是以有错误的代码示例而闻名?

4

4 回答 4

3

如果pos确实如此10,那么它将是越界访问,并且越界访问数组是未定义的行为,因此即使程序当时看起来正常工作,任何事情都可能发生,结果是不可靠的。C99 标准Annex J.2 未定义行为草案包含以下项目符号:

数组下标超出范围,即使一个对象显然可以使用给定的下标访问(如在给定声明 int a[4][5] 的左值表达式 a[1][7] 中)(6.5.6)。

我手边没有K&R的副本,但勘误表没有针对这个问题列出任何内容。我最好的猜测是条件应该<而不是>=.

于 2013-10-22T02:32:25.240 回答
2

上面的代码只要pos == 9 传递给该函数就可以了。如果pos ==10当它通过时,它的未定义行为和..你是正确的,应该避免它。

但是,它可能会或可能不会给出分段错误。

于 2013-10-22T02:32:51.240 回答
2

在C 中超出数组的边界永远不会永远都可以。(或任何语言)。

如果 10 真的传递给该函数,那肯定是一个错误。虽然有更好的方法来做到这一点,但该函数至少应该在尝试将其用作索引之前验证它pos是否在范围内。line

于 2013-10-22T02:29:54.437 回答
1

my_type buffer[SOME_CONSTANT_NAME];几乎总是一个错误。

像您在问题中提供的代码是大多数安全问题的根源:当缓冲区溢出时,它会调用未定义的行为,并且未定义的行为(如果它不直接使程序崩溃)可以经常被攻击者利用在您的流程中执行他们自己的代码。

所以,我的建议是远离所有固定的缓冲区大小,要么使用 C++,要么std::vector<>动态分配足够的内存来适应。asprintf()Posix 2008 标准即使在具有函数和朋友的 C 语言中也使这变得非常容易。

于 2013-10-22T11:51:23.937 回答