简体   繁体   中英

Do i need to use 2D arrays for an array of strings in C?

I want my program to read N words from a text file and save them in an array. My question is, do i need a 2D Array eg: char **wordList or is the 1D Array in the example below sufficient? The output is correct except from the last string which as you can see is weird. Also, am i allocating sufficient memory for the array and why does the last output string come out wrong?

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

void populateWordsArray(int);

FILE *file;
char *word;
char **wordList;

/*
* Function populateWordsArray: reads N words from
* the given file and creates an array with them.
* Has one argument: int N - the number of words to read.
*/
void populateWordsArray(int N) {
    int i = 0;
    while(!feof(file) && i < N) {
    fscanf(file,"%s",&word[i]);
    printf("%s\n",&word[i]);
    i++;
    }
}

int main(int argc,char *argv[]) { // argv[1] = op argv[2] = name
    int N = 0;

    file = fopen(argv[2],"r");

    if(file == (FILE *) NULL) { // check if the file opened successfully
        fprintf(stderr,"Cannot open file\n");
    }

    fscanf(file,"%d",&N); // get the N number
    word = malloc(N * sizeof(char));

    populateWordsArray(N);
    // write a switch method for the various ops
    // call the appropriate function for each operation

    free(word);
    fclose(file);
    return 0;
}

Output:

this
is
a
test!
with
files.
new
line,
here.
ere.

text file content:

10 this is a test! with files.
new line, here.

Your example is wrong. When executing the line fscanf(file,"%s",&word[i]); , the third argument should be the address where the function will write the read data. In your case, word[i] is the i -th element of the array and &word[i] is its address. So, the word will be stored with the first character at the word[i] . Your code only prints something because you print it immediately. Also, you don't get a segfault by pure chance. If you want to read a string into a buffer, you first need to allocate the space for the buffer. By using char ** , you can make it into a 2D array by first allocating sufficient space for the array of pointers and then allocate sufficient space for each of the pointers to hold an address to a string.

I have rewritten your program for you:

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

#define MAX_STRING_LENGTH 100

void populateWordsArray(int);

FILE *file;
char **wordList;

void populateWordsArray(int N) 
{
    int i = 0;
    while(i < N && fscanf(file,"%s", wordList[i]) == 1) // fscanf returns the number of successfully read items. If it's not 1, the read failed. You can also check if the return value is not EOF but, in this situation, it's the same.
    {
        printf("%s\n", wordList[i]); // i-th element is an address to a buffer that contains 100 bytes
        i++;
    }
}

int main(int argc,char *argv[]) 
{ 
    int N = 0, i;

    file = fopen(argv[1],"r"); // Indexing starts from 0 in C. Thus, 0th argument is the executable name and 1st is what you want.

    if(file == NULL)  // No need to cast NULL into a specific type.
    {
        fprintf(stderr,"Cannot open file\n");
        return 1; // You might want to end the program here, possibly with non-zero return value.
    }

    fscanf(file,"%d",&N);
    wordList = malloc(N * sizeof(char*)); // Allocating space for pointers
    for(i=0; i<N; i++)
    {
        wordList[i] = malloc(MAX_STRING_LENGTH); // Allocating space for individual strings 
    }

    populateWordsArray(N);

    for(i=0; i<N; i++)
    {
        free(wordList[i]);
    }
    free(wordList);
    fclose(file);
    return 0;
}

I'd also advise against using global variables here.

EDIT: As the comments may suggest, this code is not the best solution. First, all the words might not fit into a 100 byte buffer. To alleviate this issue, allocate a large, fixed-size buffer, read every word into it, then allocate corresponding number of bytes for wordList[i] (don't forget the terminating null byte) and copy the data from the fixed-size buffer into wordList[i] .

Also, the code has some missing error checks. For instance, the file may exist but is empty, in which case fscanf(file,"%d",&N); will return EOF . Also, the number at the beginning of the file may not be corresponding to the number of the lines that follow or N might be a negative number (the code allows for it by specifying it to be int ).

EDIT2: As @bruno suggested, I made a version that I think is more bulletproof than the previous one. It's possible that I omitted something, I'm in a bit of a hurry. If so, let me know below.

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

#define MAX_STRING_LENGTH 512


char line_buffer[MAX_STRING_LENGTH]; // A line of the maximum size that can occur.

char** populateWordsArray(unsigned wantedLines, FILE* file, unsigned *readLines);


char** populateWordsArray(unsigned wantedLines, FILE* file, unsigned *readLines)
{
    *readLines=0;
    char** wordList;
    // Allocating space for pointers
    wordList = malloc(wantedLines * sizeof(char*));
    if(!wordList)
    {
        fprintf(stderr,"Cannot allocate sufficient space for the pointers.\n");
        exit(EXIT_FAILURE); // You may return NULL here and check it afterwards. The same goes for all the error checking inside this function
    }

    while(*readLines < wantedLines && fscanf(file,"%s", line_buffer) == 1)
    {
        wordList[*readLines] = malloc(strlen(line_buffer)+1);
        if(!wordList[*readLines])
            break;
        if(NULL == (wordList[*readLines]=strdup(line_buffer)))
            break;
        (*readLines)++;
    }

    return wordList;
}

int main(int argc,char *argv[]) 
{ 
    unsigned N = 0, i, M;
    char **wordList;
    FILE *file;


    file = fopen(argv[1],"r"); // Indexing starts from 0 in C. Thus, 0th argument is the executable name and 1st is what you want.

    if(file == NULL)  // No need to cast NULL into a specific type.
    {
        fprintf(stderr,"Cannot open file\n");
        return 1; // You might want to end the program here, possibly with non-zero return value.
    }

    if(fscanf(file,"%d",&N) != 1)
    {
        fprintf(stderr,"Cannot read the number of lines. Empty file?\n");
        return 1;
    }


    wordList = populateWordsArray(N, file, &M);

    printf("Printing the read lines:\n");
    for(i=0; i<M; i++)
    {
        printf("%s\n", wordList[i]);
    }

    for(i=0; i<M; i++)
    {
        free(wordList[i]);
    }
    free(wordList);
    fclose(file);
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM