Я не знаком со способом преобразования двумерного массива из C в массив строк, который я могу использовать в Swift. Насколько мне известно, в Swift нет быстрого и простого способа сделать это.
Это заголовок моей функции C:
char **getAllFilePaths(const char path []);
Я пробовал следующее:
//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)
}
(У меня есть этот код от Martin R: https://stackoverflow.com/a/38422783/10269733. К сожалению, он больше не работает в Swift 4.2)
Я весь день ищу решение этой проблемы, поэтому открыт для любых творческих идей!
Обновлено:
Этот код отлично работает
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;
}



Проблема не в коде Swift (и не в каких-либо изменениях Swift 4.2). Настоящая проблема в том, что функция C возвращает переменную «2-мерный массив».
char paths[FILES_MAX][PATH_MAX];
как char ** для вызывающего - но это несовместимые типы: один из них
массив массивов (все символы находятся в непрерывной памяти), другой - указатель на указатель. Должно появиться предупреждение компилятора, например
incompatible pointer types returning 'char [...][...]' from a function with result type 'char **'
Вы мог возвращаете paths из функции, если вы объявляете что-то вроде
typedef char PathName[PATH_MAX];
PathName *getAllFilePaths(const char path []) { ... }
Но это будет импортировано в Swift как
typealias PathName = (Int8, Int8, ... , Int8)
public func getAllFilePaths(_ path: UnsafePointer<Int8>!) -> UnsafeMutablePointer<PathName>!
где PathName - это кортеж символов PATH_MAX - довольно громоздко
использовать из Swift! Еще проблема в том, что звонящий не знает
сколько элементов массива было заполнено.
Лучше определите paths как массив указателей char:
static char *paths[FILES_MAX] = { NULL };
и заполните его списком указателей char с завершающим нулем. Вот
чрезмерно упрощенный пример:
char **getAllFilePaths(const char path []) {
paths[0] = "foo";
paths[1] = "bar";
paths[2] = NULL;
return paths;
}
В вашем реальном приложении вы не добавляете строку литералы, поэтому вы, вероятно, нужно дублировать строки
paths[j] = strdup(someString);
это означает, что в какой-то момент строки должны быть снова free()d, чтобы избежать
утечки памяти.
кредиты на "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;
}
Помимо заголовков / мостов:
// 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)