I am learning some new things in C++, and I was trying to test this strncpy_s function from Visual C++. However I am running into some problems as program crashes and I dunno whats going on but I am sure it is a pretty stupid problem. The source code is something like this:
#include "stdafx.h"
#include <iostream>
#include <cstdio>
#include <cstring>
int main()
{
char *p;
p=(char *)malloc(sizeof(char)*strlen("Hello!\n"));
strncpy_s(p,strlen("Hello!\n"),"Hello!\n",strlen("Hello!\n"));
std::cout << p;
std::cout << strlen("Hello!\n") << std::endl;
return 0;
}
As I said I am not using std::string coz I want to try this new function and know how it works.
I take my comments back, I read the documentation more carefully. Your code is passing invalid parameters, and is invoking the invalid parameter handler. Maybe that's what's happening. Namely:
p=(char *)malloc(sizeof(char)*strlen("Hello!\n"));
This line allocates room for 7 characters, which is the length of the string, but not enough room for a null terminator. (This is generally an error)
The documentation for strncpy_s says: These functions try to copy the first D characters of strSource to strDest, where D is the lesser of count and the length of strSource. If those D characters will fit within strDest (whose size is given as numberOfElements) and still leave room for a null terminator, then those characters are copied and a terminating null is appended; otherwise, strDest[0] is set to the null character and the invalid parameter handler is invoked, as described in Parameter Validation.
Are you possibly seeing the "invalid parameter handler"?
You need 1 more character for terminating character ('\0') so you need to replace strlen("Hello!\n") with strlen("Hello!\n") + 1. You could store this length in some variable instead of calling strlen again. Also since you are using C++ you could use new / delete instead of malloc / free:
int len = strlen("Hello!\n") + 1;
char *p;
p = new char[len];
strncpy_s(p, len, "Hello!\n", len);
std::cout << p << len << std::endl;
delete[] p;
The 'security-enhanced' string functions are essentially designed to crash if they run into a problem.
From Microsoft's docs on strncpy_s():
These functions try to copy the first D characters of strSource to
strDest, where D is the lesser of count and the length of strSource.
If those D characters will fit within strDest (whose size is given as
numberOfElements) and still leave room for a null terminator, then
those characters are copied and a terminating null is appended;
otherwise, strDest[0] is set to the null character and the invalid
parameter handler is invoked, as described in Parameter Validation.
There is an exception to the above paragraph. If count is _TRUNCATE,
then as much of strSource as will fit into strDest is copied while
still leaving room for the terminating null which is always appended.
The description of the Paramter Validation is:
The behavior of the C Runtime when an invalid parameter is found is to
call the currently assigned invalid parameter handler. The default
invalid parameter invokes Watson crash reporting, which causes the
application to crash and asks the user if they want to load the crash
dump to Microsoft for analysis. In Debug mode, an invalid parameter
also results in a failed assertion.
This behavior can be changed by using the function
_set_invalid_parameter_handler to set the invalid parameter handler to your own function. ...
Your example program provides a buffer that is too small by one char (there's no room for the null terminator).
So unless you specifically change what should happen when the strxxxx_s functions do when an error is encountered, your program will crash by design. The idea is that a crash is better than a bug that may open a security hole.
strlen(s) will only tell you the length of the string without the NUL terminator. To allocate space into which to copy a string, you normally use strlen(s)+1, to add space for a NUL terminator.
You need to allocate strlen("Hello!\n") + 1 (for the null terminator). And sizeof(char) is utterly pointless since sizeof(char) is guaranteed to be 1 on all platforms.
Why are you using malloc or char* if you're using C++?
std::string p = "Hello!\n";
std::cout << p;
std::cout << p.length();
or
std::string* p = new std::string("Hello!\n");
std::cout << p;
std::cout << p->length();
But to answer your original question you probably need to use strlen(p) + 1
And if you need a char* from your string you can use p.c_str()
Related
I was playing around with c strings in c++ and found some behavior I don't understand when I don't terminate a char array.
char strA[2] = {'a','\0'};
char strB[1] = {'b'};
cout << strA << strB;
I would expect this to print ab, but instead it prints aba. If I instead declare strB before strA, it works as expected. Could someone explain what's going on here?
This is undefined behaviour and you simply are lucky that replacing the declaration of these 2 arrays works for you. Let's see what is happening in your code:
char strA[2] = {'a','\0'};
Creates an array that can be treated like a string - it is null terminated.
char strB[1] = {'b'};
Creates an array that cannot be treated like a string, because it lacks the null terminating character '\0'.
std::cout << strA << strB;
The first part, being << strA, works fine. It prints a since strA is treated as a const char*, which provided as an argument for std::ostream& operator << will be used to print every character untill the null terminating character is encountered.
What happens then? Then, the << strB is being executed (actually what happens here is a little different and more complicated than simply dividing this line into two, separate std::cout << calls, but it does not matter here). It is also treated as a const char*, which is expected to ended with mentioned '\0', however it is not...
What does that lead to? You are lucky enough that there randomly is only 1 character before (again - random) '\0' in memory, which stops the (possibly near-infinite) printing process.
Why, if I instead declare strB before strA, it works as expected?
That is because you were lucky enough that the compiler decided to declare your strA just after the strB, thus, when printing the strB, it prints everything that it consists + prints strA, which ends with null terminating character. This is not guaranteed. Avoid using char[] to represent and print strings. Use std::string instead, which takes care of the null terminating character for you.
When printing char arrays, the C (and C++) convention is to print all bytes until a '\0'.
Because of how the local variables are organized, strB's memory is behind strA's, so when printing strB the printing just 'overflows' and keeps printing strA until the terminating '\0'.
I guess when the deceleration is reversed, the printing of strB is terminated by a 0 that is just there because nothing else was set there, but you shouldn't rely on that - this is called a garbage value.
Don't use unterminated C-strings, at all. Also avoid C-strings in general, you can use C++ std::string which are much more secure and fun.
When I run this code on my computer, I have a bunch (exactly seven) of weird chars printed between the ab to the a, which are probably whatever was between strA's and strB's memory spaces.
When I reverse the declarations, I get ab$%^& where $%^& are a bunch of weird chars - the ones between the end of strB's memory to the next random \0.
I'm trying to get the temporary folder of a user on Windows by using the GetTempPathA function.
LPSTR ptcPath = new CHAR(MAX_PATH);
GetTempPathA(MAX_PATH - 1, ptcPath);
std::cout << "Temporary path : " << ptcPath << std::endl;
So the program outputs correctly the temporary path but crashes right after that (it's part of a big code).
I'm sure the crash comes from GetTempPathA since the program works fine if I comment that line.
It crashes whatever the size I allocate or put instead of MAX_PATH - 1 except for all the values under <size of the path> + 1 that prints gibberish.
I put MAX_PATH - 1 as a value because it solved the problem for someone else on Google Groups.
With
new CHAR(MAX_PATH)
you allocate space for a single CHAR and initialize that one to MAX_PATH. That means your call to GetTempPathA will write out of bounds of that single CHAR element, leading to undefined behavior.
You probably mean
new CHAR[MAX_PATH]
which allocates an array of MAX_PATH elements.
Wow, where should we begin?
the main issue is the expression
new CHAR(MAX_PATH);
this does not allocate an array of characters, but only one, singular character with the value of MAX_PATH.
so in a sense, it as if you've written down
char* c = new char(static_cast<char>(MAX_PATH))
but instead, simply use the fact that std::string has to keep its internal buffer contiguous:
std::string buffer;
buffer.resize(MAX_PATH + 1);
const auto new_size = GetTempPathA(buffer.size(), &buffer[0]); //deal with newsize == 0
buffer.resize(new_size);
anyway, it is worth mentioning that new,new[],delete,delete[] are not used anymore in modern C++. opt for std::vector and std::string for raw memory for IO operations, and std::unique_ptr/std::shared_ptr for all the rest.
i'm trying to convert a string to char array; it goes fine for 3 letters, but some weird chars come for longer strings.
the script works fine till i print the whole array at once...
std::string word;
std::cout<< "enter word: ";
cin>>word;
cin.ignore();
//std::cout<< word ":"<<word.length();
int len=word.length();
char char1[len-1];
cout<<"\n";
for (int a=0;a<len;a++)
{
char1[a]=word.at(a);
std::cout<<char1[a];
}
cout<<"\ncheckline: "<<char1;
for input "abcd"
output is "abcdπ"
You have four problems.
Firstly, the size of an array must be compile time constant. len is not a compile time constant, so the program is ill-formed according to the standard. Henceforth, I shall assume that your compiler supports "Variable Length Array" language extension. In standard C++, you must allocate dynamic memory if the size is determined at run time.
Secondly, you allocate too little memory, and overflow your buffer. If you allocate memory for len - 1 characters, then the last valid index is len - 1 - 1. Writing to char1[len - 1] as you do, has undefined behaviour. char1[len] would be required for len characters.
Thirdly, you didn't terminate the string with null. The standard requires that any string inserted into a stream must be null terminated. However, you pass char1 into std::cout even though it does not contain a null terminated string. The behaviour is undefined. Note, that would have to also remember to allocate memory for this null termination character: char1[len + 1]; ... char1[len] = '\0'.
Lastly, what you're attempting is probably pointless. Re-view the reasons why you think you need to do this. I suspect that those reasons are misguided.
This is trivial, probably silly, but I need to understand what state cout is left in after you try to print the contents of a character pointer initialized to '\0' (or 0). Take a look at the following snippet:
const char* str;
str = 0; // or str = '\0';
cout << str << endl;
cout << "Welcome" << endl;
On the code snippet above, line 4 wont print "Welcome" to the console after the attempt to print str on line 3. Is there some behavior I should be aware of? If I substitute line 1-3 with cout << '\0' << endl; the message "Welcome" on the following line will be successfully printed to the console.
NOTE: Line 4 just silently fails to print. No warning or error message or anything (at least not using MinGW(g++) compiler). It spewed an exception when I compiled the same code using MS cl compiler.
EDIT: To dispel the notion that the code fails only when you assign str to '\0', I modified the code to assign to 0 - which was previously commented
If you insert a const char* value to a standard stream (basic_ostream<>), it is required that it not be null. Since str is null you violate this requirement and the behavior is undefined.
The relevant paragraph in the standard is at §27.7.3.6.4/3.
The reason it works with '\0' directly is because '\0' is a char, so no requirements are broken. However, Potatoswatter has convinced me that printing this character out is effectively implementation-defined, so what you see might not quite be what you want (that is, perform your own checks!).
Don't use '\0' when the value in question isn't a "character"
(terminator for a null terminated string or other). That is, I think,
the source of your confusion. Something like:
char const* str = "\0";
std::cout << str << std::endl;
is fine, where str points to a string which contains a '\0' (in this
case, two '\0'). Something like:
char const* str = NULL;
std::cout << str << std::endl;
is undefined behavior; anything can happen.
For historical reasons (dating back to C), '\0' and 0 will convert
implicitly to any pointer type, resulting in a null pointer.
A char* that points to a null character is simply a zero-length string. No harm in printing that.
But a char* whose value is null is a different story. Trying to print that would mean dereferencing a null pointer, which is undefined behavior. A crash is likely.
Assigning '\0' to a pointer isn't really correct, by the way, even if it happens to work: you're assigning a character value to a pointer variable. Use 0 or NULL, or nullptr in C++11, when assigning to a pointer.
Just regarding the cout << '\0' part…
"Terminating the string" of a file or stream in text mode has an undefined effect on its contents. The C++ standard defers to the C standard on matters of text semantics (C++11 27.9.1.1/2), and C is pretty draconian (C99 §7.19.2/2):
Data read in from a text stream will necessarily compare equal to the data that were earlier written out to that stream only if: the data consist only of printing characters and the control characters horizontal tab and new-line; no new-line character is immediately preceded by space characters; and the last character is a new-line character.
Since '\0' is a control character and cout is a text stream, the resulting output may not read as you wrote it.
Take a look at this example:
http://ideone.com/8MHGH
The main problem you have is that str is pointer to a char not a char, so you should assign it to a string: str = "\0";
When you assign it to char, it remains 0 and then the fail bit of cout becomes true and you can no longer print to it. Here is another example where this is fixed:
http://ideone.com/c4LPh
I am stumped by the behaviour of the following in my Win32 (ANSI) function:
(Multi-Byte Character Set NOT UNICODE)
void sOut( HWND hwnd, string sText ) // Add new text to EDIT Control
{
int len;
string sBuf, sDisplay;
len = GetWindowTextLength( GetDlgItem(hwnd, IDC_EDIT_RESULTS) );
if(len > 0)
{
// HERE:
sBuf.resize(len+1, 0); // Create a string big enough for the data
GetDlgItemText( hwnd, IDC_EDIT_RESULTS, (LPSTR)sBuf.data(), len+1 );
} // MessageBox(hwnd, (LPSTR)sBuf.c_str(), "Debug", MB_OK);
sDisplay = sBuf + sText;
sDisplay = sDisplay + "\n\0"; // terminate the string
SetDlgItemText( hwnd, IDC_EDIT_RESULTS, (LPSTR)sDisplay.c_str() );
}
This should append text to the control with each call.
Instead, all string concatenation fails after the call to GetDlgItemText(), I am assuming because of the typecast?
I have used three string variables to make it really obvious. If sBuf is affected then sDisplay should not be affected.
(Also, why is len 1 char less than the length in the buffer?)
GetDlgItemText() corretly returns the content of the EDIT control, and SetDlgItemText() will correctly set any text in sDisplay, but the concatenation in between is just not happening.
Is this a "hidden feature" of the string class?
Added:
Yes it looks like the problem is a terminating NUL in the middle. Now I understand why the len +1. The function ensures the last char is a NUL.
Using sBuf.resize(len); will chop it off and all is good.
Added:
Charles,
Leaving aside the quirky return length of this particular function, and talking about using a string as a buffer:
The standard describes the return value of basic_string::data() to be a pointer to an array whose members equal the elements of the string itself.
That's precisely what's needed isn't it?
Further, it requires that the program must not alter any of the values of that array.
As I understand it that is going to change along with the guarantee that all bytes are contiguous. I forget where I read a long article on this, but MS already implements this it asserted.
What I don't like about using a vector is that the bytes are copied twice before I can return them: once into the vector and again into the string. I also need to instantiate a vector object and a string object. That is a lot of overhead. If there were some string friendly of working with vectors (or CStrings) without resorting to old C functions or sopying characters one by one, I would use them. The string is very syntax friendly in that way.
The data() function on a std::string returns a const char*. You are not allowed to right into the buffer returned by it, it may be a duplicated buffer.
What you could do instead is to used a std::vector<char> as a temporary buffer.
E.g. (untested)
std::vector<char> sBuf( len + 1 );
GetDlgItemText( /* ... */, &sBuf[0], len + 1 );
std::string newText( &sBuf[0] );
newText += sText;
Also, the string you pass to SetDlgItemText should be \0 terminated so you should used c_str() not data() for this.
SetDlgItemText( /* ... */, newText.c_str() );
Edit:
OK, I've just checked the contract for GetWindowTextLength and GetDlgItemText. Check my edits above. Both will include the space for a null terminator so you need to chop it off the end of your string otherwise concatenation of the two strings will include a null terminator in the middle of the string and the SetDlgItemText call will only use the first part of the string.
There is a further complication in that GetWindowTextLength isn't guaranteed to be accurate, it only guarantees to return a number big enough for a program to create a buffer for storing the result. It is extremely unlikely that this will actually affect a dialog box item owned by the calling code but in other situations the actual text may be shorter than the returned length. For this reason you should search for the first \0 in the returned text in any case.
I've opted to just use the std::string constructor that takes a const char* so that it finds the first \0 correctly.
The standard describes the return value of basic_string::data() to be a pointer to an array whose members equal the elements of the string itself. Further, it requires that the program must not alter any of the values of that array. This means that the return value of data() may or may not be a copy of the string's internal representation and even if it isn't a copy you still aren't allowed to write to it.
I am far away from the win32 api and their string nightmare, but there is something in the code that you can check. Standard C++ strings do not need to be null terminated and nulls can happen anywhere within the string. I won't comment on the fact that you are casting away constantness with your C-style cast, which is a problem on its own, but rather on the strange effect you are
When you initially create the string you allocate extra space for the null (and initialize all elements to '\0') and then you copy the elements. At that point your string is len+1 in size and the last element is a null. After that you append some other string, and what you get is a string that will still have a null character at position len. When you retrieve the data with either data() (does not guarantee null termination!) or c_str() the returned buffer will still have the null character at len position. If that is passed to a function that stops on null (takes a C style string), then even if the string is complete, the function will just process the first len characters and forget about the rest.
#include <string>
#include <cstdio>
#include <iostream>
int main()
{
const char hi[] = "Hello, ";
const char all[] = "world!";
std::string result;
result.resize( sizeof(hi), 0 );
// simulate GetDlgItemText call
std::copy( hi, hi+sizeof(hi), const_cast<char*>(result.data()) ); // this is what your C-style cast is probably doing
// append
result.append( all );
std::cout << "size: " << result.size() // 14
<< ", contents" << result // "Hello, \0world!" - dump to a file and edit with a binary editor
<< std::endl;
std::printf( "%s\n", result.c_str() ); // "Hello, "
}
As you can see, printf expects a C-style string and will stop when the first null character is found, so that it can seem as if the append operation never took place. On the other hand, c++ streams do work properly with std::string and will dump the whole content, checking that the strings were actually appended.
A patch to your append operation disappearing would be removing the '\0' from the initial string (reserve only len space in the string). But that is not really a good solution, you should never use const_cast (there are really few places where it can be required and this is not one of them), the fact that you don't see it is even worse: using C style casts is making your code look nicer than it is.
You have commented on another answer that you do not want to add std::vector (which would provide with a correct solution as &v[0] is a proper mutable pointer into the buffer), of course, not adding the extra space for the '\0'. Consider that this is part of an implementation file, and the fact that you use or not std::vector will not extend beyond this single compilation unit. Since you are already using some STL features, you are not adding any extra requirement to your system. So to me that would be the way to go. The solution provided by Charles Bailey should work provided that you remove the extra null character.
This is NOT an answer. I have added it here as an answer only so that I can use formatting in a long going discussion about const_cast.
This is an example where using const_cast can break a running application:
#include <iostream>
#include <map>
typedef std::map<int,int> map_type;
void dump( map_type const & m ); // implemented somewhere else for concision
int main() {
map_type m;
m[1] = 10;
m[2] = 20;
m[3] = 30;
map_type::iterator it = m.find(2);
const_cast<int&>(it->first) = 10;
// At this point the order invariant of the container is broken:
dump(); // (1,10),(10,20),(3,30) !!! unordered by key!!!!
// This happens with g++-4.0.1 in MacOSX 10.5
if ( m.find(3) == m.end() ) std::cout << "key 3 not found!!!" << std::endl;
}
That is the danger of using const_cast. You can get away in some situations, but in others it will bite back, and probably hard. Try to debug in thousands of lines where the element with key 3 was removed from the container. And good luck in your search, for it was never removed.