2

I'm trying to copy data that conatin '\0'. I'm using C++ . When the result of the research was negative, I decide to write my own fonction to copy data from one char* to another char*. But it doesn't return the wanted result ! My attempt is the following :

#include <iostream>


char* my_strcpy( char* arr_out,  char* arr_in, int bloc )
{
 char* pc= arr_out;

 for(size_t i=0;i<bloc;++i)     
 {

        *arr_out++ = *arr_in++ ;
 }

 *arr_out = '\0';
 return pc;
}

int main()
{
    char * out= new char[20];
    my_strcpy(out,"12345aa\0aaaaa  AA",20);
    std::cout<<"output data: "<< out << std::endl;
    std::cout<< "the length of my output data: " << strlen(out)<<std::endl;
    system("pause");
    return 0;
}

the result is here: enter image description here

I don't understand what is wrong with my code.

Thank you for help in advance.

xtensa1408
  • 584
  • 12
  • 32
  • 5
    Your function almost works correctly (aside from the last access to `arr_out` that is out of bounds). But both `std::cout` and `strlen` interpret a `\0` as a string terminator. – user703016 Sep 30 '14 at 10:47
  • `for (blah) { cout << out[blah]; }` but what you want `\0` to come out as I don't know.. – Neil Kirk Sep 30 '14 at 10:48
  • 1
    `char out[20]; std::copy_n("12345aa\0aaaaa AA", 20, out);` – Neil Kirk Sep 30 '14 at 10:49
  • 2
    Why not use `std::string` or `std::vector`? That would be the usual way of doing things in C++. – James Kanze Sep 30 '14 at 10:50
  • I'm trying to copy a cipher text and by bad luck my cipher text contain the symboles '\' and '0' adjacent – xtensa1408 Sep 30 '14 at 10:52
  • @james Kanze, I have to use char* for two reasons. First, I'm using binary file. Second, I have many functions already writed that accept char* as parameter – xtensa1408 Sep 30 '14 at 10:54
  • Can you cast each char to int before you output? – Neil Kirk Sep 30 '14 at 10:57
  • If all you want to do is copy a block of memory, you should look at memcpy() as an alternate to strcpy(). These are both C functions. Personally, I would prefer to use std::vector and assignment... that would embrace the "C++ way" more cleanly... and lead to a shorter implementation with lower risk of bugs. – aSteve Sep 30 '14 at 10:59
  • @ Neil Kirk To clarify What I do: I have a variable (type std:: string) which I should to copy to char*. So I have to convert std::string which contains strange characters to char*. That's why it's very bad idea to convert each carater to int. – xtensa1408 Sep 30 '14 at 11:02
  • @aSteve I tested memcpy and I have also the same problem – xtensa1408 Sep 30 '14 at 11:04
  • 1
    I've just noticed Chris' similar answer below. He's right - you don't have a problem with zero bytes and memcpy() - but when you pass a char* to "cout <<" - cout will assume it is a null-terminated string. The data will be copied - but not displayed. Write a function that iterates over all 20 bytes explicitly... you can use 'isprint()' to see if the character is printable, or not, and output some other representation for non-printable (including zero) bytes. – aSteve Sep 30 '14 at 11:09
  • That's exactly what I want. So I had to use isprint() to see if the character is prentable ;) A followup to this post here: https://stackoverflow.com/questions/26121894/how-could-i-copy-data-using-my-own-fonction-of-copy – xtensa1408 Sep 30 '14 at 13:34

6 Answers6

1

Your my_strcpy is working fine, when you write a char* to cout or calc it's length with strlen they stop at \0 as per C string behaviour. By the way, you can use memcpy to copy a block of char regardless of \0.

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
1

If you know the length of the 'string' then use memcpy. Strcpy will halt its copy when it meets a string terminator, the \0. Memcpy will not, it will copy the \0 and anything that follows.

