I'm trying to call a simple tutorial C function from Objective-C and I can't figure out how to split up the arguments being passed, which is two strings.
int cFunction(int argc, char **argv)
{
int i;
printf("argc = %d\n", argc);
for (i = 0; i < argc; i++) {
printf("argv[%d] = \"%s\"\n", i, argv[i]);
}
return 0;
}
I've tried cFunction(3, "string1 string2"), cFunction(3, "string1", "string2"), and cFunction(3, args), with args being an NSArray composed of [textField1 stringValue] and [textField2 stringValue]
I get EXC_BAD_ACCESS when trying to printf argv[i]. I've also tried passing 2 as the value of argc.
How should this be called? Thanks
In this function parameter list, argv is a pointer to a C string array (char **).
Using the Objective-C NSString or NSArray type isn't possible with this function, First, convert each string using -(const char *)cStringUsingEncoding:(NSStringEncoding)encoding;, and store them into a C array of strings.
// myString is "arg1"
// mySecondString is "arg2"
char *cString = [myString
cStringUsingEncoding:NSUTF8StringEncoding];
char *cSecondString = [mySecondString
cStringUsingEncoding:NSUTF8StringEncoding];
char *myStrings[2] = { cString, cSecondString };
int returnCode = cFunction(2, myStrings);
This should work.
Related
I just recently started learning Objective C, when i run the next program i get error
"program received signal: "EXC_BAD_ACCESS"
For the code line
if([*userChoice isEqualToString:#"yes"])
The full code is:
void initGame (void);
void restartGame(void);
void toGoOn(char *playerChoice);
int guess=-1;
int from=-1;
int to=-1;
bool playStatus=true;
bool gameStatus=true;
int answer=-1;
NSString *userChoice[10];
//if true the game is on
int main (int argc, const char * argv[])
{
#autoreleasepool {
GuessManager *game=GUESS;
NSLog(#"Hello, lets play");
NSLog(#"Please provide a positive range in which you would like to play");
do{
initGame();
[game setnumberToGuess:from :to];
do {
printf("Make you guess:");
scanf("%d", &guess);
[game setUserGuess:guess];
[game checkUserGuess];
if([game getDidIgetIt])
{
playStatus=false;
}
else
{
playStatus=true;
}
} while (playStatus);
restartGame();
}while(gameStatus);
printf("Thanks For Playing PanGogi Games! GoodBye");
}
return 0;
}
void initGame (void)
{
printf("from:");
scanf("%d",&from);
printf("to:");
scanf("%d",&to);
}
void restartGame(void)
{
printf("Would you like to continue?(yes/no)");
scanf("%s",&userChoice);
//scanf("%d",&answer);
// if(answer==1)
if([*userChoice isEqualToString:#"yes"])
{
gameStatus=true;
}
else
{
gameStatus=false;
}
}
I understand that its related to the NSString variable userChoice and how its used in
the if, but what i cant find is what am i doing wrong.
Please help :)
You have 3 errors in the code
1) I think you are getting confused with NSString and C style char array... You just need to use single NSString object to save multi character data..
NSString *userChoice;
2) Since you want to input data using scanf, you need a C style character array. scanf won't work with NSString types.
char tempArray[10];
int count = scanf("%s",&tempArray);
userChoice = [NSString stringWithBytes:tempArray length:count encoding: NSUTF8StringEncoding];
3) Now you can use NSString directly.. No need for pointer like syntax
if( [userChoice isEqualToString: #"yes"]){
.....
.....
}
You're using NSString as if it was char. It's not. It's a class that represents a string.
The scanf function is a C function and needs a char array, not an NSString.
char str[80];
scanf("%s", &str);
You can initialize an NSString object with a char array like this:
NSString *userChoice = [NSString stringWithCString:str encoding:NSASCIIEncoding];
And compare like this:
if ([userChoice isEqualToString:#"yes"]) {
...
} else {
...
}
Need to generated some random 10 byte length string in c function and call the function from objective-c. So, I'm creating a pointer to uint8_t and passing it to C function. The function generates random bytes and assigns them to *randomString. However, after returning from function to objective-c randomValue pointer points to NULL.
Here's my random function in C:
void randomString(uint8_t *randomString)
{
randomString = malloc(10);
char randomByte;
char i;
for (i = 0; i < 10; i++) {
srand((unsigned)time(NULL));
randomByte = (rand() % 255 ) + 1;
*randomString = randomByte;
randomString++;
}
}
Here's objective-c part:
uint8_t *randomValue = NULL;
randomString(randomValue); //randomValue points to 0x000000
NSString *randomString = [[NSString alloc] initWithBytes:randomValue length:10 encoding:NSASCIIStringEncoding];
NSLog(#"Random string: %#", randomString);
A more natural semantic, like malloc() itself would be:
uint8_t * randomString()
{
uint8_t *randomString = malloc(10);
srand((unsigned)time(NULL));
for (unsigned i = 0; i < 10; i++)
randomString[i] = (rand() % 254) + 1;
return randomString;
}
Pointers are passed by value, so randomValue will remain NULL after the call of randomString. You need to pass a pointer to a pointer in order to make it work:
void randomString(uint8_t **randomString) {
*randomString = malloc(10);
// ... the rest of your code goes here, with an extra level of indirection
}
uint8_t *randomValue = NULL;
randomString(&randomValue);
You probably should be using uint8_t **randomeValue instead of uint8_t *.
To develop a calculator in xcode I am using a c class for converting infix to postfix expression and its evaluation. But I have an NSString in my View controller class and I need to pass this NSString to a C class where the conversion and evaluation happens. How can I do this?
I think you need to convert NSString to cString. It can be done by
[str UTF8String]
Assuming you have an NSMutableArray containing NSString objects, and you want to convert this to a C array containing C strings, you need to allocate memory for a C array of char * of suitable size (e.g., count of the NSMutableArray + maybe 1 extra if you want a NULL terminator for the array, otherwise you need to pass along the array's element count everywhere). Then for each element of the NSMutableArray, populate the corresponding index in the C array with the C string returned by UTF8String of the NSString object.
Note that the C strings returned by UTF8String are “autoreleased”, so if you need to keep them around for longer than the current autorelease context, you need to duplicate them (e.g., strdup) and then free them (and the C array, which you need to free in any case) after you're done with them.
For example:
#include <Foundation/Foundation.h>
#include <stdio.h>
#include <string.h>
void printCStringsInArray(const char **arr) {
int i = 0;
while (*arr) { printf("%d: %s\n", i++, *arr++); }
}
int main () {
const char **carr;
#autoreleasepool {
NSMutableArray *arr;
const char **p;
arr = [NSMutableArray arrayWithObjects:#"11", #"+", #"12", nil];
p = carr = malloc(sizeof(*carr) * ([arr count] + 1));
for (NSString *s in arr) {
*p++ = strdup([s UTF8String]);
}
*p = NULL; // mark the end of carr
}
printCStringsInArray(carr);
{ // free the C strings and array
const char **p = carr;
while (*p) { free(*p++); };
free(carr);
}
return 0;
}
This prints:
0: 11
1: +
2: 12
edit: Of course if you just want to call some C function taking individual strings as const char * from your otherwise Objective-C code, you don't need to do anything this complicated, just convert each string on the fly with UTF8String and use the NSMutableArray for iteration, etc. But then one might wonder what it is that you can do with C strings that you couldn't do directly with NSStrings.
It's easy because you can use NSString's UTF8String method, but you have to handle memory allocation: allocate a block to contain the string.Choose if to wrap it into a NSData so that it will be released automatically when out of the pool block, or if to free it manually:
#autoreleasepool
{
NSArray* strings= #[ #"Hello", #"Hey" ]; // Your strings
// This could be also a NSMutableArray if you need it
NSMutableArray* cstrings=[NSMutableArray new]; // C strings
for(NSString* string in strings) // Iterate over all the strings
{
const char* temp=[string UTF8String]; // Get C string
const unsigned long length=strlen(temp); // Allocate memory for it
char* string=(char*)malloc((length+1)*sizeof(char));
strcpy(string,temp); // Copy it
// Store it inside NSData
[cstrings addObject: [NSData dataWithBytesNoCopy: string length: (length+1)*sizeof(char)]];
}
}
The following is tried to print out N number of spaces (or 12 in the example):
NSLog(#"hello%#world", [NSString stringWithCharacters:" " length:12]);
const unichar arrayChars[] = {' '};
NSLog(#"hello%#world", [NSString stringWithCharacters:arrayChars length:12]);
const unichar oneChar = ' ';
NSLog(#"hello%#world", [NSString stringWithCharacters:&oneChar length:12]);
But they all print out weird things such as hello ÔÅÓñüÔÅ®Óñü®ÓüÅ®ÓñüÔ®ÓüÔÅ®world... I thought a "char array" is the same as a "string" and the same as a "pointer to a character"? The API spec says it is to be a "C array of Unicode characters" (by Unicode, is it UTF8? if it is, then it should be compatible with ASCII)... How to make it work and why those 3 ways won't work?
You can use %*s to specify the width.
NSLog(#"Hello%*sWorld", 12, "");
Reference:
A field width, or precision, or both, may be indicated by an asterisk
( '*' ). In this case an argument of type int supplies the field width
or precision. Applications shall ensure that arguments specifying
field width, or precision, or both appear in that order before the
argument, if any, to be converted.
This will get you what you want:
NSLog(#"hello%#world", [#"" stringByPaddingToLength:12 withString:#" " startingAtIndex:0]);
I think the issue you have is you are misinterpreting what +(NSString *)stringWithCharacters:length: is supposed to do. It's not supposed to repeat the characters, but instead copy them from the array into a string.
So in your case you only have a single ' ' in the array, meaning the other 11 characters will be taken from whatever follows arrayChars in memory.
If you want to print out a pattern of n spaces, the easiest way to do that would be to use -(NSString *)stringByPaddingToLength:withString:startingAtIndex:, i.e creating something like this.
NSString *formatString = #"Hello%#World";
NSString *paddingString = [[NSString string] stringByPaddingToLength: n withString: #" " startingAtIndex: 0];
NSLog(formatString, paddingString);
This is probably the fastest method:
NSString *spacesWithLength(int nSpaces)
{
char UTF8Arr[nSpaces + 1];
memset(UTF8Arr, ' ', nSpaces * sizeof(*UTF8Arr));
UTF8Arr[nSpaces] = '\0';
return [NSString stringWithUTF8String:UTF8Arr];
}
The reason your current code isn't working is because +stringWithCharacters: expects an array with a length of characters of 12, while your array is only 1 character in length {' '}. So, to fix, you must create a buffer for your array (in this case, we use a char array, not a unichar, because we can easily memset a char array, but not a unichar array).
The method I provided above is probably the fastest that is possible with a dynamic length. If you are willing to use GCC extensions, and you have a fixed size array of spaces you need, you can do this:
NSString *spacesWithLength7()
{
unichar characters[] = { [0 ... 7] = ' ' };
return [NSString stringWithCharacters:characters length:7];
}
Unfortunately, that extension doesn't work with variables, so it must be a constant.
Through the magic of GCC extensions and preprocessor macros, I give you.... THE REPEATENATOR! Simply pass in a string (or a char), and it will do the rest! Buy now, costs you only $19.95, operators are standing by! (Based on the idea suggested by #JeremyL)
// step 1: determine if char is a char or string, or NSString.
// step 2: repeat that char or string
// step 3: return that as a NSString
#define repeat(inp, cnt) __rep_func__(#encode(typeof(inp)), inp, cnt)
// arg list: (int siz, int / char *input, int n)
static inline NSString *__rep_func__(char *typ, ...)
{
const char *str = NULL;
int n;
{
va_list args;
va_start(args, typ);
if (typ[0] == 'i')
str = (const char []) { va_arg(args, int), '\0' };
else if (typ[0] == '#')
str = [va_arg(args, id) UTF8String];
else
str = va_arg(args, const char *);
n = va_arg(args, int);
va_end(args);
}
int len = strlen(str);
char outbuf[(len * n) + 1];
// now copy the content
for (int i = 0; i < n; i++) {
for (int j = 0; j < len; j++) {
outbuf[(i * len) + j] = str[j];
}
}
outbuf[(len * n)] = '\0';
return [NSString stringWithUTF8String:outbuf];
}
The stringWithCharaters:length: method makes an NSString (or an instance of a subclass of NSString) using the first length characters in the C array. It does not iterate over the given array of characters until it reaches the length.
The output you are seeing is the area of memory 12 Unicode characters long starting at the location of your passed 1 Unicode character array.
This should work.
NSLog(#"hello%#world", [NSString stringWithCharacters:" " length:12]);
I'm trying to read in and parse an xml document in an iPhone app. I begin parsing and then use the override method:
static void startElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI,
int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes)
I then try to convert the attributes to a string with:
NSString *str1 = [[NSString alloc] initWithCString:attributes encoding:NSUTF8StringEncoding];
Why does the attributes parameter have two ** in front of it. And why when trying to extract the data and convert it to a string with the above code do I get the warning:
passing argument 1 of 'initWithCString:encoding:' from incompatible pointer type.
The documentation for libxml's start element callback states that the pointer is to an array that hold 5 values for each attribute (the number of attributes is returned in nb_attributes). This means that every 5th value in the array is a new attribute item.
The five items for each attribute are:
localname (the name of the attribute)
prefix (the namespace of the attribute)
URI
[start of] value (a pointer to the start
of the xmlChar string for the value)
end [of value] (a pointer to the end of the
xmlChar string for the value)
So you need to step through the array, get each value out of the items for the first attribute, then use the start value pointer to get the xmlChar string that is length = end - start. Then start over with the next attribute till you read in nb_attributes worth.
If that makes your head ache then I strongly suggest you switch to Apple's NSXMLParser (link may require login, or use this link NSXMLParser). In which case you would get the attributes as an NSDictionary. To get all the attributes out of it you could do the following:
for (NSString *attributeName in [attributeDict allKeys]) {
NSString *attributeValue = [attributeDict objectForKey:attributeName];
// do something here with attributeName and attributeValue
}
If you have access to the iPhone developer site then look at the example SeismicXML.
The sample is great except for two things:
you need to bump 'i' by 5 after each loop since there are 5 items for each attribute.
doing strlen() on both begin and end is expensive; it's easier to simply subtract begin from end
for (int i = 0; i < nb_attributes*5; i += 5)
{
const char *attr = (const char *)attributes[i];
const char *begin = (const char *)attributes[i + 3];
const char *end = (const char *)attributes[i + 4];
int vlen = end - begin;
char val[vlen + 1];
strncpy(val, begin, vlen);
val[vlen] = '\0';
NSLog(#"attribute %s = '%s'", attr, val);
}
The accepted answers explanation is correct, but it's helpful to view some example code too. Here is just one way to extract the value from the attributes, at least it works when I tested it. I'm far from being a C guru though.
for (int i = 0; i < nb_attributes; i += 5) {
const char *attr = (const char *)attributes[i];
const char *begin = (const char *)attributes[i + 3];
const char *end = (const char *)attributes[i + 4];
int vlen = strlen(begin) - strlen(end);
char val[vlen + 1];
strncpy(val, begin, vlen);
val[vlen] = '\0';
NSLog(#"attribute %s: %d = %s", attr, i, val);
}
NSXMLParser is nice, but from what I can tell, it downloads the entire XML before processing. Using libxml it can read in chunks at a time. It allows greater flexibility, but higher learning curve.
The '**' notation means "pointer to a pointer." In C/C++, a "string" is represented by an array of characters. An array is actually just a pointer under the covers, so a string in C/C++ can actually be declared as either "char[]" or "char*". The [] notation compiles down to a pointer to an array.
A common example of this is the typical "main" function in C/C++:
int main(int argc, char **argv)
Which is equivalent to:
int main(int argc, char *argv[])
argv is an array of char* "strings" (the command-line arguments to the program).
I can't provide an example at the moment, but it looks like you need to iterate over attributes to access the individual strings. For example, attributes[0] would be the first attribute string (an xmlChar*). You should be able to convert each individual attribute to an NSString.
const xmlChar **namespaces is an array of CStrings (int nb_namespaces tells you how many). If you want each namespace as an NSString, you could do something like the following:
NSMutableArray *namespaces = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < nb_namespaces; i++) {
NSString *namespace = [[NSString alloc] initWithCString:attributes[i] encoding:NSUTF8StringEncoding];
[namespaces addObject:namespace];
}
The initWithCString method is expecting xmlChar *, which is a pointer to an xmlChar (the first char in a CString).
xmlChar ** means pointer to a pointer to an xmlChar (the first char in the first CString).