Thread: A stupid question...probably

  1. #1
    Registered User Phoenix_Rebirth's Avatar
    Join Date
    Dec 2005
    Location
    Cyprus
    Posts
    68

    A stupid question...probably

    A stupid question then...
    I was wondering, in C when you wanted to pass an argument in a function that would take the changes within the function and retain them (that is not a copy) you would pass a pointer to it.

    In C++ there is this by reference thing where when you declare your function you declare the argument in discussion something like this void f(int&....) right? Then if you change that int in the function when you return to the main program it has also changed... (Personally i think it;s a lot easier than the pointers so I was wondering why the two was of refernce in C++ if someone can answer me in this as well...)

    The stupid question. If I have a pointer to struct and I want to pass it in an argument I would probably do it like a pointer to pointer in C - which makes it more confusing - so can i pass it like this in C++ void f(node*&, ....)

    Actually I tried it and I think it worked although I didnt check it out thoroughly cause I was doing something else and I lost that program. So now I am doing something else where I work with pointers and if this thing works it would help that having a function with an argument like this node*** (I have a pointer which I pass into a function and then on to another function)

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Ok, let's answer your "stupid" question first:

    If you have a function like this:
    Code:
    struct node {
       ....
    };
    
    void modify_node_ptr(struct node **ptr)
    {
        *ptr = some_other_pointer;
    ...
    }
    ....
        struct node *ptr;
    ...
        modify_node_ptr(&ptr);
    ...
    So to get a pointer to a pointer, you just have a pointer type and take the address of it with the & operator.

    The C++ principle of references is just pointers in disguise - the compiler will translate a reference into a pointer behind the scenes. It makes a difference when it comes to how you use the variable that is a reference, vs. the use of a pointer (e.g. *ptr = something, ref = something, ptr->someitem, ref.someitem), but there is really no difference in the machine code generated by the compiler.

    Edit, and yes: you can use func(node * &ptr) in C++ if you like - it would be a little bit confusing, but sure, you can do that.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User Phoenix_Rebirth's Avatar
    Join Date
    Dec 2005
    Location
    Cyprus
    Posts
    68
    Aha, thanks a lot, actually I was a little confused because I know that the operators * and & are contrary... Anyway thanks for the explanation

  4. #4
    Registered User
    Join Date
    May 2006
    Posts
    630
    Quote Originally Posted by matsp View Post
    you can use func(node * &ptr) in C++ if you like
    If you use this, do you change *ptr the same way as in your code example?

    Like:

    *ptr = some_other_pointer;

  5. #5
    Registered User Phoenix_Rebirth's Avatar
    Join Date
    Dec 2005
    Location
    Cyprus
    Posts
    68
    No I use it like everywhere else. For instance
    Code:
    int main() {
    ....
    node* p = new node();
    ...
    f(p);
    ...
    }
    
    void f(node*& q){
    ...
    q= some other node*
    
    }

  6. #6
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937
    Also note that a reference is not always a pointer. It is, quite literally, an alias, and so in many cases does not involve a pointer.
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> If you use this, do you change *ptr the same way as in your code example?

    No. You would use ptr = some_other_pointer;

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by CodeMonkey View Post
    Also note that a reference is not always a pointer. It is, quite literally, an alias, and so in many cases does not involve a pointer.
    Which means, basically that in this example:

    Code:
    int x = 0;
    int &y = x;
    y = 1;
    The compiler is not necessarily going to create a pointer to x and then dereference it on the line "y = 1." It is trivial to analyze the situation and see that you can just set x directly. No pointer is necessarily involved.

    This example seems contrived, but when you get into situations with nested inline functions a lot of references can just "boil away." They are NOT pointers.

  10. #10
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >but when you get into situations with nested inline functions a lot of references can just "boil away."
    It's not even that specialized. There are tons of times when references can just become the original object, depending on how the compiler handles symbols.
    My best code is written with the delete key.

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by brewbuck View Post
    Which means, basically that in this example:

    Code:
    int x = 0;
    int &y = x;
    y = 1;
    The compiler is not necessarily going to create a pointer to x and then dereference it on the line "y = 1." It is trivial to analyze the situation and see that you can just set x directly. No pointer is necessarily involved.

    This example seems contrived, but when you get into situations with nested inline functions a lot of references can just "boil away." They are NOT pointers.
    True - in the case of inline functions the "reference" becomes the oriignal variable with a different name. But then that can be said about other inline cases too - I'm pretty sure that if you do:
    Code:
    int foo(int *x) {
      *x = 7;
    }
    
    int main()
    {
        int a;
        foo(&a);
    }
    the compiler may well not produce any pointer to a at all - because it can analyze the situation and realize that it can just set a to 7.

    So, when references are really used (and not optimized away) they are essentially pointers.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    So, when references are really used (and not optimized away) they are essentially pointers.
    If that's taken as true, then they are pointers that are always valid. They're never NULL or out-of-scope or dangling. At least, not very often. You could probably get a dangling reference if you tried hard enough.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by dwks View Post
    If that's taken as true, then they are pointers that are always valid. They're never NULL or out-of-scope or dangling. At least, not very often. You could probably get a dangling reference if you tried hard enough.
    Not VERY hard either:
    Code:
    class something 
    {
    public:
       somememberfunc();
    ....
    };
    
    void somefunc()
    {
       something *ptr;
       something &ref;
       
       ptr = new something;
       ref = *ptr;
       delete ptr;
       ref.somememberfunc();
    }
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    I don't think that will compile. Don't references have to be initialized upon declaration?

    But something like this would work:
    Code:
    void somefunc()
    {
       something *ptr;
       
       ptr = new something;
       something &ref = *ptr;
       delete ptr;
       ref.somememberfunc();
    }
    References are never NULL, at least.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  15. #15
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    References, unlike pointers, aren't objects. That's very important when you argue about the legality of programs under the strict terms of the standard - in particular, whether a program violates the ODR or not.

    A trick used in some Boost libraries depends on this. Let's see if I remember.

    The problem is this: consider a library that requires some complex, predefined object to be available to the programmer. Let's say, Boost.Lambda and its placeholders, _1, _2 etc.
    Lambda is a header-only library. There are no source files to place definitions in. This is a problem, because objects have to be defined. But if you place the definitions in header files, you'll get redefinition linker errors.
    No problem, you may think, I'll just use an unnamed namespace (you've considered making the objects static, too, but static is just an inferior method of achieving the same; the entire below discussion applies to static stuff, too), and all problems will go away:
    Code:
    namespace boost { namespace lambda {
    namespace {
      very_special_type _1;
      very_special_type _2;
    }
    }}
    Right?

    Not right. Aside from several compilers having trouble with unnamed namespaces in header files due to precompiled headers, there's also a language standard issue. What if the user (or the library, for that matter) has an inline function in a header?
    Code:
    inline void foo()
    {
       namespace ll = boost::lambda;
       some_algorithm(some_parameters, ll::_1 + ll::_2);
    }
    This looks legal. Only, it isn't.

    With inline functions, the compiler (or linker, but the language definition calls it all the implementation) has to accept that every file contains a definition of the function. It has to collapse these definitions, and use just one. However, that means that all definitions must be exactly the same.

    They aren't. Let's review what we know about unnamed namespaces.
    Code:
    namespace
    {
      declarations;
    }
    This is equivalent to
    Code:
    namespace unique_name {}
    using namespace unique_name;
    namespace unique_name
    {
      declarations;
    }
    where unique_name is a name that is unique to each translation unit. This means that, since code outside the translation unit can't recreate the name, they cannot refer to elements of the namespace (except indirectly, through pointers, references and template parameters).

    The net effect of this is that in translation unit 1, the placeholder objects are boost::lambda::unique1::_1 etc., whereas in translation unit 2, the placeholder objects are boost::lambda::unique2::_1 etc. The inline function foo(), present in both translation units, refers to the unique1 variants of the placeholders in translation unit 1, to the unique2 variants in the other. In other words, the definition of foo() differs between the translation units; the ODR has been violated.

    Does it matter in the practical world?

    Apparently. Boost enforces a rule that unnamed namespaces are disallowed in their headers. There's a real reason for that, not just some language lawyer stuff. We've had problems, though I can't remember what they were.

    Sooooo ... where do references come in?

    Well, if we can't use unnamed namespaces, we've got to find another way. There's another thing that the compiler collapses between translation units, and that's templates. You can put a template definition in a header file - in fact you have to - and the compiler will accept it, no matter in how many files the definition is. So you could declare the placeholders like this:
    Code:
    template <typename T>
    struct placeholders
    {
      static const very_special_type _1;
      static const very_special_type _2;
    };
    template <typename T>
    const very_special_type placeholders<T>::_1;
    template <typename T>
    const very_special_type placeholders<T>::_2;
    OK, but now the syntax is awkward. Not only does client code have to refer to the placeholders as, for example, placeholders<int>::_1, but you actually have to enforce the same type, because otherwise the objects aren't the objects your library uses, and it all wouldn't work.
    The second is easy to deal with.
    Code:
    typedef placeholders<int> args;
    Now it's args::_1 and args::_2. Better. Still, not quite satisfactory.

    And now references come in. We go back to the unnamed namespace.
    Code:
    namespace
    {
      const very_special_type &_1 = placeholders<int>::_1;
      const very_special_type &_2 = placeholders<int>::_2;
    }
    Now _1 and _2 can be used plainly.

    Because references aren't objects, they're just object aliases. And in the definition of an inline function, the actual objects count, which are the same for all translation units, even if the references are different.

    Admittedly, it's a grey area of the standard. You can make a case for both interpretations. But that's enough - together with the fact that the real-world problems didn't occur with this solution - to make Boost accept this variant.


    Phew, that was long and useless. But it should show that there are differences between pointers and references beside syntax.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Stupid Question Probably
    By Kyrin in forum C Programming
    Replies: 2
    Last Post: 05-07-2006, 12:51 AM
  2. Replies: 7
    Last Post: 11-04-2005, 12:17 AM
  3. Stupid Question
    By digdug4life in forum C++ Programming
    Replies: 22
    Last Post: 05-17-2005, 11:43 AM
  4. stupid, stupid question
    By xelitex in forum C++ Programming
    Replies: 5
    Last Post: 12-22-2004, 08:22 PM
  5. Stupid question: What does Debugger do?
    By napkin111 in forum C++ Programming
    Replies: 6
    Last Post: 05-02-2002, 10:00 PM