C++ Questions

Pages: 1234... 13
I have not done more than a 2D array, just initializing and printing it out with a nested for loop. Maybe if I ever get to a 3d+ it will be easier with the stdlib update.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
using namespace std;

class MainClass
{
	int age = 21;
	friend class BackDoor;
public:
	static int staticN1;
	static const int staticN2 = 22;
	int idNum = 7854;
};

int MainClass::staticN1 = 11;

class BackDoor
{
public:
	void PrintFromMainClass(const MainClass& refMainClass) const
	{
		cout << refMainClass.age << endl;
		cout << refMainClass.staticN1 << endl;
		cout << refMainClass.staticN2 << endl;
	}
};

int main()
{
	MainClass MainClass1;

	cout << MainClass::staticN1 << endl;
	cout << MainClass().staticN1 << endl;
	cout << MainClass1.staticN1 << endl;
	//cout << staticN1 << endl; //NOT WORK, need Class or instance of class

	BackDoor BD1;
	BD1.PrintFromMainClass(MainClass1);
	//cout << BD1.idNum << endl;//NOT WORK, Need to access thru instance of source class

	return 0;
}
/*
11
11
11
21
11
22
*/


3) Is this statement true, that the below line is more expensive, because it has to create the object first before reaching the static variable. I know static remains viable between classes and is like a global variable for the class in question.

 
     cout << MainClass().staticN1 << endl;


 
     cout << MainClass::staticN1 << endl;


Which would make this line above less expensive, is that true? Does the staticN1 in the last line get destroyed after the call and if I never created a MainClass in the first place or does it just stay in memory? I think it gets destroyed after the call.

You always need a class or an object of the class to access the static variable, right?
Any other way to access that staticN1 variable from the main, just curious?
I know, I know, if you need a global variable just make one (above main or #define).


4) This is more of a note than a question.
1
2
	static int staticN1;
	static const int staticN2 = 22;


I saw basic examples of static within classes and then when I went out experimenting of course I ran into snags. I didn't understand why it was done that way, but I think I have a better feel for them now. It now makes sense that you can have a static "const" initialized within a class and if any other instance of the class attempts to re-initialize the const, the compiler will flag it.

But if they gave you the ability to initialize the "non-const" version of it WITHIN classes, then things could have gotten messy and it could have been a nightmare trying to track it down. So that is why the "non-const" initialization is forced outside of the class.


5) This one is more of a note as well.
BD1 does not have direct access to idNum, but needs to access via the class or object of the class.
Even though you have a friend of a class, and the variable within the class granting friend permission is public, one still needs an instance of a class to reach the variable and you can't have direct access to it. Really, if a "MainClass" object was never created, then the "BackDoor" class really cannot access it anyway...unless you guys have a sneaky way of doing it?

I know I can also access like this, "MainClass().idNum".


 
//cout << BD1.idNum << endl;  //NOT WORK, Need to access thru instance of source class 
I have not done more than a 2D array, just initializing and printing it out with a nested for loop.

Yeah, 2D arrays are useful for things like maps (e.g. in a tile-based game). I don't know when I needed a 3D array.

What I use to do when I need a 2D array of dynamic size is to use a plain std::vector and then calculate the index as x + y * width.

1
2
3
4
5
6
7
8
9
10
11
std::size_t width = 100;
std::size_t height = 20;
std::vector<Tile> map(width * height);

for (std::size_t y = 0; y < height; ++y)
{
	for (std::size_t x = 0; x < width; ++x)
	{
		// do something with map[x + y * width] ...
	}
}

I think it's quite nice once you get used to it but you could always wrap it in a class if you want to hide the "ugly" details.

Note that this is more efficient than having a vector of vectors.

std::mdspan is so new that I haven't tried it yet so I don't know if it makes things easier or if it just overcomplicates things.

3) Is this statement true, that the below line is more expensive, because it has to create the object first before reaching the static variable.

The expression MainClass().staticN1 does two things:

A) It creates an object.
B) It accesses a static variable.

The expression MainClass::staticN1 only does B which is why it's potentially faster.

Does the staticN1 in the last line get destroyed after the call and if I never created a MainClass in the first place or does it just stay in memory?

