【问题标题】:Segfault when returning a value from C to R using .Call (incompatible pointer types returning)使用 .Call 将值从 C 返回到 R 时出现段错误(返回的指针类型不兼容)
【发布时间】:2019-02-01 17:36:44
【问题描述】:

我正在学习使用 C 编程语言,并且可能遇到返回不兼容指针类型的问题。

我有一个包含两列的文本文件,R 将其作为 data.frame 读取。读完这篇文章后,我想在 C 中执行一个 .Call 函数,该函数将读取该 date.frame 并将返回 R 中变量的值,但是在尝试返回值时会发生段错误。我找不到这个问题的解决方案,谁能帮帮我?

文本文件分为两列,如下例所示。

Q0045   YJL166W
Q0045   YDL085W
Q0045   YDR119W-A

这里是读取data.frame的C代码。

#include <Rinternals.h>
#include <Rdefines.h>
#include <R.h>
#include <stdlib.h>
#include <stdio.h>

char **test(SEXP lst){
  int i,elLength;
  int len = length(lst);
  SEXP col1, col2;
  char ***target = malloc(sizeof(char **) *len);
  col1 = VECTOR_ELT(lst, 0);
  col2 = VECTOR_ELT(lst, 1);
  elLength = length(col1);
  target[0] = malloc(sizeof(char *) * elLength);
  target[1] = malloc(sizeof(char *) * elLength);
  for (i=0; i<elLength; i++) {
    target[0][i] = CHAR(STRING_ELT(col1, i));
    target[1][i] = CHAR(STRING_ELT(col2, i));

  }

  return target;
} 

在此之后,我在终端中使用命令行创建 .so 文件:

R CMD SHLIB test.c

最后是 R 中读取文件并执行 .Call 的代码。

dyn.load("/home/lab/test.so")
fileR = data.frame(read.table("file.txt", sep = "\t", stringsAsFactors = FALSE))
fileFromC = .Call("test", fileR)

之后我从终端在 R 中运行错误:

 *** caught segfault ***
address 0x310000c0, cause 'memory not mapped'

如果我只打印并返回 R_NilValue,则不会显示错误。但我需要将 C 进程返回到一个新变量。

【问题讨论】:

  • 内存在freed之后就不能使用了,为什么要退货呢?
  • 同样target的类型是char ***,但是函数的返回类型是char **
  • 为什么targetchar ***?据我了解您的代码,您的循环复制字符,而不是字符串。如果你想打印字符,为什么你的格式是%s %s\n?您使用第 1 列中的字符串长度进行字符串分配和循环。当 col2 中的字符串比 col1 中的字符串短时应该发生什么?

标签: c r segmentation-fault


【解决方案1】:

malloc() 不是在 R 扩展中处理内存分配的正确方法。

请查看 R 文档,6.1 Memory allocation 部分

6.1.1 临时存储分配

这里 R 将在对 .C、.Call 或 .External 的调用结束时回收内存。使用

char *R_alloc(size_t n, int size)

每个分配 n 个单位的 size 字节。

即您可以将此分配器用于您想要从 C API 分配到 return 的内存。

【讨论】:

  • 另外,free 指令应该被删除。
  • 我更改了执行内存分配的代码行,并删除了示例中的空闲指令,但错误仍然存​​在。 char ***target = (char *) R_alloc(len, sizeof(char **));
  • 你确定 R 可以处理原始的、非结构化的 C 数据作为返回值吗?你不应该构建一个 R 数据结构,例如类似VECTORMATRIX
  • 感谢您的评论斯特凡。我会试试的。
【解决方案2】:

我可以用下面的代码做我需要的事情。感谢所有合作的人。

#include <Rinternals.h>
#include <Rdefines.h>
#include <R.h>
#include <stdlib.h>
#include <stdio.h>

char **stripList(SEXP lst){
  int i, j, elLength;
  int len = length(lst);
  char *rans;
  SEXP col1, col2;
  char ***target = (char *) R_alloc(len, sizeof(char **));

  col1 = VECTOR_ELT(lst, 0);
  col2 = VECTOR_ELT(lst, 1);

  elLength = length(col1);

  SEXP ans = PROTECT(Rf_allocMatrix(STRSXP,elLength, len));

  target[0] = malloc(sizeof(char *) * elLength);
  target[1] = malloc(sizeof(char *) * elLength);
  for (i=0; i<elLength; i++) {
    target[0][i] = CHAR(STRING_ELT(col1, i));
    target[1][i] = CHAR(STRING_ELT(col2, i));
  }

for (int i = 0; i < elLength; i++) for (int j = 0; j < len; j++) 
  SET_STRING_ELT(ans, i + j * elLength, Rf_mkChar(target[j][i]));

  UNPROTECT(1);

  return ans;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-12
    • 1970-01-01
    • 2021-04-15
    • 2012-10-08
    • 1970-01-01
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多