LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 04-05-2009, 03:44 AM   #1
Dinithion
Member
 
Registered: Oct 2007
Location: Norway
Distribution: Slackware 14.1
Posts: 446

Rep: Reputation: 59
C++ pointers


This is probably a long shot, but the people in here are really smart so I will post the question anyway.

Background: I'm currently enrolled in a computer programming course. It is really basic and it doesn't really go in depth to pointers. I find that a bit unprofessional so I thought it would be a good idea to do it my self. I have this code:

Code:
//Simple program to test out pointers and dynamic memory.

#include <iostream>
using namespace std;
void additem(int * accounts, int &loop); //Function to add an entry
void expand(int * accounts, int &loop);  //Function to expand the array

int main() {
        int loop = 1;
        int * accounts = new int[loop];

        //Runs until the input is '.'. (Modified to make debug easier).
        do {
                additem(accounts, loop);
        } while (loop < 5);
        delete[] accounts;
}

void additem(int * accounts, int &loop) {
        int input;
        cout << "Enter a number: ";
        cin >> input;
        accounts[loop-1] = input; //Adding the input to the array
        expand(accounts, loop); //Resizing the array

}

void expand(int * accounts, int &loop) {
        int * acctmp = new int[loop]; //Temporary array to hold the original array
        for (int i = 0;i < loop;i++) {
                acctmp[i] = accounts[i];
        }

        //Resizing original array
        delete[] accounts;
        accounts = new int[loop+1];

        //copying back the content
        for (int i = 0;i < loop;i++) {
                accounts[i] = acctmp[i];
        }
        delete[] acctmp;

        loop++;
}
This code worked fine until I split them up from one main function to three functions. The function compiles fine, but when I run the program it works to expand it a few times, but for some reason I get an error after adding 4 items. (Buffer overflow or something?)

Now, keep in mind that i'm a complete novice, so the code is poor. But does anyone out there see the reason for this error?
 
Old 04-05-2009, 06:50 AM   #2
jf.argentino
Member
 
Registered: Apr 2008
Location: Toulon (France)
Distribution: FEDORA CORE
Posts: 493

Rep: Reputation: 50
Hello,