Chris
  • 2,655
  • 2
  • 18
  • 22
  • I tried to do this with memcpy() but I had the same problem – xtensa1408 Sep 30 '14 at 10:56
  • If you did memcpy (dst, src, 20) then the string definitely copied. Display of the string would stop at the \0 though. Perhaps what you are looking for is a way to break string up to display separate parts? – Chris Sep 30 '14 at 11:00
  • If cout and strlen stop at '\0',How can I be certain that my string is copied. I'm looking for converting std::string to char*. – xtensa1408 Sep 30 '14 at 11:25
  • You actually have two null terminated strings back to back. You could have three, or more. So you need a looped cout to display each string. – Chris Sep 30 '14 at 12:02
  • A followup to this post here: https://stackoverflow.com/questions/26121894/how-could-i-copy-data-using-my-own-fonction-of-copy – xtensa1408 Sep 30 '14 at 13:35
  • 1
    Read the other question, but it has external-site JS present which is blocked. Your output string is not going to work, there are other escape sequences which can manipulate the output, delete a line and so on. The only safe way to do what you are trying to do is to hex dump it. To prove this, you cannot print a \0 physically, so in the resulting string, how are you going to determine where the \0 was? – Chris Sep 30 '14 at 15:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/62196/discussion-between-sadok-and-chris). – xtensa1408 Sep 30 '14 at 15:48
1

