C++ Pointers assignment gives unexpected(?) results - c++

int* snap = nullptr;
int last = -1;
void func(int* md){
if(snap!=nullptr) {
last = *snap;
}
snap = md;
cout<<last<< " "<<*snap<<endl;
}
int main() {
for(int i =0;i<10;i++) {
int arg = i;
func(&arg);
}
return 0;
}`
Output
-1 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
Shouldn't the 'last' variable have the previous iteration's value of 'snap'. But the values are equal. What am I doing wrong here?

On the second iteration of your for loop, snap points to a dangling pointer. The int arg that it points to has fallen out of scope.
While it is a dangling pointer, you dereference it, causing undefined behavior.

Related

Trouble in sorting vector strings using std::sort() using custom compare function

I am trying to sort strings according to a rule. The c++ code works in most cases but in some cases gives an error:
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
I have seen this error before, it happens when we try to initialize a string as null pointer or 0 (which is internally converted as a null pointer). I have checked that error is happening during sorting and only when I use the custom function for comparison. Once again, I don't know why it is happening only in certain cases.
The code is:
#include <algorithm>
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
using std::vector;
using std::string;
bool greater(string a, string b) {
int i = 0;
a = a + a[0];
b = b + b[0];
while(i<a.size() && i<b.size()) {
if(a[i] != b[i]) {
if(a[i] - '0' > b[i] - '0')
return true;
else return false;
}
i++;
}
}
string largest_number(vector<string> a) {
std::sort(a.begin(), a.end(), greater);
std::stringstream ret;
for (size_t i = 0; i < a.size(); i++) {
ret << a[i];
}
string result;
ret >> result;
return result;
}
int main() {
int n;
std::cin >> n;
vector<string> a(n);
for (size_t i = 0; i < a.size(); i++) {
std::cin >> a[i];
}
std::cout << largest_number(a);
return 0;
}
One of the cases where it gives error is:
100
2 8 2 3 6 4 1 1 10 6 3 3 6 1 3 8 4 6 1 10 8 4 10 4 1 3 2 3 2 6 1 5 2 9 8 5 10 8 7 9 6 4 2 6 3 8 8 9 8 2 9 10 3 10 7 5 7 1 7 5 1 4 7 6 1 10 5 4 8 4 2 7 8 1 1 7 4 1 1 9 8 6 5 9 9 3 7 6 3 10 8 10 7 2 5 1 1 9 9 5
SOLUTION:
Thanks for help. The error was occurring as the compare function was not returning anything once it goes out of the loop. Here is the updated function that works.
bool greater(string a, string b) {
int i = 0;
a = a + a[0];
b = b + b[0];
while(i<a.size() && i<b.size()) {
if(a[i] > b[i])
return true;
i++;
}
return false;
}
The only problem I can see with the code (actually I tried in an online compiler and it gave me this warning) is that you don't return anything from greater if the two strings are equal:
bool greater(string a, string b) {
int i = 0;
a = a;
b = b;
while(i<a.size() && i<b.size()) {
if(a[i] != b[i]) {
if(a[i] - '0' > b[i] - '0')
return true;
else return false;
}
i++;
}
// <---- what about here?
}
Try inserting a return statement. (Of course, if two things are equal then neither is greater than the other, so specifically it is a return false you want.) Falling off the end of a function without a return statement is undefined behaviour (except for int main() or any function returning void), so you could expect to see anything at all when this happens, including the type of crash you got.
[Edit: as gst says, you also get to that point if one string is a prefix of the other e.g. "1" and "10". So you'll need a bit more code to test which is which, rather than just a return false. But you could see if that one line would fix the crash: it would be logically wrong but at least not undefined behaviour.]

Creating multidimensional array on the heap

After some experiments I've come up with these four ways of creating multidimensional array on the heap (1 and 2 is kinda the same except that the result for 1 I wanted reference):
#include <memory>
#include <iostream>
template <typename T>
void printArr(T const &arr)
{
std::cout << typeid(T).name() << "\t";
for (int x = 0; x < 2; ++x)
for (int y = 0; y < 2; ++y)
for (int z = 0; z < 2; ++z)
std::cout << arr[x][y][z] << " ";
std::cout << std::endl;
}
int main()
{
int(&arr)[2][2][2] = reinterpret_cast<int(&)[2][2][2]>(*new int[2][2][2]{ { { 1,2 },{ 3,4 } }, { { 5,6 },{ 7,8 } } });
printArr(arr);
delete[] &arr;
int(*arr2)[2][2] = new int[2][2][2]{ { { 1,2 },{ 3,4 } },{ { 5,6 },{ 7,8 } } };
printArr(arr2);
delete[] arr2;
std::unique_ptr<int[][2][2]> arr3(new int[2][2][2]{ { { 1,2 },{ 3,4 } },{ { 5,6 },{ 7,8 } } });
printArr(arr3);
std::unique_ptr<int[][2][2]> arr4 = std::make_unique<int[][2][2]>(2);
printArr(arr4);
return 0;
}
Tested this on various online compilers without problems, so what I would like to know if they are valid ways as well?
Here is the demo https://ideone.com/UWXOoW and output:
int [2][2][2] 1 2 3 4 5 6 7 8
int (*)[2][2] 1 2 3 4 5 6 7 8
class std::unique_ptr<int [0][2][2],struct std::default_delete<int [0][2][2]> > 1 2 3 4 5 6 7 8
class std::unique_ptr<int [0][2][2],struct std::default_delete<int [0][2][2]> > 0 0 0 0 0 0 0 0
I think your first example is undefined behavior or at least would lead to undefined behavior as soon as you'd try to actually access the array through the reference arr.
new int[2][2][2] creates an array of two int[2][2]. It returns you a pointer to the first element of this array ([expr.new] ยง1). However, a pointer to the first element of an array and a pointer to the array itself are not pointer interconvertible. I'm not sure if there is a definitive answer yet to the philosophical question "does the act of dereferencing an invalid pointer itself already constitute undefined behavior?". But at least accessing the reference obtained from your reinterpret_cast should definitely violate the strict aliasing rule. The other three should be fine.
Edit:
As there still seems to be some confusion, here a more detailed explanation of my argument:
new int[2][2][2]
creates an array of two int[2][2] and returns a pointer to the first element of this array, i.e., a pointer to the first int[2][2] subobject within this array and not the complete array object itself.
If you want to get an int(*)[2][2][2] from new, you could, e.g., do
new int[1][2][2][2]

Array Pointer Returns Garbage [duplicate]

This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
C++ correct way to return pointer to array from function
(7 answers)
Closed 5 years ago.
I am trying to subtract two integers of the same size without using an explicit loop using valarray.
For this purpose I've written a function i.e subtract(int *firstarray, int *secondarray). However, the subtraction occurs correctly in function as printed out. But when returned to main() the first two values of array contain garbage. What is my mistake?
int* subtract(int* lastline, int* firstline){// takes two user defined arrays and performs subtraction
std::valarray<int> foo (firstline, 7); // 6 6 5 4 5 6 7
std::valarray<int> bar (lastline,7); // 1 8 8 8 8 8 8
std::valarray<int> res (7);
res=bar-foo; //subtracts two valarrays
for (size_t i=0; i<NUMBEROFCLASSES;i++){
cout<<res[i]<<" "; //prints 5 -2 -3 -4 -3 -2 -1
}
return &res[0];
}
int main(){
int first[7]={6,6,5,4,5,6,7};
int second[7]={1,8,8,8,8,8,8};
int *e= subtract(first, second);
cout<<endl;
for(int i=0; i<7;i++){
cout<<e[i]<<" "; // prints 0 0 -3 -4 -3 -2 -1
}
return 1;
}
res is a variable with automatic storage duration, meaning that it will be destroyed right when the function exits. e is a dangling pointer, so accessing it is undefined behavior. You can return a std::valarray instead.
std::valarray<int> subtract(int* lastline, int* firstline){
// Stuff
return res;
}

Print contents of array modified in function

In my main() function I have declared an array of type int with the numbers 1 to 10. I then have two other functions of type int* that take this array and its size as parameters, perform some operations, and each returns a pointer to the new array. Where I'm having issues is with a third function that prints the contents of the array.
#include <iostream>
using namespace std;
const int SIZE_OF_ARRAY = 10;
int main() {
int array[SIZE_OF_ARRAY] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *ptr1 = 0;
ptr1 = function1(array, SIZE_OF_ARRAY);
print(array, SIZE_OF_ARRAY);
cout << endl;
int *ptr2 = 0;
ptr2 = function2(array, SIZE_OF_ARRAY);
print(array, SIZE_OF_ARRAY);
return 0;
}
void print(int array[], const int SIZE_OF_ARRAY)
{
for (int i = 0; i < (SIZE_OF_ARRAY * 2); i++)
{
cout << array[i] << " ";
}
}
int* function1(int array[], const int SIZE_OF_ARRAY)
{
int *ptr = new int[SIZE_OF_ARRAY];
// Do stuff.
return ptr;
}
int* function2(int array[], const int SIZE_OF_ARRAY)
{
int *ptr2 = new int[SIZE_OF_ARRAY * 2];
// Create new array double in size, and set contents of ptr2
// to the contents of array. Then initialize the rest to 0.
return ptr2;
}
As expected here, the result of calling the print() function twice is something like:
1 2 3 4 5 6 7 8 9 10 465738691 -989855001 1483324368 32767 -1944382035 32767 0 0 1 0
1 2 3 4 5 6 7 8 9 10 465738691 -989855001 1483324368 32767 -1944382035 32767 0 0 1 0
But I want the result to be like this instead:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0
How can I accomplish this? (Note that for this assignment I'm using C++98). Thanks in advance.
new int[SIZE_OF_ARRAY] allocates memory, but doesn't assign values to the array elements. What you are seeing is what was in that memory when it got allocated for the array. You can change your function2 to assign zeroes to array elements, if that's what you want.
First of all, you want to print different number of elements on the two calls to print, so you should not delegate deciding whether to multiply the size by two to the print, but rather do it on the calling side. Change the print function to only iterate up to SIZE_OF_ARRAY, and change the two places where you call it to:
print(ptr1, SIZE_OF_ARRAY);
and
print(ptr2, SIZE_OF_ARRAY * 2);
correspondingly.
Now, I assume that your second function does assign values to all 20 elements, but if it does not, the ones to which it did not assign values would continue containing garbage. To get around it, just initialize them at the beginning of the second function:
int *ptr2 = new int[SIZE_OF_ARRAY * 2];
for (size_t i = 0; i < SIZE_OF_ARRAY * 2; ++ i) ptr2[i] = 0;
With these two changes you should get the desired behavior.
Also, if you allocate something with new[], you need to delete it with delete[], otherwise you get a memory leak. Add these two lines at the end of main:
delete[] ptr1;
delete[] ptr2;
Note, that using delete instead of delete[] would be wrong in this case. If something is allocated as an array, it must be deleted as an array.

Variable Value Changes By Itself

I've been pretty confused while programming before, but this one takes the cake. Basically I set the value in one for loop, and in the following iteration it changes to the value of the next one.
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < numWords[i]; ++j) //numWords [0] = 9, numWords [1] = 7
{
stb[i][j].word = const_cast<char*>(is (j + 1,1).c_str()); //is(int,length[opt]) converts int to string, c_str() returns const char *, but I need char *
cout << is(j+1,1) << ' ' << stb[i][j].word << '\n';
}
}
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < numWords [i]; ++j)
{
cout << stb[i][j].word << ' ';
}
cout << '\n';
}
Output:
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
1 1
2 2
3 3
4 4
5 5
6 6
7 7
7 7 7 7 7 7 7 7 7
7 7 7 7 7 7 7
My only guess now is something with the const, but it doesn't make sense why it would keep changing all previous array elements...
This is pretty simple. Your program has undefined behaviour (if my assumptions about is() are correct).
is(int, length) returns a std::string by value. You get a pointer to some internal structure in that string by using c_str(). This string is then destructed at the end of the full-expression. This destruction invalidates the pointers that you obtained from c_str().
This means that you fill up the array with pointers to invalid memory. You then read from these pointers to print out the contents of the array. Reading from invalid memory results in undefined behaviour.
A possible explanation for the observed behaviour is this:
Each string that is returns reuses the same memory. In the first loop you read from the memory before it has been overwritten by another call to is, and so you get the correct value. In the second loop you read from the memory after it has been overritten, and so you get the final value in the array.