Help with Heap and Pointers

Pages: 12
Below is my first attempt to use 'new' to allocate heap memory, and 'memset' to initialise the address indicated by a pointer.


When using memset - How could I insert a string termination code to prevent cout from printing garbage.

Works fine if I manually copy contents of h into buffer before printing with cout?

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 <iostream>

int main()
{
int i;

char* buffer = new char[8];  // buffer is a char-pointer, holding the starting address to a 8x (1byte)char sized memory space allocation on the heap

char h[8] = "1234567"; // h is a char array of size 8x (1byte)chars, initiallised with "1234567"
	
memset(buffer, 65, 8);// initialise 8x (1byte)char memory allocations with the value 65 (ASCII "A") starting at address held in pointer buffer 
	
std::cout << buffer << "Z" << std::endl; // print value held in 8x (1byte)chars of heap starting at address buffer followed by "Z"

for (i = 0; i < 8; i++)
{
buffer[i] = h[i];  // initialse 8x (1byte)chars of heap memory with "1234567"
}

std::cout << buffer << "Z" << std::endl; // print value held in 8x (1byte)chars of heap starting at address buffer, followed by "Z"

delete [] buffer; // release heap allocation
return 0;
};
Well, you could just declare buffer to have size 9 instead, and manually set the last item (buffer[8]) to '\0'. memset won't do that for you.

BTW, you need #include <cstring> to reliably compile your code.
Last edited on
Sorry @lastchance but that didn't work for me.

cout still prints the garbage before the "Z" now only on a new line.

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
#include <iostream>
#include <cstring>

int main()
{
int i;
char* buffer = new char[8];  // buffer is a char-pointer, holding the starting address to a 8x (1byte)char sized memory space allocation on the heap

char h[8] = "1234567"; // h is a char array of size 8x (1byte)chars, initiallised with "1234567"
	
memset(buffer, 65, 7);// initialise 7x (1byte)char memory allocations with the value 65 (ASCII "A") starting at address held in pointer buffer 

buffer[7] = '\n';  // adds string terminator
	
	std::cout << buffer << "Z" << std::endl; // print value held in 8x (1byte)chars of heap starting at address buffer

	for (i = 0; i < 8; i++)
	{
		buffer[i] = h[i];  // initialse 8x (1byte)chars of heap memory with "1234567"
	}

	std::cout << buffer << "Z" << std::endl; // print value held in 8x (1byte)chars of heap starting at address buffer

	delete [] buffer; // release heap allocation
	return 0;
};

ok - got it:

1
2
3
memset(buffer, 65, 7);// initialise 8x (1byte)char memory allocations with the value 65 (ASCII "A") starting at address held in pointer buffer 
	
buffer[7] = NULL; // add NULL string terminator 


Thank you
Mmm, I meant '\0' rather than '\n', but I was half asleep.

The reason your other technique worked when memset didn't was that the initialisation quietly added a null terminator.
You can value-initialise buffer to all \0 when it is defined. Rather than the for loop you can use snprintf().

Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <cstring>

int main() {
	char* buffer {new char[8]{}};
	const char h[] {"1234567"};

	std::cout << buffer << "Z\n";

	memset(buffer, 'A', 7);

	std::cout << buffer << "Z\n";

	snprintf(buffer, 8, "%s", h);

	std::cout << buffer << "Z\n";

	delete[] buffer;
}



Z
AAAAAAAZ
1234567Z

Thank you @seeplus

Your solution is much better.

I hadn't used snprintf before, and it's nice that it automatically adds the string terminator.

The reference page for snprintf indicates that it requires #include <cstdio> - am I reading this correctly?

Could you please help me with these further questions:

What is the difference between:

char h[8] = "1234567";

and

const char h[] {"1234567"};

I get that const tells the compiler that h is a constant, but is there a material difference between my = and your {} or is it just style preference?

Lastly, and the area where I'm most confused is:

char* buffer {new char[8]{}};

I understand that it allocates 8x (1byte) chars of heap, and that it initialises this allocation with 8x \0 - but I don't understand how.

Can you please walk me through the two {} pairs and what they indicate / control ?

The reference page for snprintf indicates that it requires #include <cstdio> - am I reading this correctly?

Yes., include <cstdio>.

What is the difference between

In the first snippet you are telling the compiler how many elements you want in your array, the second snippet the compiler determines the number of elements by the number in the constant char C string (string literal) used to initialize the array.

Using brackets for initializing a variable is AKA "uniform initialization", the preferred method for initialization since C++11.
https://mbevin.wordpress.com/2012/11/16/uniform-initialization/

One key factor of "uniform initialization" is the compiler can't perform implicit type conversions (type narrowing). A string literal can't be used to initialize a non-const C string.

The 2nd snippet is declared as being const, so it is not alterable.

The 3rd snippet is another example of "uniform initialization."
The reference page for snprintf indicates that it requires #include <cstdio> - am I reading this correctly?


iostream often does this itself depending upon the compiler used. It does with VS hence it compiled OK for me and I forgot to include cstdio explicitly...

