Solved

Dynamic strings / malloc

Posted on 2003-11-30
4
3,051 Views
Last Modified: 2007-12-19
Greetings

I have a function that needs to return a pointer to an array of characters that contains five tab-delimited strings, for example:

"string1\tstring2\tstring3\tstring4\tstring5"

Each of the five strings' size is dynamically allocated inside a while loop before being assigned.  Here is a simple version of what I have.  Let's assume RecoLabelToString properly malloc()s and returns an array of characters:

TCHAR * string;
TCHAR * retString;

while (iterator) {
  string = RecoLabelToString();
}

return string;

This does work, and returns the last iteration's version of string, which is "string5" in my example above.  However, I need to dynamically size retString each iteration of the loop, add a tab, and concatenate string to it.  I'm having a lot of trouble.  I've tried a bunch of things like:

TCHAR * string[5]
TCHAR * retString;
int iterResults = 0;
int totalLength = 0;
int numResults;

while (iterator) {
  string[iterResults] = RecoLabelToString();
  totalLength = totalLength + sizeof(string[iterResults]);
  iterResults++;
}

numResults = iterResults;

retString = malloc(totalLength);

for (iterResults = 0; iterResults < numResults; iterResults++) {
  if (iterResults == 0) {
    retString = strcat(retString, "\t");
  }
  retString = strcat(retString, string[iterResults]);
}

return retString;

I've tried many versions of the above, always to some form of crash or heap error.  I have never worked with C-style strings before and I really don't know where to begin troubleshooting.  Any help would be very appreciated.

Thanks
0
Comment
Question by:v2000
  • 2
4 Comments
 
LVL 16

Accepted Solution

by:
_nn_ earned 250 total points
ID: 9846435
I see a first problem here

  totalLength = totalLength + sizeof(string[iterResults]);

sizeof(string[iterResults]) is actually sizeof(TCHAR *), which is 4 most of the time. If you want the length (in bytes) of the string, you'll need something like :

  totalLength = totalLength + _tcslen(string[iterResults]) * sizeof(TCHAR*);


Then, you must think of adding some "bits" to this total, since you need room for 4 tabs and a terminating \0. So :

totalLength += 5 * sizeof(TCHAR);
retString = (TCHAR*)malloc(totalLength);

Then, you need to initialize that buffer to an empty string, else concatenating (as you plan to use) won't work properly, so something like :

retString[0] = 0;

Finally, don't mix TCHAR and one-byte characters function calls like strcat(). So, use the T* macros and/or functions so that you can compile in both ANSI and UNICODE :

_tcscat(retString, _T("\t")); // no need to assign the returned pointer

and

_tcscat(retString, string[iterResults]);  // no need to assign the returned pointer


A last word : I see

string[iterResults] = RecoLabelToString();

I don't know what TCHAR * is returned here. If it's a dynamically allocated buffer, don't forget to free it. If it's a static one, I'm afraid you could possibly not get the results you're expecting. In case you still have problems, pasting the source code of that function would probably help.
0
 
LVL 1

Author Comment

by:v2000
ID: 9846738
Greetings

Thanks for the response.  Unfortunately I'm still having some trouble -- the malloc() seems to be destroying whatever data is in the string[] array, even if I don't return retString.  Here's my source of the entire function:

