虽然您发现您对 格式说明符 的错误使用以及需要指向 scanf 中的变量的指针是您的主要问题,但正确使用 scanf 的另一个方面不是尚未解决。
您不能简单地写scanf ("%d", &intr_class); 来获取用户输入而不验证返回以确保成功转换为整数。在您确信intr_class 拥有一个有效值之前,您必须这样做。 (在您尝试使用intr_class 之前,如果您有任何此类要求,您还需要确保该值在可接受的范围内)
这适用于所有用户输入,无论您使用什么功能来获取它,但它尤其适用于scanf。如果用户在响应您的提示时输入了错误的内容怎么办?例如
Enter the number of instruction classes: rabbits
然后呢?除非您检查返回值,否则您永远不会知道 intr_class 未设置,如果未初始化,任何访问该值的尝试都是未定义行为。
此外,scanf 遇到错误时会停止读取,而在stdin 中留下任何未读取的字符。那么,当您尝试阅读其他内容时会发生什么?让我们考虑一个例子:
Enter the frequency of the machine (MHz): 800
这个条目是成功还是失败? (答案:失败)为什么? stdin 仍然包含 "rabbits\n800\n"(每次用户按下 Enter 时都会添加 '\n',并且您不会每次输入都失败的唯一原因是 '%d' 格式说明符会占用前导空格),但 "rabbits" 不是空格...)
因此,您必须加倍小心使用scanf 进行用户输入,不仅要考虑转换是成功还是失败,还要考虑输入缓冲区中剩余的任何字符(如果您的下一种输入类型,则为'\n'不占用前导空格,或转换失败后留在输入缓冲区中的任何字符)。
管理此问题的一种方法是在每次调用 scanf 后手动删除留在 stdin 中的所有字符。 (注意:应该检查返回不是EOF)。在您调用scanf 并检查返回不是EOF 后,您可以简单地调用一个简单的函数来读取并丢弃所有剩余的字符,例如:
/* function to empty remaining characters in stdin before next input
* to help avoid the pitfalls associated with using scanf for user input.
*/
void emptystdin()
{
int c = getchar();
for (; c != '\n' && c != EOF; c = getchar()) {}
}
避免使用全局变量以及使用struct 保存所有参数的非常好的建议也应该得到解决。如果您定义了一个结构(称为struct parameters_t),在main() 中创建并声明它的一个实例(例如称为parameters),那么您只需将parameters 的地址传递给您的params() 函数并填写params 中的值。您只需将函数声明更改为包含指向结构类型的指针void params (stuct parameters_t *p),然后在params 函数中提示并填写p->intr_class 等。
为了简单起见(并且避免一直为该类型键入struct parameters_t,您可以简单地创建一个typedef(别名),将parameters_t 定义为struct parameters_t 的别名以减少打字。
您还应该将返回类型从 void 更改为可以有意义地指示您在 params 中输入的成功/失败的内容
将这些部分放在一个简短的示例中,您可以执行类似于以下的操作:
#include <stdio.h>
#include <stdlib.h>
/* declare a struct to associate all the values */
typedef struct {
int intr_class,
freq,
class1,
cpi_1,
class2,
cpi_2,
class3,
cpi_3;
} parameters_t;
/* function to empty remaining characters in stdin before next input
* to help avoid the pitfalls associated with using scanf for user input.
*/
void emptystdin()
{
int c = getchar();
for (; c != '\n' && c != EOF; c = getchar()) {}
}
/* params takes pointer to (address of) a struct parameters_t and
* prompts for and fills each value. a tmp struct is used to avoid
* changing any values in 'p' in case of a partial fill. returns
* address of p on success, NULL otherwise indicating error.
*/
parameters_t *params (parameters_t *p){
parameters_t tmp = { .intr_class = 0 };
printf ("Enter the number of instruction classes: ");
if (scanf ("%d", &tmp.intr_class) != 1)
return NULL;
printf ("Enter the frequency of the machine (MHz): ");
if (scanf ("%d", &tmp.freq) != 1)
return NULL;
printf ("Enter CPI of class 1: ");
if (scanf ("%d", &tmp.cpi_1) != 1)
return NULL;
printf ("Enter instruction count of class 1 (millions): ");
if (scanf ("%d", &tmp.class1) != 1)
return NULL;
printf ("Enter CPI of class 2: ");
if (scanf ("%d", &tmp.cpi_2) != 1)
return NULL;
printf ("Enter instruction count of class 2 (millions): ");
if (scanf ("%d", &tmp.class2) != 1)
return NULL;
printf ("Enter CPI of class 3: ");
if (scanf ("%d", &tmp.cpi_3) != 1)
return NULL;
printf ("Enter instruction count of class 3 (millions): ");
if (scanf ("%d", &tmp.class3) != 1)
return NULL;
*p = tmp; /* assign temp values to struct p */
return p;
}
/* simple function to print values stored in p */
void prnparams (parameters_t *p)
{
if (!p || p->intr_class == 0) {
fprintf (stderr, "error: parameters empty or NULL\n");
return;
}
printf ("parameters:\n"
" instruction classes: %d\n"
" frequency (MHz) : %d\n"
" CPI of class 1 : %d\n"
" class 1 inst count : %d\n"
" CPI of class 2 : %d\n"
" class 2 inst count : %d\n"
" CPI of class 3 : %d\n"
" class 3 inst count : %d\n",
p->intr_class, p->freq, p->cpi_1, p->class1,
p->cpi_2, p->class2, p->cpi_3, p->class3);
}
int main (void) {
char menuchoice;
/* declare a struct & iniailize all values zero */
parameters_t parameters = { .intr_class = 0 };
for (;;) { /* loop until user quits */
/* print out menu list */
printf ("\nMenu of Options:\n"
"______________\n"
" a) Enter Parameters\n"
" b) Calculate average CPI of a sequence of instructions\n"
" c) Calculate total execution time of a sequence of "
"instructions\n"
" d) Calculate MIPS of a sequence of instructions\n"
" p) Print stored values\n"
" e) Quit\n\n"
"Enter selection: ");
if (scanf ("%c", &menuchoice) == EOF) { /* check user cancels input */
putchar ('\n'); /* tidy up before exit */
break;
}
if (menuchoice != '\n') /* make sure user didn't just hit [Enter] */
emptystdin(); /* remove all chars from stdin */
switch(menuchoice){
case 'a':
if (!params(¶meters))
fprintf (stderr, "error: params() failed.\n");
emptystdin(); /* critical here or menuchoice would be '\n' */
break;
case 'b':
//avgCPI();
break;
case 'c':
//calcExTime();
break;
case 'd':
//calcMIPS();
break;
case 'p':
prnparams (¶meters);
break;
case 'e':
exit(0);
default:
fprintf (stderr, "error: invalid menuchoice.\n");
break;
}
}
return 0;
}
使用/输出示例
$ ./bin/scanfparams
Menu of Options:
______________
a) Enter Parameters
b) Calculate average CPI of a sequence of instructions
c) Calculate total execution time of a sequence of instructions
d) Calculate MIPS of a sequence of instructions
p) Print stored values
e) Quit
Enter selection: a
Enter the number of instruction classes: 10
Enter the frequency of the machine (MHz): 20
Enter CPI of class 1: 30
Enter instruction count of class 1 (millions): 40
Enter CPI of class 2: 50
Enter instruction count of class 2 (millions): 60
Enter CPI of class 3: 70
Enter instruction count of class 3 (millions): 80
Menu of Options:
______________
a) Enter Parameters
b) Calculate average CPI of a sequence of instructions
c) Calculate total execution time of a sequence of instructions
d) Calculate MIPS of a sequence of instructions
p) Print stored values
e) Quit
Enter selection: p
parameters:
instruction classes: 10
frequency (MHz) : 20
CPI of class 1 : 30
class 1 inst count : 40
CPI of class 2 : 50
class 2 inst count : 60
CPI of class 3 : 70
class 3 inst count : 80
Menu of Options:
______________
a) Enter Parameters
b) Calculate average CPI of a sequence of instructions
c) Calculate total execution time of a sequence of instructions
d) Calculate MIPS of a sequence of instructions
p) Print stored values
e) Quit
Enter selection: e
仔细看。刚学的时候有很多东西要消化。因此,如果您有任何问题,请发表评论,我很乐意为您提供进一步的帮助。