Phil
Phil

Reputation: 36289

Will memcpy copy a string correctly?

I am working on implementing some low-level file writing, where the file format is specific down to each bit. I need to copy a string from an NSString into null-terminated string with length 16 (which is not assignable, according to Xcode). I am a total n00b when it comes to c, and want to be sure I understand this correctly. This is what I am currently doing:

//I have a non-null NSString called _friendly_name.
const char *string = [_friendly_name UTF8String];
//profile.friendly is a utf-8 null-terminated string
memcpy(&profile.friendly_name, &string, 16);

This has not yet been tested, but I want to be sure this will work. Will this provide the behavior I am expecting? Or should I be copying the string a different way (such as strcpy)?

Upvotes: 3

Views: 9295

Answers (4)

qPCR4vir
qPCR4vir

Reputation: 3571

//I have a non-null NSString called _friendly_name.
const char *string = [_friendly_name UTF8String];
//profile.friendly is a utf-8 null-terminated string  : char [17] ??
memcpy(profile.friendly_name, string, 16);
profile.friendly_name[16]='\0';

EDIT: Yes, exactly. Adding a NULL at ende. Actually it could be better to initialize to all 0 before copying. strcpy will not work if _friendly_name don't have a '\0' before position 16. strncpy will be fine.

EDIT2:

Some problem arised why we dont have all the needed information:

1- You need an NULL-terminated string of length 16 (16 character+0 as in char var[17];) or you need a 16 byte field terminated witch NULL as in char var[16];?

2- what is the desred "error" management: if the original string is larged than "16" we just truncate it, or we trow an error?

Assuming you need a field of 16 bytes and a truncate string you can use:

strncpy(profile.friendly_name, string, 15);
profile.friendly_name[15]='\0';

EDIT3:

Also... to truncate have to be made carefully: we dont want to truncate a multibyte character...

Upvotes: 0

Dietrich Epp
Dietrich Epp

Reputation: 213338

You can use memcpy() or strcpy(), but you'll have to do the bounds checking yourself and there are other errors in your code.

So, I take it you want to copy an NSString into a char[16] array. You can use an NSString builtin method to do this, and you don't have to use memcpy() yourself:

NSString *src;
char dest[16];
NSUinteger destlen;
[src getBytes:dest
    maxLength:sizeof(dest) - 1
    usedLength:&destlen
    encoding:NSUTF8StringEncoding
    options:0
    range:NSMakeRange(0, [src length])
    remainingRange:NULL];
dest[destlen] = '\0';

If you want to use memcpy(), then you'll have to do it this way:

NSString *src;
char dest[16], *srcUtf8;
size_t len;

srcUtf8 = [src UTF8String];
len = strlen(srcUtf8);
if (len >= sizeof(dest))
    len = sizeof(dest) - 1;
memcpy(dest, srcUtf8, len);
dest[len] = '\0';

Errors in your code

This code has two errors in it!

memcpy(&profile.friendly_name, &string, 16); // Wrong!

First of all, &string is wrong. It should be string, because string is a pointer to the string data you want to copy. If you copy &string, you will get a pointer and some random bits of stack data copied instead. In other words, you'll get garbage.

Secondly, 16 is wrong. You can only copy 16 bytes if you know that string points to at least 16 bytes of data. This will cause a segmentation fault (crash your program, hard) if string is less than 16 bytes the following data in memory is not readable. It might not crash today, but maybe it will crash next week, and you'll have forgotten all about it?

It's wrong, don't pass 16 unless you know the source is at least 16 bytes long.

Upvotes: 5

Josh Petitt
Josh Petitt

Reputation: 9579

Have you read the doc? memcpy will work, but you pass in the number of bytes to copy. It will copy the number of bytes you specify, irrelevant of a NULL. strcpy you don't pass the length, it will copy bytes up to and including the first NULL.

http://www.cplusplus.com/reference/cstring/memcpy/

http://www.cplusplus.com/reference/cstring/strcpy/

Upvotes: 2

Bob Murphy
Bob Murphy

Reputation: 5964

memcpy() should work fine, but strcpy() will work fine too. You just need to make sure that profile.friendly_name is big enough to hold what you're copying into it.

Upvotes: 3

Related Questions