kusur
kusur

Reputation: 618

Appending an output file in C

I am solving a problem on USACO. In the problem, I have to take two strings as inputs and calculate the numerical values modulo 47. If the values are same, then GO is to be printed otherwise STAY has to be printed. The initial numerical value will be calculated by taking the product of the numerical values of the alphabets ( 1 for A and similarily 26 for Z ) and then the final number will be calculated by using modulo.

My program is being compiled withour any error and the first case is also a success. But the problem is in the second case and the way my file is being appended. The program is as follows:-

#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;
}

OUTPUT:-

output_image

The first case is the set two strings:-

and the second case is given in the error notification itself.

If I remove the comment symbols from (2) and put it on (1), then the program is working fine because I can see the contents of the file and they appear just as the grader system wants. It isn't happening for the actual statement of (1). The comments of line (4) and (6) are also fine but not the line (1). I am not able figure this out. Any help?

Upvotes: 0

Views: 143

Answers (1)

Runium
Runium

Reputation: 1085

First a few notes:

  • main(): a decent main is either:

    int main(void)
    or
    int main(int argc, char *argv[])
    
  • Using malloc() you should always check if it returns NULL, aka fail, or not.

  • Always free() malloc'ed objects.
  • Everyone has his/hers/its own coding style. I have found this to be invaluable when it comes to C coding. Using it as a base for many other's to. Point being structured code is so much easier to read, debug, decode, etc.

More in detail on your code:

Signature of cal()

First line in main you declare the signature for cal(). Though this works you would probably put that above main, or put the cal() function in entirety above main.

Max length

You have a define #define MAX 6 that you never use. If it is maximum six characters and you read a string, you also have to account for the trailing zero.

E.g. from cplusplus.com scanf:

specifier 's': Any number of non-whitespace characters, stopping at the first whitespace character found. A terminating null character is automatically added at the end of the stored sequence.

Thus:

#define MAX_LEN_NAME    7
...
comet = malloc(sizeof(char) * MAX_LEN_NAME);

As it is good to learn to use malloc() there is nothing wrong about doing it like this here. But as it is as simple as it is you'd probably want to use:

char comet[MAX_LEN_NAME] = {0};
char group[MAX_LEN_NAME] = {0};

instead. At least: if using malloc then check for success and free when done, else use static array.

Safer scanf()

scanf() given "%s" does not stop reading at size of target buffer - it continues reading and writing the data to consecutive addresses in memory until it reads a white space. E.g.:

/* data stream = "USACOeRRORbLAHbLAH NOP" */
comet = malloc(szieof(char) * 7);
scanf("%s", buf);

In memory we would have:

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; */
...

And when reading the proposed stream/string above we would not read USACOe in to comet but we would continue reading beyond the range of comet. In other words (might) overwriting other variables etc. This might sound stupid but as C is a low level language this is one of the things you have to know. And as you learn more you'll most probably also learn to love the power of it :)

To prevent this you could limit the read by e.g. using maximum length + [what to read]. E.g:

 scanf("%6[A-Z]", comet);
         | |      |
         | |      +------- Write to `comet`
         | +-------------- Read only A to Z
         +---------------- Read maximum 6 entities

Input data

Reading your expected result, your errors, your (N) comments etc. it sound like you should have a input file as well as an output file.

As your code is now it relies on reading data from standard input, aka stdin. Thus you also use scanf(). I suspect you should read from file with fscanf() instead.

So: something like:

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;
}

...

The cal() function

First of, the naming. Say this was the beginning of a project that eventually would result in multiple files and thousand of lines of code. You would probably not have a function named cal(). Learn to give functions good names. The above link about coding style gives some points. IMHO do this in small projects as well. It is a good exercise that makes it easier when you write on bigger to huge ones. Name it e.g. cprod_mod_47().

Then the mod variable (and might c) is superfluous. An alternative could be:

int cprod_mod_47(char *str)
{
    int prod = 1;

    while (*str)
        prod *= *(str++) - 'A' + 1;

    return prod % 47;
}

Some more general suggestions

When compiling use many warning and error options. E.g. if using gcc say:

$ gcc -Wall -Wextra -pedantic -std=c89 -o my_prog my_prog.c

This is tremendous amount of help. Further is the use of tools like valgrind and gdb invaluable.

Upvotes: 2

Related Questions