Static variables are not destroyed until the end of the program. Whether or not you have created any MainClass objects doesn't matter.

The temporary object that gets created by the MainClass() expression on the first line will get destroyed at the end of that line (when the statement ends) but that has no effect on the static variables.

4) [...] if they gave you the ability to initialize the "non-const" version of it WITHIN classes, then things could have gotten messy and it could have been a nightmare trying to track it down. So that is why the "non-const" initialization is forced outside of the class.

It's possible to initialize any static class variable inside the class body by using the inline keyword (since C++17). This can also be used for defining global variables outside classes in header files without running into multiple definition errors. Just make sure the definition is the same in all translation units.

5) [...] Even though you have a friend of a class, and the variable within the class granting friend permission is public, one still needs an instance of a class to reach the variable and you can't have direct access to it.

Yes. You need to specify which MainClass::idNum that you want to access. There might be zero or a million such variables (because you might have zero or a million MainClass objects). There is no way the compiler could magically guess which one you wanted.
Last edited on
3) OK, I thought maybe the static variable gets destroyed, because there was no instance of the class and therefore no reason to keep it around and continue to waste memory. At least until an obj is created and then the static var stuck around until the end of the program. Good to know.

I find myself asking about everything now, which is a departure from when I had first read the material and where I was just happy to be able to duplicate.

4) I will have to try that, to inline a static variable inside a class then.

5) That makes sense.


Using a vector to store 2D is beautiful and can save so much processing time and resources when doing thousands and millions. This will come in handy! Did you see this somewhere being used a long time ago and the beauty stuck with you since then?

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
36
37
38
39
40
41
42
43
#include <iostream>
#include<vector>
using namespace std;

int main()
{
	std::size_t width = 100;
	std::size_t height = 20;
	std::vector<int> map(width * height);

	//Print with index
	for (std::size_t y = 0; y < height; ++y)
	{
		for (std::size_t x = 0; x < width; ++x)
		{
			size_t index = x + y * width;
			map[index] = index;
			cout << "[" << index << "] = " << map[index] << endl;
		}
		cout << endl; 
	}

	cout << "***************" << endl;
	cout << "***************" << endl;

	//Print with width/height
	for (std::size_t y = 0; y < height; ++y)
	{
		for (std::size_t x = 0; x < width; ++x)
		{
			cout << map[x + y * width] << ", ";
		}

		cout <<"\b  \n\n";
	}

	cout << "***************" << endl;
	cout << map.size() << endl;
	cout << map.capacity() << endl;
	cout << "***************" << endl;

	return 0;
}
OK, I thought maybe the static variable gets destroyed, because there was no instance of the class and therefore no reason to keep it around and continue to waste memory.

It can be used without an instance of the class and the value that it has should not be lost so there is every reason why it needs to be kept around.

Using a vector to store 2D is beautiful and can save so much processing time and resources when doing thousands and millions. This will come in handy! Did you see this somewhere being used a long time ago and the beauty stuck with you since then?

Yeah, it's something I learned about years ago after reading about it on the web somewhere.
What I use to do when I need a 2D array of dynamic size is to use a plain std::vector and then calculate the index as x + y * width.


The same concept can be used for 3d, 4d etc arrays. Usually you'd override the operator() so that myarray(1,2,3) etc works as array element access. The , operator is (which version?) being removed from use within [] so that the operator[] can be overridden in the same way eg myarray[1,2,3] etc which currently evaluates as myarray[3].
Using a vector to store 2D

That's a vector of vectors. :Þ

Creating an actual 2D vector of vectors has an advantage over the simulated 2D container using a 1D container...

...being able to add/remove elements from an already created vector of vectors at run-time without having to juggle the logic of conversion for simulated 2D. It is possible to add or remove an entire vector (row) in a vector of vectors seamlessly as well.

That advantage can be used for a container of any multi-dimensions. Be it 3D, 4D or higher.

Couple that ability for a vector of vectors not requiring every contained vector to hold the same number of elements with a couple of template functions and outputting a 1D, 2D or 3D vector to be as easy as outputting POD single elements.

1
2
3
4
5
6
7
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v)
{ for (auto const& x : v) { os << x << ' '; }  return os; }

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<std::vector<T>>& v)
{ for (auto const& x : v) { os << x << '\n'; } return os; }

