-2

I have an issue that is related to my programming project this semester. I have a dynamic array that has a structure, that structure contains a parameter for a string. The thing is I need to change that string from being static , to dynamic. I have written the function that asks for the string and stores it in a dynamic array and then returns it. Later in the program I will need to write the info from the arrays (in which the dynamic string is stored) to a binary file. How can I achieve this and how can I know when and how I am properly freeing the memory of that dynamic string? If I free the array in which the string is stored, do I also free the memory in which the string itself is stored?

My array which will store the string:

inspections *inspectionsArray;


char* dynamicString(){
    char *stringPointer = NULL,*paux = NULL, char;
    int stringSize = 1,stringEnd = 0,i;
    stringPointer = (char*)malloc(sizeof(char)); 
    if (stringPointer == NULL) {
        printf("\nError alocating memory");
    }else{
        printf("\nEnter String: ");
        while (char != EOF && char != '\n') {
            char = getc(stdin); //Obter o char do stding
            paux = realloc(stringPointer, stringSize*sizeof(char));
            if (paux != NULL) {
                stringPointer = paux;
                stringPointer[stringEnd] = char; //meter o char na string
                stringSize++; //incrementar o tamanho da string
                stringEnd++;
            }else{
                printf("\nErro a realocar memoria para a string");
            }
        }
        stringPointer[stringEnd] = '\0';
        printf("\nString: %s", stringPointer);
    }
    return stringPointer;
}

The Structure in which I will have my string stored in.

typedef struct
{
    //A bunch of parameters in here
    char *dynamicString;

} inspection;


enter code here

Function I use to store data to array:

inspectionArray* registerInspection(inspection *inspectionArray){
    char *string;
    //I removed a bunch of code to not confuse things
    string = dynamicString;

    return inspection;
}

Function to save file:

void saveBinaryFile(inspection *inspections, int inspectionCounter)
{
    FILE * inspectionArrayFile;


    fileInspections = fopen("inspections.dat","wb"); 


    if (fileInspections == NULL)
    {
        perror("\nError opening the file");
    }
    else
    {
        fwrite(&inspectionCounter, sizeof (int), 1, inspectionArrayFile);
        fwrite(inspections, sizeof(tipoVeiculo), inspectionCounter, inspectionArrayFile);
        // I write a bunch of stuff and verify it properly
        fclose(inspectionArrayFile);

    }
}

How do I properly read and free the string ?

I tried this:

