Archived

This topic is now archived and is closed to further replies.

Carradine

CString causes memory leaks on its own?

Recommended Posts

Carradine    303
I am trying to find out how the CString function works in terms of allocation. For example I do a simple operation like this: CString tempStr; tempStr = getcwd(NULL,_MAX_PATH); and it loads in the correct information. getcwd() returns type (char *) and puts it into the CString. However if I exit out of the program at that point, it causes a memory leak to occur, yet i have not personally used new or delete operators. Instead, I need to do this: char temp[_MAX_PATH]; getcwd(temp,_MAX_PATH); tempStr.Format(temp); To prevent the leaks. I also have a problem of saving and loading type CString into files, and I am trying to figure out if these two problems are related. Does anyone have more knowledge on how CString works?

Share this post


Link to post
Share on other sites
CheeseGrater    122
The problem isn't CString, it's your code. Assignment creates a copy.

From MSDN:

quote:

The _getcwd function gets the full path of the current working directory for the default drive and stores it at buffer. The integer argument maxlen specifies the maximum length for the path. An error occurs if the length of the path (including the terminating null character) exceeds maxlen. The buffer argument can be NULL; a buffer of at least size maxlen (more only if necessary) will automatically be allocated, using malloc, to store the path. This buffer can later be freed by calling free and passing it the _getcwd return value (a pointer to the allocated buffer).



[edited by - cheesegrater on June 6, 2002 10:48:19 AM]

Share this post


Link to post
Share on other sites
stimarco    1071
quote:
Original post by Carradine
CString tempStr;



At this point, "tempStr" -- a pointer -- is either uninitialised, or set to 'null'. (Most people assume it'll always be 'null' here, but that's a dangerous assumption to make. Some compilers only set it to 'null' during debug builds; release builds often skip this step to increase performance.)

A pointer needs to be initialised to a value. Unfortunately, C++ makes it very easy to initialise a pointer to a buffer _without_ your realising that a buffer allocation has taken place. (This is particularly the case with the older APIs, such as Win32. This API which predates the rise of C -- let alone C++ -- was originally designed with Pascal compilers in mind. Hence the need to use non-standard prefixes when working with it.)

Now the problem comes in on the next line...

quote:

tempStr = getcwd(NULL,_MAX_PATH);



... which *allocates a buffer for you*, returning a pointer to it in "tempStr". This buffer isn't created by the usual 'new' mechanism -- the API predates C++, so it uses C's older _malloc() functions instead. (This is what passing 'NULL' as the first argument does. This is also why your alternative method avoids the problem as it means you do the allocation explicitly.)

Releasing this buffer can be messy as the C memory manager isn't required by the ISO C++ defintion to tell the C++ memory manager (represented by 'new' and 'delete') about what it's doing. Using delete() on "tempStr" can therefore cause problems depending on how your particular libraries are implemented. (For instance, "delete(tempStr)" might throw an exception, because the C++ allocation routines can't find an entry for the pointer. A crap library might just crash.)

It is usually better go via a temporary (char *) buffer when working with old API functions like this getcwd(), as this is how they were intended to be used.

*

You can use an LPCTSTR cast to get a pointer into the CString's buffer, but you should bear in mind that, aside from the pointer being read-only, there are conversion issues to consider: Depending on the compiler and platform, a CString object may use ASCII, Unicode or multi-byte character sets. Most older C/Pascal-based API functions are entirely based on ASCII, so it's not safe to assume that what you get out of getcwd() can be thrown straight into a CString unchanged. (Windows 9x functions do tend to use ASCII, but Windows 2000 and Windows XP both support Unicode right down to the filesystem level.)




--
Sean Timarco Baggaley

[edited by - stimarco on June 6, 2002 11:32:37 AM]

Share this post


Link to post
Share on other sites
IndirectX    122
quote:
Original post by stimarco
At this point, "tempStr" -- a pointer -- is either uninitialised, or set to 'null'.


tempStr is not a pointer, it's a class object, and it is initialized to an empty string.
quote:

Now the problem comes in on the next line...
... which *allocates a buffer for you*, returning a pointer to it in "tempStr".


The function returns a pointer to a malloc()-allocated buffer, a copy of which [the buffer] is then stored in tempStr via the assignment operator. Since the pointer wasn't saved, this line produces a memory leak.
quote:

Releasing this buffer can be messy as the C memory manager isn't required by the ISO C++ defintion to tell the C++ memory manager (represented by 'new' and 'delete') about what it's doing. Using delete() on "tempStr" can therefore cause problems depending on how your particular libraries are implemented.


The compiler won't let you call 'delete' on 'tempStr' because tempStr is not a pointer.
quote:

(For instance, "delete(tempStr)" might throw an exception, because the C++ allocation routines can't find an entry for the pointer. A crap library might just crash.)


Try compiling this in the first place.
quote:

It is usually better go via a temporary (char *) buffer when working with old API functions like this getcwd(), as this is how they were intended to be used.


No, in fact it's good that the function can allocate the buffer for you: you won't have to be concerned with buffer overflows and string truncations.
quote:

Depending on the compiler and platform, a CString object may use ASCII, Unicode or multi-byte character sets. Most older C/Pascal-based API functions are entirely based on ASCII, so it's not safe to assume that what you get out of getcwd() can be thrown straight into a CString unchanged.


Most, if not all, CRT routines are implemented in all three versions. See the tchar.h include file for portable macros. There's, for instance, _getcwd, _wgetcwd, and _tgetcwd.

Carradine: try this.

CString tempStr;
char *tempBuf = getcwd(NULL,_MAX_PATH);
// Or Unicode-ready version for stimarco:
LPTSTR tempBuf = _tgetcwd(NULL,_MAX_PATH);
tempStr = tempBuf;
free(tempBuf);


[edited by - IndirectX on June 6, 2002 11:46:03 AM]

Share this post


Link to post
Share on other sites
Carradine    303
Yes, I am basically using the char* method for now, but you mention that getcwd() is an older function for retrieving the current working directory, is there newer version of the function?

Share this post


Link to post
Share on other sites