Re: C++ Object Pointers

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance

From: Joseph M. Newcomer (newcomer_at_flounder.com)
Date: 05/25/04


Date: Tue, 25 May 2004 05:09:14 -0400

I shoud add that on a RISC machine, a call is a control transfer, so the RISC equivalent
of

f(a, b, c)

is

        push c
        push b
        call f
        push a

because the call instruction will transfer control but the instruction following it will
be executed next, before the first instruction of the function f! This is because RISC
machines, to reduce "decode depth", have already fetched the next instruction before they
realize that they are transferring control. There are good reasons for this in terms of
performance, but it drives you crazy writing a compiler, or trying to read the generated
code.
                                        joe

On Tue, 25 May 2004 10:34:55 +0530, "Jugs" <jagadish@suntecgroup.com> wrote:

>Thank you Joseph.
>
>"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message
>news:ah73b09kodotogi45gbv3bna0pos993kfc@4ax.com...
>> If you pass an object, it is in effect assigned to the parameter. This
>means that it
>> requires a copy constructor be defined; if one is not, you can't pass the
>object by value,
>> only by reference/pointer.
>>
>> You have the calling sequence wrong. It pushes the rightmost argument onto
>the stack, then
>> the next-to-rightmost, and so on, right-to-left, and only THEN does it
>push the return
>> address onto the stack
>>
>> f(a, b, c)
>>
>> consists of
>> push c
>> push b
>> push a
>> call f
>> add sp,12
>>
>> with a CDECL linkage. It is the call instruction that pushes the return
>address on the
>> stack. The call instruction does the push and sets the IP to the address
>f.
>>
>> Suppose I had a struct, e.g., a POINT, then I would have
>>
>> f(a, b, pt)
>>
>> push pt,y
>> push pt.x
>> push b
>> push a
>> call f
>> add sp, 16
>>
>> Now this is just symbolic of what happens; the compiler may do a couple
>push instructions,
>> or it may do a movs. So the function
>>
>> void f(int a, int b, POINT pt)
>>
>> will have the formal pt reference the address of pt.x, on the stack. But
>note that with a
>> copy constructor, the sequence to push a copy of the object onto the stack
>can be
>> arbitrarily complex.. It indeed pushes the complete object (the exception
>is arrays, where
>> the value of the array is always the address of the first element, and
>arrays are always
>> passed by reference).
>>
>> class Thing {
>> public:
>> char t[100];
>> };
>>
>> Thing t;
>>
>> void SomeFunction(Thing t)
>>
>> will get a complete copy of the object pushed onto the stack, and consume
>100 bytes of
>> stack space (don't pass large objects; besides being SLOW, it consumes a
>lot of stack
>> space, which might become an issue if the objects are really large, or
>your stack calls
>> are very deep)
>>
>> Turn on an assembly listing and study the code that is produced. Most
>compilers have an
>> option to see the generated code. It might look pretty ugly on RISC
>machines, because RISC
>> machines typically will execute the instruction following a control
>transfer, e.g., if the
>> instructions equence is, to express it as if it were an X86,
>>
>> mov ax, 1
>> sub ax, c
>> jmp L
>> mul ax, 2
>>
>> L: mov n, ax
>>
>> then in a RISC architecture, this probably means that the value stored in
>n is (1 - c) *
>> 2. It gets even triciker when the control transfer is conditional, because
>the instruction
>> following the control transfer is still executed. But check your RISC
>documentaiton. The
>> last I looked, the MIPS 3000, IBM RISC 6000 (know as the PowerPC), SPARC,
>etc. all worked
>> this way, but I have been spared looking at RISC machines since 1990.
>> joe
>>
>> On Mon, 24 May 2004 11:36:14 +0530, "Jugs" <jagadish@suntecgroup.com>
>wrote:
>>
>> >Hi Joseph,
>> >
>> >Thanks for a very detailed reply.
>> >
>> >The code fragment I attached to my previous query was not taken from any
>of
>> >my projects, and I know how I could get over this problem. My intention
>was
>> >only to know how C++ works under the hood. I saw this behaviour some time
>> >back, and was very curious to know how this works, thats why I thought I
>> >will post it in some communities like this.
>> >
>> >Can you pl. tell me what happens when a class object is passed by value
>to a
>> >function. I know that, the copy constructor of that class will be invoked
>to
>> >create a new object( will use bit-bit copy, If one doesn't exists ). When
>a
>> >function is invoked, the caller pushes the return address to the stack
>> >first, then it will push all the arguments one by one( this depends on
>the
>> >calling convention ), and then it will jump to the address of the
>function
>> >to be invoked. Pl. correct me if I am wrong.
>> >Now my question is, when an object ( not the pointer/reference ) is
>pushed
>> >to the stack during a function call, what is actually getting pushed ?
>> >Is it the address of the object OR the complete object ?
>> >In case of the latter, if the size of the object is say 100, then it will
>> >eat 100 bytes of stack memory. Is this the right way of doing it ?
>> >
>> >I'm working in HP11i and using aCC compiler.
>> >
>> >Thanks and Regards
>> >Jugs
>> >
>> >"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message
>> >news:fs7sa01vvuq84korojv87h2iak4j3ub47h@4ax.com...
>> >> I'd say this code is so defective that it cannot possibly work.
>> >>
>> >> First, you are using malloc, which is always a Big Red Flag saying "I'm
>> >clueless". Get rid
>> >> of it and use 'new'. This is C++, not C, and should not be treated as
>C.
>> >Break obsolete
>> >> programming habits. malloc is one of them.
>> >>
>> >> Now let's look at what you did:
>> >>
>> >> You pass in a pointer to a variable that is set to NULL.
>> >>
>> >> You reassign that pointer to be a fixed size value. Since you are
>> >reassigning the pointer
>> >> which is the parameter to the function, it can have no possible impact
>on
>> >the variable
>> >> that was passed in as the parameter. This is elementary C parameter
>> >passing semantics. You
>> >> don't even need to understand C++.
>> >>
>> >> You return from the function, thus losing the only copy of that
>pointer,
>> >and having made
>> >> no change in the variable s.
>> >>
>> >> So what you see is exactly what you asked for.
>> >>
>> >> Now as to how to fix it, you COULD have written, using the C++
>"reference"
>> >parameter
>> >> notation,
>> >>
>> >> void Allocate( char * & s)
>> >> {
>> >> s = new char[100];
>> >> }
>> >>
>> >> although this is remarkably silly for a variety of reasons. First, it
>is
>> >allocating a
>> >> fixed size char array, which rarely makes sense in modern programming
>> >methodologies.
>> >> CString or std::string do not require fixed preallocations. In
>addition,
>> >it is very bad
>> >> style to assume that your app uses char * values for characters; in
>modern
>> >programming,
>> >> you would want to use TCHAR so that with a flip of a couple compiler
>> >options you could
>> >> build a Unicode app.
>> >>
>> >> It says that what you are getting is a REFERENCE to a char * variable.
>> >>
>> >> Second, it is substituting syntactic saccharine for a simple operation;
>> >why not do
>> >> s = new char[100];
>> >> or better still, if you want char and fixed allocation
>> >> char s[100];
>> >> which although represent construably horrible style, don't require
>calling
>> >a method whose
>> >> semantics are harder to figure out. The second one is by far
>preferrable,
>> >because it does
>> >> not require heap allocation.
>> >>
>> >> The best style would be
>> >> CString s;
>> >>
>> >> s = _T("Test");
>> >> this would be considered representative of Best Practice (I don't use
>> >std::string but
>> >> using that in an analogous fashion is another example of Best
>Practice).
>> >>
>> >> In a similar fashion, substituting a function called Allocate for a
>> >general object is
>> >> equally silly. Write
>> >> Base * obj = new Base;
>> >> and you have what you need, in an obvious fashion, in a way that is
>> >consistent with C++
>> >> usage. It feels very much like you are trying to transplant obsolete C
>> >methodologies (for
>> >> example, putting constructor code in a subroutine) to C++.
>> >>
>> >> Pointers are passed to a function in EXACTLY the same way primitive
>types
>> >are passed. For
>> >> example:
>> >>
>> >> int a = 7;
>> >> Add(a);
>> >>
>> >> void Add(int x)
>> >> {
>> >> x+= 1;
>> >> }
>> >>
>> >> would NOT change the value of a; it will increment the value of the
>> >parameter x, which is
>> >> then discarded upon return.
>> >>
>> >> Had you wanted to modify a, you would have written
>> >>
>> >> int a = 7;
>> >> Add(&a);
>> >>
>> >> void Add(int * x)
>> >> {
>> >> (*x)++;
>> >> }
>> >>
>> >> which means you could have written your code as
>> >>
>> >> void Allocate(char * * s)
>> >> {
>> >> *s = (char *)malloc(100);
>> >> }
>> >>
>> >> which, although it has all the major defects of your original
>philosophy,
>> >at least
>> >> performs the operation you expect. It would still represent horrendous
>> >style.
>> >>
>> >> I don't see how your example of allocating a Base object could possibly
>> >work, for the same
>> >> reason. It should also fail with the pointer being NULL. It would make
>> >sense to write
>> >> Base * s = new Base;
>> >> and get rid of the Allocate call entirely. It is a waste of characters.
>> >>
>> >> In general, in programming in C++ (and particularly in MFC), forget
>that
>> >strcpy, strcat,
>> >> and similar functions exist. They are so rarely needed that in the
>> >incredibly rare cases
>> >> where they make sense, the need to use them (or more likely, tcscpy and
>> >tcscat, so your
>> >> code is Unicode-aware) becomes obvious.
>> >> joe
>> >>
>> >>
>> >> On Fri, 21 May 2004 14:53:19 +0530, "Jugs" <jagadish@suntecgroup.com>
>> >wrote:
>> >>
>> >> >Hi,
>> >> >
>> >> >I just couldn't find any other forumn, where I can post my C++
>questions.
>> >So
>> >> >sorry for posting it here in the MFC discussion board.
>> >> >
>> >> >Pl. see the code below.
>> >> >
>> >> >void Allocate( char* s )
>> >> >{
>> >> >s = (char*)malloc( 100 );
>> >> >}
>> >> >
>> >> >int main( )
>> >> >{
>> >> >char* s = NULL;
>> >> >Allocate( s );
>> >> >strcpy( s,"Test"); //I know that this will fail. b'coz I still have a
>> >NULL
>> >> >pointer in s. Initially s was pointing to NULL, and from the function
>> >> >Allocate 100 bytes of memory was allocated in some memory location,
>and
>> >> >address of s was made to point to that location. But inside the main,
>s
>> >is
>> >> >still pointing to NULL. Pl. correct me if I am wrong.
>> >> >
>> >> >}
>> >> >
>> >> >Now my question is
>> >> >
>> >> >void Allocate( Base* s )
>> >> >{
>> >> >s = new Base;
>> >> >}
>> >> >
>> >> >int main( )
>> >> >{
>> >> >Base* obj = NULL;
>> >> >Allocate( obj );
>> >> >obj->some_member = 20; // this works...
>> >> >
>> >> >}
>> >> >
>> >> >So I assume, object pointers are passed to functions in a different
>way
>> >> >compared to primitive type pointers. Can anyone pl. comment on this.
>> >> >
>> >> >Regards
>> >> >Jugs...
>> >> >
>> >> >
>> >>
>> >> Joseph M. Newcomer [MVP]
>> >> email: newcomer@flounder.com
>> >> Web: http://www.flounder.com
>> >> MVP Tips: http://www.flounder.com/mvp_tips.htm
>> >
>>
>> Joseph M. Newcomer [MVP]
>> email: newcomer@flounder.com
>> Web: http://www.flounder.com
>> MVP Tips: http://www.flounder.com/mvp_tips.htm
>

Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm



Relevant Pages

  • Re: C++ Object Pointers
    ... push the return ... > address onto the stack ... The call instruction does the push and sets the IP to the address ... It might look pretty ugly on RISC ...
    (microsoft.public.vc.mfc)
  • Re: Some confusion on Stack operation
    ... By integrating 16-bit and 32-bit segments into a single protected-mode task. ... assembler to differentiate between 'push ax' and 'push eax'. ... instruction coding is the same. ...
    (alt.lang.asm)
  • Re: To Richard Heathfield: enoughs enough
    ... > A single CISC instruction that searches for a NUL byte takes Otime. ... language" in a typical machine with a typical stack. ... push intIndex1 ...
    (comp.programming)
  • Re: Some confusion on Stack operation
    ... Using A 16-Bit TSS with 32-Bit Constructs ... assembler to differentiate between 'push ax' and 'push eax'. ... instruction coding is the same. ... ELSE (* OperandSize = 16*) ...
    (alt.lang.asm)
  • Re: Porting VMS back to VAX ?
    ... >> I was never convinced that RISC was better than CISC until I experienced ... > What is the difference between CISC and RISC? ... complicated (the VAX polynomial instruction comes to mind but they had other ... sequence of microcode instructions internal to the CPU. ...
    (comp.os.vms)