【问题标题】:How can I safely make a float *x into a const float *const *y?如何安全地将 float *x 转换为 const float *const *y?
【发布时间】:2011-05-17 08:55:58
【问题描述】:

我有一个函数 audioReceived (float * input, int bufferSize, int nChannels),我想在其中调用一个需要 const float *const *inputBuffers 的库中的函数。

显然,强制转换 const float *const *inputBuffers = (const float* const*)input; 可以编译,但这是一个糟糕的主意,会导致程序崩溃、杀死小猫等等。没有人需要修改原始的 float* input,它正在处理传入的音频数据。

我该如何以正确的方式做到这一点?

编辑:这里还有一些代码。 audioReceived 是:

void testApp::audioReceived (float * input, int bufferSize, int nChannels){ 

     Vamp::RealTime rt = Vamp::RealTime::fromMilliseconds(ofGetSystemTime());
     float const *const tmp[] = { input, 0 };    
     Vamp::Plugin::FeatureSet fs = myPlugin->process(tmp, rt);
 }

库函数process 虚拟定义在基类中:

 /**
 * Process a single block of input data.
 * 
 * If the plugin's inputDomain is TimeDomain, inputBuffers will
 * point to one array of floats per input channel, and each of
 * these arrays will contain blockSize consecutive audio samples
 * (the host will zero-pad as necessary).  The timestamp in this
 * case will be the real time in seconds of the start of the
 * supplied block of samples.
 *
 * If the plugin's inputDomain is FrequencyDomain, inputBuffers
 * will point to one array of floats per input channel, and each
 * of these arrays will contain blockSize/2+1 consecutive pairs of
 * real and imaginary component floats corresponding to bins
 * 0..(blockSize/2) of the FFT output.  That is, bin 0 (the first
 * pair of floats) contains the DC output, up to bin blockSize/2
 * which contains the Nyquist-frequency output.  There will
 * therefore be blockSize+2 floats per channel in total.  The
 * timestamp will be the real time in seconds of the centre of the
 * FFT input window (i.e. the very first block passed to process
 * might contain the FFT of half a block of zero samples and the
 * first half-block of the actual data, with a timestamp of zero).
 *
 * Return any features that have become available after this
 * process call.  (These do not necessarily have to fall within
 * the process block, except for OneSamplePerStep outputs.)
 */
virtual FeatureSet process(const float *const *inputBuffers,
               RealTime timestamp) = 0;

在实际的标题中:

FeatureSet process(const float *const *inputBuffers, Vamp::RealTime timestamp);

我认为EXC_BAD_ACCESS 可能源于需要一个零填充数组的库函数,而我没有给它一个。 (a) 这听起来是否合理,以及 (b) 如果是,是时候提出不同的 SO 问题了吗?

到目前为止,感谢大家的帮助,这非常有启发性/澄清/教育/有趣。

【问题讨论】:

  • 这没有多大意义。你从一个指向浮点数的指针开始,你想从中得到一个指向浮点数的指针,其中插入了很多常量。嗯?
  • 为什么还有c++标签?不是纯C题吗?
  • 老实说,当谈到 consts 我问“嗯?”也 - 但这是特征提取库想要的,我试图在开始尝试重写库之前先给它想要的东西。 (无论如何,这可能只会导致更多的混乱;我的工作假设是编写该代码的人比我知道的多一点——尽管可能不比你们中的一些人多。)
  • @sbi:它也适用于 C++,如果他用 C++ 编译,那么地板对在 C++ 中有效但在 C 中无效的解决方案开放。
  • @ickydog:你绝对确定它是const float* const*(最好写成float const* const*)?不只是float const* const?您确定您已经正确解释了库函数的输入内容吗?文档是怎么说的?

标签: c++ pointers constants


【解决方案1】:

语义在这里很重要。从参数名称中,我可以猜到您要调用的函数接受多个缓冲区,因此它需要一个指向浮点数的指针数组(即数组数组)。由于您只有一个数组,因此您需要创建一个包含原始指针的数组,并将其传递给函数。

