How to actually "clear" a vector in C++? - c++

How to clear content in a simple way?
If I use vec_vec.clear(); only, there is still something in the vector that has not been cleaned up.
#include <iostream>
#include <vector>
int main()
{
std::vector<std::vector<int>> vec_vec(10);
vec_vec[0].push_back(1);
vec_vec[0].push_back(2);
vec_vec[0].push_back(3);
vec_vec[0].push_back(4);
for (auto i : vec_vec[0])
std::cout << i << " ";
std::cout << "." << std::endl;
vec_vec.clear();
for (auto i : vec_vec[0])
std::cout << i << " ";
std::cout << "." << std::endl;
vec_vec[0].clear();
for (auto i : vec_vec[0])
std::cout << i << " ";
std::cout << "." << std::endl;
for (int i=0; i<vec_vec.size(); i++)
vec_vec.erase(vec_vec.begin() + i);
for (auto i : vec_vec[0])
std::cout << i << " ";
std::cout << "." << std::endl;
return 0;
}
1 2 3 4 *
0 0 3 4 *
*
*

vec_vec.clear();
for (auto i : vec_vec[0])
After this clear, vec_vec is empty, so the expression vec_vec[0] has undefined behavior.
Undefined behavior means anything at all might happen, and it's the fault of the program, not the fault of the C++ compiler, library, etc. So it might act like an empty vector, it might crash your program, it might print some values, or it might do what you expect today, then break at the worst possible time later on.
See also this Q&A on Undefined, unspecified, and implementation-defined behavior.

Related

Is map::find safe?

I'm trying to test if "find" is safe in std:map so I've erased an element after I use "find" to test it but the iterator to the element is still valid. Even if I use find again it finds the erased element again.
According to documentation:
Iterators, pointers and references referring to elements removed by the function are invalidated.
All other iterators, pointers and references keep their validity.
Why does the second iterator auto it_2 = numeros.find("uno"); finds the element if it was erased?
Why does std::cout << it->first << " : " << it->second << std::endl; after "erase" prints the element? It means find is safe when the element is removed from the map?
This is my example.
#include <iostream>
#include <map>
int main(int argc, char *argv[])
{
std::map<std::string,unsigned int> numeros = { {"uno",1}, {"dos",2}, {"tres",3}};
auto it = numeros.find("uno");
std::cout << it->first << " : " << it->second << std::endl;
std::cout << std::endl;
numeros.erase("uno");
std::cout << it->first << " : " << it->second << std::endl;
std::cout << std::endl;
auto it_2 = numeros.find("uno");
std::cout << it_2->first << " : " << it_2->second << std::endl;
std::cout << std::endl;
for (auto i=numeros.begin(); i!=numeros.end(); ++i)
std::cout << i->first << " : " << i->second << std::endl;
return 0;
}
Output
uno : 1
uno : 1
uno : 1
dos : 2
tres : 3
Thank you!
numeros.erase("uno"); invalidates the iterator it as per your quote. This means that you are not allowed to dereference the iterator anymore. Doing so anyway has undefined behavior.
Therefore your program has undefined behavior because you dereference the iterator in the next line
std::cout << it->first << " : " << it->second << std::endl;
Undefined behavior means your program could do anything. There are no guarantees that anything specific will happen anymore. There are no guarantees that you will get any error or warning either.
(Assuming you corrected the undefined behavior above:)
The second find in
auto it_2 = numeros.find("uno");
does not find the erased element. If .find does not find any element it returns the past-the-end iterator numeros.end(), which is what is happening here. Dereferencing the past-the-end iterator also has undefined behavior. So the following line
std::cout << it_2->first << " : " << it_2->second << std::endl;
which dereferences the past-the-end iterator also causes undefined behavior and your program to have no behavior guarantees.
You need to always check the result of find against end to verify that it found an element:
if(it_2 != numeros.end()) {
std::cout << it_2->first << " : " << it_2->second << std::endl;
} else {
std::cout << "uno not found!" << std::endl;
}

Why does my pointer to the first element of a vector get lost?

