0

I have a text file from which I read words. After that, I have to write in a binary file each word and near it the row and column where it appears. At _strdup(p) my programm crashes. Does anyone know why? I would appreciate your help. Here is the code:

void create(const char *filename, const char ****matrix) {

    FILE *u, *f;
    u = fopen(filename, "wb");
    assert(u != NULL);
    f = fopen("in.txt", "r");
    assert(f != NULL);
    (*matrix) = (char ***)malloc(sizeof(char **) * 1);
    int i = 0;
    int j=0; char buff[1024];
    while (fgets(buff, 1024, f)!=NULL) {

        (*matrix) = realloc((*matrix), (i + 1) * sizeof(char **));
        char *p = strtok(buff, " ().,");
        (*matrix)[i] = (char **)malloc(sizeof(char *));
        while (p) {

                (*matrix)[i] = (char **)realloc((*matrix)[i], sizeof(char *)*(j + 1));
                strcpy((*matrix)[i], buff);
                (*matrix)[i][j] = _strdup(p);

                fwrite((*matrix)[i][j], sizeof(char *), 1, u);
                fwrite(&i, sizeof(int), 1, u);
                fwrite(&i, sizeof(int), 1, u);
                j++;

                (*matrix)[i][j] = NULL;

            p = strtok(NULL, " ().,");
        }
        (*matrix)[i] = NULL;
        i++;
        printf("\n");

    }
    fclose(u);
    fclose(f);
}
Duck Dodgers
  • 3,409
  • 8
  • 29
  • 43
Andre
  • 1
  • Why are you writing pointer values to a file (at `fwrite((*matrix)[i][j], sizeof(char *), 1, u);`)? – Ian Abbott Jan 02 '19 at 11:20
  • You probably need to set `j = 0;` inside the outer `while` loop, before the start of the inner `while` loop. – Ian Abbott Jan 02 '19 at 11:22
  • `(*matrix)[i][j] = NULL;` and `(*matrix)[i] = NULL;` leak memory. – Ian Abbott Jan 02 '19 at 11:24
  • Just before the string duplicate call, you have allocated memory of `sizeof(char *)*(j + 1));`, where `j` is 0. So, 1 char. Surely you need to check how long your strings are? – doctorlove Jan 02 '19 at 11:32
  • @doctorlove I think that is just extending the array of char pointers by 1. The allocation for the string is done by the `_strdup` call and the returned pointer is stored in the newly created space. – Ian Abbott Jan 02 '19 at 11:37
  • @IanAbbott the pointer stores my word from the text that I want to show – Andre Jan 02 '19 at 13:18
  • @IanAbbott is there another way to write it in the binary file? Sorry I ask such this question but I'm totally beginner at binary files. – Andre Jan 02 '19 at 13:20
  • Provide example of your input file. – ryyker Jan 02 '19 at 15:47
  • "is there another way to write it in the binary file?" You probably need to write the actual string contents rather than a pointer to the string contents, because the pointer will be meaningless and invalid for whatever program reads back the binary file. You'll need to take account of the fact that strings are variable length, so you will either need to write the null terminator after the string, or write a string length before writing the string. (Writing the length first may be more useful for whatever reads the string back from the file so it knows how much memory to allocate.) – Ian Abbott Jan 02 '19 at 18:33
  • @ryyker Afiseaza pe ecran continutul fisierului de intrare, mai putin primele m si ultimele n linii (1p). Valorile m si n si numele fisierului de – Andre Jan 02 '19 at 23:09
  • V-ați oprit comentariul în mijlocul propoziției și nu mi-ați adresat solicitarea. Puteți edita postarea dvs. cu câteva linii de conținut pentru un fișier de intrare normal? (De asemenea, folosiți engleza în orice comentarii.) – ryyker Jan 03 '19 at 13:03
  • See additional responses to your questions in comments under my answer... – ryyker Jan 03 '19 at 15:00

1 Answers1

0

The call to _strdup is likely failing because memory for (*matrix)[i][j] has not been allocated properly..

Using as many pointers as you are to read words from a file is not necessary, but given that you are using them, memory needs to be created for each, in the right order.

That is

matrix = malloc(sizeof(char *));

is the first of locations required before creating any others

For example, the method to create a properly allocated collection of pointers, for a 4 dimension array, might look like this:

char **** Create4D(int hR, int p, int c, int r)     
{
    char ****arr;
    int    w,x,y;

    arr = calloc(hR, sizeof(char *));
    for(w=0;w<hR;w++)
    {
        arr[w] = calloc(p, sizeof(char *));
        for(x=0;x<p;x++)
        {
            arr[w][x] = calloc(c, sizeof(char *));
            for(y=0;y<c;y++)
            {                                        
                arr[w][x][y] = calloc(r, 1); 
                                      // ^ sizeof(char) == 1
            }
        }
    }
    return arr;
}

Note the italicized is to remind you that this is not actually creating a matrix, just a collection of memory addresses, some with space for pointers, others with space for strings of char.

To call this function:

char ****matrix = Create4D(2, 4, 6, 8);
if(matrix)//always test for success.  If failed, matrix is set to NULL.
{ 
    //use matrix
    //With these arguments, 48 (2*4*6) pointers are created, 
    //each with space allocated to contain strings of 8 char each
    ....

Note also that in all of these calls, casting the return of [m][c]alloc is not used here, because it is not recommended in C. Also, if you plan create all of this memory, corresponding calls to free() must be made for each call to calloc().

Memory from calling the method above on my system is illustrated here:

enter image description here

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • ok, I've got the memory thing, but I have a question: why at the first calloc instead of arr = calloc(hR, sizeof(char *)); you did't do arr = calloc(hR, sizeof(char ***)); and the following ones with sizeof(char **), (char *) ? – Andre Jan 02 '19 at 23:00
  • Because the size of memory being created is `== sizeof (char *)`. That is a 32bit memory location for a `char ***` is the same as that for a `char *` and the same for a `char ******`. They are all 4 bytes. If using 64bit addressing, then the size for each of them would be 8 bytes. You can easily prove this by simply executing `int size = sizeof(char *);` then `int size2 = sizeof(char ******);`. `size` and `size2` will be the same value. They are just pointers. The first is a pointer to a `char` sized memory location, the address for which takes 4 bytes, the second is a ... – ryyker Jan 03 '19 at 12:52
  • ... pointer to a bunch of other pointers, i.e. `char *****`, still only requires 4 bytes, as does `char ****`, and `char ***`, and so on. Keep in mind using more than 2 pointers (eg. `char **var` is rarely if ever needed to read and parse a file. But when they ***are*** used it is for the purpose of creating addressable locations to _simulate_ array like indexing, as illustrated in the answer, where In the example above, there are 2 blocks, each with 4 rows, each of those with 6 columns. All 48 of those are memory locations pointing to indexable space enough for 8 char. – ryyker Jan 03 '19 at 13:23
  • okk, but if I've got a matrix of strings like in my programm, I don't know the right dimensions, right? I could I know how much memory I should storage , without giving random numbers at malloc? – Andre Jan 03 '19 at 21:27
  • @Andre - I typically read through the file first (using `fopen`, `fgets`, `strtok`, `fclose`, etc., to determine things like longest line and number of lines, or word count, longest word, depending on what I need in that program. Then use those values to create a 2D matrix with number of strings, and length of longest string as the arguments (like the example code I provided, except with only 2 arguments) – ryyker Jan 03 '19 at 22:05