We have a heap-allocated array of custom structures that is pointed to by a local pointer. The pointer to the array is checked for nullptr. However, during my loop, VC++ complains that the first attempt to use an indexed entry in the array is "Dereferencing NULL pointer 'ppi'".
I'm having a dumb moment here I think, but there doesn't seem to be any way to satisfy the 6011 warning... how do I correct this scenario?
I have included some snippets of code to briefly illustrate the code in question.
// Previously, SystemInfoObject.PeripheralPortInfo is heap-alloc'd to contain
// multiple PeripheralInfo structures, and
// SystemInfoObject.PeripheralPortInfoCount is adjusted to the number
// of elements.
PeripheralInfo *ppi = nullptr;
ppi = SystemInfoObject.PeripheralPortInfo; // Set our local pointer
if (ppi != nullptr)
{
for (int i = 0; i < SystemInfoObject.PeripheralPortInfoCount; i++)
{
if (_tcsncmp(ppi[i].PortName, _T("\\\\"), 2) == 0) // C6011
{
// Some code
}
}
}
Visual Studio strikes again, I didn't see the loop condition that required certain code later on to change the pointer ppi and the null condition was never re-checked on subsequent loop iterations.
Wish I could delete my question! All set!
Related
I have some issues with the CMonthCalCtrl control and modernizing my code. The first problem is related to the BOLDDAY macro.
This macro is used to adjust day states (making specific dates bold on the calendar) and the concept is described in detail here. As documented, you need to define a macro:
#define BOLDDAY(ds, iDay) if(iDay > 0 && iDay < 32) \
(ds) |= (0x00000001 << (iDay-1))
Here is my code that uses this macro so that you have some context:
void CMeetingScheduleAssistantDlg::InitDayStateArray(int iMonthCount, LPMONTHDAYSTATE pDayState, COleDateTime datStart)
{
int iMonth = 0;
COleDateTimeSpan spnDay;
CString strKey;
SPECIAL_EVENT_S *psEvent = nullptr;
if (pDayState == nullptr)
return;
memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);
if (m_pMapSPtrEvents == nullptr && m_Reminders.Count() == 0)
{
return;
}
spnDay.SetDateTimeSpan(1, 0, 0, 0);
auto datDay = datStart;
const auto iStartMonth = datStart.GetMonth();
auto iThisMonth = iStartMonth;
auto iLastMonth = iThisMonth;
do
{
strKey = datDay.Format(_T("%Y-%m-%d"));
if (m_pMapSPtrEvents != nullptr)
{
psEvent = nullptr;
m_pMapSPtrEvents->Lookup(strKey, reinterpret_cast<void*&>(psEvent));
if (psEvent != nullptr)
{
BOLDDAY(pDayState[iMonth], datDay.GetDay());
}
}
if (m_Reminders.HasReminder(datDay))
{
BOLDDAY(pDayState[iMonth], datDay.GetDay());
}
datDay = datDay + spnDay;
iThisMonth = datDay.GetMonth();
if (iThisMonth != iLastMonth)
{
iLastMonth = iThisMonth;
iMonth++;
}
} while (iMonth < iMonthCount);
}
Everywhere I use this BOLDDAY macro I get a code analysis warning (C26481):
warning C26481: Don't use pointer arithmetic. Use span instead (bounds.1).
It is not clear to me if the problem is with the BOLDDAY macro or my own code?
Update
I still get the warning when I turn the macro into a function:
Update 2
If it helps, I currently call the InitDayStateArray function in the following ways:
Method 1:
void CMeetingScheduleAssistantDlg::SetDayStates(CMonthCalCtrl &rCalendar)
{
COleDateTime datFrom, datUntil;
const auto iMonthCount = rCalendar.GetMonthRange(datFrom, datUntil, GMR_DAYSTATE);
auto pDayState = new MONTHDAYSTATE[iMonthCount];
if (pDayState != nullptr)
{
InitDayStateArray(iMonthCount, pDayState, datFrom);
VERIFY(rCalendar.SetDayState(iMonthCount, pDayState));
delete[] pDayState;
}
}
Method 2
void CMeetingScheduleAssistantDlg::OnGetDayStateEnd(NMHDR* pNMHDR, LRESULT* pResult)
{
NMDAYSTATE* pDayState = reinterpret_cast<NMDAYSTATE*>(pNMHDR);
MONTHDAYSTATE mdState[3]{}; // 1 = prev 2 = curr 3 = next
const COleDateTime datStart(pDayState->stStart);
if (pDayState != nullptr)
{
InitDayStateArray(pDayState->cDayState, &mdState[0], datStart);
pDayState->prgDayState = &mdState[0];
}
if (pResult != nullptr)
*pResult = 0;
}
Perhaps if the container for the LPMONTHDAYSTATE information is tweaked somehow it would contribute to resolve this span issue?
Sample code provided by Microsoft used to be published as code that compiles both with a C and C++ compiler. That limits availability of language features, frequently producing code that particularly C++ clients shouldn't be using verbatim.
The case here being the BOLDDAY function-like macro, that's working around not having reference types in C. C++, on the other hand, does, and the macro can be replaced with a function instead:
void bold_day(DWORD& day_state, int const day) noexcept {
if (day > 0 && day < 32) {
day_state |= (0x00000001 << (day - 1));
}
}
Using this function in place of the BOLDDAY macro silences the C26481 diagnostic.
While that works, I'm at a complete loss to understand where the compiler is seeing pointer arithmetic in the macro version. Regardless, replacing a function-like macro with an actual function (or function template) where possible is always desirable.
Update
Things are starting to make sense now. While replacing the function-like macro with a function, as suggested above, is desirable, it will not resolve the issue. My test happened to have used pDayState[0] which still raises C26481 for the macro, but not for the function. Using pDayState[1] instead, the diagnostic is raised in either case.
Let's put the pieces of the puzzle together: Recall that the array subscript expression p[N] is exactly identical to the expression *(p + N) when p is a pointer type and N an integral type. That explains why the compiler is complaining about "pointer arithmetic" when it sees pDayState[iMonth].
Solving that is fairly straight forward. As suggested by the diagnostic, use a std::span (requires C++20). The following changes to InitDayStateArray() make the C26481 diagnostic go away:
void CMeetingScheduleAssistantDlg::InitDayStateArray(int iMonthCount,
LPMONTHDAYSTATE pDayState,
COleDateTime datStart)
{
std::span const day_month_state(pDayState, iMonthCount);
// ...
// memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);
std::fill(begin(day_month_state), end(day_month_state), 0);
// ...
do
{
// ...
{
bold_day(day_month_state[iMonth], datDay.GetDay());
}
}
if (m_Reminders.HasReminder(datDay))
{
bold_day(day_month_state[iMonth], datDay.GetDay());
}
// ...
} while (iMonth < day_month_state.size());
}
A std::span "describes an object that can refer to a contiguous sequence of objects". It takes the decomposed pointer and size arguments that describe an array and reunites them into a single object, recovering the full fidelity of the array.
That sounds great. But remember, this is C++, and there's a caveat: Just like its evil C++17 ancestor std::string_view, a std::span is an unhesitating factory for dangling pointers. You can freely pass them around, and hang on to them far beyond the referenced data being alive. And this is guaranteed for every specialization, starting with C++23.
The other issue is, that addressing this one diagnostic now has several others pop out of nowhere, suggesting that std::span isn't good enough, and gsl::span should be used instead. Addressing those would probably warrant another Q&A altogether.
Code:
m_cbReminderInterval.ResetContent();
for (int i = 1; i <= m_iMaxReminderInterval; i++)
{
COMBOBOXEXITEM cmbItem = {};
CString strNumber;
strNumber.Format(_T("%d"), i);
cmbItem.mask = CBEIF_TEXT;
cmbItem.iItem = static_cast<INT_PTR>(i) - 1;
cmbItem.pszText = strNumber.GetBuffer(_MAX_PATH);
strNumber.ReleaseBuffer(); // TODO: When should I release the buffer - NOW or AFTER the InsertItem call?
m_cbReminderInterval.InsertItem(&cmbItem);
}
My question is:
Is it better to use GetString instead of GetBuffer in this context? The only issue I see is that pszText is LPWSTR whereas GetString returns LPCWSTR. If I should continue to use GetBuffer then when should it actually be released? Before or after the InsertItem call?
There's a common pattern in the Windows API you'll see over and over again: Structures that are less const-correct than what would appear to be possible. Undoubtedly, some of them are oversights, but not this one: COMBOBOXEXITEM is used both to insert and query an item's data.
This is hinted to, in part, in the documentation for the pszText member:
A pointer to a character buffer that contains or receives the item's text. If text information is being retrieved, this member must be set to the address of a character buffer that will receive the text.
The second part of the contract is omitted from the documentation, sadly. When setting an item's text, the control makes a copy of the string passed in, and neither takes ownership over the pointed to data, nor modifies it. In other words: When using the COMBOBOXEXITEM structure to insert an item, all pointers can be assumed to point to const.
Following that, it is perfectly valid to pass the pointer received from GetString():
for (int i = 1; i <= m_iMaxReminderInterval; i++)
{
COMBOBOXEXITEM cmbItem = {};
CString strNumber;
strNumber.Format(_T("%d"), i);
cmbItem.mask = CBEIF_TEXT;
cmbItem.iItem = static_cast<INT_PTR>(i) - 1;
cmbItem.pszText = const_cast<TCHAR*>(strNumber.GetString());
m_cbReminderInterval.InsertItem(&cmbItem);
}
According to CSimpleStringT::GetBuffer:
If you use the pointer returned by GetBuffer to change the string contents, you must call ReleaseBuffer before you use any other CSimpleStringT member methods.
You are not modifying the string, so you don't need to call ReleaseBuffer.
But as you said, it's better to use GetString, at least you indicate your intent to NOT modify it.
Hey I'm new to C++ and I am trying to find out if a specified registry index exists. I have to check multiple locations due to the possibility of the software being run on a 64bit machine and being under the WOW6432Node key instead of the usual position. When RegQueryValueExA (using visual c++ 6.0 on xp so I can't use a newer function) is run it should return a Boolean of true if the key exists, (I'll deal with getting the value of the key later). However on run it generates access violation 0xc00005. Any ideas whats gone wrong?
bool FindAndRemoveUninstall(string path){
bool result;
result = RegQueryValueExA(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ABC"), NULL, NULL, NULL, (unsigned long *)MAX_PATH);
if (result= ERROR_SUCCESS){
cout <<" is a 32 bit program\n";
//path= Value in key
}
result = RegQueryValueEx(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ABC"), NULL, NULL, NULL, (unsigned long *)MAX_PATH);
if (result= ERROR_SUCCESS){
cout << " is 64 bit program\n";
//path= Value in key
}
return true;
}
You have multiple problems.
The last parameter to RegQueryValueExA is documented as
lpcbData [in, out, optional]
A pointer to a variable that specifies the size of the buffer pointed to by the lpData parameter,
But you are not passing a pointer to a variable. You are passing (unsigned long *)MAX_PATH, which is a garbage pointer. When the operating system tries to store the result into the pointer, it takes an access violation. You need to pass a pointer to a variable, like the documentation says.
The next problem is that you are calling the A function (explicit ANSI) but using the TEXT macro (adaptive character set). Make up your mind which model you are using (ANSI or adaptive) and choose one model or the other. Let's assume you explicit ANSI.
The next problem is that you didn't specify an output buffer, so you don't actually retrieve the path.
Another problem is that the RegQueryValueExA function does not return a bool; it returns an error code.
Yet another problem is that your if test contains an assignment, so it does not actually test anything.
Another problem is that you didn't specify a way for the function to return the path to the caller. Let's assume you want the result to be returned in the path parameter.
Yet another problem is that you have the 32-bit and 64-bit cases reversed.
Also, you are using '\n' instead of std::endl.
The eight problem is that your function returns true even if it didn't do anything.
And the ninth problem is that the function says FindAndRemove, and it finds, but doesn't remove.
bool FindUninstall(string& path){ // parameter passed by reference, fix function name
LONG result; // change variable type
char buffer[MAX_PATH]; // provide an output buffer
DWORD bufferSize = MAX_PATH; // and a variable to specify the buffer size / receive the data size
result = RegQueryValueExA(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ABC", NULL, NULL, (LPBYTE)buffer, &bufferSize); // remove TEXT macro, pass the buffer and buffer size
if (result== ERROR_SUCCESS){ // fix comparison
cout <<" is a 64 bit program" << std::endl; // fix message
path = buffer;
return true; // stop once we have an answer
}
buffersize = MAX_PATH; // reset for next query
result = RegQueryValueEx(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ABC", NULL, NULL, (LPBYTE)buffer, &bufferSize); // remove TEXT macro, pass the buffer and buffer size
if (result== ERROR_SUCCESS){ // fix comparison
cout << " is 32 bit program" << std::endl; // fix message
path = buffer;
return true; // stop once we have an answer
}
return false; // nothing found
}
Since you are new to C++, I would recommend that you get some experience with C++ doing simpler projects before diving into more complicated things like this.
please help to figure this out.
I have some leaking code, and I don't know how to handle it
vector <ItemClass> items( 10 );
items[1] = ItemClass( "DVD Player", 560 );
items[5] = * new ItemClass( "Blu Ray Player", 900 );
How should I free memory for items[5] ?
I'm getting error on my attempts of freeing memory
delete &items[5];
delete [] &items[5];
I even tried something like
ItemClass * delItem = &items[5];
items[5] = item4;
delete delItem;
I'm getting "corruption of the heap" in VS2010 Ultimate
There is a strange use and mix up of storing new-allocated objects in vectors in your code. Usually you should handle lists all in the same way. Thus my examples below will be more explicit, and independent from each other which should help you understand the differences.
Use delete for objects only. See code below.
Use delete [] for "native" arrays only, not for vector or similar container classes or objects inside them.
I will not give an example for arrays though, since arrays might be more confusing.
Default example stack:
vector <ItemClass> items( 10 );
// does not need to be deleted because item is on the stack
ItemClass item("device1", "10");
items.push_back(item);
Default example with heap allocation:
vector <ItemClass*> items( 10 );
ItemClass* pItem = new ItemClass("device2", "20");
items.push_back(pItem);
// delete all items inside vector
for (int i = 0; i < items.size(); i++)
{
ItemClass* pToDelete = items[i];
delete pToDelete;
items.erase(i);
}
The following examples should be avoided and are for clarifying things up only! Use at own risk.
Storing addresses of stack variables:
vector <ItemClass*> items( 10 );
// does not need to be deleted because item is on the stack
ItemClass item("device3", "30");
items.push_back( &item ); // storing a reference to item
// No need to delete this item that points to something on the stack.
// However you might not be able to tell items apart which have been
// created on the heap or the stack, so just dont do it.
If you mix up with the example before and store references and pointers you must handle it yourself. My recommendation: Do not do this.
Storing dereferenced items:
vector <ItemClass> items( 10 );
ItemClass* pItem = new ItemClass("device4", "40");
items.push_back(*pItem);
// must be deleted because allocated on the heap
// Again you can not tell which item is allocated on stack or heap. Avoid this.
You don't call delete on objects that you don't own. You don't own items[5]. The items vector owns it.
What you need to delete is the ItemClass object that you are currently leaking, which is the one you are creating via new ItemClass(...) and then copying to items[5].
ItemClass *temp = new ItemClass("Blue Ray Player", 900);
items[5] = *temp;
delete temp;
If you want to remove items[5] from the vector, ask the vector to remove it:
items.erase(items.begin() + 5);
Note that this may be rather inefficient, because all later items in the vector have to be moved down one position, which entails calling the ItemClass assignment operator once for each moved item.
You might want to use a vector of pointers to ItemClass instead, or a vector of shared_ptrs, or a boost::ptr_vector.
I've got a strange problem and really don't understand what's going on.
I made my application multi-threaded using the MFC multithreadclasses.
Everything works well so far, but now:
Somewhere in the beginning of the code I create the threads:
m_bucketCreator = new BucketCreator(128,128,32);
CEvent* updateEvent = new CEvent(FALSE, FALSE);
CWinThread** threads = new CWinThread*[numThreads];
for(int i=0; i<8; i++){
threads[i]=AfxBeginThread(&MyClass::threadfunction, updateEvent);
m_activeRenderThreads++;
}
this creates 8 threads working on this function:
UINT MyClass::threadfunction( LPVOID params ) //executed in new Thread
{
Bucket* bucket=m_bucketCreator.getNextBucket();
...do something with bucket...
delete bucket;
}
m_bucketCreator is a static member. Now I get some thread error in the deconstructor of Bucket on the attempt to delete a buffer (however, the way I understand it this buffer should be in the memory of this thread, so I don't get why there is an error). On the attempt of delete[] buffer, the error happens in _CrtIsValidHeapPointer() in dbgheap.c.
Visual studio outputs the message that it trapped a halting point and this can be either due to heap corruption or because the user pressed f12 (I didn't ;) )
class BucketCreator {
public:
BucketCreator();
~BucketCreator(void);
void init(int resX, int resY, int bucketSize);
Bucket* getNextBucket(){
Bucket* bucket=NULL;
//enter critical section
CSingleLock singleLock(&m_criticalSection);
singleLock.Lock();
int height = min(m_resolutionY-m_nextY,m_bucketSize);
int width = min(m_resolutionX-m_nextX,m_bucketSize);
bucket = new Bucket(width, height);
//leave critical section
singleLock.Unlock();
return bucket;
}
private:
int m_resolutionX;
int m_resolutionY;
int m_bucketSize;
int m_nextX;
int m_nextY;
//multithreading:
CCriticalSection m_criticalSection;
};
and class Bucket:
class Bucket : public CObject{
DECLARE_DYNAMIC(RenderBucket)
public:
Bucket(int a_resX, int a_resY){
resX = a_resX;
resY = a_resY;
buffer = new float[3 * resX * resY];
int buffersize = 3*resX * resY;
for (int i=0; i<buffersize; i++){
buffer[i] = 0;
}
}
~Bucket(void){
delete[] buffer;
buffer=NULL;
}
int getResX(){return resX;}
int getResY(){return resY;}
float* getBuffer(){return buffer;}
private:
int resX;
int resY;
float* buffer;
Bucket& operator = (const Bucket& other) { /*..*/}
Bucket(const Bucket& other) {/*..*/}
};
Can anyone tell me what could be the problem here?
edit: this is the other static function I'm calling from the threads. Is this safe to do?
static std::vector<Vector3> generate_poisson(double width, double height, double min_dist, int k, std::vector<std::vector<Vector3> > existingPoints)
{
CSingleLock singleLock(&m_criticalSection);
singleLock.Lock();
std::vector<Vector3> samplePoints = std::vector<Vector3>();
...fill the vector...
singleLock.Unlock();
return samplePoints;
}
All the previous replies are sound. For the copy constructor, make sure that it doesn't just copy the buffer pointer, otherwise that will cause the problem. It needs to allocate a new buffer, not the pointer value, which would cause an error in 'delete'. But I don't get the impression that the copy contructor will get called in your code.
I've looked at the code and I am not seeing any error in it as is. Note that the thread synchronization isn't even necessary in this GetNextBucket code, since it's returning a local variable and those are pre-thread.
Errors in ValidateHeapPointer occur because something has corrupted the heap, which happens when a pointer writes past a block of memory. Often it's a for() loop that goes too far, a buffer that wasn't allocated large enough, etc.
The error is reported during a call to 'delete' because that's when the heap is validated for bugs in debug mode. However, the error has occurred before that time, it just happens that the heap is checked only in 'new' and 'delete'. Also, it isn't necessarily related to the 'Bucket' class.
What you need to need to find this bug, short of using tools like BoundsChecker or HeapValidator, is comment out sections of your code until it goes away, and then you'll find the offending code.
There is another method to narrow down the problem. In debug mode, include in your code, and sprinkle calls to _CrtCheckMemory() at various points of interest. That will generate the error when the heap is corrupted. Simply move the calls in your code to narrow down at what point the corruption begins to occur.
I don't know which version of Visual C++ you are using. If you're using a earlier one like VC++ 6.0, make sure that you are using the Multitreaded DLL version of the C Run Time Library in the compiler option.
You're constructing a RenderBucket. Are you sure you're calling the 'Bucket' class's constructor from there? It should look like this:
class RenderBucket : public Bucket {
RenderBucket( int a_resX, int a_resY )
: Bucket( a_resX, a_resY )
{
}
}
Initializers in the Bucket class to set the buffer to NULL is a good idea... Also making the Default constructor and copy constructor private will help to make double sure those aren't being used. Remember.. the compiler will create these automatically if you don't:
Bucket(); <-- default constructor
Bucket( int a_resx = 0, int a_resy = 0 ) <-- Another way to make your default constructor
Bucket(const class Bucket &B) <-- copy constructor
You haven't made a private copy constructor, or any default constructor. If class Bucket is constructed via one of these implicitly-defined methods, buffer will either be uninitialized, or it will be a copied pointer made by a copy constructor.
The copy constructor for class Bucket is Bucket(const Bucket &B) -- if you do not explicitly declare a copy constructor, the compiler will generate a "naive" copy constructor for you.
In particular, if this object is assigned, returned, or otherwise copied, the copy constructor will copy the pointer to a new object. Eventually, both objects' destructors will attempt to delete[] the same pointer and the second attempt will be a double deletion, a type of heap corruption.
I recommend you make class Bucket's copy constructor private, which will cause attempted copy construction to generate a compile error. As an alternative, you could implement a copy constructor which allocates new space for the copied buffer.
Exactly the same applies to the assignment operator, operator=.
The need for a copy constructor is one of the 55 tips in Scott Meyer's excellent book, Effective C++: 55 Specific Ways to Improve Your Programs and Designs:
This book should be required reading for all C++ programmers.
If you add:
class Bucket {
/* Existing code as-is ... */
private:
Bucket() { buffer = NULL; } // No default construction
Bucket(const Bucket &B) { ; } // No copy construction
Bucket& operator= (const Bucket &B) {;} // No assignment
}
and re-compile, you are likely to find your problem.
There is also another possibility: If your code contains other uses of new and delete, then it is possible these other uses of allocated memory are corrupting the linked-list structure which defines the heap memory. It is common to detect this corruption during a call to delete, because delete must utilize these data structures.