#include <iostream>
#include <vector>
//#include <string>
struct Point {
Point(int _x, int _y) {
x = _x;
y = _y;
}
int x;
int y;
Point *parent;
};
int main() {
Point start(3, 4);
std::vector<Point> points;
points.push_back(start);
std::cout << points.back().x << "," << points.back().y << "\n";
Point one(4, 5);
one.parent = &points.at(0);
//std::cout << "testing: " << one.parent->x << "," << one.parent->y << "\n";
points.push_back(one);
std::cout << "One: " << points[1].x << "," << points[1].y << "\n";
std::cout << "One's parents: " << points[1].parent->x << "," << points[1].parent->y << "\n";
Point two(10, 3);
two.parent = &points.back();
points.push_back(two);
std::cout << "Two: " << points[2].x << "," << points[2].y << "\n";
std::cout << "Two's parent: " << points[2].parent->x << "," << points[2].parent->y << "\n";
Point three(12, 7);
three.parent = &points[1];
points.push_back(three);
std::cout << "Three: " << points[3].x << "," << points[3].y << "\n";
std::cout << "Three's parents: " << points[3].parent->x << "," << points[3].parent->y << "\n";
return 1;
}
I get the following results:
3,4
One: 4,5
One's parents: 0,0
Two: 10,3
Two's parent: 4,5
Three: 12,7
Three's parents: 4,5
Even though I made one's parent point to the vector's first element, the value ends up being 0,0. But, the other pointers point to the element that I want it to.
std::vector has a capacity. If you add elements beyond the vector's current capacity, vector may decide to allocate a larger block, move the existing elements, and then add the new elements. This must have happened in your case too.
You can bump the capacity of an existing vector with reserve. This will not yet add extra elements; it just prepares the vector.
While MSalters correctly explained the reason of the issue, and the possible solution for this particular case, the general practice is a bit different. Because vector may be reallocated at any moment, it's generally bad idea to store pointers to its elements.
You may use indices instead, which will be valid regardless of the re-allocation; or you may consider using different data structure, such as std::list.
Elements of std::list remain in the same location all their lifetime.

Is it possible to have memory problems that don’t crash a program?

