7

Is there an exact equivalent to strncpy in the C++ Standard Library? I mean a function, that copies a string from one buffer to another until it hits the terminating 0? For instance when I have to parse strings from an unsafe source, such as TCP packets, so I'm able to perform checks in length while coping the data.

I already searched a lot regarding this topic and I also found some interesting topics, but all of those people were happy with std::string::assign, which is also able to take a size of characters to copy as a parameter. My problem with this function is, that it doesn't perform any checks if a terminating null was already hit - it takes the given size serious and copies the data just like memcpy would do it into the string's buffer. This way there is much more memory allocated and copied than it had to be done, if there were such a check while coping. 

That's the way I'm working around this problem currently, but there is some overhead I'd wish to avoid:

 // Get RVA of export name const ExportDirectory_t *pED = (const ExportDirectory_t*)rva2ptr(exportRVA); sSRA nameSra = rva2sra(pED->Name); // Copy it into my buffer char *szExportName = new char[nameSra.numBytesToSectionsEnd]; strncpy(szExportName, nameSra.pSection->pRawData->constPtr<char>(nameSra.offset), nameSra.numBytesToSectionsEnd); szExportName[nameSra.numBytesToSectionsEnd - 1] = 0; m_exportName = szExportName; delete [] szExportName; 

This piece of code is part of my parser for PE-binaries (of the routine parsing the export table, to be exact). rva2sra converts a relative virtual address into a PE-section relative address. The ExportDirectory_t structure contains the RVA to the export name of the binary, which should be a zero-terminated string. But that doesn't always have to be the case - if someone would like it, it would be able to omit the terminating zero which would make my program run into memory which doesn't belong to the section, where it would finally crash (in the best case...).

It wouldn't be a big problem to implement such a function by myself, but I'd prefer it if there were a solution for this implemented in the C++ Standard Library.

3
  • 7
    std::strings aren't null-terminated. Do you want a copy of the string, or just a copy of everything up to the first null byte (which may or may not be there)? Commented Jan 16, 2012 at 19:35
  • Oh, good to know. I'd like to copy everything up to the first null byte. Commented Jan 16, 2012 at 19:41
  • 1
    Be careful what you ask for. Commented Aug 6, 2014 at 6:15

7 Answers 7

12

If you know that the buffer you want to make a string out of has at least one NUL in it then you can just pass it to the constructor:

const char[] buffer = "hello\0there"; std::string s(buffer); // s contains "hello" 

If you're not sure, then you just have to search the string for the first null, and tell the constructor of string to make a copy of that much data:

int len_of_buffer = something; const char* buffer = somethingelse; const char* copyupto = std::find(buffer, buffer + len_of_buffer, 0); // find the first NUL std::string s(buffer, copyupto); // s now contains all the characters up to the first NUL from buffer, or if there // was no NUL, it contains the entire contents of buffer 

