Like Tree4Likes

Widening and Narrowing concepts

This is a discussion on Widening and Narrowing concepts within the C++ Programming forums, part of the General Programming Boards category; I haven't done C/C++ in a while, so I just randomly decided to get some confusion sorted out..Please look at ...

  1. #1
    The Dragon Reborn
    Join Date
    Nov 2009
    Location
    Dublin, Ireland
    Posts
    629

    Widening and Narrowing concepts

    I haven't done C/C++ in a while, so I just randomly decided to get some confusion sorted out..Please look at the code and comments below. Maybe you can answer some of the questions in there. Or correct opinions.
    Please, if you answer make your comments simple and concise. And less terminology, the usually confuse me. I appreciate this a lot thanks.

    Code:
    #include <iostream> 
    using namespace std ; 
    int main()
    {
        //widening values ; 
        {
            char a2 = 'A'; 
            int a1 = a2; //Compiler widens implicitly.  Nothing is lost.  Here it knows the data type of a2 hence can look up ascii data
            
            //cout << a1 ; // should get 65
        }
        //narrowing values 
        {
            int a1 = 65 ;
            char a2 = (int)a1 ; //narrowing occurs implicitly in C++, but to show it explicitly anyways.       
            //cout << endl << a2 ; //should get 'A'
        }
        
        //widening addresses
        {
            char a = 'A';
            char *a1 = &a ;
            int **a2 = (int**)&a1 ; //all the compiler knows is it is storing an address a2 will point to an integer type address. 
            int b=**a2 ; //It doesn't know that the address in a2 belongs to a char so will just will return the binary representation..
            cout << endl <<(char) b; //have to cast it to tell compiler that b actually HAS or POINTS to a character. Not an integer.  (char)b should print the character
            //if i printed this way: cout << endl << b sometimes I get 65 or random crap
            //This could explain why in polymorphism the compiler needs pointers and vtables to look at the correct object and function to call. Otherwise it might just believe that the type of object and functinon 
            //to call belongs to the base type. Without vtables it implicitly believes that its type is of base due to upcasting.
        }
        
        //widening addresses part
        {
            //i could do it this way
             char a = 'A' ;
            int *a1 = (int*)&a ; 
            cout << endl << (char)*a1 ;
            // if I printed it this way: cout << endl << *a1 sometimes I get 65 or random crap
            // I get 65, so it still doesn't look up the ascii tables because it genuinely believes a1 POINTS TO AN INT. EEJIT
            //The compiler still doesn't know that the actual type is a character because due to the cast, it believes that it is pointing to an integer..hence we get 65 as expected.
            //(char)a1 will solve the problem.
        }
        //narrowing
        {
            int a1 = 65; 
                
            char *s = (char*)&a1; 
            cout << endl << *s;  //So far either way I print I get correct values
        }
        //So what happens with Narrowing? I put a larger content to box that can hold little content. 32bytes >> 8bytes since 8bytes can hold up to 255 
        //no data is truly lost however the  compiler complains.
        
        //But with widening, sometimes when I print as an integer I get what I should expect for the binary representation of the character it points to..
        //otherwise I get random crap. Because the compiler sees that the pointer type is integer but is only pointing to a memory location of only 8bytes..
        //the compiler will do what it thinks is right, at the moment of printing (I believe it wouldn't widen the memory location permanenty after the cast) 
        //it adds an extra 3bytes to the memory location to make 4bytes or whatever the case is depending on the system. 
        //This extra space might contain lots of stack garbage of might be filled with 0? Hence sometimes I get correct value otherwise ........?
        //I do not know.
        cin.get() ;
        return 0 ;
    }
    You ended that sentence with a preposition...Bastard!

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    I'm pretty sure every comment you have under narrowing and widening addresses is wrong, and so is some of the code. (EDIT: Rereading, the stuff at the very bottom looks fine.)

    C++ doesn't do any widening or narrowing when dealing with pointers (all pointers, on just about any architecture currently still running, are the same size). The type only refers to how many bytes are fetched from the address; if the compiler is looking at an int* it will fetch four bytes (or maybe eight if you've got eight-byte ints); if it is looking at a char*, it will fetch 1. For instance, let's suppose you put this:
    Code:
    int a = 0x11223344;
    char *b = (char *)&a;
    cout << *b;
    You will see D on the screen, because the memory looks like this:
    Code:
         +--+--+--+--+
    a -> |44|33|22|11|
         +--+--+--+--+
    I'm assuming you're not on a Motorola chip or some other big-endian device. When you assign that address to a char*, nothing happens to the address, it still points to that 0x44 byte, which is what you get when you dereference the b pointer.

    Similarly:
    Code:
    char a = 'D';
    int *b = (int *)&a;
    cout << *b;
    is going to give you ... who knows what? The memory looks like:
    Code:
         +--+--+--+--+
    a -> |44|??|??|??|
         +--+--+--+--+
    The first byte we know, but the other three belong to other variables, or who knows what. So when we dereference that pointer as an int*, we are going to grab four bytes, of which only one byte is ours. Hence the value will change from run to run.
    Last edited by tabstop; 09-03-2011 at 12:21 PM.

  3. #3
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,452
    Whilst pointers are generally the same size on common architectures, what does vary is alignment.

    Typically, to access an n-byte type (say a 4-byte int), the address has to be a multiple of n (in this case 4).
    If you force an odd address (pointing to a char) and then try and dereference it as an int (through a cast to an int pointer), then you're in voodoo territory. Worst case, you get a bus error and your program is no more.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  4. #4
    The Dragon Reborn
    Join Date
    Nov 2009
    Location
    Dublin, Ireland
    Posts
    629
    Thanks guys for replying
    tabstop your little diagram helps make clear what I have been trying to understand.
    But I don't understand why you would say:
    I'm pretty sure every comment you have under narrowing and widening addresses is wrong, and so is some of the code.
    I just read a book on widening and narrowing which prompted me to ask this question.
    But I guess I was wrong in saying widening the addresses, is it more correct to say.

    If I have 2 pointers one points to a char and the other points to an int.
    If I force the char pointer to be a pointer to an int and store the return address in an int* variable. Whenever I use the int* variable to access data the
    contents or memory location rather than the address is padded with an extra 3bytes like your diagram shows. And since stack data is different on run time like you said it will show garbage.
    However, when it is the other way round. When I use the char* pointer to access the memory location that references an int, it just picks the first byte losing some data.
    And the first byte it gets depends whether it is on a big or small endian machine...

    @Salem,
    yeah, it is very risky doing that. Not even wise I guess...but this way I know what to do and not what to do.

    By the way is the stuff about the polymorphism correct?
    You ended that sentence with a preposition...Bastard!

  5. #5
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    Your original comments read to me (then, and now) as though you were expecting the original value to be fetched (using the "correct" data type) and then converted later, which isn't the case. If that's not what you meant, then we're ok.

    For polymorphism, the idea is that if you pass the object, then a copy is made of the object -- and if you pass it to a function expecting a base class object, that's the only thing that a copy is going to be made of (see "type slicing"). There is no derived class object available to the function, so polymorphism cannot happen. You need to prevent a copy being made, hence passing as a pointer or reference.

  6. #6
    The Dragon Reborn
    Join Date
    Nov 2009
    Location
    Dublin, Ireland
    Posts
    629
    Nah, what I meant was:
    the data is fetched according to the current data type accessing the value at that memory location, not the "correct" or "original" data type.

    Mmn your polymorphism answer confused me.

    There is no derived class object available to the function, so polymorphism cannot happen. You need to prevent a copy being made, hence passing as a pointer or reference.
    I'm not passing an object to a function in polymorphism, am I?

    I just assign a derived object to a base class, upcasting treats the derived object as if it was the base. But for the compiler to see that what the base pointer is pointing to or referencing isn't a base object or derived object..it needs to use pointers and vtables.

    From what I've read of java all that stuff is done for you.
    You ended that sentence with a preposition...Bastard!

  7. #7
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    Quote Originally Posted by Eman View Post
    I'm not passing an object to a function in polymorphism, am I?
    Well, that's the point, I think.

    Code:
    int this_function_requires_an_object(Object foo);
    int this_function_takes_a_pointer(Object *foo);
    If Derived is a derived class from object, then you can pass a derived object to the first function above; however, because it is pass-by-value, a copy of that Derived object is made for the function to use (so that changes made to the object inside the function do not propagate outside the function). Since the function wants an Object, an Object is made out of that Derived object (by removing all the bits that are "extra"). So the original Derived object is never used, only the Object.

  8. #8
    The Dragon Reborn
    Join Date
    Nov 2009
    Location
    Dublin, Ireland
    Posts
    629
    Quote Originally Posted by tabstop View Post
    Well, that's the point, I think.

    Code:
    int this_function_requires_an_object(Object foo);
    int this_function_takes_a_pointer(Object *foo);
    If Derived is a derived class from object, then you can pass a derived object to the first function above; however, because it is pass-by-value, a copy of that Derived object is made for the function to use (so that changes made to the object inside the function do not propagate outside the function). Since the function wants an Object, an Object is made out of that Derived object (by removing all the bits that are "extra"). So the original Derived object is never used, only the Object.
    Oh yes I get that..but we are talking about overloaded functions in this case, when we pass a derived object to a function that accepts base Object.upcasting occurs and like you said the member functions or data that derived object has...because it is "upcasted" for lack of a better word they will be removed if the base class doesn't have it ..to use the derived member functions we will have to downcast it.

    But what does this have to do with polymorphism hence my confusion.
    Polymorphism is ability to run the same function but with different implementations on a set of object that related to a common base type.

    In your example it is an overloaded function that takes a base class object using pass by pointer or value..
    Wait if we pass by pointer value..does the derived object get upcasted?
    Edit: I think it does, but it will lose those extra bits...
    You ended that sentence with a preposition...Bastard!

  9. #9
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    That's not an overloaded function in my example. That's just an example of a conversion that happens during a function call.

    My point was something like this. Suppose Object has a virtual function bob that is overloaded in Derived. Then from the above:
    Code:
    int this_function_requires_an_object(Object foo) {
        foo.bob(); //WILL call the foo from Object, since there is no chance of getting a Derived object to this point in the code
    }
    
    int this_function_takes_a_pointer(Object *foo) {
        foo->bob(); //will call Derived if a pointer-to-Derived is passed in
    }
    So if conversion happens (i.e. in the function call) then polymorphism cannot happen (because there's no longer a Derived object).

  10. #10
    The Dragon Reborn
    Join Date
    Nov 2009
    Location
    Dublin, Ireland
    Posts
    629
    So if conversion happens (i.e. in the function call) then polymorphism cannot happen (because there's no longer a Derived object).
    It cannot happen if I didn't pass the Object by pointer value. But why does it work when I pass it by pointer value? The compiler shouldn't be able to know that its original type is of derived..but it should "believe" it is the base type.

    That's another thing i'm confused about..
    when I did
    Code:
    //to keep things simple
         int a =1 ;
         char *b  = (char*)&a ;
    To get correct output using a I may have to cast the content of to an int.

    But for polymorphism using your example pass by pointer or this example...
    Code:
    Shape *a = new Circle()
    The compiler thinks a is pointing to a shape object (Circle is a Shape so I guess that is correct, but I mean base object Shape), yeah? But it uses vtables to make the correct function call..

    passing by pointer value..does the compiler think Object *foo is pointing to a base object, but because the function is declared virtual it will call the correct function.

    If so why is it that
    Shape a = circle() ; //can't remember how to declare it
    won't call the correct function, even though it is declared virtual? Why does it rely on the pointer or handle?

    I hope that was clear..but to iterate..

    if Shape *a = new Circle() ;
    and Shape a = Circle() ; the compiler thinks it is pointing to base class Shape object..and member function foo is virtual..how is it that base function foo gets called for Shape a but calls correctly for Shape *a
    Last edited by Eman; 09-03-2011 at 04:21 PM.
    You ended that sentence with a preposition...Bastard!

  11. #11
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    You probably believe that at the end of this line of code
    Code:
    Shape a = Circle();
    that a Circle object exists. That is untrue. The Circle constructor is called to create a temporary object; the copy constructor then copies the Shape base-object only to a, and then the temporary Circle object is destroyed.

    In the line
    Code:
    Shape *a = new Circle();
    a new Circle object is constructed, and then the pointer a is set to point to the Shape sub-base-object inside of the Circle. Because this Circle object exists, it can be queried at run-time to determine what type it is, which is what is necessary for polymorphism to happen. That querying only happens whenever you call a virtual function.
    Eman and iMalc like this.

  12. #12
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,638
    It cannot happen if I didn't pass the Object by pointer value. But why does it work when I pass it by pointer value? The compiler shouldn't be able to know that its original type is of derived..but it should "believe" it is the base type.
    Have you read about virtual method tables?


    That's another thing i'm confused about..
    when I did
    Code:
    //to keep things simple
         int a =1 ;
         char *b  = (char*)&a ;
    To get correct output using a I may have to cast the content of to an int.
    That doesn't have a whole lot to do with polymorphism. If you want to point at a byte but print the underlying integer you would need to cast back to int* to get the computer to look up all four bytes.

  13. #13
    The Dragon Reborn
    Join Date
    Nov 2009
    Location
    Dublin, Ireland
    Posts
    629
    Quote Originally Posted by tabstop View Post
    You probably believe that at the end of this line of code
    Code:
    Shape a = Circle();
    that a Circle object exists. That is untrue. The Circle constructor is called to create a temporary object; the copy constructor then copies the Shape base-object only to a, and then the temporary Circle object is destroyed.
    Oh wow, yeah..I thought that the Circle object existed at that point...that makes sense now.

    Have you read about virtual method tables?
    Yes, but that was what I was wondering..why doesn't it work if I sent just object value..it doesn't process the vtables. And tabstop answered that. I thought the Circle object existed, while it was only the Shape object that is returned.
    That doesn't have a whole lot to do with polymorphism. If you want to point at a byte but print the underlying integer you would need to cast back to int* to get the computer to look up all four bytes.
    But it has some? Upcasting and Downcasting might take place..and that will decide how many number of bytes are retrieved or what is retrieved. Another thing I was trying to understand.
    Last edited by Eman; 09-04-2011 at 01:40 AM.
    You ended that sentence with a preposition...Bastard!

  14. #14
    The Dragon Reborn
    Join Date
    Nov 2009
    Location
    Dublin, Ireland
    Posts
    629

    Wrote this this morning..

    Code:
    #include <iostream> 
    using namespace std ; 
    class Shape
    {
        public: 
            virtual void draw() 
            {
                cout << "\nDraw a Shape!" ; 
            }
        
    };
    class Square:public Shape
    {
        public: 
            virtual void draw()
            {
                cout << "\nDrawing a square!" ; 
            }
    };
    class Triangle:public Shape
    {
        public:
            virtual void draw()
            {
                cout << "\nDrawing a circle!" ; 
            }
    };
    int main()
    {
        Shape a  =  Square();
              
        Square s = (Square)a; //It wouldn't let me do this
        
        
        Shape *b = new Square();
        
        Square *t = (Square*)b ;//however it lets me do this
        //It wouldn't let me do this
        Square q = (Square)*b ; //The square objects exist...why does the cast not work? 
        
        return 0 ;
    }
    Code:
    Square s = (Square)a; //It wouldn't let me do this
    Why won't it let me downcast? Same as this:
    Code:
    Square q = (Square)*b
    But this works?

    Code:
     Square *t = (Square*)b ;//however it lets me do this
    Hypocritical, I think.
    EDIT:
    I realize that doing Square q = (Square)*b is the same as Square s = (Square)a..but in the former..*b points to an existing Circle object..so it should've worked
    Last edited by Eman; 09-04-2011 at 02:56 AM.
    You ended that sentence with a preposition...Bastard!

  15. #15
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,295
    Because you're doing the same thing as was mentioned in tabstop's post. In your case 'a' is only a Shape, not a Square.
    You can't turn a Shape into a Square (unless there is a Square constructor that takes a shape, that is).

    'b' however, points to a Square so casting it to Square* is all fine and dandy. Bear in mind though that it doesn't know for sure that what you're telling it is correct. It just blindly casts the pointer and if it happened to point to a Hexagon say, then something will may go wrong later when you try and use it. The safer way to do that cast is to use dynamic_cast.

    With the last line assigning to q, you're dereferencing the Shape* pointer 'b', and so it thinks you're trying to copy a Shape into a Square again. The polymorphism only works so long as you are dealing with method calls through a pointer, or doing rtti queries with the pointer, but once you dereference to get the actual object then it will be of the type that the pointer says it is.
    Last edited by iMalc; 09-04-2011 at 03:21 AM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C++0x Concepts fall
    By Mario F. in forum General Discussions
    Replies: 32
    Last Post: 07-27-2009, 02:31 AM
  2. Need help with concepts for first game
    By blankstare77 in forum Game Programming
    Replies: 29
    Last Post: 10-01-2005, 08:53 AM
  3. Programming concepts
    By lyx in forum C++ Programming
    Replies: 2
    Last Post: 12-02-2003, 11:37 PM
  4. Java Concepts
    By mart_man00 in forum Tech Board
    Replies: 11
    Last Post: 09-04-2003, 05:14 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21