So my assignment is:
Using the strncpy and strncat functions in #include<cstring>,
implement a function
void concat(const char a[ ], const char b[ ], char result[ ],
int result_maxlength)
that concatenates the strings a and b to the buffer result. Be sure
not to overrun the result. It can hold result_maxlength characters,
not counting the \0 terminator. (That is, the buffer has
buffer_maxlength + 1 bytes available.) Be sure to provide a ‘\0’
terminator.
My solution (thus far) is below but I don't know what I'm doing wrong. Not only do I get a run-time check failure 2 error when I actually run the program, but I'm unsure where I should be adding the \0 terminator or even if I should be using strncat rather than strncpy. Hopefully someone can lead me in the right direction. And yes this is hw. That's why I said just lead me in the right direction so that I can try to figure it out :p
#include <iostream>
#include <cstring>
using namespace std;
void concat(const char a[ ], const char b[ ], char result[ ], int result_maxlength);
int main()
{
char a[] = "Woozle";
char b[] = "Heffalump";
char c[5];
char d[10];
char e[20];
concat(a, b, c, 5);
concat(a, b, d, 10);
concat(a, b, e, 20);
cout << c << "\n";
cout << d << "\n";
cout << e << "\n";
return 0;
}
void concat(const char a[ ], const char b[ ], char result[ ], int result_maxlength)
{
strncat(result, a, result_maxlength);
strncat(result, b, result_maxlength);
}
At the very least your result is uninitialized before the first strncat in concat.
EDIT: And yes, as Michael Burr points out your result size is supposed to be changing as you progress and calculated from the very start. Actually, it is misleading name you picked, because it is the max size of source, not destination.
The last argument to strncat() represents the remaining space available in the buffer - not the full size of the buffer.
Also note that that argument includes the spot that the terminating null character will need, so you'll need to account for that since the spec for concat() is otherwise.
Finally, according to the concat() spec, the result placed in the buffer should not be concatenated to the existing contents of the buffer (those contents should be replaced). Also, make sure you test that your function properly handles a zero length result_maxlength argument being passed in.
strncpy from a to result (whatever is smaller, lenght of a or result_maxlength)
strncat from b to remaining of result (whatever is smaller, lenght of b or result_maxlength- lenght of a)
before every return just put a \0 at last position result[result_maxlength-1] ='\0';
It's actually not specified WHAT to do if result is too short, should you add trailing 0 or not. I guess you'd better terminate that string.
tip : remaining of result is result+strlen(a)
Related
Beside most common (format) function C++20 also comes with format_to_n that takes output iterator and count.
What I am looking for is the way to make sure that in case I ran out of space that my string is still zero terminated.
For example I want the following program to output 4 instead of 42.
#include<string>
#include<iostream>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
void f(char* in){
fmt::format_to_n(in, 2,"{}{}", 42,'\0');
std::cout << in;
}
int main(){
char arr[]= "ABI";
f(arr);
}
Is this possible without me manually doing the comparison of number of written chars and max len I provided to function?
If you are wondering why I use '\0' as an argument:
I have no idea how to put terminating char in format string.
note: I know that for one argument I can specify max len with :. but I would like a solution that works for multiple arguments.
format_to_n returns a result. You can use that struct:
void f(char* in){
auto [out, size] = fmt::format_to_n(in, 2, "{}", 42);
*out = '\0';
std::cout << in;
}
Note that this might write "42\0" into in, so adjust your capacity as appropriate (2 for a buffer of size 3 is correct).
format_to_n returns a struct containing, among other things, the iterator past the last character written. So it's quite easy to simply check the difference between that iterator and the original iterator against the maximum number of characters, and insert a \0 where appropriate:
void f(char* in)
{
const max_chars = 2;
auto fmt_ret = fmt::format_to_n(in, max_chars,"{}", 42);
char *last = fmt_ret.out;
if(last - in == max_chars)
--last;
*last = '\0';
std::cout << in;
}
Note that this assumes that the array only holds exactly the number of characters (including the NUL terminator) as the number you attempted to pass to format_to_n. The above code will therefore overwrite the last character written with a NUL terminator, essentially doing further truncation.
If instead you pass to format_to_n the number of characters in the array - 1, then you can simply always write the NUL terminator to fmt_ret.out itself.
I have a string like,
string str="aaa\0bbb";
and I want to copy the value of this string to a char* variable. I tried the following methods but none of them worked.
char *c=new char[7];
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
strcpy(c,str.data()); // c="aaa"
str.copy(c,7); // c="aaa"
How can I copy that string to a char* variable without loosing any data?.
You can do it the following way
#include <iostream>
#include <string>
#include <cstring>
int main()
{
std::string s( "aaa\0bbb", 7 );
char *p = new char[s.size() + 1];
std::memcpy( p, s.c_str(), s.size() );
p[s.size()] = '\0';
size_t n = std::strlen( p );
std::cout << p << std::endl;
std::cout << p + n + 1 << std::endl;
}
The program output is
aaa
bbb
You need to keep somewhere in the program the allocated memory size for the character array equal to s.size() + 1.
If there is no need to keep the "second part" of the object as a string then you may allocate memory of the size s.size() and not append it with the terminating zero.
In fact these methods used by you
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
str.copy(c,7); // c="aaa"
are correct. They copy exactly 7 characters provided that you are not going to append the resulted array with the terminating zero. The problem is that you are trying to output the resulted character array as a string and the used operators output only the characters before the embedded zero character.
Your string consists of 3 characters. You may try to use
using namespace std::literals;
string str="aaa\0bbb"s;
to create string with \0 inside, it will consist of 7 characters
It's still won't help if you will use it as c-string ((const) char*). c-strings can't contain zero character.
There are two things to consider: (1) make sure that str already contains the complete literal (the constructor taking only a char* parameter might truncate at the string terminator char). (2) Provided that str actually contains the complete literal, statement memcpy(c,str.data(),7) should work. The only thing then is how you "view" the result, because if you pass c to printf or cout, then they will stop printing once the first string terminating character is reached.
So: To make sure that your string literal "aaa\0bbb" gets completely copied into str, use std::string str("aaa\0bbb",7); Then, try to print the contents of c in a loop, for example:
std::string str("aaa\0bbb",7);
const char *c = str.data();
for (int i=0; i<7; i++) {
printf("%c", c[i] ? c[i] : '0');
}
You already did (not really, see edit below). The problem however, is that whatever you are using to print the string (printf?), is using the c string convention of ending strings with a '\0'. So it starts reading your data, but when it gets to the 0 it will assume it is done (because it has no other way).
If you want to simply write the buffer to the output, you will have to do this with something like
write(stdout, c, 7);
Now write has information about where the data ends, so it can write all of it.
Note however that your terminal cannot really show a \0 character, so it might show some weird symbol or nothing at all. If you are on linux you can pipe into hexdump to see what the binary output is.
EDIT:
Just realized, that your string also initalizes from const char* by reading until the zero. So you will also have to use a constructor to tell it to read past the zero:
std::string("data\0afterzero", 14);
(there are prettier solutions probably)
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
char p [] = "TEST";
strcat (p, "VAL");
cout << p;
return 0;
}
If what I understand is correct, a statement like char p [] = "TEST"; will allocate space from stack. When I call strcat() for such a string how the storage for p[] is adjusted to accommodate extra characters?
Last cout prints "TESTVAL". Is it valid to call strcat like this? If yes, how this works? I might be having problem with my understanding, but feeling like I lost touch. So this could easily be a dumb question. Please shed some light.
The storage is not adjusted, the call is not valid, and the behaviour of the code is undefined.
when you write
char buffer[] = "some literal";
it is expanded to
char buffer[sizeof("some literal")] = "some literal";
which has exact size to store "some literal" and nothing more.
when you concate another string in the end of the current buffer- you write beyond the boundries of the array - having undefined behavior.
another issue that in C++, we usually use std::string to handle strings, which does all the memory adjustment for us automatically.
p reserves space for 5 characters (4 + 1 for the null terminator). You are then appending 3 more characters which needs room for 8 (7 + 1 for the null). You don't have enough room for that and will be overwriting the stack. Depending on your compiler and build settings, you may not see any difference as potentially, the compiler leaves spaces between stack variables. On an optimised release build, you will probably get a crash.
If you change your code to look like this, you should see that sentinel1 & 2 are no longer 0 (it depends on the compiler which one will get trashed).
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
int sentinel1 = 0;
char p [] = "TEST";
int sentinel2 = 0;
strcat (p, "VAL");
cout << p << sentinel1 << sentinel2;
return 0;
}
I have a char array called names[50]
Basically, I use
strncpy(this->names, names, sizeof(names))
however this will only truncate characters at the end.
How do I truncate characters from the start?
For example, BillSteveLinusMikeGeorgeBillSteveLinusMikeGeorgeGeorge should be teveLinusMikeGeorgeBillSteveLinusMikeGeorgeGeorge
If I have understood correctly then using the string you showed as an example you have to write
strncpy( this->names, names + 5, sizeof(names) - 5 );
You can change the source address for strncpy:
strncpy(this->names, &(names[10]), num_of_chars_to_copy);
Notice that no null-character is implicitly appended at the end of the destination string if the source string is longer than num.
You need to be clear what you want to do... is names[] variable in length from call to call? Is this->names a fixed length? Note that the length for the number of bytes to copy should be the number of bytes available in this->names... Otherwise you run the risk of overflowing the memory.
I designed for you this simple function, You can use it as reference code for more complex issue:
void BackStrCopy(char* src, char* dest, int srcsize, int destsize)
{
if(srcsize >= destsize )
{
do
dest[destsize--] = src[srcsize--];
while( destsize + 1 );
}
}
int main()
{
char* src = "BillSteveLinusMikeGeorgeBillSteveLinusMikeGeorgeGeorge";
char dest[50];
BackStrCopy(src, dest, strlen(src), 25);
}
I tested it end work.
I thing that the function code does not require any comment:)
If my solution help you, please remember to check it as answered.
Ciao
I've got function, that saves values that takes in the argument. And I have to implement two ways, to accept the input - as a char* and then via strncpy.
Ie: a . Add("123456/7890", "John", "Doe", "2000-01-01", "Main street", "Seattle");
It works allright, untill I use strncpy:
bool status;
char lID[12], lDate[12], lName[50], lSurname[50], lStreet[50], lCity[50];
strncpy(lID, "123456/7890", sizeof ( lID));
strncpy(lName, "John", sizeof ( lName));
strncpy(lSurname, "Doe", sizeof ( lSurname));
strncpy(lDate, "2000-01-01", sizeof ( lDate));
strncpy(lStreet, "Main street", sizeof ( lStreet));
strncpy(lCity, "Seattle", sizeof ( lCity));
status = c . Add(lID, lName, lSurname, lDate, lStreet, lCity);
//is true
strncpy(lID, "987654/3210", sizeof ( lID));
strncpy(lName, "Freddy", sizeof ( lName));
strncpy(lSurname, "Kruger", sizeof ( lSurname));
strncpy(lDate, "2001-02-03", sizeof ( lDate));
strncpy(lStreet, "Elm street", sizeof ( lStreet));
strncpy(lCity, "Sacramento", sizeof ( lCity));
// notice, that I don't even save it at this point
strncpy(lID, "123456/7890", sizeof ( lID));
strncpy(lDate, "2002-12-05", sizeof ( lDate));
strncpy(lStreet, "Sunset boulevard", sizeof ( lStreet));
strncpy(lCity, "Los Angeles", sizeof ( lCity));
status = c . Resettle(lID, lDate, lStreet, lCity);
status = c . Print(cout, "123456/7890");
//is true
At this point I want to print out values for ID 123456/7890... so Name:John, Surname:Doe etc.
Neverthless It prints out values, that were saved as the last ones:
123456/7890 Freddy Kruger
2002-12-05 Sunset boulevard Los Angeles
2002-12-05 Sunset boulevard Los Angeles
My Add is declared as:
bool Add(const char * id,
const char * name,
const char * surname,
const char * date,
const char * street,
const char * city);
Resettle function is delared similar to Add, it just doesn't take name and surname arguments.
All values are saved to char ** arrays.
Could you please advice me, how to handle this situation, to be able to accept properly both inputs?
Ps: for char* input whole program works allright, so I don't expect any bug there..
Pps: pls don't advice me to use strings or any other constructions I don't use here - I'm very limited on imports, thus I use char* and other stuff...
I don't think you should be using sizeof here. strncpy needs the length of the string(number of chars to be copy from destination string to source string).
sizeof is the size of the pointer (e.g sizrof(IID) = address size, om my system its 4).
I think you need strlen(). Also, this needs to be called on the source pointer, not the destination pointer.
strncpy(lID, "987654/3210", strlen ("987654/3210"));
Be sure that IID is enough long to copy string, otherwise buffer overflow can be problem
read char * strncpy ( char * destination, const char * source, size_t num );, and
Copy characters from string
Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
Thanks to #JonathanLeffler: (The strlen() means the data won't be null-terminated. That's bad. At least copy strlen() + 1 bytes to get the null-terminator)
Note: No null-character is implicitly appended at the end of destination if source is longer than num (thus, in this case, destination may not be a null terminated C string).
Also read about: size_t strlen ( const char * str );
The problem is not in the strncpy() operations. It is in the material you've not shown us:
c.Add()
c.Resettle()
c.Print()
One or more of those has problems, but since we can't yet see them, we can't help you debug them.
Demonstration of strncpy()
There's a discussion of the behaviour of strncpy() in the comments to voodoogiant's answer. Here's a demonstration that:
strncpy(target, "string", strlen("string"));
does not null-terminate the output:
#include <string.h>
#include <stdio.h>
int main(void)
{
char buffer[32];
memset(buffer, 'X', sizeof(buffer));
printf("Before: %.*s\n", (int)sizeof(buffer), buffer);
strncpy(buffer, "123456/7890", strlen("123456/7890"));
printf("After: %.*s\n", (int)sizeof(buffer), buffer);
return(0);
}
Output:
Before: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
After: 123456/7890XXXXXXXXXXXXXXXXXXXXX
(Yes, I'm quite aware that the memset() does not null-terminate the buffer; it doesn't need to for this example because the printing is done carefully.)
As we do not know the datatype of lID et al. and hence the sizeof it is difficult to comment. Besides why not use the string container from STL