I wrote a text cipher program. It seems to works on text strings a few characters long but does not work on a longer ones. It gets the input text by reading from a text file. On longer text strings, it still runs without crashing, but it doesn’t seem to work properly.
Below I have isolated the code that performs that text scrambling. In case it is useful, I am running this in a virtual machine running Ubuntu 19.04. When running the code, enter in auto when prompted. I removed the rest of code so it wasn't too long.
#include <iostream>
#include <string>
#include <sstream>
#include <random>
#include <cmath>
#include <cctype>
#include <chrono>
#include <fstream>
#include <new>
bool run_cypher(char (&a)[27],char (&b)[27],char (&c)[11],char (&aa)[27],char (&bb)[27],char (&cc)[11]) {
//lowercase cypher, uppercase cypher, number cypher, lowercase original sequence, uppercase original sequence, number original sequence
std::ifstream out_buffer("text.txt",std::ios::in);
std::ofstream file_buffer("text_out.txt",std::ios::out);
//out_buffer.open();
out_buffer.seekg(0,out_buffer.end);
std::cout << "size of text: " << out_buffer.tellg() << std::endl;//debug
const int size = out_buffer.tellg();
std::cout << "size: " << size << std::endl;//debug
out_buffer.seekg(0,out_buffer.beg);
char *out_array = new char[size + 1];
std::cout << "size of out array: " << sizeof(out_array) << std::endl;//debug
for (int u = 0;u <= size;u = u + 1) {
out_array[u] = 0;
}
out_buffer.read(out_array,size);
out_buffer.close();
char original[size + 1];//debug
for (int bn = 0;bn <= size;bn = bn + 1) {//debug
original[bn] = out_array[bn];//debug
}//debug
for (int y = 0;y <= size - 1;y = y + 1) {
std::cout << "- - - - - - - -" << std::endl;
std::cout << "out_array[" << y << "]: " << out_array[y] << std::endl;//debug
int match;
int case_n; //0 = lowercase, 1 = uppercase
if (isalpha(out_array[y])) {
if (islower(out_array[y])) {
//std::cout << "out_array[" << y << "]: " << out_array[y] << std::endl;//debug
//int match;
for (int ab = 0;ab <= size - 1;ab = ab + 1) {
if (out_array[y] == aa[ab]) {
match = ab;
case_n = 0;
std::cout << "matched letter: " << aa[match] << std::endl;//debug
std::cout << "letter index: " << match << std::endl;//debug
std::cout << "case_n: " << case_n << std::endl;//debug
}
}
}
if (isupper(out_array[y])) {
for (int cv = 0;cv <= size - 1;cv = cv + 1) {
if (out_array[y] == bb[cv]) {
case_n = 1;
match = cv;
std::cout << "matched letter: " << bb[match] << std::endl;//debug
std::cout << "letter index: " << match << std::endl;//debug
std::cout << "case_n: " << case_n << std::endl;//debug
}
}
}
if (case_n == 0) {
out_array[y] = a[match];
std::cout << "replacement letter: " << a[match] << " | new character: " << out_array[y] << std::endl;//debug
}
if (case_n == 1) {
std::cout << "replacement letter: " << b[match] << " | new character: " << out_array[y] << std::endl;//debug
out_array[y] = b[match];
}
}
if (isdigit(out_array[y])) {
for (int o = 0;o <= size - 1;o = o + 1) {
if (out_array[y] == cc[o]) {
match = o;
std::cout << "matched letter: " << cc[match] << std::endl;//debug
std::cout << "letter index: " << match << std::endl;//debug
}
}
out_array[y] = c[match];
std::cout << "replacement number: " << c[match] << " | new character: " << out_array[y] << std::endl;//debug
}
std::cout << "- - - - - - - -" << std::endl;
}
std::cout << "original text: " << "\n" << original << "\n" << std::endl;
std::cout << "encrypted text: " << "\n" << out_array << std::endl;
delete[] out_array;
return 0;
}
int main() {
const int alpha_size = 27;
const int num_size = 11;
char l_a_set[] = "abcdefghijklmnopqrstuvwxyz";
char cap_a_set[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char n_a_set[] = "0123456789";
std::cout << "sizeof alpha_set: " << std::endl;//debug
char lower[alpha_size] = "mnbvcxzasdfghjklpoiuytrewq";
char upper[alpha_size] = "POIUYTREWQASDFGHJKLMNBVCXZ";
char num[num_size] = "9876543210";
int p_run; //control variable. 1 == running, 0 == not running
int b[alpha_size]; //array with values expressed as index numbers
std::string mode;
int m_set = 1;
while (m_set == 1) {
std::cout << "Enter 'auto' for automatic cypher generation." << std::endl;
std::cout << "Enter 'manual' to manually enter in a cypher. " << std::endl;
std::cin >> mode;
std::cin.ignore(1);
std::cin.clear();
if (mode == "auto") {
p_run = 2;
m_set = 0;
}
if (mode == "manual") {
p_run = 3;
m_set = 0;
}
}
if (p_run == 2) { //automatic mode
std::cout <<"lower cypher: " << lower << "\n" << "upper cypher: " << upper << "\n" << "number cypher: " << num << std::endl;//debug
run_cypher(lower,upper,num,l_a_set,cap_a_set,n_a_set);
return 0;//debug
}
while (p_run == 3) {//manual mode
return 0;//debug
}
return 0;
}
For example, using an array containing “mnbvcxzasdfghjklpoiuytrewq” as the cipher for lower case letters, I get “mnbv” if the input is “abcd”. This is correct.
If the input is “a long word”, I get “m gggz zzzv” as the output when it should be “m gkjz rkov”. Sort of correct but still wrong. If I use “this is a very very long sentence that will result in the program failing” as the input, I get "uas” as the output, which is completely wrong. The program still runs but it fails to function as intended. So as you can see, it does work, but not on any text strings that are remotely long. Is this a memory problem or did I make horrible mistake somewhere?
For your specific code, you should run it through a memory checking tool such as valgrind, or compile with an address sanitizer.
Here are some examples of memory problems that most likely won't crash your program:
Forgetting to delete a small object, which is allocated only once in the program. A memory leak can remain undetected for decades, if it does not make the program run out of memory.
Reading from allocated uninitialized memory. May still crash if the system allocates objects lazily at the first write.
Writing out of bounds slightly after an object that sits on heap, whose size is sizeof(obj) % 8 != 0. This is so, since heap allocation is usually done in multiples of 8 or 16. You can read about it at answers of this SO question.
Dereferencing a nullptr does not crash on some systems. For example AIX used to put zeros at and near address 0x0. Newer AIX might still do it.
On many systems without memory management, address zero is either a regular memory address, or a memory mapped register. This memory can be accessed without crashing.
On any system I have tried (POSIX based), it was possible to allocate valid memory at address zero through memory mapping. Doing so can even make writing through nullptr work without crashing.
This is only a partial list.
Note: these memory problems are undefined behavior. This means that even if the program does not crash in debug mode, the compiler might assume wrong things during optimization. If the compiler assumes wrong things, it might create an optimized code that crashes after optimization.
For example, most compilers will optimize this:
int a = *p; // implies that p != nullptr
if (p)
boom(p);
Into this:
int a = *p;
boom(p);
If a system allows dereferencing nullptr, then this code might crash after optimization. It will not crash due to the dereferencing, but because the optimization did something the programmer did not foresee.

Assigning a vector element to result of function that invokes emplace_back?

The test method on the following class does not have the effect I would expect it to. I have a suspicion it is something to do with the fact that the invocation of emplace_back somehow invalidates the reference obtained via the subscript.
Either way I would expect the second print in test to result in
v[0] = 1
however both result in
v[0] = 5
suggesting that the assignment does not take place.
class FooBar {
vector<size_t> v;
public:
size_t add(size_t x) {
cout << "add(" << x << ")" << endl;
size_t K(v.size());
v.emplace_back(x);
return K;
}
void test(size_t idx) {
cout << "v[" << idx << "] = " << v[idx] << endl;
v[idx] = add(0);
cout << "v[" << idx << "] = " << v[idx]<< endl;
}
};
int main(int argc, char* argv[])
{
FooBar f;
f.add(5);
f.test(0);
}
I know that I can get around the problem by creating a temporary to store the result of add and then perform the assignment but I am interested as to why I cannot use just a straight assignment and why I do not get any kind of error when attempting to perform this.
Compiled and tested with MSVC (Visual Studio 2015).
The line
v[idx] = add(0);
is cause for undefined behavior. You are modifying the contents of v in add while assuming that v[idx] will be valid.
For predictable behavior, you can use:
void test(size_t idx) {
cout << "v[" << idx << "] = " << v[idx] << endl;
size_t val = add(0);
v[idx] = val;
cout << "v[" << idx << "] = " << v[idx]<< endl;
}

printing two adjacent values returned from adjacent_find

I want to print the first 2 values where the next is doubled from the current value.
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
bool doubled (int x, int y) { return x*2 == y; }
int main()
{
deque<int> di;
deque<int>::iterator diiter;
for (int i=0; i<=10; i+=2) di.insert(di.end(), i);
for (diiter = di.begin(); diiter != di.end(); ++diiter)
cout << *diiter << " ";
cout << endl;
diiter = adjacent_find(di.begin(), di.end(), doubled);
if (diiter != di.end()) {
cout << "found " << *diiter << " at " << distance(di.begin(), diiter)+1
<< " and " << *(++diiter) << " at " << distance(di.begin(), diiter)+1
<< endl;
}
}
the output is
0 2 4 6 8 10
found 4 at 3 and 4 at 2
not what I expected, which should be:
0 2 4 6 8 10
found 2 at 2 and 4 at 3
What's wrong with my code? I don't understand how the second position is decremented from the first one when I actually incremented it.
Thanks for all help.
Your program is giving strange results because it does not take in to account the fact, that order of evaluation of arguments to a function(In this case operator <<) is Unspecified.
My Answer here, explains the problem in detail & should be a good read.
You need to cout them on separate statements.
cout << "found " << *diiter;
cout << " at " << distance(di.begin(), diiter)+1;
cout << " and " << *(++diiter);
cout << " at " << distance(di.begin(), diiter)+1;
cout << endl;
This works well & outputs the correct/desired output.