0

I am having trouble with a struct array. I need to read in a text file line by line, and compare the values side by side. For example "Mama" would return 2 ma , 1 am because you have ma- am- ma. I have a struct:

typedef struct{
    char first, second;
    int count;
} pair;

I need to create an array of structs for the entire string, and then compare those structs. We also were introduced to memory allocation so we have to do it for any size file. That is where my trouble is really coming in. How do I reallocate the memory properly for an array of structs? This is my main as of now (doesn't compile, has errors obviously having trouble with this).

int main(int argc, char *argv[]){
//allocate memory for struct
pair *p = (pair*) malloc(sizeof(pair));
//if memory allocated
if(p != NULL){
    //Attempt to open io files
    for(int i = 1; i<= argc; i++){
        FILE * fileIn = fopen(argv[i],"r");
        if(fileIn != NULL){
            //Read in file to string
            char lineString[137];
            while(fgets(lineString,137,fileIn) != NULL){
                //Need to reallocate here, sizeof returning error on following line
                //having trouble seeing how much memory I need
                pair *realloc(pair *p, sizeof(pair)+strlen(linestring));
                int structPos = 0;
                for(i = 0; i<strlen(lineString)-1; i++){
                    for(int j = 1; j<strlen(lineSTring);j++){
                        p[structPos]->first = lineString[i];
                        p[structPos]->last = lineString[j];
                        structPos++;
                    }
                }
            }
        }
    }
}
else{
    printf("pair pointer length is null\n");
}

}

I am happy to change things around obviously if there is a better method for this. I HAVE to use the above struct, have to have an array of structs, and have to work with memory allocation. Those are the only restrictions.

Zach Caudle
  • 145
  • 2
  • 13
  • Those are some serious nested loops. Have you considered breaking your main function into some subroutines? – Tom Apr 17 '12 at 18:52
  • I was going to actually create new methods for everything. I was just trying to figure out how this works for now. – Zach Caudle Apr 17 '12 at 19:02
  • @Tom: I don't see a lot of code that I would refactor out into separate functions here (whether or not I would refactor the logic is another issue). The fact that a nested loop exists does not mean it should be refactored. That's 16 lines of code in main, I don't think that's a bad thing. – Ed S. Apr 17 '12 at 19:13
  • @EdS. TBH, I didn't take a close look at the code; I was just a bit alarmed by the presence multiple nested loops. – Tom Apr 17 '12 at 19:19
  • 1
    @Tom: Haven't done much image processing then I guess :D – Ed S. Apr 17 '12 at 19:19
  • @EdS. Actually, that's one of my favorite areas of Comp Sci. – Tom Apr 17 '12 at 19:20
  • @Tom: Well... nested loops abound – Ed S. Apr 17 '12 at 19:21

2 Answers2

3

Allocating memory for an array of struct is as simple as allocating for one struct:

pair *array = malloc(sizeof(pair) * count);

Then you can access each item by subscribing "array":

array[0] => first item
array[1] => second item
etc

Regarding the realloc part, instead of:

pair *realloc(pair *p, sizeof(pair)+strlen(linestring));

(which is not syntactically valid, looks like a mix of realloc function prototype and its invocation at the same time), you should use:

p=realloc(p,[new size]);

In fact, you should use a different variable to store the result of realloc, since in case of memory allocation failure, it would return NULL while still leaving the already allocated memory (and then you would have lost its position in memory). But on most Unix systems, when doing casual processing (not some heavy duty task), reaching the point where malloc/realloc returns NULL is somehow a rare case (you must have exhausted all virtual free memory). Still it's better to write:

pair*newp=realloc(p,[new size]);
if(newp != NULL) p=newp;
else { ... last resort error handling, screaming for help ... }
huelbois
  • 6,762
  • 1
  • 19
  • 21
  • Wouldn't an array of pair be a "pair*", not a "pair **"? – Tom Apr 17 '12 at 19:04
  • ho ho, my mistake ! Corrected it, thanks ! I was thinking to one thing, and writing something else ... ! – huelbois Apr 17 '12 at 19:10
  • 2
    Please don't cast the return value of `malloc` in C. It's not just a style issue, it can actually hide the fact that you forgot to include `stdlib.h`. C is not C++ – Ed S. Apr 17 '12 at 19:10
  • I read that somewhere but wasn't sure. You don't need to cast it with (pair*)? Also, do I need to initialize the array, or is that done in the pair*array=... portion? If so, mind explaining where? – Zach Caudle Apr 17 '12 at 19:16
  • Ed S. edited my code to remove the cast, since it wasn't needed in C (malloc returns a void* value). malloc does not initialize the memory it allocates, you need to set the values yourself (like array[x].count=0, etc). Or instead, use calloc (which sets everything to zero) – huelbois Apr 17 '12 at 19:20
  • @ZachCaudle: C has no problem implicitly converting a `void*` to any other type of pointer, so no, there is no need to cast the return value of `malloc`. `malloc` simply allocates a bunch of memory, it does not initialize your structures for you. You will need to do that yourself. – Ed S. Apr 17 '12 at 19:20
  • Maybe I am asking this wrong, which shows how poorly I understand it. Do I need to declare array as an array anywhere, or is that done with the pointer function? In other words, with the pointer code, can I do array[0] or do I need to initialize it as an array to do that? – Zach Caudle Apr 17 '12 at 19:24
  • There are two things: first, you allocate memory to store N pairs (malloc). Then, you make "p" var point to this area. p being a pointer, it can be used similarly as an array (there's a slight difference between a declared array and a pointer, but keep it for later). So, as soon as you have done p=malloc(sizeof(pair)*count), you can use p[0], p[1], etc. – huelbois Apr 17 '12 at 19:50
0

So if I get this right you're counting how many times pairs of characters occur? Why all the mucking about with nested loops and using that pair struct when you can just keep a frequency table in a 64KB array, which is much simpler and orders of magnitude faster.

Here's roughly what I would do (SPOILER ALERT: especially if this is homework, please don't just copy/paste):

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

void count_frequencies(size_t* freq_tbl, FILE* pFile)
{
    int first, second;

    first = fgetc(pFile);
    while( (second = fgetc(pFile)) != EOF)
    {
        /* Only consider printable characters */
        if(isprint(first) && isprint(second))
            ++freq_tbl[(first << 8) | second];

        /* Proceed to next character */
        first = second;
    }
}

int main(int argc, char*argv[])
{
    size_t* freq_tbl = calloc(1 << 16, sizeof(size_t));;
    FILE* pFile;
    size_t i;

    /* Handle some I/O errors */
    if(argc < 2)
    {
        perror ("No file given"); 
        return EXIT_FAILURE;
    }

    if(! (pFile = fopen(argv[1],"r")))
    {
        perror ("Error opening file"); 
        return EXIT_FAILURE;
    }

    if(feof(pFile))
    {
        perror ("Empty file"); 
        return EXIT_FAILURE;
    }

    count_frequencies(freq_tbl, pFile);

    /* Print frequencies */
    for(i = 0; i <= 0xffff; ++i)
        if(freq_tbl[i] > 0)
            printf("%c%c : %d\n", (char) (i >> 8), (char) (i & 0xff), freq_tbl[i]);
    free(freq_tbl);

    return EXIT_SUCCESS;
}

Sorry for the bit operations and hex notation. I just happen to like them in such a context of char tables, but they can be replaced with multiplications and additions, etc for clarity.

smocking
  • 3,689
  • 18
  • 22