c语言 - 这个带有此索引的数组如何工作?K&R练习



我正在读K&R、 目前在第1章。在阅读了一节并尝试解决问题后,我喜欢在网上查看其他解决方案,只需看看解决同一问题的不同方法。

练习1-14说,我们需要打印一个输入中不同字符频率的直方图。我发现的这个解决方案只考虑了字母字符:

#include <stdio.h>
#define MAX 122
#define MIN 97
#define DIFF 32
int main(){
int c = EOF;
int i, j;
int array[MAX - MIN];
printf("%d  ", MAX - MIN);
for (i = MIN; i <= MAX; i++){
array[i] = 0;
printf("%d  ", i);
}
while ((c = getchar()) != EOF){
if (c >= MIN)
++array[c];
else {
++array[c + DIFF];
}
}
for (i = MIN; i <= MAX; i++){
printf("|%c%c|", i - DIFF, i);
for (j = 1; j <= array[i]; j++){
putchar('*');
}
putchar('n');
}
return 0;
}

虽然我理解这段代码背后的逻辑,但我不明白array[]数组是如何工作的,也不明白为什么工作。声明数组时,其大小为25 (MAX - MIN).。此数组的索引范围应从0到24。然而,在第一个循环期间,使用从97122的值对数组进行索引。但是,如果索引从一个大得多的值开始,那么如何访问数组呢?循环不应该是吗

for (i = 0, i < MAX - MIN; i++)

对我来说,如何从索引数组毫无意义

array[97] ... array[122]

编辑:

我在第一个循环中添加了printf("%d ", MAX - MIN);printf("%d ", i);,试图看看它是否真的在从97开始对数组进行索引。

int array[MAX - MIN];

这里,数组的大小是25,因为是197-97 = 25

for (i = MIN; i <= MAX; i++){
array[i] = 0;

这里,array[i]的索引越界,因为数组的大小是25和值,而MIN97

此外,++array[c];j <= array[i];由于越界而未定义。

GCC编译器生成警告:

source_file.c: In function ‘main’:
source_file.c:14:10: warning: array subscript is above array bounds [-Warray-bounds]
array[i] = 0;
^
source_file.c:20:12: warning: array subscript is above array bounds [-Warray-bounds]
++array[c];
^
source_file.c:20:12: warning: array subscript is above array bounds [-Warray-bounds]
source_file.c:28:27: warning: array subscript is above array bounds [-Warray-bounds]
for (j = 1; j <= array[i]; j++){
^

C11 J.2未定义的行为

  • 将指针添加到数组对象或刚好超出数组对象和整数类型,会产生指向刚好超出数组的结果数组对象,用作一元*运算符的操作数评估(6.5.6)。

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

实际上,for循环正在越界访问数组,调用未定义的行为,这意味着您的程序可能会崩溃。

MAX - MIN给出25,而您在[97122]中访问具有索引的数组,这显然是错误的。

类似地,++array[c]for (j = 1; j <= array[i]; j++)也调用Undefined Behavior,因为它们也超出了界限。

附言:你需要声明你的数组大小为MAX - MIN + 1,因为英文字母表有26个字母。

数组有no boundary checking in C,程序员有责任处理它。

因此,当您将数组声明为时

int array[ MAX - MIN ] ;

你不局限于只使用sizeof( int ) * (MAX - MIN),但如果你不在这个范围内使用,行为就会非常不稳定。

还有其他编程语言强制执行数组边界检查,但不强制执行c,当然,除非它经过严格的编译步骤,包括字符串警告检查。

所以在这种情况下,即使程序工作,它也是不正确的。

也许要理解的底线是

"工作代码可能并不总是正确的代码">

最新更新