ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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?
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"...
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.
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.
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 ....
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
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. :-)
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.
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?
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.
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
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
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.
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".
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.