I found out by happy coincidence and accident these two functions work with 3D as well as 1D/2D for writing something like this:
std::cout << v; or std::cout << v << '\n';

Not the most elegant display of data, but it works.

These two functions were crufted years ago, with later C++ enhancements I'm sure they could be improved. They work for my personal needs, so I don't see much reason to improve them.

C++23 added the ability to do formatting ranges so using std::println for formatted output can accept a 1D vector and output all the elements.

See: https://cplusplus.com/forum/general/285737/

As usual not every compiler at this time has the support for formatting ranges, VS 2022 certainly doesn't, but there is a workaround available.

I honestly don't know if this formatting ranges applies to 2D or higher multi-dimensional containers as well, or if it works with a C++ container other than std::vector. More testing to find out!

Personally I find the skull sweat of an actual multi-dimensional container easier to deal with, and reading code is less burdensome IMO than "just where the frack am I" of simulated 2D code.

YMMV.
The use case I had in mind was when all "rows" had the same length and the size never or very rarely changed. When I said "dynamic size" earlier I just meant that the size wasn't known at compile time because otherwise you could simply use an array of arrays. Using a vector of vectors (or array of pointers to arrays) in this case has unnecessary overhead and is less cache friendly.

I think it's a good idea to wrap this in a class like seeplus said, but what I meant was that even if you don't it's not too bad. Initializing a vector of vectors is more complicated in my opinion and it also takes more work if you want to verify that the sizes are correct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// initialization
std::vector<int> map1(width * height);

// size verification
assert(map1.size() == width * height);


// initialization
std::vector<std::vector<int>> map2(height, std::vector<int>(width));

// size verification
assert(map2.size() == height);
for (const std::vector<int>& row : map2)
{
	assert(row.size() == width);
}
Last edited on
Efficiency, same as beauty, is in the eye of the beholder. Your method(s) works for you, mine works for me.

Albeit to say, I don't pooh-pooh something new or different out of hand either. Build a better code mouse-trap, show it to me without over-indulgent lecturing, and I'll look and hopefully learn another way to mash code.

I freely admit there are possible enhancements and refinements that could be made to my test code I write. Absolutely, without a doubt.

I am not now nor ever will be a professional programmer, so the "solutions" I show are unarguably not "the best." Nor are the snippets "bullet-proof." They were created to solve a particular problem or limited set of problems. Snippets that can get recycled into other projects I might cruft in the future.

I personally prefer to work with true multi-dimensional containers. They are conceptually and intellectually easier for me to understand and work with.

YMMV.
the reason I use 1d to represent 2d comes from what I needed to do.
that was a mix of:
frequent transpose operations for some weird matrix math and for efficiency
frequent resize operations (eg append solution to a matrix for a dumb RREF solver)
frequent reshape operations (because original code was matlab based and coder was ... unique in how he did stuff)
and the like. true 2d containers would have been a nightmare with this stuff, having to basically copy/destroy original constantly instead of shuffle & go.
It was also easier to control my memory, as I had large pools preallocated and sliced them up as needed for temporary/intermediate results and then recycled.

most of that is moot now... eigen finally brings to c++ what was missing back then, when only the difficult to use BLAS & friends were available. I found the older tools slow because each function required you to call 3 or 4 others before it (factor it some specific way, or normalize it, or whatnot) first.

It also let you touch every element efficiently, for save/load or basic stuff like matrix * constant. Even copying ... 1d allowed easy memcpy calls.

I suspect high performance math stuff has to be done 1d way, but there are so many new tricks in c++ it may not be as hard a requirement as it once was.
Last edited on
Some places/libraries call multi-D array a "matrix" (just like jonnin did in the post above). In case of libraries there are both types and corresponding algorithms. For example, some tasks need only "sparse matrix".

Nd implemented with 1d allocates memory for all elements.
Vector of vectors may leave some "rightmost columns" of some rows unallocated.

Triangular and diagonal matrices have well defined areas that will not be accessed (and thus need not be allocated).

Sparse matrices are probably "mostly empty".
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include<string>

using namespace std;