TCHAR * Recognizer_RecognizeInk(Recognizer * This, Ink * pInk)
{
  /* Local data */
  InkStroke * pStroke;
  Long retval;
  HIterator it;
  HIterator segIt;
  HIterator wholeIt;
  Long length;
  int totalLength = 0;
  TCHAR * string[4];
  int numResults = 5;
  int iterResults = 1;
  int iterStrings = 0;
  UShort * label = Null;
  TCHAR * retString = Null;
  TCHAR * tempString;

  /* Add ink to the handwriting context */
  pStroke = pInk->pFirstStroke;
  while (pStroke)
  {
    voAddStroke(This->hApi, This->hhc, COORD__LONG, &pStroke->points[0].x, sizeof(InkPoint), &pStroke->points[0].y, sizeof(InkPoint), (UShort)pStroke->n_points);
    pStroke = pStroke->pNextStroke;
  }
 
  /* Recognize */
  voRecognize(This->hApi, This->hhc, voNull, 0);

  if (voGetResultEx(This->hApi, This->hhc, voNull, VO_RC_EX__FIRST_CANDIDATE, &wholeIt) == -1) {
      goto clear;
  }

  /* Let's try segments */
  if (voGetResultEx(This->hApi, This->hhc, wholeIt, VO_RC_EX__FIRST_SEGMENT, &segIt) == -1) {
      goto clear;
  }

      while (segIt)
      {
        iterResults = 1;
        voGetResultEx(This->hApi, This->hhc, segIt, VO_RC_EX__FIRST_CANDIDATE, &it);

        while (it)
        {
            length = 0;

            retval = voGetResultEx(This->hApi, This->hhc, it, RC_EX__LABEL, Null);

            label = TRealloc(UShort, label, length + (retval / sizeof(UShort)));

            retval = voGetResultEx(This->hApi, This->hhc, it, RC_EX__LABEL, &label[length]);
            length += retval / sizeof(UShort);
            string[iterResults - 1] = RecoLabelToString(This->hApi, This->hresAK, label, length);      

            if (++iterResults == numResults)
            {
                  voGetResultEx(This->hApi, This->hhc, segIt, VO_RC_EX__STOP_ITERATION, &it);
            }
            else
            {
                  voGetResultEx(This->hApi, This->hhc, segIt, VO_RC_EX__NEXT_CANDIDATE(VO_CLO__ANY), &it);
            }
        }
        voGetResultEx(This->hApi, This->hhc, wholeIt, VO_RC_EX__NEXT_SEGMENT, &segIt);
      }
clear:
  /* Clear ink from the handwriting context */
  voClearInk(This->hApi, This->hhc, False, True);

 
  for (iterStrings = 0; iterStrings < iterResults; iterStrings++) {
        totalLength = totalLength + _tcslen(string[iterStrings]) * sizeof(TCHAR *);
  }
  totalLength += 5 * sizeof(TCHAR);

  retString = (TCHAR *)malloc(totalLength);
  retString[0] = 0;

 
  _tcscat(retString, string[0]);
  TFree(TCHAR, string[0]);
  for (iterStrings = 0; iterStrings < iterResults; iterStrings++) {
        _tcscat(retString, _T("\t"));
        _tcscat(retString, string[iterStrings]);
        TFree(TCHAR, string[iterStrings]);
  }
 
  return retString;
}

As you asked, I'll also include the RecoLabelToString function:

TCHAR * RecoLabelToString(HApi hApi, HRes hresAK, UShort * label, Long length)
{
  Long i;
  Word wClassId;
  TCHAR buffer[1024];
  Long str_len;
  TCHAR * string;

  str_len = 0;
  for (i = 0; i < length; i++)
  {
    wClassId = AlphabetIndexToClassId(hApi, hresAK, label[i]);
    ClassIdToString(wClassId, buffer);
    str_len += _tcslen(buffer);
  }

  string = TAlloc(TCHAR, str_len+2);
  if (string)
  {
    str_len = 0;
    for (i = 0; i < length; i++)
    {
      wClassId = AlphabetIndexToClassId(hApi, hresAK, label[i]);
      ClassIdToString(wClassId, &string[str_len]);
      str_len += _tcslen(&string[str_len]);
    }
    string[str_len] = _T('\0');
  }
  return string;
}

We needn't concern ourselves with any other function called in these functions -- rest assured, the code works if I comment out the mallocing business and the concatenate business and just return string[0].  But of course I only get one string then, instead of the tab-delimited bunch that the application using this DLL will expect.

Any help is appreciated.
0
 
LVL 45

Expert Comment

by:Kdo
ID: 9847719
Hi v2000,

If you take the branch to the clear: label, iterResults will be set to its default value of 1.  The concatenation loop will only insert string[0].  This sounds like what you are seeing.

Also, string[] is defined as an array of 4 pointers.  Does it need to be 5?

Kent
0
 
LVL 1

Author Comment

by:v2000
ID: 9847827
Thanks _nn_ -- While your exact edits didn't do the trick, the use of the unicode character functions was the key, I believe.  I had some more pointer issues that I didn't catch at first, and I believe I also had a problem finding the length of any of the string[]s.  I implemented another function and it did the trick, in combination with the T functions.

I haven't written in C in four years...  and it shows.  Thanks again.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

An Outlet in Cocoa is a persistent reference to a GUI control; it connects a property (a variable) to a control.  For example, it is common to create an Outlet for the text field GUI control and change the text that appears in this field via that Ou…
Windows programmers of the C/C++ variety, how many of you realise that since Window 9x Microsoft has been lying to you about what constitutes Unicode (http://en.wikipedia.org/wiki/Unicode)? They will have you believe that Unicode requires you to use…
The goal of this video is to provide viewers with basic examples to understand opening and writing to files in the C programming language.
The goal of this video is to provide viewers with basic examples to understand how to use strings and some functions related to them in the C programming language.

746 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

11 Experts available now in Live!

Get 1:1 Help Now