You can wrap the second version (which always works, even if there isn't a NUL in the buffer) up into a tidy little function:

std::string string_ncopy(const char* buffer, std::size_t buffer_size) { const char* copyupto = std::find(buffer, buffer + buffer_size, 0); return std::string(buffer, copyupto); } 

But one thing to note: if you hand the single-argument constructor a const char* by itself, it will go until it finds a NUL. It is important that you know there is at least one NUL in the buffer if you use the single-argument constructor of std::string.

Unfortunately (or fortunately), there is no built in perfect equivalent of strncpy for std::string.

Sign up to request clarification or add additional context in comments.

1 Comment

Is this a useful shorthand for the same functionality: std::string(buffer, strnlen(buffer, buffer_size)) ?
3

The std::string class in STL can contain null characters within the string ("xxx\0yyy" is a perfectly valid string of length 7). This means that it doesn't know anything about null termination (well almost, there are conversions from/to C strings). In other words, there's no alternative in the STL for strncpy.

There are a few ways to still accomplish your goal with a shorter code:

const char *ptr = nameSra.pSection->pRawData->constPtr<char>(nameSra.offset); m_exportName.assign(ptr, strnlen(ptr, nameSra.numBytesToSectionsEnd)); 

or

const char *ptr = nameSra.pSection->pRawData->constPtr<char>(nameSra.offset); m_exportName.reserve(nameSra.numBytesToSectionsEnd); for (int i = 0; i < nameSra.numBytesToSectionsEnd && ptr[i]; i++) m_exportName += ptr[i]; 

Comments

2

Is there an exact equivalent to strncpy in the C++ Standard Library?

I certainly hope not!

I mean a function, that copies a string from one buffer to another until it hits the terminating 0?

Ah, but that's not what strncpy() does -- or at least it's not all it does.

strncpy() lets you specify the size, n, of the destination buffer, and copies at most n characters. That's fine as far as it goes. If the length of the source string ("length" defined as the number of characters preceding the terminating '\0') exceeds n, the destination buffer is padded with additional \0's, something that's rarely useful. And if the length if the source string exceeds n, then the terminating '\0' is not copied.

The strncpy() function was designed for the way early Unix systems stored file names in directory entries: as a 14-byte fixed-size buffer that can hold up to a 14-character name. (EDIT: I'm not 100% sure that was the actual motivation for its design.) It's arguably not a string function, and it's not just a "safer" variant of strcpy().

You can achieve the equivalent of what one might assume strncpy() does (given the name) using strncat():

char dest[SOME_SIZE]; dest[0] = '\0'; strncat(dest, source_string, SOME_SIZE); 

This will always '\0'-terminate the destination buffer, and it won't needlessly pad it with extra '\0' bytes.

Are you really looking for a std::string equivalent of that?

EDIT : After I wrote the above, I posted this rant on my blog.

1 Comment

I'm aware of these facts (except the thing about the zero padding), but it was the 'best' function in the Standard C/C++ Library I was able to find to fill my needs. As you are able to see in my example given in the first post, I added the terminating null byte manually. Doesn't strncat search for the first null byte in the destination string and then starts to append at exactly that position it found the null byte while checking if the length is exceeded and finally adds a terminating null? Therefore I'd had to set the first byte of my target buffer to null manually, am I right?
1

There is no built-in equivalent. You have to roll your own strncpy.

#include <cstring> #include <string> std::string strncpy(const char* str, const size_t n) { if (str == NULL || n == 0) { return std::string(); } return std::string(str, std::min(std::strlen(str), n)); } 

Comments

1

The string's substring constructor can do what you want, although it's not an exact equivalent of strncpy (see my notes at the end):

std::string( const std::string& other, size_type pos, size_type count = std::string::npos, const Allocator& alloc = Allocator() ); 

Constructs the string with a substring [pos, pos+count) of other. If count == npos or if the requested substring lasts past the end of the string, the resulting substring is [pos, size()).

Source: http://www.cplusplus.com/reference/string/string/string/

Example:

#include <iostream> #include <string> #include <cstring> int main () { std::string s0 ("Initial string"); std::string s1 (s0, 0, 40); // count is bigger than s0's length std::string s2 (40, 'a'); // the 'a' characters will be overwritten strncpy(&s2[0], s0.c_str(), s2.size()); std::cout << "s1: '" << s1 << "' (size=" << s1.size() << ")" << std::endl; std::cout << "s2: '" << s2 << "' (size=" << s2.size() << ")" << std::endl; return 0; } 

Output:

s1: 'Initial string' (size=14) s2: 'Initial string' (size=40) 

Differences with strncpy:

  • the string constructor always appends a null-terminating character to the result, strncpy does not;
  • the string constructor does not pad the result with 0s if a null-terminating character is reached before the requested count, strncpy does.

Comments

0

Use the class' constructor:

string::string str1("Hello world!"); string::string str2(str1); 

This will yield an exact copy, as per this documentation: http://www.cplusplus.com/reference/string/string/string/

3 Comments

OP is not looking for strcpy but strncpy.
@pmr: OP clarified he only wants up to the first NULL byte.
Doesn't protect against reading beyond the known bounds of the buffer.
0

std::string has a constructor with next signature that can be used :

string ( const char * s, size_t n ); 

with next description:

Content is initialized to a copy of the string formed by the first n characters in the array of characters pointed by s.

4 Comments

Note that this will not stop at null bytes (it says "array of characters", not "C-string"). That may or may not be what you want.
Thank you for your reply, but that isn't exactly what I wanted to know. The constructor copies everything until it hits the first null byte OR, if a size is given, everything, just like memcpy. If there is a null byte in my source string, the content after it is also copied.
@SethCarnegie Yes, that's why I added the description. Maybe this constructor is not what the OP needs
This doesn't respect null terminators, and is quite a deceptive constructor to me. With: const char arr[10] = "Hello"; std::string a(arr); std::string b(arr, 10); a != b, because b has 4 uninitialised bytes included at the end to make up the length of 10. It is sad that std::strings can have \0 chars in them, which doesn't make sense in most cases.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.