如果函数有一个单独的参数来表示它被传递的数组的长度(即缓冲区的数量),那么使用一元 & 运算符获取地址并传递长度 1 就足够了,否则你需要创建一个临时的空终止数组:

float const *const tmp[] = { input, 0 };

并将其传递给函数。

【讨论】:

  • 是的,我怀疑是这样的。然而,在不知不觉中,这一切都只是猜测! OP 应该阅读库文档。
  • @Tomalak,我一直是个好孩子并且阅读了它,但文档并不完全清楚/完整。鉴于它是开源的,我希望对此有所帮助,但首先我必须弄清楚它是如何工作的!
  • @ickydog:不,他是认真的。此外,您可能还会点击this。我认为我们已经达到了从所提供的有限信息中所能告诉您的极限。
  • 实际上,我的意思是没有&。已更正。
【解决方案2】:

&input 对演员本身来说应该足够了。注意内部函数的参数是一个指向指针的指针。

编辑:

要根据原始问题的 cmets 要求获取以空值结尾的输入缓冲区列表,您可以使用:

float const * const buffers[] = {input, 0};

【讨论】:

  • 可能就够了。这取决于被调用的函数。
  • 以为可能是它,但它EXC_BAD_ACCESS在运行时。
  • @Kevin:我意识到我的答案不适用于这里(因为该函数需要const char * float *,而不是const float **)所以我删除了它。如果@Juraj 对他的答案进行了空编辑,我可以删除我的反对票。
  • 如果库需要一个最终元素为NULL 的数组,这可能还不够。在不知道库期望什么的情况下,这里的断言很危险。
  • @Oli:我不明白你的意思。转换本身是正确的。我在 OP 关于所需格式的说明之前很久就发布了答案。问题是关于选角的,我已经回答了。不过我可以编辑答案。
【解决方案3】:
float *

不一样

float **

所以将input 转换为inputBuffers 是行不通的。

通常,从非 const 转换为 const 是隐式的,你不需要做任何特别的事情。您不能轻易地从 const 转换为非 const。仔细想想,这是合乎逻辑的。

【讨论】:

  • 这是正确的,但有一些重要的警告。例如,您不能将 T** 隐式转换为 const T**
  • 没问题。 T** 不能隐式转换为T const**,但可以转换为T const* const*
【解决方案4】:

您需要将float ** 传递给它,即指向浮点数的指针。 consts 只是告诉您该函数不会修改任何指向的值。

如果您有一个潜在可变数量的频道nChannels,每个频道有bufferSize 浮动,那么您可以设置必要的存储,例如

float **data = new float *[nChannels];
for (int c = 0; c < nChannels; ++c) {
    data[c] = new float[bufferSize];
}

之后data 以正确的格式传递给此函数。 (该函数知道这些东西有多大,因为您之前已经向插件构造函数提供了通道数和块大小。)

您的示例代码显示您有一个单指针 float *,这表明您可能从交错格式的数据开始(即在单个数组中交替通道值)。如果是这种情况,您需要解交错到单独的通道中才能传递给process 函数:

for (int i = 0; i < bufferSize; ++i) {
    for (int c = 0; c < nChannels; ++c) {
        data[c][i] = input[i * nChannels + c];
    }
}
myPlugin->process(data, rt);

最后,不要忘记delete[] 成员指针以及之后的双指针。

for (int c = 0; c < nChannels; ++c) {
    delete[] data[c];
}
delete[] data;

另一方面,如果您知道您将只有一个数据通道 - 并且您在插件构造函数中提供了 1 作为通道计数 - 那么您可以像其他人一样做一个额外的间接建议:

myPlugin->process(&input, rt);

(披露:我写的代码让提问者感到困惑)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-29
    • 2019-12-06
    • 2020-04-19
    相关资源
    最近更新 更多