Solved

Difficulty using calloc to size/resize data structure arrays for C app

Posted on 2006-06-24
20
723 Views
Last Modified: 2012-05-05
Hello everyone,

I'm creating a MFC DLL which will be called by a C app.  I need to dynamically create/resize structure arrays so Im using calloc/resize/free.  While calloc seems to work fine for simple data types I'm having difficulty working with structure arrays.  Consider the following ex which compiles fine in VS 2K3 C++.


#include "stdafx.h"
#include "malloc.h"

struct Samp {
      char * string1;
      char * string2;
};


extern "C" _declspec(dllexport) int myFunc(Samp * samp)
{
      int    elems = 20; //For example only;My app will size/resize as it needs to.
      samp = (Samp *)calloc(elems, sizeof(Samp)); //Appears to succeed but Samp can not be referenced as an array
      if (samp != NULL)
      {
            for (int i = 0; i < elems; i++0
            {
                  samp[i].string1 = (char *)calloc(100, sizeof(char));
                  samp[i].string2 = (char *)calloc(100, sizeof(char));
                  // string1 and string2 remain undefined

                  //unable to assign values to samp[i].stringx via strcpy()
            }

                                //cleanup
            for (int i = 0; i < elems; i++0
            {
                  free(samp[i].string1);
                  free(samp[i].string2);
            }
            free(samp);
      } else {
            //insufficient memory; error generated

      }
      
}

0
Comment
Question by:rbradleyinetlp
  • 11
  • 6
  • 2
  • +1
20 Comments
 
LVL 48

Expert Comment

by:AlexFM
Comment Utility
Do you mean that allocated array cannot be referenced by function caller? In this case you need ** parameter:

extern "C" _declspec(dllexport) int myFunc(Samp ** samp)
{
     int    elems = 20;
     *samp = (Samp *)calloc(elems, sizeof(Samp));
     ...
}

Or error is in the function itself? Function interace is not clear - why do you need Samp* parameter?
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
My problem is two fold.
1) samp cannot be referenced as an array after calloc.  
2) samp member variables can not be allocated/used

After the call to calloc, samp is defined; however, the Watch windows shows samp[0] as an "undefined value".  samp.string is also "undefined value"

I receive a null reference exception when I used
extern "C" _declspec(dllexport) int myFunc(Samp ** samp)
{
     int    elems = 20;
     *samp = (Samp *)calloc(elems, sizeof(Samp));
     ...
}
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
To clarify further, the error is in the function iteslf.  The paramater is needed as an inpu/output parameter between the C application and the C++ DLL.  The C application will create the pointers, my DLL will allocate memory for them and populate with data then return to the calling appl, the calling appl will then manipulate the arrays and free memory.
0
 
LVL 48

Expert Comment

by:AlexFM
Comment Utility
According to your description, function definition should be:

extern "C" _declspec(dllexport) int myFunc(Samp ** samp)
{
     int    elems = 20;
     *samp = (Samp *)calloc(elems, sizeof(Samp));
     ...
}

Caller:
int* p;
myFunc(&p);
// work with array allocated by myFunc

About bug inside of function, your description is missing details. What does this mean "cannot be referenced"? Do you have exception? What exception exactly, what is code line?
Why do you use calloc and not C++ new? C client doesn't care what is the way of allocation. In any case, you need to write function in Dll which releases this pointer.
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
I am able to reference the allocated memory as a single structure instead of as an array of structures.

strcpy(samp->string, "hello");     //has no effect but does not generate compile/runtime errors
strcpy(samp[0]->string, "hello"); //index '0' out of bounds

In the code below the first pass works fine.  The second pass generates a Null Ref Exception when I allocate memory for the char*.  If I do not calloc the char * within the structure then I'll get a Null Ref Exception with strcpy.


extern "C" _declspec(dllexport) int myFunc(Samp ** samp)
{
  int elems = 20;      //app will dynamiclaly decide how many elements are necessary
  int calcElems = 0;

  *samp = (Samp *)calloc(elems, sizeof(Samp));

  calcElems = _msize(*samp)/sizeof(Samp);
  for (int index = 0; index < calcElems; index++)
  {
    samp[index]->string1 = (char *)calloc(10, sizeof(char));
      //if index==0 calloc works; if index==1 calloc generates Null Ref Exception
      //after calloc string1 and string2 still shows as "Undefined Value" in Watch window.
    strcpy(samp[index]->string1, "hello");
      //no effect; const "hello" is never copied to string1
  }
}
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
The third line in my previous post should read ...
strcpy(samp[1]->string1, "hello"); //generates null ref exception not index out of bounds

To answer your prior questions.
The C appl will be responsible for freeing the memory I allocate after it manipulates the data.  To my knowledge C does not support delete and mixing new/free is a no-no.
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
A lot of problems related to memory allocations in MFC DLLs have to do with failure to use

    AFX_MANAGE_STATE( AfxGetStaticModuleState() );

