3

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)?

congusbongus
  • 13,359
  • 7
  • 71
  • 99
Phil
  • 35,852
  • 23
  • 123
  • 164

4 Answers4

5

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.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • OK. but char friendly_name[16] will be an c-string of length 15 at most (meaning strlen(friendly_name) will <= 15 normally) . – qPCR4vir Feb 18 '13 at 23:22
  • @qPCR4vir: I don't see what `strlen(friendly_name)` has to do with anything. We're initializing `friendly_name`, so there is no point in calling `strlen()` on it. – Dietrich Epp Feb 18 '13 at 23:47
  • @qPCR4vir: About NUL-termination: go read the documentation yourself. I'm not going to copy and paste it for you. – Dietrich Epp Feb 18 '13 at 23:49
  • OK: I'm assuming now that srcUtf8 = [src UTF8String]; add a 0 at end. (than I dont see a great point in the whole Q). Anyway, if len >= sizeof(dest)) you will need to add the 0 at end (len+1) manually – qPCR4vir Feb 19 '13 at 00:31
  • @qPCR4vir: I changed the `memcpy()` version to add the `'\0'`, but on a further note we don't really know if truncation is the desired behavior. I'm also a little confused as to why you're answering questions about Objective C if you can't find the API documentation. – Dietrich Epp Feb 19 '13 at 02:02
  • This was a great answer! Thanks for the detailed response. – Phil Feb 19 '13 at 04:26
  • :-) The Q was taget as C too, and I answered very "conservative", and it is correct. – qPCR4vir Feb 19 '13 at 06:27
3

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.

Bob Murphy
  • 5,814
  • 2
  • 32
  • 35
2

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/

Josh Petitt
  • 9,371
  • 12
  • 56
  • 104
0
//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...

qPCR4vir
  • 3,521
  • 1
  • 22
  • 32