Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C++ (http://www.go4expert.com/forums/cpp/)
-   -   Simple Programming Tips On C/C++ (http://www.go4expert.com/forums/simple-programming-tips-c-cpp-t699/)

subhasish 9Apr2006 19:51

Simple Programming Tips On C/C++
 
Scope of Variables Declared in for()

The new ANSI C++ standard specifies that variables declared as in for(int i=1; ...) have a scope local to the for statement. Unfortunately, older compilers (like Visual C++ 5.0) use the older concept that the scope is the enclosing group. Below, I list two possible problems arising from this change and their recommended solutions.

Say you want to use the variable after the for() statement. You would have to declare the variable outside of the for() statement.
Code: CPP

int i;
for(i=1; i<5; i++)
{ /* do something */ }
if (i==5) ...

Say you want to have multiple for() loops with the same variables. In this case, you'd put the for statement in its own group. You could also declare the variable outside of the 'for', but that would make it slightly trickier for an optimizing compiler (and a human) to know what you intended.
Code: CPP

{
  for(i=1; i<5; i++)
  { /* do something */ }
}


subhasish 9Apr2006 19:53

inline vs. __forceinline
 
MS Visual C++, as well as several other compilers, now offer non-standard keywords that control the inline expansion of a function, in addition to the standard inline keyword. What are the uses of the non-standard keywords? First, let's review the semantics of inline. The decision whether a function declared inline is actually inline-expanded is left to the sole discretion of the compiler. Thus, inline is only a recommendation. For example, the compiler may refuse to inline functions that have loops or functions that are simply too large, even if they are declared inline.
By contrast, the non-standard keyword __forceinline overrides the compiler's heuristics and forces it to inline a function that it would normally refuse to inline. I'm not sure I can think of a good reason to use __forceinline as it may cause a bloated executable file and a reduced instruction-cache hit. Furthermore, under extreme conditions, the compiler may not respect the __forceinline request either. So, in general, you should stick to good old inline. inline is portable and it enables the compiler to "do the right thing".

__forceinline should be used only when all the following conditions hold true: inline is not respected by the compiler, your code is not to be ported to other platforms, and you are certain that inlining really boosts performance (and of course, you truly need that performance boost).

subhasish 9Apr2006 19:55

Debugging the Memory Leaks in MS VC++ 6.0
 
The failure to deallocate previously allocated memory is known as a memory leak. A memory leak is one of those hard to detect bugs, and it may cause unpredictable behavior in your program.
To allocate memory on the heap, you call new. To deallocate, you call delete. If a memory object has not been deallocated, a memory leak dump for a leak can be seen in the Output window in the end of a VC++ debug session. The dump is as follows:

{N} normal block at 0x00421C90, 12 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD

In which N is a unique allocation request number that represents the sequence number of an allocation of the "leaked" object.
This dump is not very helpful. To improve this dump, you can insert additional code that extends the memory leak dump with a file name and line number within this file, where the "leaked" allocation has occurred. This capability began with MFC 4.0 with the addition of the Standard C++ library. The MFC and C run-time library use the same debug heap and memory allocator. Here's the additional code:
Code: CPP

#ifdef _DEBUG                              //for debug builds only
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
#endif

The __FILE__ is an ANSI C macro defined by compiler. The preprocessor fills the macro with a string, whose content is the current file name, surrounded by double quotation marks.
An improved memory leak dump is as follows:

Path\Filename (LineNumber): {N} normal block at 0x00421C90, 12 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD

MS VC++ AppWizard and ClassWizard place the additional code, shown above, in the .CPP files that they create by default. The filename that is shown above will be a .CPP file name or .H file that has a template class code in them where the N-th object was allocated.
Having the location where the "leaked" N-th object was allocated is not enough. You need to know when the memory leak occurred, because the line with allocation code can be executed hundreds of times. This is why setting a simple breakpoint on this line may not be adequate.

The solution is to break an execution of the debugged program only at the moment when the "leaked" N-th object is allocated. To do this, stop the execution of your program in the debugger just after the beginning. Type in _crtBreakAlloc in the Name column of the Watch window and press the Enter key. If you use the /MDd compiler option, type in {,,msvcrtd.dll}*__p__crtBreakAlloc() in the Name column. Replace a number in the Value column with the value of N. Continue to debug using the same execution path, and the debugger will stop the program when the "leaked" N-th object will be allocated.

subhasish 9Apr2006 19:56

Negative Numbers Represented in C++
 
You probably know that integers are represented in binary--in base 2. This is pretty straightforward for positive numbers, but it means you must choose an encoding for representing negatives. The encoding used by C++ (as well was by C and Java) is two's complement.
In two's complement, the first bit of a negative number is always 1. Otherwise, the number is 0 or postive. To find the bitstring representing a negative number, you take the bitstring representing the corresponding positive number and flip all the bits. Next, add 1 to the result.

In the following example, Ive used 4-bit numbers for simplicity:


-5d = -(0101b) = (1010b + 1b) = 1011b

Notice that -1d is represented as all 1's:

-1d = -(0001b) = 1110b + 1 = 1111b

A nice property of this encoding is that you can subtract by negating and then adding the following:

7d - 3d = 0111b
- 0011b

=> 0111b
+ 1100b + 1

=> 0111b
+ 1101b
= 0100b = 4d

Yet another nice property is that overflows and underflows wrap around and cancel one another out, like this:

5d + 6d = 0101b
+ 0110b
= 1011b
= -(0100b + 1)
= -0101b = -5d

If you subtract 6 from this result (by adding its negation), youll get 5 back. First, compute -6:

-6d = -(0110b) = 1001b + 1 = 1010b

Then, add -6d to -5d to get the original value:

1011b
+ 1010b
= 0101

Matthew Johnson

subhasish 9Apr2006 19:58

Access a Class Member Function Without Creating a Class Object
 
In some cases, it is possible to call a class member function without creating the class object.
In the following example, the program will print "hello world" although class A has never been created. When the program enters the "PrintMe" function, the "this" pointer is zero. This is fine as long as you don't access data members through the "this" pointer.

Code: CPP

#include <stdio.h>
class A {
public:
    void PrintMe();
};


void A::PrintMe()
{
    printf("Hello World\n");
}

void main()
{
    A* p = 0;
    p->PrintMe();

}


subhasish 9Apr2006 20:00

Notes about the system() Function
 
The system() function (declared in <cstdlib>) launches another program from the current program. As opposed to what most users think, it doesn't return the exit code of the launched process. Instead, it returns the exit code of the shell that launches the process in question. Consider the following example:
Code: CPP

int main()
{
 int stat = system("winword.exe"); //launch Word and wait
                                   // until the user closes it
}

When the system() call returns, the value assigned to stat is the exit code of the shell process that in turn launches Word, not Word’s own exit status. Thus, examining the return code of system() is pretty useless in most cases. To collect the exit code of a launched application, you have to use an Interprocess Communication mechanism such as signals, pipes etc.

subhasish 9Apr2006 20:02

Using the Volatile Keyword to Avoid Failures
 
When compiling a program, the compiler adds some optimizations that may cause your application to misbehave. For example consider the following code:
Code: CPP

// To avoid threads waiting on the critical section in vain
if (m_instance == NULL)
{
    EnterCiriticalSection(pcs);
    if (m_instance == NULL)
        m_instance = new MyInstance();
}

The compiler may cache the second condition (m_instance == NULL) and not update the content of m_instance if it has been changed by another thread. The solution is to declare the instance with the volatile keyword. This tells the compiler to get the content of m_instance every time it is used and not cache its content.

The declaration is:

volatile MyInstance* m_instance;

subhasish 9Apr2006 20:16

Testing the Copy Constructor and the Assignment Operator
 
You have written a test program for your class X, and everything worked fine. How did you test the copy constructor and the assignment operator?
Code: CPP

class X
{
public:
      X (const X&);
      const X& operator= (const X&);
};

Okay, you called both methods, and neither hung up. But did you test the logical independence of source and target? Consider this:
Code: CPP

X a1, a2;
a1 = a2;

The meaning of operator= should be two-fold: a1 is a logical copy of a2, but afterwards, there should be no further connection between these two objects: No operation on a1 should affect a2 and vice versa. Except for very seldom, specific cases, deviating from this rule makes maintenance a nightmare. You should conform to this rule, and you should test it. (If you think that code review by eye suffices, then recall that constructors and the assignment operator are never inherited. Did you take all possible subtleties like this into account in your code review?)

subhasish 9Apr2006 20:18

Determine Object Size Without Using the sizeof() Operator
 
The sizeof() operator gives you the number of bytes required for storing an object. It also operates on a data type or a value.
Another way of determining the size of an object is to use pointer arithmetic, as in the following example:
Code: CPP

struct point {
    long x, y;
};

int main()
{
    struct point pt = {0}, *ppt = &pt;
    unsigned char *p1 = NULL, *p2 = NULL;
    size_t size = 0;

    p1 = (unsigned char*)(ppt);
    p2 = (unsigned char*)(++ppt);
    size = p2 - p1; // size is now 8 bytes (2 longs)
    // same as sizeof(struct point) or sizeof(pt)

    return 0;
}


subhasish 9Apr2006 20:20

Overloaded Operators May Not Have Default Parameters
 
Unlike ordinary functions, overloaded operators cannot declare a parameter with a default value (overloaded operator() is the only exception):
Code: CPP

class Date
{
  private:
    int day, month, year;
  public:
    Date & operator += (const Date & d = Date() ); //error, default arguments are not allowed
};

This rule may seem arbitrary. However, it captures the behavior of built-in operators, which never have default operands either.


All times are GMT +5.5. The time now is 04:54.