in the DLL (at every entry point).  Some related info:
   TN058: MFC Module State Implementation
   http://msdn.microsoft.com/library/en-us/vcmfc98/html/_mfcnotes_tn058.asp

I'm not certain, but I think the problem is more pronounced in Debug builds -- somehow relating to the internal tracking that MFC does in making and verifying allocations (e.g., the replacement of the "new" operator with DEBUG_NEW).

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
This MSDN article relates to using STL objects in DLLs, but I believe that the memory allocation issues are similar

     You may experience an access violation when you access an STL object through
     a pointer or reference in a different DLL or EXE (originally Q172396)
     http://support.microsoft.com/default.aspx?scid=kb;en-us;172396

=-=-==-=-=-=-
Another thing to try (though I'm not sure why) would be to use new rather than calloc.  It's possible that the new handler will contain special handling that might eliminate the problems.
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
I am using AFX_MANAGE_STATE( AfxGetStaticModuleState() ); in the exported function of my DLL (not reflected in my sample).  My code for allocating memory is actually in helper functions where data is retrieved from a database, manipulated, stored in the allocated memory so the C appl can then use the data and free memory.

I'm not even progressing beyond the point of allocating memory so right now the problem is local to the routines responsible for allocating memory.  Not in the return to the caller.
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
Just as a sanity check, I tried this:
#include "stdafx.h"
#include "malloc.h"
#include <string.h>

struct Samp {
      char * string1;
      char * string2;
};

extern "C" _declspec(dllexport) int myFunc(Samp * samp)
{
      int    elems = 20; //For example only;My app will size/resize as it needs to.
      samp = (Samp *)calloc(elems, sizeof(Samp)); //Appears to succeed but Samp can not be referenced as an array
      if (samp != NULL) {
            for (int i = 0; i < elems; i++ ) {
                  samp[i].string1 = (char *)calloc(100, sizeof(char));
                  samp[i].string2 = (char *)calloc(100, sizeof(char));
                  // string1 and string2 DO NOT remain undefined
                  // NOT unable to assign values to samp[i].stringx via strcpy()
                  strcpy( samp[i].string1, "Hi there 1" );
                  strcpy( samp[i].string2, "Hi there 2" );
            }

            //cleanup
            for (int j = 0; j < elems; j++ ) {
                  free(samp[j].string1);  // (see note)
                  free(samp[j].string2);
            }
            free(samp);
      } else {
            //insufficient memory; error generated
      }
      return elems;
}


int main(int argc, char *argv[])
{
      Samp* pSamp= 0;
      myFunc( pSamp );
      return(0);
}

=-=-=-=-=-=-=-=-=-=-=-=-=
It compiles without error, allocates the string data storage, allows assignment of string data to the allocations,  and frees without error.

One thing it does NOT do is return a valid pointer to the allocated data (related to the need to pas a Samp** rather than a Samp* -- as discussed above).  But first things first... your version of this code needs to work before the next step can be taken.

-- Dan
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:rbradleyinetlp
Comment Utility
Are you saying you were able to copy strings into the string1 and string2 char*??

I tried this again and after the first pass in the for loop samp[0].string1 is undefined.  If I try to see its value in the Command Window I get the following.  In case something was changed I copied the code you pasted in.  If it is working for you I'm at a loss.

Command Window - Immediate
-----------------------------------------------------------------
? samp[0].string1
error: index '0' out of bound for pointer/array 'samp'
? (Samp *)samp
0x00000000 { string1=<undefined value> }
    string1: <undefined value>
    string2: <undefined value>
? samp
{Samp} { string1=<undefined value> }
    string1: <undefined value>
    string2: <undefined value>
-----------------------------------------------------------------

This is immediately after the first pass through the for loop.
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
Just for kicks I tried new/delete.  Result is the same.  I also tried calloc/free & new/delete with char arrays (no calls to DLL) and they works fine.

Samp *samp;
samp = (Samp *)new Samp[20];
samp[0].string1 = (char *)new char[10]; // does not generate an exception
strcpy(samp[0].string1, "hello");              // does not generate an exception but also does not copy data

//now I run the following commands in Command Window
//------------------------------------------------------------------------------
//Command Window - Immediate
//------------------------------------------------------------------------------
//? samp
//{Samp} { string1=<undefined value> string2=<undefined value> }
//   string1: <undefined value>
//   string2: <undefined value>
//
//? samp[0].string1
//error: index '0' out of bound for pointer/array 'samp'
//
//? (Samp *)samp
//0x00000000 { string1=<undefined value> string2=<undefined value> }
//    string1: <undefined value>
//    string2: <undefined value>
//------------------------------------------------------------------------------

delete [] samp;
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
I started from scratch with a new project and it works to some degree.  I think the problem I was having has something to do with .NET.  Originally I'd created a "Console Application .NET" mindless of any differences.  This time I created a "Win32 Console Application".

I'm having trouble figuring out the indirection necessary for the myFunc() call.  It is still bombing out allocating memory for the char * on the second pass.
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
The one last effort paid off.  The following worked fine in the new non-.NET appl.


#include "stdafx.h"
#include "conio.h"
#include "malloc.h"

struct Samp {
      char * string1;
      char * string2;
};


extern "C" _declspec(dllexport) Samp * myFunc()
{
      int nElems = 0;
      int index = 0;
      Samp *samp;

      samp = (Samp *)calloc(20, sizeof(Samp));

      if (samp)
      {
            nElems = (int)(_msize(samp)/sizeof(Samp));

            //      allocate memory for each char * in the array
            for (index = 0; index < nElems; index ++)
            {
                  //if (index > 0)
                  //      samp[index] = (Samp *)calloc(1, sizeof(Samp));
                  samp[index].string1 = (char *)calloc(10, sizeof(char));
                  samp[index].string2 = (char *)calloc(10, sizeof(char));

                  sprintf(samp[index].string1, "Hello %02d", index);
                  sprintf(samp[index].string2, "%04d", (index + 100));
            }

      }
      return samp;
}


int _tmain(int argc, _TCHAR* argv[])
{
      int nElems = 0;
      int index = 0;

      Samp *samp = 0;
      samp = myFunc();

      nElems = (int)(_msize(samp)/sizeof(Samp));

      //      print values
      for (index = 0; index < nElems; index ++)
      {
            printf(samp[index].string1); printf("\t");
            printf(samp[index].string2); printf("\n");
      }

      //      cleanup
      if (samp)
      {
            nElems = (int)(_msize(samp)/sizeof(Samp));

            for (index = 0; index < nElems; index ++)
            {
                  free(samp[index].string1);
                  free(samp[index].string2);
            }

            free(samp);
      }

      //      exit
      printf("\n\nPress Any Key To Exit ... ");
      getch();

      return 0;
}
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
My next suggestion was for you to make myFunc() return a Samp* -- just to simplify it (but I see you hit on that yourself :-).

An important error-reducing trick is this one:
              free(samp[index].string1);
              samp[index].string1= 0;   // null the pointer to help detect invalid re-use
              ...
             free(samp);
             samp= 0;       // null the pointer to help detect invalid re-use

It only makes a difference if your program erroneously attempts to write new data into the allocation after it has been freed.  But if the program does make that mistake, you will get an immediate exception -- so it helps you track down the error in seconds rather than hours or days ;-)