int main()
{
	char inputChar{ 'y' };
	string str1{};
	do
	{
		cout << "Enter a multi-string: ";
		getline(cin, str1);
		cout << "YOU ENTERED = " << str1 << endl;

		cout << "Enter more strings (y/n): ";
		cin >> inputChar;
		cin.ignore();

	} while (inputChar != 'n');
	

	//Test successive cin>>
	cout << "Want eggs: ";
	cin >> inputChar;
	if (inputChar == 'y') cout << "You want eggs!" << endl;

	cout << "Want bacon: ";
	cin >> inputChar;
	if (inputChar == 'y') cout << "You want bacon!" << endl;

	cout << "Want salt: ";
	cin >> inputChar;
	if (inputChar == 'y') cout << "You want salt!" << endl;


	char c{ };
	cout << c << endl;

	char c1{'\0'};
	cout << c1 << endl;

	char c2 = {'\0'};
	cout << c2 << endl;

	char c3 = '\0';
	cout << c3 << endl;

	char c4{ 0 };
	cout << c4 << endl;

	char c5 = { 0 };
	cout << c5 << endl;

	char c6 = 0 ;
	cout << c6 << endl;

	char c7 = NULL;	//Seems to work, NULL macro (#define NULL 0)
	cout << c7 << endl;

	char c8 = char(0);	//unncessary conversion
	cout << c8 << endl;

	return 0;
}




6) I read my chapter on streams and learned new things. I was hoping to get more answers, but I did not come across mention of an issue that I had when first starting out. I ran into an issue using a getline() followed by a cin, where it would skip the getline() and act weird. I researched that you can add cin.ignore() to fix this, but never knew why this is a problem in the first place? "

.ignore() ignores or clears the character(s) from the input buffer. But why is this not a problem in my sample that shows successive cin's, it seems to clear out there and is ready for the next cin? What is the cause of this problem and where does it stem from?

7) Are those all legitimate ways to initialize an empty char? I prefer the quicker:
 
char c{ };


7) Are those all legitimate ways to initialize an empty char?

Yeah except
char c7 = NULL; //Seems to work, NULL macro (#define NULL 0)
Because it is allowed for an implementation to do #define NULL nullptr or something to that effect. That won't compile.
Last edited on
6). Stream extraction (>>) and getline() don't work well with each other as they are basically opposites in white space handling.

>> ignores initial white space chars, extracts what it can and leaves the terminating char in the buffer.

getline() doesn't skip initial white space chars, extracts what it can and then extracts the terminating char.

So a >> following a >> is ok as the second >> just skips the white space left by the first. getline() followed by a >> is also OK. getline() followed by getline() is also OK.

The problem is >> followed by getline() as getline() doesn't first skip the terminating white-space char left by >> (the '\n') and uses it as it's input - which terminates getline() !!

If you're mixing >> with getline() then usually after >> you use .ignore() to remove the remaining white-space from >>. This is often coded as below to remove all chars remaining in the buffer:

 
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');



7) char c{}; is the recommended way and is the recommended way to initialise any type that doesn't have a default constructor.
Last edited on
This was written before I saw seeplus's reply above.

SubZeroWins wrote:
I ran into an issue using a getline() followed by a cin, where it would skip the getline() and act weird. I researched that you can add cin.ignore() to fix this, but never knew why this is a problem in the first place? "

This is a common problem when << is followed by getline.

It is important to understand that streams are just sequences of characters that is written to at one end and read from at the other end (like a queue).

When you type something and press enter it will add the characters that you have typed + a newline character '\n' to the end of the standard input stream (std::cin). If the stream is empty when you try to read from it it will wait until more input has been supplied.

When you use << to read from a stream it will first discard any leading whitespace characters (e.g. spaces, tabs, newlines) and then it will start reading the value of whatever data type that it is that you're trying to read.

Example:
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
#include <sstream>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	std::istringstream stream(" Hello 1.5 2.7");
	
	// stream buffer content: " Hello 1.5 2.7"
	
	std::string str;
	stream >> str;
	std::cout << "'" << str << "'\n"; // prints 'Hello'
	
	// stream buffer content: " 1.5 2.7"
	
	double dval;
	stream >> dval;
	std::cout << dval << "\n"; // prints 1.5
	
	// stream buffer content: " 2.7"
	
	int ival;
	stream >> ival;
	std::cout << ival << "\n"; // prints 2
	
	// stream buffer content: ".7"
}

