Convert a C String Array to a Swift String Array - arrays

I am not familiar with a way to convert a two dimensional array from C to an array of Strings which I can use in Swift. As far as I know there is no quick and easy way to do this in Swift.
This is the header of my C function:
char **getAllFilePaths(const char path []);
I tried the following:
//Get the pointer of the String array
if var ptr = getAllFilePaths(a) {
//Check if the current String is null
while let s = ptr.pointee {
//Copy the String into an array
a_paths.append(String(cString: s)) //<- Crash: Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
//Go to the next adress
ptr += 1
}
print(a_paths)
}
(I have this code from Martin R: https://stackoverflow.com/a/38422783/10269733. Unfortunately it doesn't work anymore in Swift 4.2)
I am searching for a solution for this problem all day, therefore I am open for any kinds of creative ideas!
EDIT:
This code works perfectly
char paths [FILES_MAX][PATH_MAX];
static size_t i = 0;
char **getAllFilePaths(const char path []){
PrintFile(path);
size_t j;
for(j = 0; j < i; j++){
printf("%s\n", paths[j]);
}
return paths;
}

The problem is not in the Swift code (or related to any Swift 4.2 changes).
The real problem is that the C function returns the “2-dimensional array” variable
char paths[FILES_MAX][PATH_MAX];
as a char ** to the caller – but that are incompatible types: one is an
array of arrays (with all characters being in contiguous memory), the other is a pointer to a pointer. There should be a compiler warning like
incompatible pointer types returning 'char [...][...]' from a function with result type 'char **'
You could return paths from the function if you declare something like
typedef char PathName[PATH_MAX];
PathName *getAllFilePaths(const char path []) { ... }
But that would be imported to Swift as
typealias PathName = (Int8, Int8, ... , Int8)
public func getAllFilePaths(_ path: UnsafePointer<Int8>!) -> UnsafeMutablePointer<PathName>!
where PathName is a tuple of PATH_MAX characters – quite cumbersome
to use from Swift! Another problem is that the caller does not know
how many arrays elements have been filled.
Better define paths as an array of char pointers:
static char *paths[FILES_MAX] = { NULL };
and fill it with a NULL-terminated list of char pointers. Here is a
over-simplified example:
char **getAllFilePaths(const char path []) {
paths[0] = "foo";
paths[1] = "bar";
paths[2] = NULL;
return paths;
}
In your real application you do not add string literals, so you'll probably
have to duplicate the strings
paths[j] = strdup(someString);
which means that at some point the strings must be free()d again, to avoid
memory leaks.

credits to "https://oleb.net/blog/2016/10/swift-array-of-c-strings/"
C:
int myCFunc(char *const argv[], size_t howmany )
{
int i;
for(i=0; i < howmany; i++){
printf("\n%s", argv[i]);
}
return 1;
}
Apart from headers/bridging:
// Swift:
import Foundation
public func withArrayOfCStrings<R>(
_ args: [String],
_ body: ([UnsafeMutablePointer<CChar>?]) -> R
) -> R {
var cStrings = args.map { strdup($0) }
cStrings.append(nil)
defer {
cStrings.forEach { free($0) }
}
return body(cStrings)
}
func goLowlevel(arguments: [String]) -> Int {
let count = arguments.count
return withArrayOfCStrings(arguments) {
argv in
myCFunc(argv, count)
return 10
}
}
let arr = ["AA", "BBB", "CCCC"]
goLowlevel(arguments: arr)

Related

Dynamically increasing C string's size

I'm currently creating a program that captures user's keypresses and stores them in a string. I wanted the string that stores the keypresses to be dynamic, but i came across a problem.
My current code looks something like this:
#include <stdio.h>
#include <stdlib.h>
typedef struct Foo {
const char* str;
int size;
} Foo;
int main(void)
{
int i;
Foo foo;
foo.str = NULL;
foo.size = 0;
for (;;) {
for (i = 8; i <= 190; i++) {
if (GetAsyncKeyState(i) == -32767) { // if key is pressed
foo.str = (char*)realloc(foo.str, (foo.size + 1) * sizeof(char)); // Access violation reading location xxx
sprintf(foo.str, "%s%c", foo.str, (char)i);
foo.size++;
}
}
}
return 0;
}
Any help would be appreciated, as I don't have any ideas anymore. :(
Should I maybe also allocate the Foo object dynamically?
First, in order to handle things nicely, you need to define
typedef struct Foo {
char* str;
int size
} Foo;
Otherwise, Foo is really annoying to mutate properly - you invoke undefined behaviour by modifying foo->str after the realloc call in any way.
The seg fault is actually caused by sprintf(foo.str, "%s%c", foo.str, (char)i);, not the call to realloc. foo.str is, in general, not null-terminated.
In fact, you're duplicating work by calling sprintf at all. realloc already copies all the characters previously in f.str, so all you have to do is add a single character via
f.str[size] = (char) i;
Edit to respond to comment:
If we wanted to append to strings (or rather, two Foos) together, we could do that as follows:
void appendFoos(Foo* const first, const Foo* const second) {
first->str = realloc(first->str, (first->size + second->size) * (sizeof(char)));
memcpy(first->str + first->size, second->str, second->size);
first->size += second->size;
}
The appendFoos function modifies first by appending second onto it.
Throughout this code, we leave Foos as non-null terminated. However, to convert to a string, you must add a final null character after reading all other characters.
const char *str - you declare the pointer to const char. You cant write to the referenced object as it invokes UB
You use sprintf just to add the char. It makes no sense.
You do not need a pointer in the structure.
You need to set compiler options to compile **as C language" not C++
I would do it a bit different way:
typedef struct Foo {
size_t size;
char str[1];
} Foo;
Foo *addCharToFoo(Foo *f, char ch);
{
if(f)
{
f = realloc(f, sizeof(*f) + f -> size);
}
else
{
f = realloc(f, sizeof(*f) + 1);
if(f) f-> size = 0
}
if(f) //check if realloc did not fail
{
f -> str[f -> size++] = ch;
f -> str[f -> size] = 0;
}
return f;
}
and in the main
int main(void)
{
int i;
Foo *foo = NULL, *tmp;
for (;;)
{
for (i = 8; i <= 190; i++)
{
if (GetAsyncKeyState(i) == -32767) { // if key is pressed
if((tmp = addCharToFoo(f, i))
{
foo = tmp;
}
else
/* do something - realloc failed*/
}
}
}
return 0;
}
sprintf(foo.str, "%s%c", foo.str, (char)i); is ill-formed: the first argument cannot be const char *. You should see a compiler error message.
After fixing this (make str be char *), then the behaviour is undefined because the source memory read by the %s overlaps with the destination.
Instead you would need to use some other method to append the character that doesn't involve overlapping read and writes (e.g. use the [ ] operator to write the character and don't forget about null termination).

C - strtok_s - Access violation reading location

I can't understand why I get the following message for my function below (in Visual Studio 2015).
0xC0000005: Access violation reading location 0x0000002C.
I have read this answer but it does not help me.
What this code is about.
There is a string of ints separated in groups of "index,value" pairs. Indexes are unique. Each group is separated by a semi-colon. Example: 1,2;3,5;2,2;3,4
I am trying to get an array of int with each value at its index.
My code so far extracts the strings and puts it into a char* buffer.
Then I separate the groups of "index,value" by the semi-colons and store them in char** arrayKeyValue, which is a member of struct inputElement . The other struc member is a int representing the number of "index,value" groups in the array. I do this with the function "separateStringBySemicolon".
Then I try to separate each group of "index,value" into a new array, where at each "index" will match its "value". I do this by passing my struct to the function "separateKeyValue". I use strtok_s but I get an error.
The first call to the function below (token2 = strtok_s(arrayOfKeyValue[j], sepComma, &next_token2);) brings the error. I understand that token2 or next_token2 cannot be accessed, but I am not sure. And if so, why?
double* separateKeyValue(struct inputElement* inputElement)
{
int count = inputElement->length;
char** arrayOfKeyValue = inputElement->data;
double* arrayDecimal = malloc(count * sizeof(double));
char sepComma = ','; //wrong should be char sepComma[] = ",";
char* token2 = NULL;
char* next_token2;
printf("Value in arrayofkeyvalue: %s", arrayOfKeyValue[0]);
for (size_t j = 0; j < count; j++)
{
token2 = strtok_s(arrayOfKeyValue[j], sepComma, &next_token2);
unsigned int index;
sscanf_s(token2, "%d", &index);
double value;
sscanf_s(next_token2, "%d", &value);
arrayDecimal[index] = value;
printf("res[%d] = %d\n", index, arrayDecimal[index]);
printf("\n");
}
return arrayDecimal;
}
You are specifying a char constant, sepComma, as the second parameter to strtok_s, where it expects a string of delimiter characters.
(not so) Coincidentally, the ASCII value of ',' is 0x2C.

qsort() - sorting an array of strings

Can't seem to find the answer on any question here.
Basically I have this line of code:
qsort(Code_Lines, number_of_lines, sizeof(char*), myCmp);
when Code_Lines is a char** - points to an array of char*s when each one contains a string OR pointing to NULL. What I want to do is to sort all of the strings alphabetically when the strings containing NULL will be at the END of the array.
Each string within the Code_Lines will be sorted by its 4 first characters (it's unequal in length of each string - and the first four characters are always different - mentioning a number from 0001 to 9999), if it's NULL it will just put it in the end of the array.
The variable number_of_lines is containing the number of lines (code lines) that are in the array, AKA - number of elements (strings, in this case) in the array.
myCmp is my compare function and I wrote it this way:
int myCmp(const void* element1, const void* element2)
{
int return_value;
if(!element1) //element1 of them is NULL
{
return_value = 1;
}
else if(!element2) //element 2 is NULL
{
return_value = -1;
}
else
{
return_value = strncmp(*((char**)element1), *((char**)element2), 4);
}
return return_value;
}
Any idea what the problem might be ? The program just crashes there.
The function works when the array isn't containing NULL but fails when it does...
In a qsort comparison function, the arguments are pointers to the elements of the array you provided, not the elements themselves. The elements being compared obviously exist, so these pointers can never by NULL by definition. What you want to check is whether a particular element is NULL, not the pointer to element:
int myCmp(const void* element1, const void* element2)
{
int return_value;
char * e1 = *(char **) element1;
char * e2 = *(char **) element2;
if(!e1) //element1 of them is NULL
{
return_value = 1;
}
else if(!e2) //element 2 is NULL
{
return_value = -1;
}
else
{
return_value = strncmp(e1, e2, 4);
}
return return_value;
}

Remove duplicates in a string. What am I missing?

Here is my try to remove duplicates of a string, and I have two questions:
void removeDuplicates(char *original_string)
{
if(original_string == NULL) {
return;
}
int len = strlen(original_string);
if (len < 2) {
return;
}
int tail = 1;
int i;
for (i = 1; i < len; i++) {
int j;
for (j=0; j < tail; j++) {
if (original_string[i] == original_string[j]) {
break;
}
}
if (j == tail) {
original_string[tail] = original_string[i];
++tail;
}
}
}
First: What am I doing wrong that I don't see? I have found this example in a book and I believe it makes sense. Why are the duplicated characters not being deleted?
Second: When calling the function, if I do it with:
char duplicated[] = "aba";
removeDuplicates(duplicated);
I don't get an error. But if I do it with:
char *duplicated = "aba";
removeDuplicates(duplicated);
I get an Bus error: 10 in run time.
char duplicated[] = "aba";
creates an array of chars, which is writable.
char *duplicated = "aba";
creates a string literal (which is unmodifiable) then the variable duplicated is assigned to the pointer to that string literal. Since your function tries to modify the string in-place, it invokes undefined behavior when attempting to write to a string literal, hence the crash.
string literals are non-modifiable in C. it is undefined behaviour
So, duplicated has to be a local array:
or it has to be
char duplicated[] = "aba";
and not
char *duplicated = "aba";
"..." creates a constant chunk of memory holding your string.
You cannot modify it.
Therefore, modifying original_string[tail] is undefined behavior when called on a constant string.
nothing is being removed
original_string[tail] = original_string[i]
that isn't removing anything, it's replacing

Pointers to Structures in C

When trying to compile the following code, I am getting a warning that line 18 makes integer from pointer without cast and that 19 and 20 are incompatible types in assignment. I am new to structures in C, and can't seem to figure out what is wrong.
#include <stdio.h>
struct song
{ char title[70];
};
struct playlist
{ struct song songs[100];
};
void title_sort(struct playlist * list,int len)
{ int swapped = 1,i;
char hold;
while (swapped)
{ swapped = 0;
for (i = 0;i < len - 1; i++)
{ if (list->songs[i].title > list->songs[i+1].title)
{ hold = list->songs[i].title;
list->songs[i].title = list->songs[i+1].title;
list->songs[i+1].title = hold;
swapped = 1;
}
}
}
}
int main()
{ struct playlist playlist;
int i;
for (i = 0;i < 5;i++)
{ fgets(playlist.songs[i].title,70,stdin);
}
title_sort(&playlist,5);
printf("\n");
for (i = 0;i < 5;i++)
{ printf("%s",playlist.songs[i].title);
}
return 0;
}
You can't compare strings in C with >. You need to use strcmp. Also hold is char but title is char [70]. You could copy pointers to strings but arrays can't be copied with just =.
You could use strcpy like this:
void title_sort(struct playlist * list,int len)
{ int swapped = 1,i;
char hold[70];
while (swapped)
{ swapped = 0;
for (i = 0;i < len - 1; i++)
{ if (strcmp (list->songs[i].title, list->songs[i+1].title) > 0)
{ strcpy (hold, list->songs[i].title);
strcpy (list->songs[i].title, list->songs[i+1].title);
strcpy (list->songs[i+1].title,hold);
swapped = 1;
}
}
}
}
But please note that in C you need to check things like the lengths of strings, so the above code is dangerous. You need to either use strncpy or use strlen to check the lengths of the strings.
You can not use strings like that C. Strings are essentially a simple array of characters in C without specialized operators like =, < etc. You need to use string functions like strcmp and strcpy to do the string manipulations.
To be more specific : following is wrong
if (list->songs[i].title > list->songs[i+1].title)
Do it this way:
if( strcmp (list->songs[i].title , list->songs[i+1].title) > 0 )
char hold needs to be something else, perhaps char *hold, perhaps an array.
C doesn't have array assignment, although it does have structure assignment, you will need to rework that
Your first issue, on line 18, is caused by two problems. Firstly, the variable hold can only hold a single char value, and you're trying to assign an array of 70 chars to it.
First you'll need to make hold the correct type:
char hold[70];
Now, there's another problem - arrays can't just be assigned using the = operator. You have to use a function to explicitly copy the data from one array to another. Instead of your current line 18, you could use:
memcpy(hold, list->songs[i].title, 70);
You then need to do the same thing for lines 19 and 20:
memcpy(list->songs[i].title, list->songs[i+1].title, 70);
memcpy(list->songs[i+1].title, hold, 70);
Alternatively, you could write a loop and swap the two titles one char at a time.
In a similar fashion, you can't compare two strings with the simple < operator - you need to use a function for this, too (eg. strcmp).

Resources