Re snprintf(). There are also strcpy()/strncpy()/strcat()/strncat(), (excluding the _s versions which aren't standard on all compilers). The ones with the n don't guarantee null terminator and the others guarantee null terminator but not the max number of chars copied! Hence I like snprintf() which does both.
Last edited on
excluding the _s versions which aren't standard on all compilers

They are standard if the compiler can be set to use C11 (or later).

The ISO C Committee realized MS did something worthy, so added that variant to the C standard.
Thank you @seeplus @George P @lastchance - I really appreciate your help, and I'm learning a lot from it.

I'm just wondering why @seeplus changed my char h array into a const char h array?

Was it simply because the code didn't call for the value to be modified, or was there some other reason?

It seems to me that making h a const is a little limiting, and having played with the code bellow it seems fine to leave it as a char array, leaving open the option to manipulate the value in h with a char* ?

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 <iostream>
#include <cstring> // required for memset()
#include <cstdio> // required for snprintf()

#define LOGZ(s) std::cout << s << "Z\n"

int main() {
	// char* buffer = new char[8]; While this does alloacte heap, it is un-intialised which can be problematic.
	char* buffer { new char[8]{} }; // This is Uniform Initialisation - it became the standard with C++ 11. Better as the allocated heap is intialised with n times \0 (NULL)
	
	char h[8]{ "1234567" };  // The 8 is not required as memory will be dynamically allocated dependent on num char's between " "
	char* change {h}; // change points to address of 'h'

        int a{ 452 }; // This is Uniform Initialisation - it became the standard with C++ 11.
	
	a++;
        LOGZ(a);
	
	snprintf(change, 8, "9876543"); // print char array to address pointed to by 'change' which is 'h'

	 	
	LOGZ(buffer);

	memset(buffer, 'A', 7);

	LOGZ(buffer);

	snprintf(buffer, 8, "%s", h);  // uses the printf syntax to print to the pointer memory address "buffer", 8x (1byte) char, format "%s" (string), char array 'h'. string terminator is automatic

	LOGZ(buffer);

	delete[] buffer;

	return 0;
}
Last edited on
I'm just wondering why @seeplus changed my char h array into a const char h array?


Because it's value isn't/shouldn't be changed in that code. I always 'const' variables that shouldn't be changed - it helps to spot issues. If a const is tried to be changed then the compiler will complain and spit out an error. Fixing a compiler error potentially costs much less to fix than having to test/debug a program to find an issue.

If at all possible, let the compiler find issues in the code.

Last edited on
https://isocpp.org/wiki/faq/const-correctness

ISO C++ is worth spending time on it.
To be fair, in code development it might not be a great idea. If you are developing incrementally (as it is definitely good practice to do) then you would be forced to go back and remove it when you do need to change the array.

It's a personal (and possibly minority) view, based to some extent on their non-requirement in the other languages that I use, that std:: and const are being over-egged here. That runs a real risk of turning people off c++, especially if all you want to do is "get something done", rather than being so risk-averse that the code is many times the length and verbosity that it needs to be.
seeplus (3972)
...
Because it's value isn't/shouldn't be changed in that code. I always 'const' variables that shouldn't be changed - it helps to spot issues. If a const is tried to be changed then the compiler will complain and spit out an error. Fixing a compiler error potentially costs much less to fix than having to test/debug a program to find an issue.

If at all possible, let the compiler find issues in the code.


Thank you @seeplus - that clarifies it for me. I did wonder if it was just best practice when you don't plan to modify a variable, but wanted to make sure I wasn't missing something.

Last edited on
thmm (551)
https://isocpp.org/wiki/faq/const-correctness

ISO C++ is worth spending time on it.


Wow! That's a huge resource!

Thank you.

I read the early points on const correctness. I'll have to spend a lot more time at a later date looking through the rest of that sight.
1
2
3
4
5
6
7
lastchance (6281)
To be fair, in code development it might not be a great idea. If you are developing incrementally (as it is definitely good practice to do)
 then you would be forced to go back and remove it when you do need to change the array.

It's a personal (and possibly minority) view, based to some extent on their non-requirement in the other languages that I use,
 that std:: and const are being over-egged here. That runs a real risk of turning people off c++, especially if all you want to do is
 "get something done", rather than being so risk-averse that the code is many times the length and verbosity that it needs to be  


Always good to know there are a range of viewpoints - especially when people remain civil and accepting of the opposing views, which is not always a characteristic of interactions on the internet :)
Last edited on
It's one thing writing a few lines of code. It's another when you're dealing with thousands of lines. Good practice instilled at an early stage could save you getting bitten later on.
Mmm, but I find it bizarre when such monstrosities as
void g(const Foo* const* p)
are recommended as good practice (as in that ISO cpp guide).


And why on earth would I want to write two of everything?
1
2
3
4
5
6
7
class Fred { /*...*/ };
class MyFredList {
public:
  const Fred& operator[] (unsigned index) const;  // Subscript operators often come in pairs
  Fred&       operator[] (unsigned index);        // Subscript operators often come in pairs
  // ...
};

I thought the whole idea of any function was to avoid code duplication?


And having swallowed const ... there's then mutable when you realise that const wasn't so great after all.
Last edited on
Because you can have:

1
2
const FredList cfl {...};
FredList fl {...};


and you need the different overloaded operators for these. const is considered part of the function signature.

mutable is when in general the contents of a class is not expected to change for an operation (ie say display()) but you also want to know how say many times display() has been called. display() would be defined const as it's not expected to change the instantiation variables but cntDisplay would be defined mutable so that display() can update the count even though marked as const.
Last edited on
Pages: 12