基本上,我的背景是我加入一家公司,我应该用C编写一个程序,该程序需要三个输入(钢的英尺数,滚珠轴承的数量和青铜的磅数),并返回第二天可以制作多少个铃铛。
吹口哨需要0.5英尺的钢和1个滚珠轴承。制作铃铛需要1.10磅的青铜(.75磅的青铜和1个拍板(.35磅的青铜))。
这是我编写的程序。如果出现问题,我深表歉意。
#include <stdio.h>
#include <stdbool.h>
void PrintLine();
int CalculateWhistles(int steel, int bearings);
int CalculateBells(int bronze);
int main() {
bool runProgram = true;
int runAgain = 0;
while (runProgram == true) {
int ftSteelIn = 0;
int numBallBearingIn = 0;
int ibsBronzeIn = 0;
printf("\n\n"); // Formatting...
PrintLine();
printf(" How many feet of steel was received today: "); // Steel received
scanf("%d", &ftSteelIn);
printf(" How many ball bearings were received today: "); // Ball bearings received
scanf("%d", &numBallBearingIn);
printf(" How many pounds of bronze was received today: "); // Bronze received
scanf("%d", &ibsBronzeIn);
PrintLine(); // Print line
PrintLine(); // Print line
// Print company logo and program title
printf(" Acme Corporation\n");
printf(" Product Report\n");
PrintLine();// Print line
// Print number of bells and whistles
printf(" Number of Bells %d\n", CalculateBells(ibsBronzeIn));
printf(" Number of Whistles %d\n", CalculateWhistles(ftSteelIn, numBallBearingIn));
PrintLine(); // Print line
PrintLine();
printf("\n\n");
PrintLine();
// Ask the user if they would like to run the program again
printf(" Would you like to run the program again? ('1' for yes, '2' for no): ");
scanf("%d", &runAgain);
PrintLine();
// If user does not say 'yes', end the program and say goodbye
if (runAgain != 1) {
printf("\n Exitting the program. Have a good day.\n\n");
PrintLine();
runProgram = false;
}
}
return 0;
}
void PrintLine() {
int i = 0;
printf(" ");
while (i <= 70) {
printf("-");
i++;
}
printf("\n");
}
int CalculateWhistles(int steel, int bearings) {
steel = steel / 0.5;
if (steel < bearings) {
return steel;
}
else if (bearings < steel) {
return bearings;
}
}
int CalculateBells(int bronze) {
int bells = bronze / 1.1;
return bells;
}
它运行正常,没有错误。第二部分是我计算余数的部分,但是在继续该部分之前,我需要一些反馈。
任何建议都会受到赞赏,如何使其更有效或类似(我我只是在学习C)。
#1 楼
所有编程中大约有50%是关于处理错误的。您说您的代码有效,但是它无法处理错误,因此不起作用。示例1:
How many feet of steel was received today: four
程序不起作用不要说“错误,请重试”。而是显示所有提示,而无需等待任何用户输入和退出。
示例2:
How many feet of steel was received today: 3.7
程序没有说“错误,再试一次”。而是显示所有提示,而无需等待任何用户输入和退出。
示例3:
How many feet of steel was received today: 3 4
程序不会说“错误,再试一次”。相反,它假设钢为3英尺。显示“有多少个滚珠轴承”,并且不等待用户输入,并假设有4个滚珠轴承。
示例4:
Would you like to run the program again? ('1' for yes, '2' for no): yes
程序不会说“错误,请重试”。相反,它退出。
示例5:
How many feet of steel was received today: -99
How many ball bearings were received today: -99
How many pounds of bronze was received today: -99
程序决定您可以制作
-89
铃铛和-198
哨子。示例6:
How many feet of steel was received today: 999999999999999999
How many ball bearings were received today: 999999999999999999
How many pounds of bronze was received today: 999999999999999999
程序决定可以制作
-1351471477
铃铛和-2147483648
哨子。示例7:
How many feet of steel was received today: 200
How many ball bearings were received today: 100
程序决定您可以使
0
发出哨声。注意:与前面的示例不同,这不是“无法检查无效输入”的问题。评论
\ $ \ begingroup \ $
感谢您的建议,我将更改程序。如果有时间,我会添加其中的一些内容,但我不认为他们想要太复杂的程序(这实际上是初学者的课程)。我有点过分了;),所以我要补充一点,但我不希望评分员因必须检查我的代码是否包含所有多余的“垃圾”而感到沮丧。
\ $ \ endgroup \ $
– Qwurticus
2014-2-17在4:03
\ $ \ begingroup \ $
@Brendan另外50%是文档!
\ $ \ endgroup \ $
–杰森C
2014-2-17在6:20
\ $ \ begingroup \ $
如果您更改了那里过于夸张的50%,我会投票给您。如果您正确地计划了应用程序,则添加错误消息和警告所花费的时间不应超过开发总时间的10%。
\ $ \ endgroup \ $
–凯文
2014-2-17在11:52
\ $ \ begingroup \ $
@Kevin试图给出项目调试中任何常规百分比是不可能的。它取决于许多因素:技能,方法论,代码库,编程语言和个性等等。
\ $ \ endgroup \ $
– daramarak
2014-2-17在12:03
\ $ \ begingroup \ $
@daramarak这是关于添加错误处理,而不是调试。
\ $ \ endgroup \ $
–凯文
2014-02-17 12:04
#2 楼
我不确定您在课堂上走了多远,但以下是我在其他答案中没有看到的一些注意事项:可能您的
CalculateWhistles()
方法可能无法通过if
条件测试并到达非void
方法的结尾。即使您什么也不返回,您的方法对于每种情况也应始终具有return语句。例如,如果steel
等于bearings
,您的代码将做什么?int CalculateWhistles(int steel, int bearings) {
steel = steel / 0.5;
if (steel < bearings) {
return steel;
}
else if (bearings < steel) {
return bearings;
}
}
,因为如果测试条件失败,它将无法在我的系统上编译。我的编译器比大多数编译器都更严格,因此这表明您没有在启用警告的情况下进行编译,而应该将其打开。我重写了该方法,以便可以在我的系统上正确编译。
int calculateWhistles(int steel, int bearings)
{
steel /= 0.5;
if (steel <= bearings) return steel;
else return bearings;
}
在
for
方法中使用PrintLine()
循环。void PrintLine() {
int i = 0;
printf(" ");
while (i <= 70) {
printf("-");
i++;
}
printf("\n");
}
但是,当检查
for
循环时,它所做的只是打印出一些空间和一堆破折号。 printf()
语句在系统上可能会很昂贵,因此要使效率最大化,您应尽可能少地使用它们。void printLine()
{
printf(" ------------------------------------------\n");
}
但这是我们真正可以提高效率的地方效率,因为您的老师要求使用此方法:在函数上使用
inline
关键字。制作函数inline
的目的是向编译器暗示,值得花一些额外的精力来比其他方法更快地调用该函数-通常是通过将函数的代码替换为其调用程序。除了消除对调用和返回序列的需求外,它还可能允许编译器在两个函数的主体之间执行某些优化。这并不意味着您应该对所有内容进行
inline
。使用
inline
:而不是
#define
具有非常小的函数的
inline
的最佳选择:更快的代码和更小的可执行文件(更多机会保留在代码缓存中)当函数很小且经常调用时,
不要使用
inline
:具有大型函数的:导致较大的可执行文件,无论调用开销很少导致执行速度更快,无论何时很少使用该函数,都会显着降低性能。
/>
对于您的功能,也可以使用它们。
inline void printLine()
{
puts("------------------------------------------");
}
方法
bells
中不需要CalculateBells()
变量。int CalculateBells(int bronze) {
int bells = bronze / 1.1;
return bells;
}
您可以仅对带括号的参数返回数学运算。
int calculateBells(int bronze)
{
return (bronze / 1.1);
}
我的一些评论我认为没有必要。
printLine(); // Print line
您可以在一行上初始化一种类型的所有变量。
int ftSteelIn = 0;
int numBallBearingIn = 0;
int ibsBronzeIn = 0;
这也会减少代码行。
int ftSteelIn = 0, numBallBearingIn = 0, ibsBronzeIn = 0;
如果将所有方法放在
main()
之前,则不必事先进行原型设计。但是,由于我们使用的是__inline__
,我们将不得不使用prototypes。e我将让用户输入一个“ y”或“ n”的
char
来表明他们是否要再次运行该程序,也许一个do-while
循环。 您可以在某些地方使用
puts()
而不是printf()
函数。我个人觉得您的“报告”中的空间太大。
printf(" Acme Corporation\n");
但这是您自己决定的。
最终代码(更改了我的实现):
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
void printLine();
int calculateWhistles(int steel, int bearings);
int calculateBells(int bronze);
inline void printLine()
{
puts("----------------------------------------------------");
}
inline int calculateWhistles(int steel, int bearings)
{
steel /= 0.5;
if (steel <= bearings) return steel;
else return bearings;
}
inline int calculateBells(int bronze)
{
return (bronze / 1.1);
}
int main()
{
bool isRunning = true;
char runAgain = 'n'; // default to no
do
{
int ftSteelIn = 0, numBallBearingIn = 0, ibsBronzeIn = 0;
printf("How many feet of steel was received today? ");
scanf("%d", &ftSteelIn);
printf("How many ball bearings were received today? ");
scanf("%d", &numBallBearingIn);
printf("How many pounds of bronze was received today? ");
scanf("%d", &ibsBronzeIn);
for(; getchar() != '\n'; getchar())
{
} // eat superfluous input, including the newline
// Print company logo and program title
puts("\nAcme Corporation: Product Report");
printLine();
// Print number of bells and whistles
printf("Number of Bells: %d\n", calculateBells(ibsBronzeIn));
printf("Number of Whistles: %d\n", calculateWhistles(ftSteelIn, numBallBearingIn));
// Ask the user if they would like to run the program again
printf("Would you like to run the program again? (y/N) "); // capital 'N' to suggest it is default
scanf("%c", &runAgain);
if (tolower(runAgain) != 'y') isRunning = false;
} while (isRunning);
puts("Exitting the program. Have a good day.");
return 0;
}
评论
\ $ \ begingroup \ $
谢谢!我不认为if / else语句过多。 printLine()语句是多余的,我将其删除(我应该已经编写了程序,然后添加了格式)。使用for循环代替printLine()的while循环之间有真正的区别吗?我们对C的了解还不够(我对Python和C ++相当了解,通常用于循环)。我之所以使用原型,是因为我们的讲师希望我们使用它们来表明我们知道“功能是如何工作的” Rolls的眼睛。
\ $ \ endgroup \ $
– Qwurticus
2014-02-17 5:43
\ $ \ begingroup \ $
@Qwurticus啊,我一直在寻找“原型”这个词! :)在使用for循环的情况下,除了看起来更干净之外,while循环和while循环之间没有太大区别。它应该以完全相同的方式执行(尽管对于for循环,当我查看Assembly输出时,我看到的指令更少,因此效率的提高可以忽略不计)。
\ $ \ endgroup \ $
–syb0rg
2014年2月17日下午5:56
\ $ \ begingroup \ $
好的。我喜欢效率(我同意for循环看起来更干净;))。
\ $ \ endgroup \ $
– Qwurticus
2014年2月17日下午6:05
\ $ \ begingroup \ $
如果打开警告,编译器应提醒您第一点(到达非void函数的末尾)。因此,请始终在打开警告的情况下进行编译。
\ $ \ endgroup \ $
– 200_success
2014年2月17日下午6:45
\ $ \ begingroup \ $
@ syb0rg我赞成,但我认为您应该考虑添加有关内联函数的警告。内联在这个示例中确实有意义,但是您要特别注意Noobies,因为它们可能决定内联所有内容,而不管项目的规模如何。
\ $ \ endgroup \ $
– David Schwartz
14年2月18日在20:27
#3 楼
这里的其他答案非常好,尤其是布伦丹接受的答案。我想补充一条评论。您写道:任何建议,不胜感激,如何使其更有效...
我想谈谈您对只需简单的评论就可以使其“更加高效”:不用。编写代码,使其正常运行。干净地编写代码,并尽一切可能使您的设计简单明了。记录您的代码,考虑其他可能查看或使用它的人,并在将来考虑自己再回到代码中,而不记得您编写代码时的想法。
程序一旦完成写作,然后问自己:它不符合我设定的硬性能要求吗? UI太慢了吗?是这里或那里的某种算法花费的时间太长,并且实际上以明显和负面的方式影响了我程序的使用?我在某处内存不足吗?如果是这样,那么首先要集中精力在更高层次上改进任何算法或逻辑;举例来说,也许您正在排序大量数据,但它肯定太慢或占用大量资源-首先考虑使用其他排序算法。在对此感到满意之后,然后,如有必要,您可以继续进行进一步的微优化,但前提是您清楚地确定了实际瓶颈所在(例如,分析或测量功能时间,而不仅仅是盲目猜测)。
不要担心在这里或那里浪费一些CPU周期,如果这会导致干净,可维护,清晰的代码,尤其是在最初的开发过程中,您可能会意外地进行更改。您希望避免过早的优化,既过早地分散了您的实际目标,又将您锁定在某个特定的实现上,而这种实现在必要时无法轻易更改。
对于新程序员来说,开始立即进行不必要的微优化是非常普遍的。特别是在无关紧要的领域(例如,编写一个程序,会生成图像文件,但要尝试优化检查输出文件名字符串是否有效的代码。)请不要沿着那条路走!设计->实施->测试->配置文件->优化->测试,只有在不满足您的性能要求时才进行最后3次。
我知道这可能是一般建议,有点为时过早,但是如果您牢记这一点(以及此处其他最佳答案中的所有信息),您将为平滑,高效的体验做好准备。
评论
\ $ \ begingroup \ $
谢谢!我肯定会不耐烦,并且会更加专注于使代码正常工作。我听说过一些恐怖的故事,这些故事的作品效果很好,但无法阅读,对想要改进它的人有什么好处?
\ $ \ endgroup \ $
– Qwurticus
2014-2-17在6:28
\ $ \ begingroup \ $
@Qwurticus实际上,按小时付款对承包商来说很棒。承包商所钟爱的大型程序混乱无比,这会浪费可计费的时间。另外,他们不需要做任何清理,因为一旦合同确定,他们就可以进行下一场演出。
\ $ \ endgroup \ $
– corsiKa
2014-02-18 18:55
\ $ \ begingroup \ $
@corsiKa我希望您能开玩笑,因为这是可怕的建议,特别是对于新程序员而言。如果承包商想要“下次演出”,那不是最好的职业道德。但是,当我最终被雇用来清理您的烂摊子时,它对我有用。我从回头客(及其带来的推荐)中赚取的钱远比从维护中赚到的钱多-再加上良好的工作可以带来长期的领导/咨询工作(以及更快乐的最终用户,那里会有一些业障积分机会-下次获得对某个软件感到沮丧,也许它背后的开发人员也持同样的态度()。
\ $ \ endgroup \ $
–杰森C
2014-02-18 23:18
#4 楼
尽管以小写字母开头的函数是很常见的,但这取决于您编程环境中的首选。您可以选择任何一种情况,最好是提到的一种情况,除非您的讲师使用了某种情况。它只会使代码混乱,尤其是在
PrintLine()
中完成大部分工作时。由于教师需要这样做,因此您只需保留它们即可。但是,如果要保留或希望保留所分配程序的个人副本,则可以进行此更改。不需要
main()
中的else if
语句。由于该语句仅与第一个相反,因此可以使用简单的CalculateBells()
。您仍然可以保持这种方式,以防万一需要添加某些内容。此:
steel = steel / 0.5;
可以缩短为this:
steel /= 0.5;
这适用于所有数学运算符和类似情况。
对于这样的条件语句,您不需要不需要包含
else
:while (runProgram == true)
这也做同样的事情:
while (runProgram)
如果您曾经要与
true
一起使用,您将具有以下功能:while (!someCondition)
评论
\ $ \ begingroup \ $
不幸的是,$ printLine()$函数是必需的,老师希望输出的行长。 :/我将if / else更改为else语句,这对我来说是愚蠢的,但是该函数应该返回一个整数,并返回我想要的数字,但是我将其更改为float或加倍,看看它是如何工作的。感谢您的建议!
\ $ \ endgroup \ $
– Qwurticus
2014-2-17的3:58
\ $ \ begingroup \ $
@Qwurticus:在这种情况下可以。只是想确保这不是您要执行的操作。 :-)请确保不要编辑原始代码,否则答案将无效。您仍然可以添加有关打印功能的注释,以使其他人知道。
\ $ \ endgroup \ $
– Jamal♦
2014-02-17 4:00
\ $ \ begingroup \ $
@Jamal只是为了挑剔,至少没有真正的“ C命名约定”,至少在某种意义上不是某种“官方” C方式。更重要的是,程序员应该使用与所使用的其他程序员一致的样式约定,或者如果他是一个人,则至少在其所有代码中都保持一致,以便以后不要混淆自己。用C编写的程序有无数流行的命名约定,当不同的冲突时,真正的问题就会发生。
\ $ \ endgroup \ $
–杰森C
2014年2月17日在6:19
\ $ \ begingroup \ $
实际上,CalculateBells()和CalculateWhistles()应该返回整数。什么样的工厂会发出哨声?半个哨声会发出什么样的声音?
\ $ \ endgroup \ $
– 200_success
2014-02-17 6:40
\ $ \ begingroup \ $
@ 200_success:好一半的声音:-)
\ $ \ endgroup \ $
– Juha Untinen
2014-2-17在11:41
#5 楼
您在代码中嵌入了一些“魔术数字”,这是一个非常糟糕的主意,因为它们难以维护或更改。您应该分离出固定比率,并给它们取有意义的名称。在C语言中使用#define作为数字常量并不是很糟糕,因为它没有const修饰符。因此,添加#define CLAPPER_BRONZE 0.35等。最后一个微妙的地方是,您说一个哨子需要X和Y,并且已经添加了X和Y的权重(0.75 + 0.35)并实现了一个除以1.1的函数。
在代码中更直接,更透明地捕获您对领域的知识实际上要好得多。如果有人告诉您制作一个小部件需要X和Y,请对其进行编码。然后,担心编码X需要什么。这似乎有些过分,但是更改代码以适应不断变化的情况将更加容易。 (例如,我们改用X代替其他东西。)
评论
\ $ \ begingroup \ $
我不记得我的讲师是否经过了#define,所以我还不想把它扔在那里(这是C语言的新生课程),但是我会在下次编程时牢记这一点实践。您能否解释一下使“域更加透明”的最终观点?如果制作铃铛需要1.1磅青铜,不是简单地将其除以该数量即可获得最佳的铃铛数量吗? :(
\ $ \ endgroup \ $
– Qwurticus
14年2月17日在17:37
\ $ \ begingroup \ $
当X是“ 0.75磅青铜”和“ Y是0.35磅青铜”时,这似乎是巨大的杀伤力,除非您只建议用青铜/(BELL_BRONZE + CLAPPER_BRONZE)代替青铜/ 1.1。您还如何合理地编写要求:制作铃铛需要0.75磅青铜,然后再添加0.35磅青铜的要求?
\ $ \ endgroup \ $
–David Richerby
14年2月17日在18:05
\ $ \ begingroup \ $
当然,对于这个小例子来说,这太过分了。只是想刺激一个真实案例的脑细胞。
\ $ \ endgroup \ $
–数学家
14年2月18日在12:21
#6 楼
次要问题:不要写steel = steel / 0.5;
将钢除以0.5的结果就不是钢的数量。这是您可以发出的哨声数量的上限。因此,写下
int whistles = steel / 0.5;
现在重要的是:您没有“钢”。你有“钢脚”。如果规格改变并且以米为单位输入钢的数量怎么办?还是以磅为单位,您必须计算长度?最好编写
steelInFeet
,因此,如果规范发生更改,则很明显,您必须在代码中进行更改。例如:
double steelInFeet = steelInMeters / 0.3048;
int whistles = steelInFeet / 0.5;
评论
\ $ \ begingroup \ $
好的,我将更改名称,并在以后的程序中记住该名称。 :)他们还不希望我们考虑这些事情,所以尽管我怀疑不进行更改会给我带来伤害,但这样做也不会有任何伤害。
\ $ \ endgroup \ $
– Qwurticus
2014年2月17日在17:40
\ $ \ begingroup \ $
此外,x / 0.5与x * 2相同,只是更难阅读,更容易出现舍入/截断错误。 (在这种情况下没有错误,但是可能会发生。)如果有选择,我总是选择乘除法。
\ $ \ endgroup \ $
–达雷尔·霍夫曼(Darrel Hoffman)
14年2月18日在15:09
\ $ \ begingroup \ $
关于口味,我通常会省略In。所以我个人将使用steelFeet或steelMeters。我在训练应用程序中经常这样做。您将获得trainingMinutes和defaultCourseDurationHours。尽管100%的我每天都会用SteelInFeet代替钢!
\ $ \ endgroup \ $
– corsiKa
14年2月18日在18:57
#7 楼
人们对代码说了很多,所以我只说一分。您的评论很糟糕。对于初学者来说,注释很困难,因为很难告诉您代码的哪些部分是显而易见的,并且不需要更多说明,添加注释会为您带来哪些好处。随着经验的增加,您会对此有所改善。
您的大多数评论都是完全多余的,因为它们没有添加任何解释。例如,您有几个
PrintLine(); // Print line
的实例。使用注释来记录PrintLine函数,而不是调用它的位置。很明显,它会打印一行,因此您无需继续说下去。
同样,您对读取用户输入的行的评论也无法帮助读者理解代码:很明显,这些行读入了钢,黄铜和球轴承的数量。
,您根本没有记录这两个
Calculate
函数。这是程序中真正需要解释的唯一部分,因为它是唯一使用要解决的问题的特定事实的部分(例如,您需要用什么材料制作铃声或口哨声)。像int CalculateBells (int bronze)
// Calculate the number of bells that can be made from a given amount
// of bronze. Each bell requires 1.1lb.
{
[...]
}
评论
\ $ \ begingroup \ $
感谢您的建议,我的确意识到自己的评论多么愚蠢,自那以后又重新整理了一下。
\ $ \ endgroup \ $
– Qwurticus
14年2月17日在17:31
\ $ \ begingroup \ $
欢迎来到CR。很棒的第一答案。 +1
\ $ \ endgroup \ $
–rolfl
14年2月17日在17:34
#8 楼
您是否真的要使用整数进行这些计算?您不应该使用浮点变量吗?让我印象深刻的是,其他答案都集中在美学上,却没有评论代码中最关键的问题:它甚至都不起作用使用正确的输入。
编辑:实际上,其他答案都涉及这个问题,是我未能注意到这并不是一个错误,因为使用
int
确实达到了理想的答案。 >评论
\ $ \ begingroup \ $
如果它不起作用,我会发表评论。分配给int会自动将浮点值朝零舍入到下一个整数。如果您使用1.1磅的铜作为响铃,则“ int bells =铜/ 1.1;”确实可以提供正确的结果,因为您无法建立3 1/2铃。
\ $ \ endgroup \ $
– gnasher729
2014-02-17 12:46
\ $ \ begingroup \ $
确实可行,因为将结果四舍五入到最接近的int是合理的行为。此外,@ Brendan接受的答案指出该程序无法接受浮点输入。
\ $ \ endgroup \ $
– 200_success
2014-2-17在12:54
\ $ \ begingroup \ $
我纠正了。我在手机上阅读此内容,但没有意识到@Brendan的答案已经涉及浮点问题。整数截断也确实很理想,我很糟糕。
\ $ \ endgroup \ $
–蜘蛛
14年2月17日在14:18
\ $ \ begingroup \ $
@ 200_success:是的,这也是让我困惑的答案。足够的文档会有所帮助,但是OP并不能在这方面出类拔萃。
\ $ \ endgroup \ $
– Jamal♦
2014年2月17日在17:40
\ $ \ begingroup \ $
这很不好,以后我会发布原始文档/问题以使其更加清晰。此问题还有第二个“额外积分”部分,可以计算剩余部分并将其添加到第二天的物料中。那将使用浮点数/双精度数。
\ $ \ endgroup \ $
– Qwurticus
14年2月17日在17:45
评论
忘了添加,讲师想要输出的PrintLine()函数。既然您提到您是一个初学者,并且您似乎渴望学习,请查看本文。它充满了良好的编程习惯,如果您现在就开始使用它们,将会有更多的时间来学习有关编程的更多信息。
谢谢,我当然想变得更好,有时间我会阅读的! :)
“ PrintLine(); //打印行”这可能是我见过的最好的代码注释:o)