You're handle the right way how to pass the "loop" argument to be modified into the function, why not doing the same way for "accounts"?
In fact, you pass a pointer (an array in your case) to modify its contents, but you want to modify the pointer itself since you reallocate it in your "expand" function, but as soon as your going out of the "expand" function, the modification is lost since in C (and C++), a function works on copy of the arguments you pass to. To modify the arguments you need to pass them as reference (like you've done for the loop argument), or you can pass the address of the argument...

Since you're a beginner, i make other remarks:
-there's another bug in your code: you don't return anything at the end of your "main", if you're using a descent compiler, it musts warn you, and you must take in account the warnings.
-you're assuming the "new" is successful, and this is not a good practice, you have to test every function return, and handling errors (with exception in C++ or by returning an error code from your function, that must be tested be the caller).
-I don't like the "functional" organization you've made, first of all I'll separate the UI part from the functional part, then not make a function which call another function, but calling the 1st function then the 2nd one: "one function for one job no more"...

Welcome to the beautiful world of programming
 
Old 04-05-2009, 08:12 AM   #3
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by jf.argentino View Post
To modify the arguments you need to pass them as reference
A beginner might not know how to take a pointer by reference.

It is done like this:
Code:
void additem(int *& accounts, int &loop) {
...
Code:
void expand(int *& accounts, int &loop) {
There are much easier ways to expand an allocation if you insist on coding your own. But your code would be even easier and more readable if you used std::vector

I expect you want to figure such things out for yourself, so I won't go into further detail here.
 
Old 04-05-2009, 08:19 AM   #4
dmail
Member
 
Registered: Oct 2005
Posts: 970

Rep: Reputation: Disabled
Quote:
It is really basic and it doesn't really go in depth to pointers. I find that a bit unprofessional so I thought it would be a good idea to do it my self.
Well as you point out by yourself it is basic course. Whilst pointers are something that needs to be mastered your programme could be written in a way which requires no user calls to new or delete, which is maybe what they will teach you.
As jf.argentino has identified your problem I will comment on his further remarks.
Quote:
-there's another bug in your code: you don't return anything at the end of your "main", if you're using a descent compiler, it musts warn you, and you must take in account the warnings.
This is not a bug and will result in no warnings as it is valid C++ and a default return 0 will be used.
Quote:
-you're assuming the "new" is successful, and this is not a good practice, you have to test every function return, and handling errors (with exception in C++ or by returning an error code from your function, that must be tested be the caller).
For this small example this is also perfectly valid. A new call which fails will throw an std::bad_alloc exception which will propagate up the call stack. If user code does not handle it then it will call abort (maybe exit I can not recall at the present time). If it were caught what would the code do? There is no memory which can be released and the new call tried again, so you would just call exit yourself.

Last edited by dmail; 04-05-2009 at 08:20 AM.
 
Old 04-05-2009, 08:35 AM   #5
klytu
Member
 
Registered: Mar 2004
Location: Eastern United States
Distribution: Ubuntu
Posts: 65

Rep: Reputation: 15
Quote:
Originally Posted by johnsfine View Post
A beginner might not know how to take a pointer by reference.

It is done like this:
Code:
void additem(int *& accounts, int &loop) {
...
Code:
void expand(int *& accounts, int &loop) {
There are much easier ways to expand an allocation if you insist on coding your own. But your code would be even easier and more readable if you used std::vector

I expect you want to figure such things out for yourself, so I won't go into further detail here.
I agree that std::vector is the way to go, but a beginner might not have had that yet. Incidentally, when I was trying to get the OP's code to work, I had tried the code you indicate below before you had posted it:

Code:
void additem(int *& accounts, int &loop) {
...
Code:
void expand(int *& accounts, int &loop) {
But it wouldn't compile! I was in the midst of rummaging through my old C++ textbook and checked back to see if anyone had posted the correct syntax for passing a pointer by reference.

EDIT: Stupid me ... I had forgot to update the initial function declaratations. Of course it compiles!

Last edited by klytu; 04-05-2009 at 08:48 AM. Reason: I'm stupid ....
 
Old 04-05-2009, 08:42 AM   #6
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by klytu View Post
But it wouldn't compile! I was in the midst of rummaging through my old C++ textbook and checked back to see if anyone had posted the correct syntax for passing a pointer by reference.
Sorry, I should have mentioned that each of those two changes is needed twice: First in the declaration of the function, then in the definition. Earlier I just showed the change in the definition. The declarations would look like:

Code:
void additem(int *& accounts, int &loop); //Function to add an entry
void expand(int *& accounts, int &loop);  //Function to expand the array
 
Old 04-05-2009, 08:52 AM   #7
klytu
Member
 
Registered: Mar 2004
Location: Eastern United States
Distribution: Ubuntu
Posts: 65

Rep: Reputation: 15
Quote:
Originally Posted by johnsfine View Post
Sorry, I should have mentioned that each of those two changes is needed twice: First in the declaration of the function, then in the definition. Earlier I just showed the change in the definition. The declarations would look like:

Code:
void additem(int *& accounts, int &loop); //Function to add an entry
void expand(int *& accounts, int &loop);  //Function to expand the array
Yeah, I eventually figured that out. I'm just an idiot for not doing it right the first time. :-)
 
Old 04-05-2009, 08:57 AM   #8
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by Dinithion View Post
keep in mind that i'm a complete novice, so the code is poor.
I'm trying to keep that in mind and ignore aspects of the code that are merely poor rather than wrong. But I think the following merely poor chunk of code may indicate you have a serious misunderstanding of the flexibility of pointers:
Code:
        int * acctmp = new int[loop]; //Temporary array to hold the original array
        for (int i = 0;i < loop;i++) {
                acctmp[i] = accounts[i];
        }

        //Resizing original array
        delete[] accounts;
It is perfectly valid to have two pointers point to the same object. You don't need to delete through the same pointer that received the result of "new". You should keep in mind some rule for which pointer "owns" the object so you can be careful that only one pointer is used to delete and be sure no other pointer references the object after it is deleted. But you don't need to informal the compiler which pointer "owns" the object.

So that chunk of code I just quoted could have been simply:
Code:
int * acctmp = accounts;
That change does not require any changes to the rest of expand().

There is no need to allocate, copy, and delete the original. You can simply copy the pointer. In your mind (not in the code) the "ownership" of the object transfers. So it will not subsequently be a memory leak when accounts gets assigned a new object without deleting its old one, and it will be required (and is already in your subsequent code) that the object "owned" by acctmp is deleted before acctmp goes out of scope.

Last edited by johnsfine; 04-05-2009 at 08:59 AM.
 
Old 04-05-2009, 04:31 PM   #9
Dinithion
Member
 
Registered: Oct 2007
Location: Norway
Distribution: Slackware 14.1
Posts: 446

Original Poster
Rep: Reputation: 59
Alright! This is above all expectations

To answer some questions. My teacher told that pointers always got passed by reference, so I guess I misunderstood him at that point. When it come to vectors, it is not part of the course, but being a geek as I am, I have already experimented with vectors. However, in this particular task I intended to get a better understanding of pointers and how to use them more practically.


Exceptions is part of the course, but we haven't started with them yet. I haven't really tried to use them on my own either. I'm looking forward to this subject as it seems really useful to make a robust programs.

If I understand you right, johnsfine, I can use:
int * acctmp = accounts;
to create a new pointer starting at the same physical address in ram and ending at the same spot, as a replacement for my old ineffective loop?
 
Old 04-05-2009, 05:05 PM   #10
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by Dinithion View Post
My teacher told that pointers always got passed by reference, so I guess I misunderstood him at that point.
Do you now understand the difference between int* and int*& ?
Did you try that change (int* to int*& in those four places) and does it make your program work as expected?

Quote:
If I understand you right, johnsfine, I can use:
int * acctmp = accounts;
to create a new pointer starting at the same physical address in ram and ending at the same spot, as a replacement for my old ineffective loop?
I hope you understood, that part of your code was not "wrong" it just went to far more effort than was required.

I'm uncomfortable with your phrase "starting at the same physical address in ram and ending at the same spot". A pointer just points to a single address.

That "new" operator allocates an array of ints. The array of ints has a size as well as a starting address. But that size is stored somewhere invisible to your C++ code. The delete operator you call later can get that size given just the address. But the code in between cannot get that size. Just the starting address is put in accounts when you say
accounts = new int[loop];
Just the starting address is copied from accounts to acctmp when you say
int * acctmp = accounts;

Later when you say
accounts[i] = acctmp[i];
nothing checks whether either the memory pointed to be accounts or the memory pointed to by acctmp is large enough to be correctly indexed by i. The compiler simply trusts that you knew what you were doing. In fact, the way that was coded (with or without the simplification I suggested) those memory areas are large enough (because your code keeps the size in "loop" all the time and doesn't access with i>=loop.
 
Old 04-05-2009, 05:54 PM   #11
Dinithion
Member
 
Registered: Oct 2007
Location: Norway
Distribution: Slackware 14.1
Posts: 446

Original Poster
Rep: Reputation: 59
Quote:
Originally Posted by johnsfine View Post
Do you now understand the difference between int* and int*& ?
I understand the difference between pass by value and pass by reference when it comes to variables. But I cant see the difference between int* and int*&.


Quote:
Originally Posted by johnsfine View Post
I hope you understood, that part of your code was not "wrong" it just went to far more effort than was required.
Yes, that was clear, and that's the kind of experience I'm trying to learn. But in order to get real good I assume I have to read sources from other projects and try to understand what they are doing.

Quote:
Originally Posted by johnsfine View Post
I'm uncomfortable with your phrase "starting at the same physical address in ram and ending at the same spot". A pointer just points to a single address.

That "new" operator allocates an array of ints. The array of ints has a size as well as a starting address. But that size is stored somewhere invisible to your C++ code. The delete operator you call later can get that size given just the address. But the code in between cannot get that size. Just the starting address is put in accounts when you say
accounts = new int[loop];
Just the starting address is copied from accounts to acctmp when you say
int * acctmp = accounts;

Later when you say
accounts[i] = acctmp[i];
nothing checks whether either the memory pointed to be accounts or the memory pointed to by acctmp is large enough to be correctly indexed by i. The compiler simply trusts that you knew what you were doing. In fact, the way that was coded (with or without the simplification I suggested) those memory areas are large enough (because your code keeps the size in "loop" all the time and doesn't access with i>=loop.


Ok. Thanks for pointing this out. This is really interesting. The book doesn't mention this. I appreciate your help.
 
Old 04-05-2009, 08:39 PM   #12
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by Dinithion View Post
I understand the difference between pass by value and pass by reference when it comes to variables. But I cant see the difference between int* and int*&.
I'm not sure I can explain it any better than I already have. But I'll try.

The int* variable "accounts" is just a single number (like the int variable "loop"). "accounts" is not an array of ints. It is just the address of that array.

If you take that into a function as int* that will pass a copy of the address, so the function would be able to modify the contents of the int array "accounts" points to, but wouldn't be able to modify "accounts" itself, so it wouldn't be able to correctly re allocate the array elsewhere (as your expand function does).

If you take that into a function as int*& that will pass a reference to "accounts" itself, so the function can modify "accounts".
 
Old 04-06-2009, 04:35 AM   #13
Dinithion
Member
 
Registered: Oct 2007
Location: Norway
Distribution: Slackware 14.1
Posts: 446

Original Poster
Rep: Reputation: 59
Ok, I understand the difference now. Thanks for helping me out.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Pointers in C Goblin_C_Noob Programming 45 05-07-2008 05:57 PM
pointers erat123 Programming 8 06-14-2007 11:54 PM
pointers in C studentlb Programming 6 11-27-2006 02:53 PM
pointers in c++ marios_auth Programming 1 06-16-2004 08:20 AM
need help with pointers qanopus Programming 8 02-03-2003 05:09 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:05 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration