c-Goto和代码重复-在这种情况下它们是可以避免的吗



我最近遇到了一个编程问题,在我看来,解决这个问题的最佳方法是使用goto,尽管这不是一个好的做法。问题是:告诉用户输入一个正的自然数(>0(并读取输入。如果这个数字是有效的,告诉用户这个数字的平方。在输入正确的情况下执行此操作。我想出了一些解决方案,但似乎都有问题。以下是其中两个:
解决方案1-问题:使用goto

#include <stdio.h>
int main()
{
int num;
_LOOP:
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num > 0) {
printf("Square: %in", num * num);
goto _LOOP;
}
printf("Invalid numbern");
return 0;
}

解决方案2-问题:如果num>0(代码重复(

#include <stdio.h>
int main()
{
int num;
do {
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num > 0)
printf("Square: %in", num * num);
} while (num > 0);

printf("Invalid numbern");
return 0;
}

显然,有更多的方法可以解决这个问题,但我想出的所有其他方法都不使用goto来解决相同的代码重复问题。那么,有没有一种解决方案可以避免goto和代码重复?如果没有,我应该选哪一个?

以下是一半的答案;试着把缺的补上。请记住,有时最好将循环结构为";做点什么直到"而不是";在…的时候做点什么">

for (;;) {
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num <= 0)
break;
printf("Square: %in", num * num);
}
printf("Invalid numbern");

[根据@rdbo的回答更新]

脱离循环怎么办?这基本上是一个goto语句,直到循环结束,而没有显式使用goto

#include <stdio.h>
int main()
{
int num;
while(1) {
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num > 0) {
printf("Square: %in", num * num);
} else {
printf("Invalid numbern");
break;
}
}
return 0;
}

另一个选项:如果满足continue条件,则检查存储的bool。它读起来比无限循环/中断方法(对我来说(容易得多,而且没有代码重复。

#include <stdio.h>
#include <stdbool.h>
int main()
{
int num;
bool bContinue;
do {
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num > 0){
printf("Square: %in", num * num);
bContinue = true;
}
else{
printf("Invalid numbern");
bContinue = false;
}
} while (bContinue);             
return 0;
}

对于初学者来说,如果您期望一个非负数,那么变量num应该具有无符号整数类型,例如unsigned int

正如问题中所写的那样,用户可以输入无效数据或中断输入。你必须处理这样的情况。

此外,乘法CCD_ 5可能导致溢出。

使用goto而不是循环确实是个坏主意。

请注意,您应该在使用变量的最小作用域中声明变量。

对于这样的任务使用for循环也是个坏主意。使用while循环更有表现力。

程序可以按照以下方式

#include <stdio.h>
#include <stdbool.h>
int main(void) 
{
while ( true )
{
printf( "Enter a positive natural number: " );

unsigned int num;
if ( scanf( "%u", &num ) != 1 || num == 0 ) break;
printf( "Square: %llun", ( unsigned long long )num * num );
}
puts( "Invalid number" );
return 0;
}

程序输出可能看起来像

Enter a positive natural number: 100000000
Square: 10000000000000000
Enter a positive natural number: 0
Invalid number

或者,最好将最后一个输出语句移到while语句中。例如

#include <stdio.h>
#include <stdbool.h>
int main(void) 
{
while ( true )
{
printf( "Enter a positive natural number: " );

unsigned int num;
if ( scanf( "%u", &num ) != 1 || num == 0 )
{
puts( "Invalid number" );
break;
}

printf( "Square: %llun", ( unsigned long long )num * num );
}
return 0;
}

我有点惊讶还没有人提出函数分解。与其编写大量的基元语句,不如将main分解成更小的函数。除了可读性/可维护性方面的好处外,它还有助于以一种非常自然的方式消除代码重复。

在OP的情况下,从最终用户那里获得输入是一项单独的责任,并且对于单独的功能来说是一个很好的选择。

static bool user_enters_number(int *ptr_to_num)
{
printf("Enter a positive natural number: ");
return scanf("%i", ptr_to_num) == 1;
}

注意,user_enters_number显式地测试scanf的返回值。这改进了文件结尾处理。

同样,您可以赋予数字验证自己的功能。这可能看起来有些过头了(它只是num > 0,对吧?(,但它为我们提供了将验证与生成的错误消息相结合的机会。打印";无效的数字";在CCD_ 11结束时感觉不对。无效的数字并不是唯一的退出条件;文件末尾是另一个。因此,我将让验证函数来确定消息。此外,这还可以支持多种错误类型(例如,负数和零的单独消息(。

static bool is_valid_number(int num)
{
bool ok = (num > 0);
if (!ok) printf("Invalid numbern");
return ok;
}

我们现在有两个布尔类型的函数,它们可以与&&巧妙地链接在一起,并放入循环的条件部分,这是一种惯用的说法:如果这些函数中的任何一个失败(即返回false(,则立即退出循环。

剩下的是一个非常干净的main函数。

int main(void)
{
int num;
while (user_enters_number(&num) && is_valid_number(num))
{
printf("Square: %in", num * num);
}
}

要了解可维护性方面的好处,请尝试重写此代码,使其接受两个数字并打印它们的产品。

int main(void)
{
int num1, num2;
while (user_enters_number(&num1) && is_valid_number(num1) &&
user_enters_number(&num2) && is_valid_number(num2))
{
printf("Product: %in", num1 * num2);
}
}

这些更改是微不足道的,并且仅限于单个函数(不过您可能会考虑在user_enters_number中添加一个参数input_prompt(。

这种"分而治之"的方法不会对性能造成影响:智能编译器会做任何必要的事情来优化代码,例如内联函数。

相关内容

最新更新