我是C编程的新手(虽然我有Java经验)。在阅读了一些教程之后,我决定开始在Coderbyte上解决编码挑战。
我尝试的第一个挑战是this one:
挑战
让函数 FirstFactorial(num)传递 num 参数并返回它的阶乘。例如:如果 num = 4,那么您的程序应返回(4 * 3 * 2 * 1) = 24.对于测试用例,范围将介于1之间和18,输入将始终是一个整数。
样本测试案例
输入:4
输出:24输入:8
输出:40320
我的解决方案:
#include <stdio.h>
void FirstFactorial(int num[]) {
int i = num -1;
for(i ; i > 0; i--) {
num = num * i;
printf("%d",i);
}
printf("\t %d", num);
}
int main(void) {
// disable stdout buffering
setvbuf(stdout, NULL, _IONBF, 0);
// keep this function call here
FirstFactorial(gets(stdin));
return 0;
}
输入参数的值: 8
错误讯息:
main.c: In function 'FirstFactorial':
main.c:5:11: warning: initialization makes integer from pointer without a cast [-Wint-conversion]
int i = num -1;
^~~
main.c:8:15: error: invalid operands to binary * (have 'int *' and 'int')
num = num * i;
^
main.c: In function 'main':
main.c:23:18: warning: passing argument 1 of 'FirstFactorial' makes pointer from integer without a cast [-Wint-conversion]
FirstFactorial(8);
^
main.c:3:6: note: expected 'int *' but argument is of type 'int'
void FirstFactorial(int num[]) {
^~~~~~~~~~~~~~
exit status 1
所以似乎有一些问题,我有几个问题:
gets(stdin)
。我查了gets()
,glibc文档说该函数返回char*
。如何将其传递给采用int
?看起来像是
int i = num -1;
将i
初始化为4而不是7.为什么?
for循环似乎正确递减i
(i
= 7,6,5,4,3,2,1)。但是这句话:
num = num * i;
正在生成错误。这有什么问题?它看起来像是正常的乘法。
答案 0 :(得分:5)
为了将来的访客:
这是对Coderbytes用于“便利”的语言的严重滥用。 gets(stdin)
永远不应该一开始就起作用:类型不起作用。
实际发生的事情是 Coderbytes在将代码发送给编译器之前,盲目查找并替换了gets(stdin)
的第一个实例和您提供的文字字符串。这甚至不是预处理器宏,而是源代码上的盲目替换。
因此,尽管您实际上不应该这样做,但对于Coderbytes来说,这是必不可少的恶作剧:这似乎是将输入内容输入程序的唯一受支持的方式。
此外,如果您想娱乐一下,请尝试清除所有其他内容并将其放入Coderbytes:
int main(){
printf("%s", "This is a literal string containing gets(stdin) along with other words");
}
您将看到,即使在字符串文字内部也会发生替换!
答案 1 :(得分:3)
忽略gets
是危险的,因此根据Why is the gets function so dangerous that it should not be used?已完全从C语言中移除,以下是您的问题的答案:
- 我从未使用过gets(stdin)。我在C-Library参考文献中对它进行了讨论。看起来它会返回一个字符(是的用户输入)。为什么我可以将它作为整数传递给函数?
醇>
没有人使用gets(stdin)
,因为它期望参数是指向存储结果的字符缓冲区的指针,而不是stdin
。与fgets
不同,gets
只能从stdin
读取,默认设置为stdin
- 您无法对其进行更改。
您无法将其传递给期待int[]
的函数。您的编译器必须在此处提供诊断消息,因为从char*
返回的gets
与int[]
不兼容。如果您的编译器没有给出这样的消息,那么它就会被破坏,不应该被使用。
gcc编译器确实在这里给出了一条消息,而不是你引用的消息。它闻起来好像你在gnu90中运行gcc(&#34;废话模式&#34;),不建议初学者使用。请参阅本答案的底部,了解如何运行它。
- 看起来像
醇>int i = num -1;
我将我初始化为4而不是7.我不明白为什么?
该行无效C.在这种情况下,num
是一个数组,由于它是一个函数参数,因此会调整为int*
类型。 num - 1
因此给出指针算术,这不是你想要的。结果是int*
类型。您无法将结果类型为int*
的表达式分配给int
。同样,您的编译器必须提供诊断消息并且它正确执行:
warning: initialization makes integer from pointer without a cast
如果它产生了可执行文件,尽管有上述消息,该程序的行为是未定义的,因为它不是有效的C,然后可能发生任何事情。
- 但它看起来像是声明:
醇>num = num * i;
无效。
出于与上述相同的原因,num
被声明为数组,因此您无法以任何合理的方式对其执行算术。
总的来说,你不能通过&#34;进行猜测试验&amp;错误&#34;,编程不会那样工作。你必须真正知道每一行代码的作用。我强烈建议您将编译器警告调到最高级别,并确保在运行程序之前没有警告:
gcc -std=c11 -pedantic-errors -Wall -Wextra
答案 2 :(得分:0)
正如前面提到的,你不应该使用gets(),因为它非常容易出现缓冲区溢出(你肯定需要了解它);正如其他人所提到的,更好的选择是fgets。最好的方法是因为它只会写入缓冲区,最多只能指定为长度的字节数;如果它没有停止,那么它将继续写入缓冲区的末尾到其他内存,这是坏。这会导致很多安全问题,而且会导致严重崩溃。
处理代码中的其他问题,如下所示:
在C类型中,比在Java中使用的类型更流畅。变量只是记忆中的一个点;你可以将内存中的那个点解释为不同的东西,而C可以让你很容易地在不同的类型之间进行转换,如果你不小心,这可能会让你感到厌烦。
C中的数组总是指针或内存地址,因此对于num
,int* num
是指针,而不是int,它就像num
。这就是为什么当您尝试对gets
和int变量执行算术运算时,编译器会向您发出警告。这意味着你得到的东西,或fgets,是一个字符串,而不是一个int,所以将get的原始输出传递给FirstFactorial会给你带回垃圾。你需要做的是得到字符串代表的整数。
int
返回的内容可以用作FirstFactorial('4')
,因为它是一个char *,它是一个地址,可以被解释为&#34;作为整数(因为它实际上是一组字节,其中包含指向内存地址的数字)。由于它们都只是位串,编译器可以用这种方式解释它们,但会警告你,你可能没有语义上意味着你要告诉它做什么。
字符串实际上只是内存中的数字&#34;代表&#34;字符的字形(如ASCII所示:http://ee.hawaii.edu/~tep/EE160/Book/chap4/subsection2.1.1.1.html)你在做什么就像这个FirstFactorial(4)
,你想要的是strtol
。转换该字符串的最佳方式&#39; 4&#39;到4是使用atoi
(这里带有示例:https://www.techonthenet.com/c_language/standard_library_functions/stdlib_h/strtol.php),但#include <stdio.h>
void FirstFactorial(int num) {
int i = num -1;
for(i ; i > 0; i--) {
num = num * i;
// printf("%d",i);
}
printf("\t %d", num);
}
#define BUFFER_LENGTH 60
int main(void) {
char str[BUFFER_LENGTH];
// disable stdout buffering
if( fgets (str, BUFFER_LENGTH, stdin)!=NULL ) {
int num = atoi(str);
FirstFactorial( num );
}
// keep this function call here
return 0;
}
稍微容易一点,作为一个例子,它在语义上做你的代码所做的但是更安全和编译:
.php
注意#define,这是一个预处理器宏,它有助于保持缓冲区大小和传递给fgets的长度之间的共同点。如果您决定需要更改缓冲区的大小,这会有所帮助;你可能会改变缓冲区大小(比如40),但忘记改变传递给fgets的长度;你会回到fgets写入最多60个字节进入缓冲区的问题,只有40个字节,这意味着你可以覆盖20个字节的其他内存,这也是坏。
获得一本关于C的现代书籍将非常有帮助,并且一位导师或导师帮助您解决这些差异,这可以非常有利于节省时间和避免一些容易出现的陷阱并导致问题在你的代码中。如果没有关于为什么要做或不做的事情的指导,那么在接受最佳实践方面,C有点难度。