在K& R的'C编程语言'(第二版)第29页,我读过一个我认为已经破解的程序。由于我是初学者,我希望我错了,虽然我无法解释原因。
以下是代码:
#include <stdio.h>
#define MAXLINE 1000 // Maximum input line size
int get1line(char line[], int maxline);
void copy(char to[], char from[]);
// Print longest input line
int
main()
{
int len; // Current line lenght
int max; // Maximum lenght seen so far
char line[MAXLINE]; // Current input line
char longest[MAXLINE]; // Longest line saved here
max = 0;
while ((len = get1line(line, MAXLINE)) > 0)
if (len > max) {
max = len;
copy(longest, line);
}
if (max > 0) // There was a line to read
printf("Longest string read is: %s", longest);
return 0;
}
// `get1line()` : save a line from stdin into `s`, return `lenght`
int
get1line(char s[], int lim)
{
int c, i;
for (i = 0; i < lim -1 && (c = getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
// `copy()` : copy `from` into `to`; assuming
// `to` is big enough.
void
copy(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
我的困惑在于:我们使用的是get1line
函数,并假设在for
- 循环i
的末尾设置为lim -1
。然后,以下if
- 语句将在i
更新lim
,导致下一条指令(将NULL
字符放在字符串末尾的那条指令)破坏堆栈(因为s[lim]
未分配,在这种情况下)。
代码是否已损坏?
答案 0 :(得分:4)
总结:不可能同时使用i == lim-1
和c == '\n'
退出循环,因此您担心的情况永远不会出现。
详细说明:我们可以重写for循环(同时保留其含义)以使事件顺序清晰。
i = 0;
for (;;) {
if (i >= lim-1) break; /* (1) */
c = getchar();
if (c == EOF) break; /* (2) */
if (c == '\n') break; /* (3) */
s[i] = c;
++i;
}
在循环退出(1)时,c == '\n'
不是这种情况,因为如果是这种情况,则循环将在前一次的(3)处退出。*
在循环退出(2)和(3)时,不能是i == lim-1
的情况,因为如果是这种情况,则循环将退出(1)。
*这取决于lim
至少为2,因此事实上前一次循环。该程序只调用get1line
,lim
等于MAXLINE
,所以情况总是这样。**
**在lim
小于2时,通过在循环开始之前将c
初始化为'\n'
以外的值,可以使函数安全。但是如果您担心这种可能性,那么您可能还需要关注lim
INT_MIN
的可能性,以便lim-1
由于整数溢出而导致未定义的行为。< / p>
答案 1 :(得分:1)
lim == 0
代码错误,因为它使用c
未初始化并添加\ 0。
lim == 1
也是错误的,因为它使用c
未初始化。使用lim < 2
调用函数不是很有用,但它不应该像这样失败。
如果lim > 1
则功能正常
for (i = 0; i < lim -1 && (c = getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
如果i == lim-1
或c == EOF
或c == '\n'
,则循环退出。
如果第一个条件为真(i == lim-1
),则最后一个条件肯定不为真(除非lim < 2
,如上所述)。
如果第一个条件为假(i < lim-1
),那么即使循环以c == \n
退出,我们也知道缓冲区中有空格,因为我们知道{{1} }。