When you use getline to read from a stream it simply reads all characters until it finds a newline character (the newline character is discarded).

Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <sstream>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	std::istringstream stream("Test\n  x y 67 Test whatever    \n");
	
	// stream buffer content: "Test\n  x y 67 Test whatever    \n"
	
	std::string line1;
	std::getline(stream, line1);
	std::cout << "'" << line1 << "'\n"; // prints 'Test'
	
	// stream buffer content: "  x y 67 Test whatever    \n"
	
	std::string line2;
	std::getline(stream, line2);
	std::cout << "'" << line2 << "'\n"; // prints '  x y 67 Test whatever    '
	
	// stream buffer content: ""
}

What happens when you use >> followed by getline is that the getline will continue reading the rest of the line where the >> stopped reading.

Example:
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
36
#include <sstream>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	std::istringstream stream("A B\nApe\nBird\n");
	
	// stream buffer content: "A B\nApe\nBird\n"
	
	std::string a;
	stream >> a;
	std::cout << "'" << a << "'\n"; // prints 'A'
	
	// stream buffer content: " B\nApe\nBird\n"
	
	std::string b;
	std::getline(stream, b);
	std::cout << "'" << b << "'\n"; // prints ' B'
	
	// stream buffer content: "Ape\nBird\n"
	
	std::string c;
	stream >> c;
	std::cout << "'" << c << "'\n"; // prints 'Ape'
	
	// stream buffer content: "\nBird\n"
	
	std::string d;
	std::getline(stream, d);
	std::cout << "'" << d << "'\n"; // prints ''
	
	// stream buffer content: "Bird\n"
}


There are at least three different strategies to deal with this problem.


1. Always discard the rest of the line after using >>

Code that reads input from the user is responsible for reading the whole line. If you use >> to read the input then you can indeed use ignore() to discard the newline character.

1
2
3
int val;
std::cin >> val;
std::cin.ignore();

But this only works if there wasn't any additional characters between the value and the newline character (maybe the user pressed space before pressing enter).

To avoid this problem you can use the following code instead:

 
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

This will discard the rest of the line no matter how long it is. (The first argument is the maximum number of characters to discard. The second argument specifies the character where it should stop.)

Note that you place this after you have used >>, not before using getline because at that point you might not always know whether you need to discard anything.

If you want to validate the remaining characters (e.g. to see that they are all whitespace characters) you might want to use getline instead of ignore so that you can inspect them as a string.


2. Always use std::ws before using getline

A simpler strategy is to simply discard any whitespace characters before using std::getline. This usually works fine as long as the left-over characters from the previous line are all whitespace characters.

1
2
3
std::string line;
std::cin >> std::ws;
std::getline(std::cin, line);

You can even combine getline and ws into one line which can be convenient.

1
2
std::string line;
std::getline(std::cin >> std::ws, line);

This makes it behave much more like the >> operator. If the user press enter without entering anything it will continue to wait just like when using >>.

A downside is that you cannot read empty lines and any whitespace characters at the beginning of the line will be lost which is something that you might sometimes care about.


3. Always use getline

Just use std::getline to read each line from the user and if you need to extract values from the lines you can use std::istringstream.

1
2
3
4
5
6
7
std::cout << "Enter two integers: ";
std::string line;
std::getline(std::cin, line);
std::istringstream line_stream(line);
int n1, n2;
line_stream >> n1 >> n2;
std::cout << "You have entered " << n1 << " and " << n2 << ".\n";

This is essentially just a variation of strategy 1. It requires a bit more code but prevents all issues with lines interfering with each other.
Last edited on
Great explanations, super thanks!

6) I understand now that the getline() does not skip white spaces and that it treats the \n character, from the previous cin>>, as if it came from your input and just terminates the getline().

seeplus, I am curious when you say:
getline() doesn't skip initial white space chars, extracts what it can and then extracts the terminating char.


Does the buffer look like this when it is full & during creation:
buffer = Hello there!\n

And then right after getline() is finished (on the next line) and has extracted the buffer, the buffer looks like this:
buffer = ""
As it extracts the white spaces AND your input AND the \n, so that it becomes empty.

