简体   繁体   中英

C: Using malloc and realloc to double the initial memory

I'm still learning how dynamic memory works and new to the malloc, realloc business and need real help with this one. My aim here is to read a file containing some paragraph and dynamically store the lines in an array of strings. Initial number of lines that can be stored is 10 if this is insufficient we need to double the number of memory and print a message with line number where we doubled the memory.

int main(int argc, char* argv[])
{
  char* lines;
  int line = 0;

  lines = malloc(10 * sizeof(char));
  while(fgets(lines, sizeof(lines), stdin)
  {
    if(lines[strlen(lines)-1] != '\n')
    {
       lines = realloc(lines, 5);
       line++;
       printf("Reallocating to %d lines", line);
    { 
  }
}

I'm completely stuck and need any help I could find.

with your code you would be able to store only one paragraph at time. in your loop lines variable is overwritten all the time. furthermore, look at realloc() prototype:

void *realloc(void *ptr, size_t size);

and what man page says:

The realloc() function changes the size of the memory block pointed to by ptr to size bytes.

so, you always change your lines variable to be size of 5. you are not increasing size of it.
one of the possible approaches:

  • have a buffer where you store line read by fgets() ,
  • have a dynamic array of pointers to pointers; think of char** lines as a char* lines[SOME_SIZE] ,
  • every time fgets() was successful, allocate memory to store line that is currently hold by buffer and then assign it to a pointer in array.
  • if you reach the limit of rows, double the size of rows and call realloc() on array


same as Alter Mann, i'm using q to exit the loop.
here's the full code:

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

int main(void)
{
    char** lines = NULL;
    int i;
    int curr_row = 0;
    int rows = 5;
    int columns = 100;
    char buf[100];
    lines = (char**)malloc(rows*sizeof(char*));
    while (fgets(buf, columns, stdin) != NULL) {
        if (strcmp(buf, "q\n") == 0) {
            break;
        }
        lines[curr_row] = malloc(strlen(buf)+1);
        strcpy(lines[curr_row], buf);
        curr_row++;
        if (curr_row == rows) {
            rows *= 2;
            lines = (char**)realloc(lines, rows*sizeof(char*));
            printf("reallocated lines to %d rows\n",rows);
        }
    }
    printf("\nYOUR PARAGRAPH: \n");
    for (i = 0; i < curr_row; i++) {
        printf("%s",lines[i]);
        free(lines[i]);
    }
    free(lines);

    return 0;
}

always remember to free() allocated memory.

Using realloc you can increase dinamically the number of lines.

Press q to exit from loop:

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

#define BUF_LEN 64

extern char *strdup(const char *);

int main(void)
{
    char **arr = NULL;
    char buf[BUF_LEN];
    size_t i, n = 0;

    while (fgets(buf, BUF_LEN, stdin)) {
        if (strcmp(buf, "q\n") == 0) break;
        arr = realloc(arr, sizeof(*arr) * (n + 1));
        if (arr == NULL) {
            perror("realloc");
            exit(EXIT_FAILURE);
        }
        arr[n] = strdup(buf);
        if (arr[n++] == NULL) {
            perror("strdup");
            exit(EXIT_FAILURE);
        }
    }
    for (i = 0; i < n; i++) {
        printf("%s", arr[i]);
        free(arr[i]);
    }
    free(arr);
}

void* realloc (void* ptr, size_t size);

reallocates memory block to the first byte of which ptr points to.

One way to solve this problem: you need to accumulate number of bytes you need to store a file in some variable and pass this value to realloc calls.

Another way is if you need to read a file into memory i guess this can be done without realloc : just get the file length preliminarily.

And one more thing: malloc(10 * sizeof(char)) allocates memory for 10 chars, not 10 lines.

UPD:

Here is an implementation of reading a file without realloc . Maybe it's overcommented.

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

/*
 * Illustrates reading file into memory
 */
int main(int argc, char** argv) {

    // Checking command line arguments
    if(argc != 2) {
        printf("Usage:\nexec_name path/to/file");
    }

    // Opens file by path specified in first command line argument
    FILE* fileDesc = fopen(argv[1], "r");    // We got a data structure that describes file
    // Errors are possible
    if(!fileDesc) {
        printf("Error opening file %s", argv[1]);
    }

    // Figuring out file length
    while(!feof(fileDesc))    // feof() returns 0 if it encountered an end of file
        fgetc(fileDesc);    // Reads unsigned char from file and advances file cursor inside fileDesc data structure
    // Errors are possible
    if( ferror(fileDesc) ) {
        perror(argv[1]);
        return EXIT_FAILURE;
    }
    // At this point cursor of fileDesc stands at the end of file
    // Getting offset of the cursor, i.e. number of bytes
    long int numOfBytes = ftell(fileDesc);
    // Errors are possible
    if(numOfBytes == -1L) {
        printf("Error while reading file %s", argv[1]);
        return EXIT_FAILURE;
    }

    // Allocating memory to read a file
    char* fileBuf = malloc(numOfBytes * sizeof(char));

    // Placing cursor at the beginning of the file
    rewind(fileDesc);
    // BTW, you can imagine reading a file as operating with a magnetic tape:
    // read one byte, advanced tape, reached end of tape, rewinded tape.
    // This goes back to the old times when streamers were actually used as a persistent memory.

    // Reading file
    long int actRead = (long int)fread(fileBuf, sizeof(char), numOfBytes, fileDesc);

    // Assertions are nice things to control internal state of the program and localize errors
    assert(numOfBytes == actRead);

    printf("Read %i bytes", actRead);

    return EXIT_SUCCESS;

}

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