tags:

views:

308

answers:

1

Sorry, I'm not even sure how to ask, since I'm a complete newbie at C, pointers and stuff like that. There's a function that accepts an argument: char **arg. If I write that argument like so:

char *cargs[] = {"blah", NULL};

and pass it to the function:

function(cargs);

it works. but ... I have an NSArray of NSStrings and I need to make this array out of values from NSArray. I figured it should be a matter of creating a C array of the same element count as NSArray and copy the strings, converting them with cStringUsingEncoding. But I honestly have no idea how to do this, since I get confused with all those pointers and such. Any help would be appreciated.

+1  A: 

Well, the rough steps can be:

  1. use count method of NSArray to know how many NSStrings are there in the NSArray.

  2. use malloc to allocate memory for cargs, something like this

    char **cargs = (char **) malloc(sizeof(char *) * count);
    

    by your example, you may need to one more room for NULL which will be at the end of cargs.

  3. use a loop and objectAtIndex: of NSArray to get out the NSStrings, like NSString *nsstring = [array objectAtIndex:index];

  4. use method cStringUsingEncoding: to get the c-string out, better make a copy

  5. put these c-string pointers in cargs

  6. pass cargs to your function, clean and free things needed to.

It's a lot of work. 'Cause the mix of c and obj-c stuff. And a lot of manual malloc and free , messy stuff. Can't you avoid it?

--add sample code--

I'm not quite sure what your real intent is. Hope this will help.

void func(char **arg)
{
    int i;
    for(i = 0; arg[i] != NULL; i++) {
        printf("%d=%s\n", i, arg[i]);
}
}
int main(int argc, const char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSString *s1 = [NSString stringWithString:@"first"];
    NSString *s2 = [NSString stringWithString:@"second"];
    NSString *s3 = [NSString stringWithString:@"third"];

    NSArray *array = [NSArray arrayWithObjects: s1, s2, s3, nil];
    //by now, we have an NSArray of three NSStrings

    int count = [array count];
    char **cargs = (char **) malloc(sizeof(char *) * (count + 1));
    //cargs is a pointer to 4 pointers to char

    int i;
    for(i = 0; i < count; i++) {
        NSString *s = [array objectAtIndex:i];//get a NSString
        const char *cstr = [s cStringUsingEncoding:NSUTF8StringEncoding];//get cstring
        int len = strlen(cstr);//get its length
        char *cstr_copy = (char *) malloc(sizeof(char) * (len + 1));//allocate memory, + 1 for ending '\0'
        strcpy(cstr_copy, cstr);//make a copy
        cargs[i] = cstr_copy;//put the point in cargs
    }
    cargs[i] = NULL;

    func(cargs);//call the function to do something

    for(i = 0; i < count; i++) {
        free(cargs[i]);
    }
    free(cargs);

    [pool drain];
    return 0;
}
yehnan
Why send `lengthOfBytesUsingEncoding:`? `cStringUsingEncoding:` will automatically create a string of the right length; indeed, you can't tell it to use a different length.
Peter Hosey
Well, i'd be glad to avoid it, but i'm using AuthorizationExecuteWithPrivileges to run a shell script with elevated privileges and i don't know any other way to do it.
Marius
char* arg; arg = (char *)malloc([a count] + 1); for (int i = 0; i < [a count]; i++) { arg[i] = [[a get:i] cStringUsingEncoding:NSUTF8StringEncoding][0]; } arg[[a count]] = 0;This is what i've come up with with the help of my friend, but it breaks the AuthorizationExecuteWithPrivileges ...How do i format the code in the comment? :(
Marius
@Peter, you're right. Thanks.
yehnan
Well, the problem actually is ... how do i assign a cString to that array's element? I've tried: arg[1] = "test" - works. *arg[1] = [@"test" cStringUsingEncoding:NSUTF8StringEncoding][0]; - doesn't work
Marius
Marius: `cStringUsingEncoding:` returns a C string. A C string is an array of characters (`char`), ending with a zero byte (`'\0'`); `cStringUsingEncoding:` returns the address of that array of characters. Saying `[…cStringUsingEncoding:…][0]` takes the first `char` in the array—the first character of the C string. Don't try to access a specific character of the string; set `arg[i]` to the pointer to the whole string, as returned by `cStringUsingEncoding:`.
Peter Hosey
@Marius, I added a sample code. Hope it'll help. By the way, I think you don't quite understand "pointer" and "pointer to pointer". Maybe that is the problem :)
yehnan
Marius
There is a fun youtube video about pointers. http://www.youtube.com/watch?v=mnXkiAKbUPg Take your time and learn it. Enjoy. :) Keypoint: do you understand why cargs is " char ** "? Two stars.
yehnan
Thank you both for your help! @yeahnan: do i really need to make a strcopy of the cstring? I won't need it later, it's just gonna live in a current method and that's it.
Marius
The document of cStringUsingEncoding: "The returned C string is guaranteed to be valid only until either the receiver is freed, or until the current autorelease pool is emptied, whichever occurs first. ", I think it's ok to not make a copy for your case.
yehnan
Marius: I wrote a tutorial about pointers a few years ago. Read it the whole way through: http://boredzo.org/pointers
Peter Hosey