free(): double free detected in tcache 2 while deleting dynamic array [closed] - c++

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I am trying to read a .txt file and print the lines, while using only c-style string.
int main(int argc, char * argv[]){
...
size_t size = 2;
char* line = new char[size];
char c;
while (file.get(c)) {
if(c != '\n'){
line[size - 2] = c;
char* temp = new char[size + 1];
memcpy(temp, line, size * sizeof(char));
delete[] line;
line = temp;
delete[] temp;
size++;
} else{
line[size - 1] = '\n';
cout << line << endl;
delete [] line;
size = 2;
line = new char[size];
}
}
...
}
I am getting the error free(): double free detected in tcache 2. What is the problem and how can fix it?

I am getting the error free(): double free detected in tcache 2. What
is the problem and how can fix it?
By understanding the error in this code:
char* temp = new char[size + 1];
memcpy(temp, line, size * sizeof(char));
delete[] line;
line = temp;
delete[] temp;
The important thing to remember is that temp and line are pointers to an array of chars; that is they can refer to an array but they do not (by themselves) represent copies of the array. So:
// sets (line) to a memory address where (size) bytes have been
// made available for you to use. Let's assume (line) gets set to
// 0x11223344
char* line = new char[size];
[...]
// sets (temp) to the memory address where (size+1) bytes have been
// made available for you to use. For the sake of the exmaple,
// let's assume (temp) gets set here to memory-address 0x12345678.
char* temp = new char[size + 1];
// Copies (size) bytes from (line) (aka starting at memory-address 0x11223344)
// to (temp) (aka starting at memory-address 0x12345678)
memcpy(temp, line, size * sizeof(char));
// tells the heap that you no longer need the byte-array starting at 0x11223344.
// After this delete[] returns, the heap is allowed to reuse those
// bytes for other (unrelated) things, so you are no longer allowed to
// read or write that memory region!
delete[] line;
// Sets the value of the (line) pointer to point to 0x12345678 instead.
line = temp;
// tells the heap that you no longer need the bytes starting at 0x12345678.
// After this delete[] returns, the heap is allowed to use those
// bytes for other (unrelated) things, so you are no longer allowed to
// read or write that memory region!
delete[] temp;
// Note that at this point, (line) is still pointing to 0x1234568, *but*
// you are no longer allowed to use the memory at that address!
// This is a problem, because on the next iteration of your while-loop,
// you are going to write to either line[size-2] or line[size-1]; either
// either way you are writing to freed memory, which will invoke
// undefined behavior and cause problems for you.
So, how to fix this? As suggested in the comments, the best way to fix it would be to avoid new and delete[] entirely, and stick to using std::string instead, since that would avoid all the usual problems with dangling pointers, use-after-free, uninitialized-memory-reads, unterminated strings, etc.
If you must use new and delete[], however (e.g. because that's the rules of the assignment), then you need to keep track of the lifetime of each of your heap allocations; in this case, that means realizing that when you set line = temp;, that means that line is now pointing to the same array temp is pointing to, and since your code wants to use that array in the future, you shouldn't call delete[] temp; as that deletes the heap-allocation that line was pointing to, and leaves line as a dangling-pointer (i.e. pointing to memory you are no longer permitted to use).

Related

Dynamically changing the size of an array and reading in values. (w/o vectors)

Hello I am having the following difficulty,
I am trying to read in a table of doubles (1 entry per line) and store it in an array, while dynamically changing this array's size (for each line/entry). This is for a school assignment and it forbids the use of vectors(would be much easier...). The main idea that I had is to have a main array which stores the value, then store the previous values and the next one into a new array and do this iteratively. Currently, the problem that I am having is that only the last value of the table is being stored. I am aware, that somehow I need to be passing the data by refference to the global function and that the pointers that I am working with become null ater they exit the following iteration of the while. However, since the exact length of the data is unknown, this seems impossible since intializing an array in the main() is impossible (exact length not known). Any help would be appreciated.
Code posted below.
EDIT: after consideration of the two comments I made the following changes to the code, however I am not sure, whether they will behave appropriately. I added a new function called add_new_datapoint, that should globally change the values of the pointer/length and this is done by passing the values by refference. Called in the problematic else statement as add_new_datapoint(data_ptr, data_len, new_dp). Also, I am not sure that reallocating new memory to the pointer variable, will not result in a memory leak. In essence (after I reallocate data_ptr is the memory that was 'being pointed to' released or do I have to delete it and then re-inialise it in the . In such case, can I refference the pointer 'data_ptr' again in the next iteration of the loop?
I think it will be easier to simplify your posted code than trying to find all the places where you could have errors.
If you expect to see only double values in your file, you can simplify the code for reading data from the file to:
while ( data_file >> new_data_pt )
{
// Use new_data_pt
}
If you expect that there might be values other than doubles, then you can use:
while ( getline(data_file, line) )
{
std::istringstream str(line);
while ( str >> new_data_pt )
{
// Use new_data_pt
}
}
but then you have to understand the code will not read any more values from a line after it encounters an error. If your line contains
10.2 K 25.4
the code will read 10.2, encounter an error at K, and will not process 25.4.
The code to process new_data_pt is that it needs to be stored in a dynamically allocated array. I would suggest putting that in a function.
double* add_point(double* data_ptr, int data_len, double new_data_pt)
Call that function as:
data_ptr = add_point(data_ptr, data_len, new_data_pt);
Assuming the first while loop, the contents of main become:
int main()
{
std::fstream data_file{ "millikan2.dat" };
// It is possible that the file has nothing in it.
// In that case, data_len needs to be zero.
int data_len{ 0 };
// There is no need to allocate memory when there is nothing in the file.
// Allocate memory only when data_len is greater than zero.
double* data_ptr = nullptr;
double new_data_pt;
if (!data_file.good()) {
std::cerr << "Cannot open file";
return 1;
}
while ( data_file >> new_data_pt )
{
++data_len;
data_ptr = add_point(data_ptr, data_len, new_data_pt);
}
// No need of this.
// The file will be closed when the function returns.
// data_file.close();
}
add_point can be implemented as:
double* add_point(double* data_ptr, int data_len, double new_data_pt)
{
double* new_data_ptr = new double[data_len];
// This works even when data_ptr is nullptr.
// When data_ptr is null_ptr, (data_len - 1) is zero. Hence,
// the call to std::copy becomes a noop.
std::copy(data_ptr, data_ptr + (data_len - 1); new_data_ptr);
// Deallocate old memory.
if ( data_ptr != nullptr )
{
delete [] data_ptr;
}
new_data_ptr[data_len-1] = new_data_pt;
return new_data_ptr;
}
The code to track the number of bad points is a lot more complex. Unless you are required to do it, I would advise to ignore it.
You already got an excellent answer but I figured it may be helpful to point out a few mistakes in your code, so you can understand why it won't work.
In the second else scope you declare data_ptr again, even though it is visible from the outer scope. (delete[] doesn't delete the pointer itself, it just deallocates the memory the pointer points to.)
else {
double* data_temp { new double[data_len] };
std::copy(data_ptr, data_ptr + data_len - 2, data_temp);
*(data_temp + data_len - 1) = new_data_pt;
delete[] data_ptr;
double* data_ptr{ new double[data_len] }; // <- Right here
//for (int j{1}; j < data_len; j++) *(data_ptr + j) = *(data_temp + j);
std::cout << std::endl;
}
Instead you could just write data_ptr = new double[data_len]. However, that alone won't make this work.
All of your data disappears because on every iteration you create a new array, pointed to by data_temp and copy the data there, and on the next iteration you set data_temp to point to a new array again. This means that on every iteration you lose all data from previous iterations. This also causes a memory leak, since you allocate more memory every time you hit this line:
double* data_temp { new double[data_len] };
but you don't call delete[] data_temp afterwards.
I hope this helps to understand why it doesn't work.

Calling free() sometimes causes program to crash

The code below ask the user to input 10 pairs of artist and titles which can be up to 30 characters long. Everything seems to work fine with allocating the space and printing the data back out. The problem only occurs when I try to free the memory at then end and then only if one of the elements is 4 or more characters long. I suspect I am not allocating the memory correctly but I just can't see it.
// Songs.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
// Experimenting with pointers, structures and dynamic allocation of memory
//
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <iostream>
#include <stdio.h>
struct songInfo
{
char* pArtist; // char pointer for Artist data
char* pTitle; // char pointer for Title data
};
// function prototype declarations
void getSongInfo(struct songInfo *songData, char *Artist, char *Title);
void printSongInfo(songInfo *songData);
int main()
{
struct songInfo songData[10]; // setup array of 10 elements of the structure SongInfo
char sArtist[31];
char sTitle[31];
// prompt user for the artist and title 10 times once for each array element
for (int i = 0; i < 10; i++) {
printf("Artist %i: ", i + 1);
fgets(sArtist, 31, stdin);
strtok(sArtist, "\n"); // trim out return character
printf("Title %i: ", i + 1);
fgets(sTitle, 31, stdin);
strtok(sTitle, "\n"); // trim out return character
getSongInfo(&songData[i], sArtist, sTitle); // allocates the memory and stores the data into the pointer location
}
printSongInfo(songData); // printout the song data stored in the array
// free up the allocated memory space
for (int i = 0; i < 10; ++i) {
free(songData[i].pArtist);
free(songData[i].pTitle);
}
return 0;
}
void getSongInfo(struct songInfo *songData, char *Artist, char *Title) {
songData->pArtist = (char*)malloc(sizeof(Artist) + 1); // Allocate enough memory to hold the string and the null terminator
songData->pTitle = (char*)malloc(sizeof(Title) + 1);
strcpy(songData->pArtist, Artist); // Copy the data into the allocated memory location
strcpy(songData->pTitle, Title);
}
void printSongInfo(songInfo *songData) {
printf("\n%-35s %-35s\n", "Artist", "Title");
printf("%-35s %-35s\n", "-----------------------------------", "-----------------------------------");
for (int i = 0; i < 10; i++) { // iterate through the array of elements
printf("%-35s %-35s\n", songData[i].pArtist, songData[i].pTitle);
}
}
It's not free() call that is invalid, it's malloc.
If you'd print out sizeof(Artist) + 1, you'd likely get either 5 or 9 (depending on your computer architecture). And the same for Title. You check the size of pointer on your machine, which is constant, not the size of array you received.
Undefined Behvaiour means your code may do anything, including "working for now, but will break later at a correct place". You invoke UB by calling strcpy, which tries to copy data into buffer too short to contain the whole string.
You have to pass the size of array to function or calculate it using strlen inside function (and pray that the string is actually null-terminated).
void getSongInfo(struct songInfo *songData, char *Artist, char *Title) {
songData->pArtist = (char*)malloc(strlen(Artist) + 1); // Allocate enough memory to hold the string and the null terminator
songData->pTitle = (char*)malloc(strlen(Title) + 1);
strcpy(songData->pArtist, Artist); // Copy the data into the allocated memory location
strcpy(songData->pTitle, Title);
}
Use std::char_traits::length or strlen. Instead of length of the array, sizeof(Artist) gives you how many bytes a char * pointer occupies.
songData->pArtist =
(char*)malloc(std::char_traits<char>::length(Artist) +
1); // Allocate enough memory to hold the string and the null terminator
songData->pTitle =
(char*)malloc(std::char_traits<char>::length(Title) +
1); // Allocate enough memory to hold the string and the null terminator
Just a side note: using std::string and smart pointers such as std::unique_ptr and std::shared_ptr would save you lots of troubles dealing with memory issues. Overall, using modern c++ will help you write safer code more efficiently.

C++ dynamic alocate, pointers

I get this error :
"crt detected that the application wrote to memory after end of heap
buffer"
this program should write every second sign...
char x = 1;
while ( x != 0) {
char *ptr(0);
ptr = new char;
fgets(ptr, 100001, stdin);
for (char i = 0; i < sizeof(ptr); i++) {
cout << *(ptr + i);
i++;
}
cout << endl;
delete ptr;
ptr = 0;
}
ptr = new char; allocates exactly one byte for the pointer. Than fgets(ptr, 100001, stdin); attempts to read up to 100001 characters into it, obviously well beyond it's allocated boundary.
The immediate solution would be to make sure you allocate the buffer with adequate size using array form of new, i.e. ptr = new char[100001];. This, in turn, will lead to changing the delete into it's array form as well: delete[] ptr
Also, keep in mind that sizeof(ptr) will give you the size of the pointer (4 or 8 bytes on most platforms). You would want to use strlen or similar to find out when the string ends.
All suggestions above should be taken in light of the learning exercise. In real life, you should not do this, and instead use std::string and modern ways of string-oriented reading. (std::getline would be the most readily available).
sizeof returns size in bytes of the object representation of type. https://en.cppreference.com/w/cpp/language/sizeof
One other point major point to note is that ptr = new char will only allocate exactly one byte from the heap. But in your code you are trying to read 100001 bytes and obviously this is beyond the accessible memory range of that pointer.
you can modify the error as ptr = new char[100001]; When you do this, there is another change that you must do in order to avoid memory leaks.
That being, delete[] ptr. Because you have allocated 100001 adjacent bytes, you have to de-allocate them all.

Accessing a Dynamic Array causing a runtime error

I've been messing around with dynamic memory and I've hit a huge wall.
I'm trying to create a program where the user enters as many strings as they want, then can quit whenever, however after a second string is entered, the program crashes with out giving me any specific error message.
#include "stdafx.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "new"
int _tmain(int argc, _TCHAR* argv[])
{
//Variables
int i=0,end=0,requiresSize=1;
char ** temp;
char item[256]="a";
char ** requires;
//Initialize each element in requiers
requires = new char * [requiresSize];
for(int j=0;j<requiresSize*2;j++){
requires[j]= new char[256];
}
while(strcmp(item,"q-")){
end=0;
printf("Enter h- for help.\nEnter q- to quit.\n");
printf("Please enter a string\n");
gets_s(item);
if(!strcmp(item,"h-")){
printf("Enter a string to add to the list.\nEnter p- to print the list.\n");
end=1;
}
if(!strcmp(item,"q-")){
break;
}
if(!strcmp(item,"p-")){
if(requires[0]!=NULL){
for(int j=0;j<requiresSize;j++){
printf("%d. %s\n",j,requires[j]);
}
}
end=1;
}
while(end==0){
printf("check1:i=%d\n",i);
//if search index is larger than size of the array,reallocate the array
if(i>= requiresSize){
temp = new char * [requiresSize*2];
//Initialize each element in temp
printf("check2:temp initalized\n");
for(int j=0;j<requiresSize*2;j++){
temp[j]= new char[256];
}
printf("check3:temp itmes initialized\n");
for(int j =0;j<requiresSize;j++){
//for each element in requires, copy that element to temp
temp[j]=requires[j];
}
printf("check4:copied requires into temp\n");
delete * requires;
requires = temp;
printf("check5:deleted requires and set requires equal to temp\n");
delete temp;
requiresSize = requiresSize *2;
}
printf("check6:\n");
//if the index at requires is not empty, check to see if it is the same as given item
if(requires[i]!= NULL){
printf("check8:index at requires is not empty\n");
//I know the error occurs here, something to do with accessing requires[i]
if(!strcmp( item, requires[i])){
printf("check9:index at requires is the same as item\n");
//if they are the same, break out of the loop, item is already included
break;
}else{
printf("check10:index at requires is different\n");
//otherwise, increase the index and check again (continue loop)
i++;
break;
}
}else{
printf("check11:index at requires is null, item added\n");
//if the index is empty, add the item to the list and break out of loop
requires[i]= item;
break;
}
printf("check7\n");
}
}
delete requires;
return 0;
}
Thank you in advance.
You need to realize that an assignment statement such as temp = requires (in this case) is only copying over the pointer, so that temp is now pointing to the same location in memory as requires; it does NOT copy over that memory.
This is leading to two problems:
You are allocating new 256-char arrays to each element of temp, and then reassigning each char* in temp to point to a different location, leaking all of that memory; there is now no way to reference the newly-allocated memory, and thus no way for you to free it.
You are assigning the pointer temp to requires, which, again, just means the two are now pointed to the same location in memory, and then deleting temp, which frees that memory (that, again, requires is also now pointing to).
Also, if you use new[] to allocate an array, you must use delete[] to free it. So requires = new char * [requiresSize]; requires you to use delete [] requires; at the end of the program, instead of just delete requires;. Same for each 256-char element of requires.
So, replace temp[j]=requires[j]; with an appropriate call to strcpy (or strncpy). And do not delete temp; the delete [] requires; at the end will handle that, since it is now pointing to that bit of memory.

Reading string in binary files C++

I'm having a bad time with writing and reading from binary files in C++.
I learned a lot from stack questions, but I never got it working and i'm guessing that problem resides in the reading process,
Here are the read and save methods:
void date::save(ofstream& fo){
fo.write((char *) &jour, sizeof(int));
fo.write((char *)&moi, sizeof(int));
fo.write((char *)&annee, sizeof(int));
size_t len = heure.size();
fo.write((char *)&len,sizeof(size_t));
fo.write(heure.c_str(), heure.size());
}
void date::load(ifstream& fi){
fi.read((char *)&jour, sizeof(int));
fi.read((char *)&moi, sizeof(int));
fi.read((char *)&annee, sizeof(int));
size_t len;
fi.read((char *)&len, sizeof(size_t));
char* temp = new char[len+1];
fi.read(temp, len);
temp[len] = '\0';
heure = temp;
delete [] temp;
}
In additon: Is it possible to save classes with dynamic attributes directly with ofstream.write() functionality?
Many thanks.
The reading and writing seems to be correct on first sight, when assigning
heure = temp;
there is a problem.
you allocate len+1 bytes of memory. temp points to the beginning of that memory.
after your assignment (heure = temp) heure also points to that same memory. Then you call delete and from that point, any other operation may write any data to that memory (refered to as 'wild pointer').
You have to use
strcpy(heure, temp);
to copy each byte from temp to the memory that is allocated by heure. make sure of course that heure has allocated enough space to fit all the bytes of temp.