(Note: For any readers who are unaware that \0 is a single-character byte with value zero in string literals in C and C++, not to be confused with the \\0 expression that results in a two-byte sequence of an actual backslash followed by an actual zero in the string... I will direct you to Dr. Rebmu's explanation of how to split a string in C for further misinformation.)


C++ strings can maintain their length independent of any embedded \0. They copy their contents based on this length. The only thing is that the default constructor, when initialized with a C-string and no length, will be guided by the null terminator as to what you wanted the length to be.

To override this, you can pass in a length explicitly. Make sure the length is accurate, though. You have 17 bytes of data, and 18 if you want the null terminator in the string literal to make it into your string as part of the data.

#include <iostream>
using namespace std;

int main() {
    string str ("12345aa\0aaaaa  AA", 18);
    string str2 = str;
    cout << str;
    cout << str2;
    return 0;
}

(Try not to hardcode such lengths if you can avoid it. Note that you didn't count it right, and when I corrected another answer here they got it wrong as well. It's error prone.)

On my terminal that outputs:

12345aaaaaaa  AA
12345aaaaaaa  AA

But note that what you're doing here is actually streaming a 0 byte to the stdout. I'm not sure how formalized the behavior of different terminal standards are for dealing with that. Things outside of the printable range can be used for all kinds of purposes depending on the kind of terminal you're running... positioning the cursor on the screen, changing the color, etc. I wouldn't write out strings with embedded zeros like that unless I knew what the semantics were going to be on the stream receiving them.

Consider that if what you're dealing with are bytes, not to confuse the issue and to use a std::vector<char> instead. Many libraries offer alternatives, such as Qt's QByteArray

Community
  • 1
  • 1
  • Thank you for your answer. Maybe I will try to use std::vector to save my std::string . ( I'm not using Qt) – xtensa1408 Sep 30 '14 at 11:10
  • 1
    @SADOK Note that vectors are really just managed memory blocks, and you can get a char* from a std::vector (as well as the length)...same for std::string. memcpy and such are rarely-if-ever used in proper C++ programming; they are low-level and error prone. – HostileFork says dont trust SE Sep 30 '14 at 11:11
  • A followup to this post here: https://stackoverflow.com/questions/26121894/how-could-i-copy-data-using-my-own-fonction-of-copy – xtensa1408 Sep 30 '14 at 13:35
0

Your function is fine (except that you should pass to it 17 instead of 20). If you need to output null characters, one way is to convert the data to std::string:

std::string outStr(out, out + 17);
std::cout<< "output data: "<< outStr << std::endl;
std::cout<< "the length of my output data: " << outStr.length() <<std::endl;
Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • You didn't count the bytes. Undefined behavior. (I count 18.) – HostileFork says dont trust SE Sep 30 '14 at 10:54
  • I'm blocked here because I must return char* and not std::string. If not, It will be big problem for me because I will have to change all fonctions developed since two weeks of coding – xtensa1408 Sep 30 '14 at 11:16
  • @SADOK you had better to describe your original problem. If you get a `char*` containing zero bytes, you must know its length from somewhere. So you can and should pass it along. – Anton Savin Sep 30 '14 at 11:28
  • @ Anton Savin: I have question about rules in stackoverflow please. What I have to do to describe my original question ? Delete this post and replace it or I ask another question with the same topic !! ( because It is not easy to describe here my original problem) – xtensa1408 Sep 30 '14 at 11:34
  • @SADOK the question you asked here has been answered within original limitations quite correctly by several people. I think now it's too late to change the question, better ask a new one. But I don't know your specifics, for example your new question may be offtopic for SO. – Anton Savin Sep 30 '14 at 11:38
  • Is it possible to tag all people who are here in my new question !? – xtensa1408 Sep 30 '14 at 11:40
0
  • I don't understand what is wrong with my code.

    my_strcpy(out,"12345aa\0aaaaa AA",20);

Your string contains character '\' which is interpreted as escape sequence. To prevent this you have to duplicate backslash:

my_strcpy(out,"12345aa\\0aaaaa  AA",20);

Test

output data: 12345aa\0aaaaa  AA
the length of my output data: 18
kotlomoy
  • 1,420
  • 8
  • 14
  • Do you beleive I have to browse into thousand and thousand of characters and add '\' !! The variable that I have to convert to char* is the result of a function that cipher a pdf file, in other words function that cipher thousands of caracters ... – xtensa1408 Sep 30 '14 at 11:21
  • @SADOK: That's another question. Edit your post accordingly, or make another post. – kotlomoy Sep 30 '14 at 11:28
  • OK, It's better to make another post ;) – xtensa1408 Sep 30 '14 at 11:41
  • @SADOK The question title is *"How could I copy data that contain '\0' character"*...we were assuming here that you knew it was a null byte and not a two character sequence of backslash followed by a zero...correct? – HostileFork says dont trust SE Sep 30 '14 at 12:05
  • Can you give me what is the difference between null byte and a two character sequence of backslash followed by a zero. Is'nt the same ? In any case you can find [here] a description for my original problem https://stackoverflow.com/questions/26121894/how-could-i-copy-data-using-my-own-fonction-of-copy – xtensa1408 Sep 30 '14 at 13:27
-1

Your string is already terminated midway.

my_strcpy(out,"12345aa\0aaaaa  AA",20);

Why do you intend to have \0 in between like that? Have some other delimiter if yo so desire Otherwise, since std::cout and strlen interpret a \0 as a string terminator, you get surprises. What I mean is that follow the convention i.e. '\0' as string terminator

Dr. Debasish Jana
  • 6,980
  • 4
  • 30
  • 69
  • std::cout does not interpret `\0` as a string terminator. It sends the number of bytes held by the string to stdout and your terminal or whatever does what it does with it. Your terminal may interpret lots of codes in other ways (positioning the cursor, changing text color, etc.) so it's best to stay in printable range unless you know what you're doing. – HostileFork says dont trust SE Sep 30 '14 at 10:56
  • @Debasish Jana: I'm trying to copy a cipher text and by bad luck my cipher text contain the symboles '\' and '0' adjacent – – xtensa1408 Sep 30 '14 at 10:57
  • @SADOK, then your string copy function is absolutely correct, you may reserve a header in the very beginning of your text buffer to hold the intended length of the string, that would serve your purpose, any upvote pls? – Dr. Debasish Jana Sep 30 '14 at 11:06
  • It's Ok with upVote. It's done. I have already a header. It contains the length of my string which I want to convert to char* . – xtensa1408 Sep 30 '14 at 11:12
  • @SADOK, so your string should contain "17:12345aa\0aaaaa AA" instead of "12345aa\0aaaaa AA"presuming 17 is the length of the actual string that follows : delemeter – Dr. Debasish Jana Sep 30 '14 at 15:06