I am learning c++ pointers. I wrote some code to see if I understood correctly.
It might look a little weird. I made it a little complex on purpouse. Could you please tell me If I understood correctly? Or if there is something wrong that I didn't understand?
#include <iostream>
using namespace std;
int main()
{
int **x = new int*[20];
*x = new int [5]; //x[0] ==>[0,0,0,0,0]
*(x+1) = new int [3]; //x[1] ==>[0,0,0]
*x[0] = 10; //x[0][0] = 10
*(x+1)[0] = 20; //x[1][0] = 20
cout << x[0][0] << endl;
cout << x[1][0] << endl;
delete [] *x;
delete [] *(x+1);
delete [] x;
return 0;
}
Due to operator precedence, the lines
*x[0] = 10;
*(x+1)[0] = 20;
are equivalent to
*(x[0]) = 10;
*((x+1)[0]) = 20;
I am not sure whether you meant that. IMO, it will be better to use:
(*x)[0] = 10;
(*(x+1))[0] = 20;
As best as I'm able to determine, your code is correct with respect to what your comments expect is happening, prevents memory leaks, and is free of Undefined Behavior, which is all fine and good.
But consider, for a second, how much unnecessary boilerplate code you had to write to make this work. And then consider the following code using std::unique_ptr, which is exactly identical to your code in terms of behavior, but removes the boilerplate that is responsible for deleting the memory, by using a class which handles that behavior automatically:
#include<memory>
int main()
{
auto x = std::make_unique<std::unique_ptr<int[]>[]>(20);
x[0] = std::make_unique<int[]>(5); //x[0] ==>[0,0,0,0,0]
x[1] = std::make_unique<int[]>(3); //x[1] ==>[0,0,0]
*x[0] = 10; //x[0][0] = 10
//This will no longer compile because, while technically correct, this kind of pointer arithmetic
//is inherently unsafe and prone to mistakes
//*(x+1)[0] = 20; //x[1][0] = 20
x[1][0] = 20;
cout << x[0][0] << endl;
cout << x[1][0] << endl;
//No more deletes needed; `std::unique_ptr` manages its own memory and deletes when needed
return 0;
}
Now consider the next code, which simplifies further by using a more robust idiom for dynamically-sized arrays, std::vector:
#include<vector>
int main()
{
std::vector<std::vector<int>> x(20);
x[0].resize(5); //x[0] ==>[0,0,0,0,0]
x[1].resize(3); //x[1] ==>[0,0,0]
x[0][0] = 10;
x[1][0] = 20;
cout << x[0][0] << endl;
cout << x[1][0] << endl;
return 0;
}
This is a pretty clear case-study in why, for most purposes, you should prefer things like std::unique_ptr<T[]> or std::vector<T> for describing "Arrays of T". And for 2d arrays, std::unique_ptr<std::unique_ptr<T[]>[]> or std::vector<std::vector<T>>, which describe the same semantics as your original code.
The only incorrect thing is, new int[] does not initialize its memberes. After *x=new int[5], x can be {123,456,789,-123,-456} or anything else.
Related
I'm learning c++ using the book:Programming Principles and Practice using C++ by Bjarne Stroustrup.
In Chapter 19, exercise 1
implement strdup() functions which will copy a c strings into another using only de-referencing method (not subscripting).
My copying doesn't print anything I've been look for answers for days.
Please anyone can help me?
Below is the entire code:-
#include <iostream>
using namespace std;
char* strdup(const char* q) {
// count the size
int n {0};
while(q[n]) {
++n;
}
// allocate memory
char* p = new char[n+1];
// copy q into p
while(*q) {
*p++ = *q++;
}
// terminator at the end
p[n] = 0;
return p;
}
int main()
{
const char* p = "welcome";
cout << "p:" << p << endl;
const char* q = strdup(p);
cout << "q:" << q << endl;
// check to see if q get new address
cout << "&p:" << &p << endl;
cout << "&q:" << &q << endl;
return 0;
}
using only de-referencing method (not subscripting)
So this is already wrong, because is uses the subscript operator []:
// count the size
int n {0};
while(q[n]) {
++n;
}
I just don't know how to turn the pointer back to the first char.
Well, there are two basic approaches:
stop damaging your original pointer in the first place. You can introduce new variables, remember?
char* p_begin = new char[n+1];
char* p_end = p_begin + n + 1; // copy the terminator too
for (char *tmp = p_begin; tmp != p_end; *tmp++ = *q++) ;
return p_begin;
you know exactly how far to move p to get back to the original value, because you already calculated how long the string is!
while(*q) {
*p++ = *q++;
}
*p = 0; // you already moved p and aren't supposed to be subscripting anyway
return p - n;
Finally, you can get the size without subscripting using exactly the same technique: either you use a temporary variable to find the terminator, or if you advance q as you go, then subtract n from it again at the end.
Oh, and if you're having trouble visualizing the values of all your variables - learn to use a debugger (or just add lots of print statements). You need to understand how your state changes over time, and watching it is much more helpful than just observing the result of a black box and trying to guess what happened inside.
Replace this:
while(*q) {
*p++ = *q++;
}
..with this:
for (int i = 0; i < n; i++) {
p[i] = q[i];
}
Problem solved.
I want to create a std::vector<float> vpd which will be a reference to float*.
float * old = new float[6];
for (int i = 0; i < 6; i++)
{
old[i] = i;
}
vector<float> vpd(6);
auto refasd = &*vpd.begin();
*refasd = *old;
vpd[0] = 23;
cout << old[0] << endl;
How should I modify the code, if I want get 23 from cout?
You can't. std::vector is not designed to take ownership of a raw pointer.
Maybe you can make do with std::unique_ptr<float[]>, but the better solution is to directly use std::vector.
As alternative, you might use std::span (C++20)
float* old = new float[6];
std::iota(old, old + 6, 0);
std::span<float> vpd(old, 6);
vpd[0] = 23;
std::cout << old[0] << std::endl;
delete[] old;
You could also create a vector of std::reference_wrapper objects that refer to the original float array - may that be std::vector<float> or a newed float*. An example:
vector<std::reference_wrapper<float>> vpd(old, old + 6); // ¹
vpd[0].get() = 23.f;
cout << old[0] << endl; // prints 23
¹) Thanks to #StoryTeller for pointing out that vpd can be directly initialized.
As std::vector has its own memory structure you cannot map vector<float> or even vector<float*> to an array of float. However you can map each vector item to an array one.
float* old = new float[6];
for (int i = 0; i < 6; i++)
{
old[i] = i;
}
vector<float*> vpd(6);
int i = 0;
for (auto it = vpd.begin(); it != vpd.end(); it++)
{
*it = &old[i++];
}
*vpd[0] = 23;
*vpd[2] = 45;
cout << old[0] << endl << old[2] << endl;
Output
23
45
Depending on version of C++ you'll have some options (if I understand the problem):
if you have an older c++ version then c++11 I would in this case declear std::vector as:
// then you really need to make sure u delete the memory as well
std::vector<float*> vpd(6);
If however you got c++11 or higher I would use either std::share_ptr or std::unique_ptr depending on if you would like to share the memory space or not. Either this will ensure the memory is deleted by it self without having to do a "delete float*;" which is nice.
You can read about std::shared_ptr at: https://en.cppreference.com/w/cpp/memory/shared_ptr and std::unique_ptr at: https://en.cppreference.com/w/cpp/memory/unique_ptr
// For unique_ptr
std::vector<std::unique_ptr<float>> vpd(6);
// for std::shared_ptr
std::vector<std::shared_ptr<float>> vpd(6);
I would say that if you can then use unique_ptr rather then shared_ptr due to shared_ptr has som extra komplexitity within to be make sure the memory not used before deleteing memory space.
Ok so I am going to lay out two programs. Both are dynamic arrays using pointers and the new operator. But one doesn't seem to like the delete operator.
#include <iostream>
int main()
{
int *p;
p = new int[5];
for (int i = 0; i < 5; i++)
{
p[i] = 25 + (i * 10);
std::cout << p[i] << " ";
}
std::cout << std::endl;
delete [] p;
p = NULL;
return 0;
}
That's the first program. It likes the delete operator just fine. Now the program that dislikes the delete operator:
#include <iostream>
int main()
{
int x;
int *p;
p = new int[5];
*p = 4;
for (int i = 0; i < 5; i++)
{
std::cout << *p << " ";
x = *p;
p++;
*p = x + 1;
}
std::cout << std::endl;
delete [] p;
p = NULL;
return 0;
}
This program compiles just fine. But during execution, it throws an error - free(): invalid pointer: 0xfdb038 .. or whatever the memory address is for that particular execution. So, the question is:
Why can't the delete operator be used in the second case?
I don't want to have memory leak; I don't want the pointer to be dangling.
If I just say p = NULL;, then p = 0, but I believe the pointer is still dangling?, but I'm not sure. Thanks in advance.
Look at this loop in the second piece of code:
for (int i = 0; i < 5; i++)
{
std::cout << *p << " ";
x = *p;
p++;
*p = x + 1; // <--- Here
}
Notice that in this line, you write to the memory address currently pointed at by p. Since you always increment p and then write to it, you end up writing off past the end of the region that you allocated for p. (If we imagine pOrig as a pointer to where p initially points, then this writes to pOrig[1], pOrig[2], pOrig[3], pOrig[4], and pOrig[5], and that last write is past the end of the region). This results in undefined behavior, meaning that literally anything can happen. This is Bad News.
Additionally, delete[] assumes that you are passing in a pointer to the very first element of the array that you allocated. Since you've incremented p so many times in that loop, you're trying to delete[] a pointer that wasn't at the base of the allocated array, hence the issue.
To fix this, don't write to p after incrementing it, and store a pointer to the original array allocated with new[] so that you can free that rather than the modified pointer p.
You have to delete the pointer that you got from new. However, in your second code you did p++ which changed the pointer. Therefore you tried to delete a pointer you didn't get from new and delete crashes.
To fix this type of error never use new. Instead use std::vector<int> p;. Since you never need new you cannot forget a delete.
Problem is changing p in p++.
You should always store (to delete) original pointer. Like this:
#include <iostream>
int main()
{
int *original = new int[5];
int *p = original;
for (int i = 0; i < 5; i++)
{
std::cout << *p << " ";
int x = *p;
p++;
*p = x + 1;
}
std::cout << std::endl;
delete [] original;
return 0;
}
I have a homework problem that I'm working out. Me and some other students are pretty sure that our teacher misspoke, but maybe not. I checked through a bit of the questions here already and can't really find a way to use pointers to create what is essentially an array. The instructions read as follows.
Rewrite the following program to use pointers instead of arrays:
The code is this
int main()
{
int salary[20];
int i;
for (i = 0; i < 20; i++)
{
cout << "Enter Salary: ";
cin >> salary[i];
}
for (i = 0; i < 20; ++i)
salary[i] = salary[i] + salary[i] / (i + 1);
return 0;
}
My solution was this:
int main()
{
int* salary_pointer = new int;
for (int i = 0; i < 20; i++)
{
cout << "Enter Salary: ";
cin >> *(salary_pointer + i);
}
for (int i = 0; i < 20; ++i)
{
*(salary_pointer + i) = *(salary_pointer + i) + *(salary_pointer + i) / (i + 1);
cout << *(salary_pointer + i) << endl;
}
delete salary_pointer;
return 0;
}
It keeps flagging a segmentation fault at about salary number 13
My main purpose (because I'm almost positive my teacher wrote this down wrong) is to understand more about pointers, so any and all tips and tricks for learning these confusing things would be greatly appreciated. Thank you all!
Use
int* salary_pointer = new int[20];
instead, as you allocate 20 ints, not just one. Then, delete the dynamic array using
delete[] salary_pointer;
instead of delete salary_pointer. Be also careful here:
salary[i] / (i + 1);
If the operands are int, then you end up with truncation. Use salary[i]/(i + 1.) in case you want your result as a double (in which case you better make salary an array of doubles or a pointer to double so you don't have this issue anymore).
Your teacher did not misspeak. You have bugs in your program.
How many elements did you allocate?
How many elements are you trying iterate through and dereference?
How many elements did you free?
You're getting a seg fault, because you are dereferencing memory you did not allocate.
I'd be more specific, but giving too much away won't help you get better when it comes to homework.
This kind of manual memory management is done away with later when you will be using STL containers for the most part, but the relationship between pointers and arrays, and the ability to do pointer arithmetic is important.
Why is your teacher wrong?
Here is what is happening. You are creating a pointer to a SINGLE integer. As you iterate through your for loop what you are doing is actually overwriting memory that is possibly, and I STRESS possibly, being used by other bits of your program. This causes undefined behavior up to and including a crash.
Redo your memory allocation and your access violation should go away. Also, use a variable to hold your '20'. Something like const int MAX_SALARIES = 20. Learn to do this as it will often save you TONS of headaches in the future.
In this statement
int* salary_pointer = new int;
there is allocated only one object of type int.
And as there is used the division operation it is better to use type float instead of int for the array.
I would suggest the following solution. It uses only pointers.
#include <iostream>
int main()
{
const size_t N = 20;
float *salary = new float[N];
^^^^^^^^^^^^^^^^^^^^^^^^^^
for ( float *p = salary; p != salary + N; ++p )
{
std::cout << "Enter Salary: ";
std::cin >> *p;
}
for ( float *p = salary; p != salary + N; ++p )
{
*p += *p / ( p - salary + 1 );
}
delete [] salary;
^^^^^^^^^^^^^^^^
return 0;
}
struct Struct_t {
int Value1;
int Value2;
};
vector<Struct_t> Struct;
Struct.resize(10, Struct_t());
for (int i = 0; i < 10; ++i)
{
Struct[i].Value1 = (i + 10) * 3;
Struct[i].Value2 = (i + 5) * 2;
}
How can I create a pointer to Struct[i]?
What I want to do essentially is something like this, but I'm sure this can be done better:
int value = 6;
Struct_t temp = Struct[value], *s;
s = &temp;
s->Value1 = 42;
s->Value2 = 6;
Main goal is, that I can easily create a pointer to Struct[n] with 1 line/function.
So far, the provided answers are missing the elephant in the room. You could create a pointer to a vector element like so:
(Fault-prone) Code Listing
#include <iostream>
#include <vector>
struct Struct_t {
int Value1;
int Value2;
};
int main(void)
{
std::vector<Struct_t> sVec;
sVec.resize(10, Struct_t());
int count = 0;
for (std::vector<Struct_t>::iterator vIt = sVec.begin(); vIt != sVec.end(); ++vIt)
{
vIt->Value1 = (count + 10) * 3;
vIt->Value2 = (count + 5) * 2;
count++;
}
Struct_t* pStruct = &sVec[5];
std::cout << "sVec[5] = (" << pStruct->Value1 << "," << pStruct->Value2
<< ")" << std::endl;
return 0;
}
Sample Output
sVec[5] = (45,20)
However, vector is not an abstract type you want to use if you will be generating pointers to individual elements of the vector/"array". When the vector needs to be re-sized (shrink or grow), the iterators are invalidated, so your pointers will point to now-freed memory, crashing your program. If you want to have raw pointers directly to vector elements, you want to first:
Use a list rather than a vector.
Possibly use managed pointers to handle reference counts.
Finally, when dealing with template classes like vector, list, hash_table, etc, you should try to get used to using the iterator example I used above, as you don't have to worry about checking for exceptions when using an invalid index (the overloaded [] operators can throw exceptions, unless you replace them with the .at() member function instead for element access).
For a std::vector, &v[i], gives you a pointer to the i'th element. Your sample code becomes:
int value = 6;
Struct_t* s = &Struct[value];
s->Value1 = 42;
s->Value2 = 6;
So the one-liner is Struct_t* s = &Struct[value];
There is nothing wrong with doing this, but like most aspects of C++, you need to understand what you are doing. The above code is perfectly valid and guaranteed by the standard. (Also note that the code in this answer avoids an error in the original code where s was a pointer to a temporary copy and no changes were made to the contents of the Struct vector.)
Yes, resizing Struct will invalidate any pointers made before the resize, and yes, using iterators is often a better technique. But this is a correct and reasonable answer to the original question.