Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h2>Code Analysis</h2> <p>Let's dissect this a bit at a time:</p> <pre><code>int function2(struct Person *list) { </code></pre> <p>As <a href="https://stackoverflow.com/users/1428679/lserni">Iserni</a> noted in his <a href="https://stackoverflow.com/questions/13003275/malloc-struct-in-a-function-free-before-ending-program/13003302#13003302">answer</a>, this definition is inconsistent with your call to the function. I agree in general with his correction for the existing code (but I'm going to recommend modifying it shortly):</p> <pre><code>int function2(struct Person *list, int *index) struct Person *prsn = malloc(sizeof(struct Person)); // !Why do we sometimes cast the malloc or not? // I sometimes get errors when I do, sometimes when I don't, // while the surrounding code is pretty much the same. </code></pre> <p>It depends on whether you're in C or C++; in C++, you must cast the return value from <code>malloc()</code> (if you use <code>malloc()</code> at all; you usually shouldn't in C++). In C, the cast is optional. There is a school of thought that omitting the cast reveals errors that inserting the cast can hide. I don't subscribe to that; I believe that <code>malloc()</code> should have been declared via <code>&lt;stdlib.h&gt;</code> and the compiler should be warning if there is no declaration in scope, and if there's a declaration in scope, the cast can't cover up sins. The other possible problem is that you assign a pointer to a non-pointer; that would be a mistake that the compiler should be complaining about too.</p> <pre><code> assert(prsn != NULL); </code></pre> <p>This is not normally considered a sensible long-term method of handling memory allocation errors.</p> <pre><code> // User input code goes here ... // Now to save the Person created strcpy(prsn-&gt;name, nameInput); prsn-&gt;age = ageInput; list[(*index)++] = *prsn; // !Why use the dereferencing *prsn here? </code></pre> <p>Because:</p> <ul> <li><code>list</code> is a <code>struct Person *</code>.</li> <li>Therefore <code>list[i]</code> is a <code>struct Person</code> (albeit you spelled <code>i</code> as <code>(*index)++</code>).</li> <li>Therefore you must assign a <code>struct Person</code> to it.</li> <li><code>prsn</code> is a <code>struct Person *</code>.</li> <li>Therefore <code>*prsn</code> is also a <code>struct Person</code>.</li> <li>Therefore the assignment is 'correct'.</li> <li>It also gives you the leak.</li> <li>You've overwritten the content of <code>list[i]</code> with the content of <code>*prsn</code>.</li> <li>You've not save the pointer to <code>prsn</code> anywhere.</li> <li>So you leak memory as you return from the function.</li> </ul> <p>The surgery required to fix this is non-negligible:</p> <pre><code>int function2(struct Person **item) ... *item = prsn; </code></pre> <p>and you have to revise the call; I'll come back to that when dissecting <code>main()</code>.</p> <pre><code> // why not directly prsn? Or is that saving the memory address and not very useful. } </code></pre> <p>Your function is declared to return an <code>int</code> but you show no <code>return</code>. If you aren't going to return a value, declare the function as <code>void</code>, especially if you're going to ignore the return value as your code in <code>main()</code> does.</p> <p>Your last comment is mostly covered by the discussion above; saving the memory address is crucial to stopping the leak so it is very useful.</p> <blockquote> <p>And this is my main function:</p> </blockquote> <pre><code>int main(int argc, char *argv[]) { struct Person personList[MAX_SIZE]; int index; </code></pre> <p>Using an uninitialized variable is bad news. It is at best only accidentally zeroed. You can't afford to have random values in use as array indexes; initialize it explicitly. Also, we're going to need an array of pointers, not an array of structures:</p> <pre><code> struct Person *personList[MAX_SIZE]; int index = 0; ...other code... function2(personList, &amp;index); </code></pre> <p>With the revised function:</p> <pre><code> function2(&amp;personList[index++]); </code></pre> <p>This is preferable; it means that <code>function2()</code> doesn't need to know about the array; you just pass it the address of the pointer to which the allocated memory pointer should be assigned. This reduces the coupling between your <code>main()</code> function and <code>function2()</code>, which makes the code simpler all around.</p> <pre><code> // Before closing, I want to free any mallocs I have done here. free() </code></pre> <p>So you write:</p> <pre><code> for (int i = 0; i &lt; index; i++) free(personList[i]); </code></pre> <p>This releases all the memory allocated.</p> <pre><code> return 0; } </code></pre> <p>I like to see an explicit return at the end of <code>main()</code>, even though C99 says it isn't 100% necessary.</p> <p>Make sure you are compiling with enough warnings enabled. If you're using GCC, then <code>gcc -Wall</code> should be the minimum level of compilation warning you run with (and you should have no warnings in your code when you do so). I run with more stringent warnings: <code>gcc -std=c99 -Wall -Wextra -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes -Wshadow</code> usually. You need to include <code>-O3</code> (or some level of optimization) to get some warnings. The stuff about prototypes reflects paranoia about an old code-base I work with which still has K&amp;R function definitions around.</p> <hr> <h2>Answering comments</h2> <blockquote> <p>First question while I go over your details and try things out: is there a memory impact between an array of structs and an array of pointers?</p> </blockquote> <p>Yes, but it may not be what you're thinking of. If you use an array of structures, there's no need to allocate memory dynamic for the structures since the compiler's already allocated them for you. Since the purpose of the exercise is to use pointers and <code>malloc()</code>, it is better, therefore, to use pointers. In terms of space, there will be slightly more total memory used with the array of pointers (but there will be less memory leaked).</p> <blockquote> <p>I am trying to change my code to use an array of pointers. But using <code>function2(personList, &amp;index);</code> to call <code>function2</code> now gives me the following warning: <code>incompatible pointer types passing 'struct Person *[512]' to parameter of type 'struct Person *'</code>. Is it OK if I write extra code in my main question to go into details? As a note, I'm trying to reference variables as much as possible, so as to not temporarily have the program copy data from function to function.</p> </blockquote> <p>The compiler is correct if you've not made all the changes. Your code using two arguments copies more data between functions than my code using one argument.</p> <h3>Version 1</h3> <p>The following program uses the proposed single-argument <code>function2()</code> to reduce the coupling between the <code>main()</code> function and <code>function2()</code>, simplifying both.</p> <p>This code compiles with no warnings under GCC 4.7.1 on Mac OS X 10.7.5 using the command line:</p> <pre><code> gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \ -Wold-style-definition mem.c -o mem </code></pre> <p>When run under <code>valgrind</code>, it comes up with no memory leaked.</p> <pre><code>#include &lt;assert.h&gt; #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; enum { MAX_INPUT = 28 }; enum { MAX_SIZE = 3 }; struct Person { char name[MAX_INPUT]; int age; }; static void function2(struct Person **list) { struct Person *prsn = malloc(sizeof(struct Person)); assert(prsn != NULL); char *nameInput = "This is my name"; int ageInput = 29; // Again! strcpy(prsn-&gt;name, nameInput); prsn-&gt;age = ageInput; *list = prsn; } int main(void) { struct Person *personList[MAX_SIZE]; int index = 0; function2(&amp;personList[index++]); function2(&amp;personList[index++]); function2(&amp;personList[index++]); for (int i = 0; i &lt; index; i++) free(personList[i]); return 0; } </code></pre> <h3>Version 2</h3> <p>This keeps the two-argument version of <code>function2()</code> and has it do the counting that <code>main()</code> should be doing on its own. This program also compiles cleanly and runs cleanly under <code>valgrind</code>.</p> <pre><code>#include &lt;assert.h&gt; #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; enum { MAX_INPUT = 28 }; enum { MAX_SIZE = 3 }; struct Person { char name[MAX_INPUT]; int age; }; static void function2(struct Person **list, int *index) { struct Person *prsn = malloc(sizeof(struct Person)); assert(prsn != NULL); char *nameInput = "This is my name"; int ageInput = 29; // Again! strcpy(prsn-&gt;name, nameInput); prsn-&gt;age = ageInput; list[(*index)++] = prsn; } int main(void) { struct Person *personList[MAX_SIZE]; int index = 0; function2(personList, &amp;index); function2(personList, &amp;index); function2(personList, &amp;index); for (int i = 0; i &lt; index; i++) free(personList[i]); return 0; } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload