对您的代码进行初步破解可能会产生:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int hours;
int minutes;
int seconds;
} Time;
static void err_exit(char const *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
static int calculateTimeDiff(int time1, int time2)
{
int difference = time1 - time2;
return difference;
}
static void print_time(char const *pre, Time t, char const *post)
{
printf("%s%2d:%.2d:%.2d%s", pre, t.hours, t.minutes, t.seconds, post);
}
int main(void)
{
Time t1;
Time t2;
printf("Time #1: ");
if (scanf("%d:%d:%d", &t1.hours, &t1.minutes, &t1.seconds) != 3)
err_exit("Failed to read time value");
printf("Time #2: ");
if (scanf("%d:%d:%d", &t2.hours, &t2.minutes, &t2.seconds) != 3)
err_exit("Failed to read time value");
int s1 = (t1.hours * 60 + t1.minutes) * 60 + t1.seconds;
int s2 = (t2.hours * 60 + t2.minutes) * 60 + t2.seconds;
print_time("T1 = ", t1, "; ");
print_time("T2 = ", t2, "; ");
printf("Difference in seconds (T1-T2): %d\n", calculateTimeDiff(s1, s2));
return 0;
}
运行示例:
Time #1: 1:0:1
Time #2: 0:59:59
T1 = 1:00:01; T2 = 0:59:59; Difference in seconds (T1-T2): 2
OP 反击:
请!我说我是结构和函数的新手,你知道我[不]知道你的一半代码是什么意思吗?不要误会我的意思,我很高兴得到专业人士的帮助,但你要明白,这对我一点帮助都没有。只会让我更加困惑!
上面的代码是对现有代码的一个非常小的修改。下面“带有输入验证的代码”部分中的代码稍微复杂一些;你可以暂时忽略它(但到周末你应该可以理解它;它并不复杂,虽然它在使用函数以避免重复方面是无情的)。
剖析上面的代码:
- 结构类型的定义被移到外面,
main()
以便其他函数也可以使用该类型。
- 该函数
err_exit
接受一个字符串参数 ( char const *msg
) 并在标准错误(这是报告错误消息的正确文件流)上打印它,然后退出程序。退出值 0 表示成功;1 是常见的“出现问题”退出状态。你可以使用EXIT_SUCCESS
,EXIT_FAILURE
如果你愿意的话;它们被定义<stdlib.h>
为与exit()
函数一起使用。编写此函数意味着我可以使用单个函数调用报告代码中的错误,而不必每次都编写 3 或 4 行代码(取决于您的格式偏好)。使用函数是一种节省劳动力的技术。它也有首字母缩写词 DRY:不要重复自己。关键字static
对你来说并不重要——它让我的编译器对我用来防止我犯愚蠢错误的选项感到满意。如果您愿意,我们可以稍后单独讨论这个问题。
- 该功能
calculateTimeDiff()
是您的功能逐字。给定两个整数值,它返回它们之间的差。
- 该函数
print_time(char const *pre, Time t, char const *post)
接受三个参数。用于告诉编译器该const
函数不会尝试修改字符串;你可以放弃这个词const
而不会产生任何重大的不良影响。中间参数是您的Time
结构之一,因此该函数在堆栈上获取结构的副本。然后代码调用printf()
以打印前缀字符串pre
,结构中的 3 个数字Time
(%2d
意味着打印 2 位数字或空格和 1 位数字 — 到足够好的近似值;%.2d
意味着始终打印 2 位数字,必要时使用前导 0 填充),以及 . 中的后缀字符串post
。这是一种格式化时间值的简单方法。
- 说到
main()
函数, and 的声明t1
和t2
你的firstTime
and一样secondTime
,但是我拒绝写这么长的名字。
- 和你的
printf()
一样。
- 呼叫
scanf()
与您的呼叫相同,只是名称t1
代替了firstTime
。它包含在一条if
语句中以检查读取操作是否成功。如果用户输入了格式错误的输入(可能是1::0::0
),则scanf()
不会返回 3;它将为示例输入返回 1。如果用户键入midnight
,则返回 0。如果用户指定 EOF(Control-D在 Unix 或Control-ZWindows 上键入),则scanf()
返回 EOF。
- 如果
scanf()
不成功,则err_exit()
调用该函数报告问题并退出程序。
- 下一段代码本质上等同于第 6-8 点中讨论的代码,使用
t2
(而不是secondTime
)。
- 接下来的两行使用名称
s1
ands2
代替您的firstFullTime
and secondFullTime
。每行根据您的一个结构中的值计算以秒为单位的时间。一小时有60分钟,一分钟有60秒。 (t1.hours * 60 + t1.minutes)
给出由hh:mm值表示的分钟数. 当它乘以 60 并添加秒时,结果是自午夜以来的秒数,由用户键入的hh:mm:ss值表示。
- 下一段代码使用
print_time()
上面创建的函数来打印t1
and中的值t2
;打印你得到的输入是非常宝贵的,这样你就知道计算机得到了什么。大多数情况下,它与您认为计算机得到的相同,但是当出现问题时,检查您和计算机的想法是否相同很重要。在它们之间,这两个调用会生成输出,例如T1 = 1:00:01; T2 = 0:59:59;
(第二个分号后有一个空格)。如果我希望输出分布在两行上,我可以将"; "
参数替换为"\n"
.
- 最后一行
calculateTimeDiff()
使用从两个结构计算的两个整数值调用您的函数,然后使用printf()
. '1 小时 1 秒' 和 '59 分 59 秒' 之间有 2 秒,因此该calculateTimeDiff()
功能正常工作。程序退出return 0;
from main()
,表示成功。
这段代码真的没有什么复杂的。最难的部分是scanf()
,你写的——我只是添加了错误检查以确保它有效。解释远比代码难。我故意忽略了对可行但不必要的代码可能变化的描述。
带有输入验证的代码
OP 应该暂时忽略这个答案的其余部分。
然后你可能会验证一些输入:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int hours;
int minutes;
int seconds;
} Time;
static int calculateTimeDiff(int time1, int time2)
{
int difference = time1 - time2;
return difference;
}
static inline int time_to_seconds(Time t)
{
return (t.hours * 60 + t.minutes) * 60 + t.seconds;
}
static inline Time seconds_to_time(int s)
{
Time t;
t.hours = s / 3600;
t.minutes = (s / 60) % 60;
t.seconds = s % 60;
return t;
}
static void print_time(char const *pre, Time t, char const *post)
{
printf("%s%2d:%.2d:%.2d%s", pre, t.hours, t.minutes, t.seconds, post);
}
static int get_time(char const *prompt, Time *t)
{
int rc = -1; // Assume failure
printf(prompt);
if (scanf("%d:%d:%d", &t->hours, &t->minutes, &t->seconds) != 3)
fprintf(stderr, "Failed to read time value in format hh:mm:ss\n");
else if (t->seconds < 0 || t->seconds > 59)
fprintf(stderr, "Seconds value %d is not in range 0..59\n", t->seconds);
else if (t->minutes < 0 || t->minutes > 59)
fprintf(stderr, "Minutes value %d is not in range 0..59\n", t->minutes);
else if (t->hours < 0 || t->hours > 24)
fprintf(stderr, "Hours value %d is not in range 0..24\n", t->hours);
else if (t->hours == 24 && (t->seconds != 0 || t->minutes != 0))
fprintf(stderr, "Maximum time value is 24:00:00 (you gave %2d:%.2d:%.2d)\n",
t->hours, t->minutes, t->seconds);
else
rc = 0; // All OK
return rc;
}
int main(void)
{
Time t1;
Time t2;
if (get_time("Time #1: ", &t1) != 0 ||
get_time("Time #2: ", &t2) != 0)
exit(1);
int s1 = time_to_seconds(t1);
int s2 = time_to_seconds(t2);
print_time("T1 = ", t1, "\n");
print_time("T2 = ", t2, "\n");
printf("Delta (T1-T2) = %d seconds\n", calculateTimeDiff(s1, s2));
print_time("Delta = ", seconds_to_time(calculateTimeDiff(s1, s2)), "\n");
return 0;
}
示例运行(程序名称dt
):
$ dt
Time #1: 24:00:01
Maximum time value is 24:00:00 (you gave 24:00:01)
$ dt
Time #1: -1:0:0
Hours value -1 is not in range 0..24
$ dt
Time #1: 0:-1:0
Minutes value -1 is not in range 0..59
$ dt
Time #1: 0:-1:-1
Seconds value -1 is not in range 0..59
$ dt
Time #1: 25:00:00
Hours value 25 is not in range 0..24
$ dt
Time #1: 23:60:00
Minutes value 60 is not in range 0..59
$ dt
Time #1: 23:59:60
Seconds value 60 is not in range 0..59
$ dt
Time #1: 24:01:00
Maximum time value is 24:00:00 (you gave 24:01:00)
$ dt
Time #1: 24:00:01
Maximum time value is 24:00:00 (you gave 24:00:01)
$ dt
Time #1: 24:00:00
Time #2: 0:0:0
T1 = 24:00:00
T2 = 0:00:00
Delta (T1-T2) = 86400 seconds
Delta = 24:00:00
$
到目前为止,一切都很好:
$ dt
Time #1: 0:0:0
Time #2: 23:59:59
T1 = 0:00:00
T2 = 23:59:59
Delta (T1-T2) = -86399 seconds
Delta = -23:-59:-59
$
突然间,我们进入了棘手的领域。在处理带符号的分段数字时(例如这里的分段是小时、分钟、秒),您需要将符号与组件值分开。我不打算解决这个问题。