void freeArrayStrings(inspections *inspectionsArray,int counter){
    int i;
    for (i=0; i<counter; i++) {
        free(inspectionArray[i].dynamicString);
    }
}
Luis Valdez
  • 2,339
  • 4
  • 16
  • 21
  • 2
    "Talk is cheap. Show me the code - Linus" – rootkea Jan 08 '16 at 19:07
  • 1
    There are no dynamic arrays. – Iharob Al Asimi Jan 08 '16 at 19:10
  • I have loads of code , I can't put it all here. Well, I'll try – Luis Valdez Jan 08 '16 at 19:10
  • You have at least two completely separate questions in there, one about output and one about memory management. Ask *specific* questions here, and it is best if you support them with code. – John Bollinger Jan 08 '16 at 19:11
  • Well, that's what we call them here. Static or Dynamic – Luis Valdez Jan 08 '16 at 19:11
  • I'll try to be more specific , sorry. – Luis Valdez Jan 08 '16 at 19:11
  • Will _[strdup()](https://msdn.microsoft.com/en-us/library/aa273370%28v=vs.60%29.aspx)_ work for you? And _please_, no need for _all_ the code. Just include the relevant parts needed to illustrate the problem you are trying to get help with. – ryyker Jan 08 '16 at 19:12
  • 2
    @LuisValdez Try to create [mcve] – rootkea Jan 08 '16 at 19:14
  • i think you need this: [c-dynamically-growing-array](http://stackoverflow.com/questions/3536153/c-dynamically-growing-array) – milevyo Jan 08 '16 at 19:19
  • Do you mean dynamically allocated or really dynamic in the sense of changing size? – too honest for this site Jan 08 '16 at 19:21
  • I apologize for not writing a good question. I have provided some translated code. – Luis Valdez Jan 08 '16 at 19:31
  • @Olaf I don't understand the difference. The string changes size according to the characters I type. – Luis Valdez Jan 08 '16 at 20:33
  • C does not have dynamically resizing arrays. And you use pointers, not an array (well, you have to). `realloc`ing for each character typed is a bad idea in general and very inefficient. You code has many faults&flaws. How does that even compile; you declare a variable with the same name as a built-in type(`char`). Also `getchar` returns an `int`, not a `char` for good reason! And don*t cast the result of `malloc` & friends in C (you do this inconsistently - general advice is never to cast if you do not fully understand all implications). – too honest for this site Jan 08 '16 at 21:07
  • @Olaf , absolutely right. I didn't use char as a variable name, I was using the "caracter" which is char in portuguese. I translated it to char. I should use getc(); – Luis Valdez Jan 08 '16 at 21:21
  • That change was contra-productive. I did not say not to use `getc`, but that it returns an `int` for a good reason. Please read the man pages of the functions you use and know their signatures. You cannot detect `EOF` reliable and portable with a `char` variable! – too honest for this site Jan 08 '16 at 21:33
  • Getchar should work then. – Luis Valdez Jan 08 '16 at 21:38

3 Answers3

1

You should show your code in order to increase a chance of apt answer. If you ask about freeing of memory that stores pointers to another memory get from malloc like in this example:

struct str_wrap
{
    char *str;
    int  strlen;
};

int main()
{
    struct str_wrap *sw = malloc(sizeof *sw);
    if (sw == NULL) return -1;

    sw->str = malloc(strlen);
    if (sw->str == NULL)
        // handle
    sw->strlen = strlen;
    // many things with sw...

then yes, you have to call free twice (note: the same number of times you have called malloc):

    // free the memory
    free(sw->str);
    free(sw);
}
4pie0
  • 29,204
  • 9
  • 82
  • 118
1

Unless you have some specific reason for using character-oriented input for the dynamicString() function, you can greatly simplify your life by using one of the line-oriented input functions provided by the standard C library (e.g. fgets or getline). Also, in C, you traditionally you avoid the use of camelCase names and instead use lower-case for all variable and function names.

In the case of the dynamicstring() function, using getline your function reduces to:

char *dynamicstring (inspection *i)
{
    char *line = NULL;  /* buffer for input                 */
    size_t n = 0;       /* alloc size 0 - getline decides   */
    ssize_t len = 0;    /* no. of character read (or EOF)   */

    printf("\nEnter String: ");

    len = getline (&line, &n, stdin);   /* read input   */

    if (len <= 1) {  /* line contains only '\n', 0, EOF */
        free (line);
        return NULL;
    }

    while (len > 0 && line[len-1] == '\n') /* remove '\n'  */
        line[--len] = 0;

    i->dstring = line;    /* assign pointer to dstring  */

    return line;
}

(note: above i use dstring for your char *dynamicString; member)

You can refine the memory allocation size from the default allocated by getline to only that amount required for the input string by taking the length of the final line, allocating that amount of memory (+1 for the nul-terminator), and copying line to the new block of memory. You can do it all at once with the strdup function. For example, you could add the following to the function above and then return str:

    char *str = NULL;   /* pointer for final string storage */
    ...
    str = strdup (line);  /* refine allocation size     */
    free (line);

    i->dstring = str;     /* assign pointer to dstring  */

    return str;

Even with the refinement, the completed function is far simpler than your original. Now note: regardless whether you assign the return or not, since the function only returns an allocation if the input is not empty, there is a chance that some of your array of inspection string members will be allocated and some empty. Therefore, you will need to check before freeing the memory. e.g.:

    if (ia->dstring)
        free (ia->dstring);

As always, when allocating memory dynamically, use a memory error checker (like valgrind on Linux) to confirm the correct use and freeing of the memory you allocate. A short example of the use of this input function would be:

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

typedef struct {
    char *dstring;
} inspection;

char* dynamicstring (inspection *i);

int main (void) {

    inspection *ia= NULL;

    if (!(ia= malloc (sizeof *ia))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    if (dynamicstring (ia)) {
        printf ("\n inspectionArray->dynamicstring : %s\n\n", ia->dstring);
        if (ia->dstring)
            free (ia->dstring);
    }
    free (ia);

    return 0;
}

char *dynamicstring (inspection *i)
{
    char *str = NULL;   /* pointer for final string storage */
    char *line = NULL;  /* buffer for input                 */
    size_t n = 0;       /* alloc size 0 - getline decides   */
    ssize_t len = 0;    /* no. of character read (or EOF)   */

    printf("\nEnter String: ");

    len = getline (&line, &n, stdin);   /* read input   */

    if (len <= 1) {  /* line contains only '\n', 0, EOF */
        free (line);
        return NULL;
    }

    /* using shorthand 'len' instead of 'len > 0'
       both test false when 'len' is 0  */
    while (len && line[len-1] == '\n') /* remove '\n'  */
        line[--len] = 0;

    str = strdup (line);  /* refine allocation size     */
    free (line);

    i->dstring = str;     /* assign pointer to dstring  */

    return str;
}

Give it a try and let me know if you have questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thank you David. That's exactly it! – Luis Valdez Jan 10 '16 at 17:35
  • Although some of the things you wrote are in C++. Are they not? – Luis Valdez Jan 12 '16 at 22:49
  • No, all of it is 100% pure C..... What parts looking like C++ to you? I'm happy to explain anything you have a question about. It is important you understand each line and each character in each line. There is no such thing as "*close-enough*" syntax... – David C. Rankin Jan 12 '16 at 23:22
  • On the dynamicString function ,you use a while , but you didn't use any brackets. Perhaps that could be done, I just never saw it. Thank you very much. – Luis Valdez Jan 12 '16 at 23:26
  • The `while (line && line[len-1] == '\n')` does need improvement to be correct. Good catch. It should be `while (len && line[len-1] == '\n')` which is just shorthand for `while (len > 0 && line[len-1] == '\n')`. This while you haven't worked backward all the way to the beginning and the last character is a `'\n'`, then overwrite the `'\n'` with a *nul-terminating* character `0` (equivalent to `'\0'`) but less typing) and substract `1` from `len` and check again. I'll update the answer. – David C. Rankin Jan 12 '16 at 23:32
0
  1. You have an error: stringPointer = (char*)malloc(sizeof(char)); You need stringPointer = (char*)malloc(sizeof(char *)); sizeof(char) is probably 1 bytes; sizeof(char *) is probably 4. You might get away with it because some memory systems always allocate at least 32 bytes...

  2. Don't realloc on every input character. Instead, declare char buf[255] or some other maximum size, keep a size index, assign your input char to the end, and then at the end do stringPointer = malloc(isize+1) and strcpy(stringPointer,buf); If you really might need a huge input buffer, start at 4K or 1M and then reallocate when you fill that space.

Jonathan
  • 76
  • 1
  • 5
  • Won't that take up big amounts of memory though? – Luis Valdez Jan 08 '16 at 22:44
  • No - the idea is to have a single buffer allocated outside the getc loop. This will avoid a realloc call (which is relatively expensive) on every single character handled. – Jonathan Jun 24 '17 at 20:46