Odd behaviour when reading file into vector.data()

I have the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <vector>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main()
{
	vector<string> test(1);
	test[0] = "A";
	cout << test[0] << endl;

	ofstream out("Test.obj", ios::binary);
	out.write((char *) test.data(), sizeof(test));
	out.close();

	ifstream in("Test.obj", ios::binary);
	const vector<string> t(1, "E");
	cout << t[0] << endl;
	in.read((char *)t.data(), sizeof(t));
	in.close();

	string l[1];
	l[0] = "W";

	cout << t[0] << endl;

	//t[0] = "F";
	//t.data() = l;

	cout << t[0] << endl;

	return 0;
}


I have made the vector that I read the file data into const to avoid the vector or its elements being changed. And if I uncomment either of the two commented lined, I get compile time errors, just as expected due to the vector being const.

Now I know that vector has two data() methods, one that's const and one that's non-const, and it seems to me that the decision of which to use depends on the vector itself being const or not.

But then why can I read into the vector using the data() method as I do with the istream? Since the vector is constant, both calls to data() should be using the const method. So how come the one in the istream.read() isn't but instead using the non-const version?
Last edited on
test.data() is a std::string *. Line 14 overwrites the first sizeof(std::vector<std::string>) bytes (not the first sizeof(std::string) bytes) of test[0] with whatever was on the file. Since std::string contains pointer members, this effectively invalidates the object and leaks memory by making it impossible to recover the internal array used by std::string. The behavior of the program after this point is undefined.

This is a correct way to read a binary file into an std::vector:
1
2
3
4
5
std::ifstream file(path, std::ios::binary|std::ios::ate);
//TODO: check that file is open.
std::vector<char> buffer(file.tellg());
file.seekg(0);
file.read(&buffer[0], buffer.size());


There's no point in answering your questions because they exist in the context of a program that has undefined behavior.
Well, I just used string because I needed a vector with some class, and string was the first I thought of. I never considered the problems with pointers. So therefore I made my own test class and changed my code to use that.

1
2
3
4
5
6
7
8
9
10
11
#pragma once
class PointerlessClass
{
	private:
		char c;
	public:
		PointerlessClass();
		PointerlessClass(char ch);
		~PointerlessClass();
		char get() const;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "PointerlessClass.h"

PointerlessClass::PointerlessClass()
{
	c = 'U';
}

PointerlessClass::PointerlessClass(char ch)
{
	c = ch;
}

char PointerlessClass::get() const {
	return c;
}


PointerlessClass::~PointerlessClass()
{
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <vector>
#include <fstream>
#include <iostream>
using namespace std;
#include "PointerlessClass.h"

int main()
{
	vector<PointerlessClass> test(1);
	test[0] = PointerlessClass('A');
	cout << test[0].get() << endl;

	ofstream out("Test.obj", ios::binary);
	out.write((char *) test.data(), sizeof(test));
	out.close();

	ifstream in("Test.obj", ios::binary);
	const vector<PointerlessClass> t(1);
	cout << t[0].get() << endl;
	in.read((char *)t.data(), sizeof(t));
	cout << "Here:" << t[0].get() << endl;
	in.close();

	PointerlessClass l[1];
	l[0] = PointerlessClass('W');

	cout << t[0].get() << endl;

	//t[0] = PointerlessClass('F');
	//t.data() = l;

	cout << t[0].get() << endl;

	return 0;
}


This shows the exact same behaviour as the code above though. I get exactly the data back that I wrote to the file, as can be seen by the couts. And I still get errors if I remove the comments. And in this case test.data() would return PointerlessData *

So my question still stands as to why I can write t.data() in the istream but not in my own code.
Now you're overflowing the vector's internal buffer. The size parameter to write() and read() must be <= test.size().
PointerlessClass is not a POD type, anyway, so using it in conjunction with read() and write() is also unsafe (although compilers will generally produce correct code anyway).

So my question still stands as to why I can write t.data() in the istream but not in my own code.
read() can modify the contents of the vector because you're bypassing the protections of const by explicitly casting the return value of data() to a pointer to non-const type. This also has undefined behavior. A clever enough compiler could notice that the contents of the vector can't legally change and would replace line 21 with cout << "Here:U"<< endl; Existing compilers aren't clever enough to do this, but it would be a legal optimization.
Last edited on
Topic archived. No new replies allowed.