0

I am newer in C language. I want to create an array for my code to make some operation. As I said above, I am trying to learn how to use C language efficiently. My problem is this: I have a input file, let's say input.txt. I know that every line have 4 different things, 2 of them are string and 2 of them number. Also, I want to create a 2D array. But I do not know how many lines will be in input file. It depends on the user. So, I have to use malloc to make my array dynamically. So, can you help me about this problem? Maybe this is so easy, but I think reading file and create some array in C more difficult than other languages. It was so easy in Python :( I am leaving my code below. If you tell me my mistakes, I will be happy :)

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

int main(int argc, char const *argv[]) {
    char *arrChar;
    int i;
    char *str;
    char *token;
    arrChar = (char *) malloc( sizeof( char ) );
    str = (char *) malloc( sizeof( char ) );
    FILE *FileChars;
    FileChars = fopen( argv[1], "r");
    i = 0;
    while ( fgets(str, sizeof str, FileChars) != NULL) {
        int j;
        for ( j = 0; j < 4; j++ ) {
                token = strtok(str, ",");
                arrChar[i][j] = token;
            }
        i++;
         }
}
gokhan_ozeloglu
  • 134
  • 1
  • 7
  • 2
    You have allocate for one `char` variable. And then tried to access a single level pointer using subscript operator twice. Undefined behavior you have when using `fgets` - because you allow it to write to memory that you don't have. Use of `strtok` is wrong. – user2736738 Mar 03 '18 at 18:23
  • 1
    The most important issue you have appears to be the `sizeof` operator. Also, in [tag:c] you don't need to cast `malloc()`'s return value like that. – Iharob Al Asimi Mar 03 '18 at 18:23
  • And you should also, check that `FileChars` is not `NULL` after `fopen()`, if you give a wrong path, or your user don't have access to it then *undefined behavior* will occur when reading with `fgets()`. It already occurs becuse `sizeof str` is not what you think. – Iharob Al Asimi Mar 03 '18 at 18:25
  • Also, read [`strtok()`](http://man7.org/linux/man-pages/man3/strtok.3.html)'s manual page, you have to pass `NULL` for subsequent tokens. – Iharob Al Asimi Mar 03 '18 at 18:29
  • *It was so easy in Python* - some languages are friendly for string processing. C is one of the worst in terms of user friendliness, the amount of code you need to write, and also safety, but is also one of the fastest. – kfx Mar 03 '18 at 18:31
  • See [Do I cast the result of malloc?](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) – kfx Mar 03 '18 at 18:32
  • Don't you have any sample code? Because, I can understand what you mean better if I see sample code. – gokhan_ozeloglu Mar 03 '18 at 18:43
  • 1
    @fokclac Read my answer, there is "*sample code*" in it, commented so that you reall understand why every fix. – Iharob Al Asimi Mar 03 '18 at 18:44
  • @IharobAlAsimi Yes, I have seen your code just after my above post :) Thank you so much, I am going to look at it and try to understand :) – gokhan_ozeloglu Mar 03 '18 at 18:46
  • @fokclac What warning/error did your compiler give with `arrChar[i][j] = token;`. If none, what compiler are you using? – chux - Reinstate Monica Mar 03 '18 at 18:57

1 Answers1

1
  1. You need to understand precisely what the sizeof operator does, it doesn't return the size of a dynamically allocated memory block, it returns the size of a type, in case of arrays — roughly speaking — the size is part of the type specification and so it returns the number of bytes the array occupies.

    In your case sizeof(char) is the size of the type char which is required to be exactl 1 by the ( C Standard).

    And sizeof(str) is the size of the type of str which is char *, that is, the size of a pointer. It's probably 4 or 8 depending on your current platform.

    To solve this, you have to define a length to be used throughout your program as the length of the allocated chunk of memory, that after you make sure that the allocation was successful (see below).

  2. A pointer to char can point to a sequence of elements that can be interpreted as a string if it is the correct sequence. A sequence of "printable" characters followed by a '\0' or null character is considered a string.

  3. You have to pass NULL to strtok() after the first time, if you are going to be processing the same string.

  4. You should CHECK that fopen() did return a valid stream, by comparing the return value to NULL.

  5. The same as (5), for malloc() when allocation is not possible NULL is returned and using it as a valid pointer is undefined behavior.

All that said, here is what you probably wanted to write

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

#define NUM_WORDS 100

int main(int argc, char const *argv[])
{
    char *token;
    char **words;
    char line[100];
    char *ptr;
    size_t count;
    FILE *file;

    file = fopen( argv[1], "r");
    // Check that we DID open the file
    if (file == NULL) {
        perror(argv[1]);
        return EXIT_FAILURE;
    }

    // Allocate space for `NUM_WORDS' pointers
    words = malloc(NUM_WORDS * sizeof(*words));
    // Check that we did allocate enough memory
    if (words == NULL) {
        fclose(file);
        return EXIT_FAILURE;
    }

    // We use `sizeof' here because `line' is an array
    count = 0;
    while ((count < NUM_WORDS) && (fgets(line, sizeof(line), file) != NULL)) {
        ptr = line;
        do {
            token = strtok(ptr, ",");
            // We need to copy the `token' because
            // it lies within `line' and the '\0' will
            // be replaced by the original character
            // in subsequent callse to `strtok()'
            //
            // The `strdup()' will allocate enough space with
            // `malloc()' then copy the contents of `token' to the
            // allocated buffer, and return it, so we will
            // have to call `free()' on every `words' element.
            words[count++] = strdup(token);
            // Subsequent calls to `strtok()' with the same
            // string require that the first parameter is
            // NULL
            ptr = NULL;
         } while ((count < NUM_WORDS) && (token != NULL));
    }
    // Now we may print the words and free the memory
    for (size_t index = 0; index < count; ++index) {
        puts(words[index]);
        free(words[index]);
    }
    free(words);
    return 0;
}

Note that the code above, makes sure that we don't exceed the capacity of the array of pointers words1. If you need to resize it, you will need to learn how to use realloc() and do it in a specialized routine so that your code doesn't become too complex.


1Note that the allocated space has no predefined interpretation, we do interpret it as an array but it's not an array in the sense of an array definition, which line IS, having elements of type char, line can also be interpreted as a string given it has contents compatible with the defintion given in the (2) second point above.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97