-- Dan
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
Thanks for the suggestion.  Now I'm left struggling with a local heap violation.  I'm seeing a lot of posts hit on this topic but Im not finding the answer I think I need.  I am able to create the pointer local to the exe, allocate memory within the dll, write data to the memory within the dll, return to the exe caller and read the data from allocated memory ... then the free() call fails.  Debug Assertion _CrtIsValidHeapPointer Fails when I attempt to free within the exe.

Since I'm new to experts-exchange I'd also like to ask if it is appropriate that this thread continue or should it be split to another thread?

Thank you both for your input here!
0
 
LVL 49

Accepted Solution

by:
DanRollins earned 500 total points
Comment Utility
Normally, it's best to start a second question, but in this case, I think that this is really part of the original question!

And that question comes up often -- usually in conjunction with the use of STL objects in DLLs.  This search of EE may bring up a thread that provides an answer:

     http://www.experts-exchange.com/Programming/Programming_Languages/Cplusplus/Q_20417674.html?query=DLL+stl+memory

One possibility relates to using a diffferent 'Runtime library' settings in the build settings (Project>Settings>C++>Code Generation)  and that is easy to check.  If that's not it, try perusing the other threads that turn up in that search

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
0
 

Author Comment

by:rbradleyinetlp
Comment Utility
Thank you for all your help!  I'll be digging some more later.  For now we've decided to make it work with a static width array............ it works.

I want to assign points and grade but I want to take a stab at some of the suggestions in the other threads first.  I'll do so then finish here.
0
 
LVL 5

Expert Comment

by:bastibartel
Comment Utility
Hi there,

Concerning the latter question.
Maybe you are mixing release & debug builds for the dll, exe respectively. Then you will get just this CrtIsValidHeapPointer assertion.

Cheers,
Sebastian
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

The following diagram presents a diamond class hierarchy: As depicted, diamond inheritance denotes when two classes (e.g., CDerived1 and CDerived2), separately extending a common base class (e.g., CBase), are sub classed simultaneously by a fourt…
In Easy String Encryption Using CryptoAPI in C++ (http://www.experts-exchange.com/viewArticle.jsp?aid=1193) I described how to encrypt text and recommended that the encrypted text be stored as a series of hexadecimal digits -- because cyphertext may…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now