我正在解决USACO的一个问题。在这个问题中,我必须将两个字符串作为输入并计算模数为47的数值。如果值相同,则打印GO,否则必须打印STAY。初始数值将通过取字母数字的乘积来计算(A为1,Z为26),然后使用模数计算最终数字。
我的程序正在编译时没有任何错误,第一个案例也是成功的。但问题出在第二种情况和我的文件被追加的方式。该计划如下: -
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define MAX 6
main()
{
int cal(char *ptr);
int a,b;
char *comet,*group;
FILE *fptr;
comet=malloc(6*sizeof(char));
group=malloc(6*sizeof(char));
scanf("%s",comet);
a=cal(comet);
scanf("%s",group);
b=cal(group);
fptr=fopen("ride.out","a+"); (1)
//fptr=fopen("ride.txt","a+"); (2)
if(a==b)
fprintf(fptr,"GO\n"); (3)
//printf("GO\n"); (4)
else
fprintf(fptr,"STAY\n"); (5)
//printf("STAY\n"); (6)
fclose(fptr);
return 0;
}
int cal(char *ptr)
{
int c,prod=1,mod;
while(*ptr)
{
c=(*ptr++)-'A'+1;
prod=prod*c;
}
mod=prod%47;
return mod;
}
输出: -
第一种情况是设置两个字符串: -
,第二种情况在错误通知中给出。
如果我从(2)中删除注释符号并将其放在(1)上,那么程序工作正常,因为我可以看到文件的内容,它们就像分级系统想要的那样出现。 (1)的实际陈述没有发生。第(4)和(6)行的注释也很好,但不是第(1)行。我无法解决这个问题。有什么帮助吗?
答案 0 :(得分:2)
首先注意几点:
main()
:体面的主要是:
int main(void)
or
int main(int argc, char *argv[])
使用malloc()
时,您应该始终检查它是否返回NULL
,又称是否失败。
free()
malloc'ed对象。更详细的代码:
main
中的第一行,您声明了cal()
的签名。虽然此有效,但您可能会将其放在main
之上,或将cal()
函数完整地放在main
之上。
您有一个永远不会使用的定义#define MAX 6
。如果最多六个字符并且您读取了一个字符串,则还必须考虑尾随零。
E.g。来自cplusplus.com scanf:
说明符's':任意数量的非空白字符,在找到的第一个空白字符处停止。在存储序列的末尾会自动添加一个终止空字符。
因此:
#define MAX_LEN_NAME 7
...
comet = malloc(sizeof(char) * MAX_LEN_NAME);
因为学习使用malloc()
很好,所以在此处执行此操作时没有错误。 但是因为它很简单,你可能想要使用它:
char comet[MAX_LEN_NAME] = {0};
char group[MAX_LEN_NAME] = {0};
代替。至少:如果使用malloc
然后检查是否成功并在完成后自由,否则使用静态数组。
scanf()
给定"%s"
并不会停止读取目标缓冲区的大小 - 它继续读取和将数据写入内存中的连续地址,直到它读取空白区域。
E.g:
/* data stream = "USACOeRRORbLAHbLAH NOP" */
comet = malloc(szieof(char) * 7);
scanf("%s", buf);
在记忆中我们会:
Address (example)
0x00000f comet[0]
0x000010 comet[1]
0x000011 comet[2]
0x000012 comet[3]
0x000013 comet[4]
0x000014 comet[5]
0x000015 comet[6]
0x000016 comet[7]
0x000017 /* Anything; Here e.g. group starts, or perhaps fptr */
0x000018 /* Anything; */
0x000019 /* Anything; */
...
在阅读上面提到的流/字符串时,我们不会将USACOe
读入comet
,但我们会继续阅读超出范围comet
。换句话说(可能)覆盖其他变量等。这可能听起来像愚蠢但由于C是低级语言,这是你必须知道的事情之一。当你了解更多时,你很可能也会学会爱它的力量:)。
为防止出现这种情况,您可以限制阅读时间,例如使用maximum length
+ [what to read]
。 E.g:
scanf("%6[A-Z]", comet);
| | |
| | +------- Write to `comet`
| +-------------- Read only A to Z
+---------------- Read maximum 6 entities
阅读您的预期结果,错误,(N)
评论等。听起来您应该有输入文件和输出文件。
现在,您的代码依赖于从标准输入读取数据,即stdin
。因此,您还使用scanf()
。我怀疑你应该read from file with fscanf()
代替。
所以:像:
FILE *fptr_in;
char *file_data = "ride.in";
int res;
...
if ((fptr_in = fopen(file_data, "r")) == NULL) {
fprintf(stderr, "Unable to open %s for reading.\n", file_data);
return 1; /* error code returned by program */
}
if ((res = fscanf(fptr_in, "%6[A-Z]%*[ \n]", comet)) != 1) {
fprintf(stderr, "Read comet failed. Got %d.\n", res);
return 2;
}
b = cal(comet);
if ((res = fscanf(fptr_in, "%6[A-Z]%*[ \n]", group)) != 1) {
fprintf(stderr, "Read group failed. Got %d.\n", res);
return 2;
}
...
首先,命名。说这是一个项目的开始,最终会导致多个文件和数千行代码。您可能没有名为cal()
的函数。学会给函数起好名字。关于编码风格的上述链接给出了一些观点。恕我直言也在小项目中这样做。这是一个很好的练习,当你写大到大的时候会更容易。将其命名为cprod_mod_47()
。
然后mod
变量(可能是c)是多余的。另一种选择可能是:
int cprod_mod_47(char *str)
{
int prod = 1;
while (*str)
prod *= *(str++) - 'A' + 1;
return prod % 47;
}
编译时使用许多警告和错误选项。例如。如果使用gcc说:
$ gcc -Wall -Wextra -pedantic -std=c89 -o my_prog my_prog.c
这是一个巨大的帮助。此外,使用valgrind
和gdb
等工具也非常宝贵。