Also, when we say:
buffer = ""
It is implied that there is a null terminator '\0' at the end of that buffer, otherwise on the next read it will read beyond the boundary until if finds a '\0'.

Peter, I did not know when you stream into a variable that it extracts and removes the type of the variable. It figures, like with many things here my knowledge is incomplete. I will have to experiment with this & the 3 solutions, much thanks!

7) This line seems to compile fine in VS and when I hover over the NULL it gives "#define NULL 0" as the macro used and literally translates to 0.

 
char c7 = NULL;	//Seems to work, NULL macro (#define NULL 0) 

#define NULL 0

Bjarne Stroustrup:
Should I use NULL or 0?
In C++, the definition of NULL is 0, so there is only an aesthetic difference. I prefer to avoid macros, so I use 0. Another problem with NULL is that people sometimes mistakenly believe that it is different from 0 and/or not an integer. In pre-standard code, NULL was/is sometimes defined to something unsuitable and therefore had/has to be avoided. That's less common these days.

If you have to name the null pointer, call it nullptr; that's what it's called in C++11. Then, "nullptr" will be a keyword.

https://www.stroustrup.com/bs_faq2.html#null
#define NULL ((void*)0) //C
#define NULL 0 //C++

Also, when we say:
buffer = ""
It is implied that there is a null terminator '\0' at the end of that buffer, otherwise on the next read it will read beyond the boundary until if finds a '\0'.

Stream buffers are not strings. Exactly how it's stored is usually not something we need to know about when we use the streams.

#define NULL ((void*)0) //C
#define NULL 0 //C++

NULL is only meant for pointers. You sometimes see people use NULL for characters or other integer types but that is wrong. It was never intended to be used like that and it's not guaranteed to work.

In C++ NULL is often defined as 0 (the type doesn't have to be int) but since C++11 it's also allowed to be defined as nullptr.
See https://en.cppreference.com/w/cpp/types/NULL

In C it can be defined as 0 (the type doesn't have to be int) or ((void*)0). Since C23 it can also be defined as nullptr.
See https://en.cppreference.com/w/c/types/NULL

Don't spend too much time thinking about this. There is no reason to use NULL in modern C++. If you want a null pointer simply use nullptr. If you want a null character simply use '\0'.
Last edited on
Thanks.
I think I will, stick with 0 and not use NULL, and use nullptr for pointers.

Did I get the comments section below correct, for the buffer that is within the loop? I understand that the buffer is a series of chars and not a string object. I also know that an array of chars should have a terminating null character '\0' if programmed correctly, I wonder if there is ever a time where this might not be the case on the other end but probably not.

I had made this, around this morning:

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
//LOOP 1
cout << "Enter a multi-string: ";
//start buffer = '\0'
getline(cin, str1);	     
//during creation buffer = Hello there!\n
//after extracted buffer = "".....should be a '\0' at end

cout << "YOU ENTERED = " << str1 << endl;

cout << "Enter more strings (y/n): ";
//start buffer = ""
cin >> inputChar;
//during creation buffer = 'y'\n
//after extraction buffer = \n
//cin.ignore();

//_______________________________________________________________________
//LOOP 2
cout << "Enter a multi-string: ";
//start buffer = \n
getline(cin, str1);
/*
getline() does not ignore white spaces (space, tab, \n...)
and so takes "\n" and treats it as if you input an "enter".
after extraction buffer = ""
*/

cout << "YOU ENTERED = " << str1 << endl;

cout << "Enter more strings (y/n): ";
//start buffer = ""
cin >> inputChar;
//during creation buffer = 'y'\n
//after extraction buffer = \n
//cin.ignore(); 

Last edited on
I also know that an array of chars should have a terminating null character '\0' if programmed correctly

No, there is no such requirement. There is a convention to put a null character at the end of strings. We usually call these "null-terminated strings" or "C-strings". But as has already been stated, stream buffers are not strings. Streams can also be used to read binary data so it needs to be able to handle null characters (zero bytes) as part of the stream.

When I said "buffer" I just meant all the input data that has been made available to the program but hasn't been read yet. Where and how this data is actually being stored behind the scenes was not relevant for what I tried to explain.
Last edited on
Pages: 1234... 13