【问题标题】:Difference between passing array, fixed-sized array and base address of array as a function parameter传递数组、固定大小数组和数组基地址作为函数参数的区别
【发布时间】:2023-03-16 12:51:01
【问题描述】:

如果我想将已知或未知大小的数组作为函数参数传递,我对使用哪种语法感到困惑。

假设我为此目的有这些变体:

void func1(char* str) {
    //print str
}

void func2(char str[]) {
    //print str
}

void func3(char str[10]) {
    //print str
}

使用它们各自的优缺点是什么?

【问题讨论】:

  • C 还是 C++?选择一个。
  • 是c还是c++有什么区别?
  • 在 C 语言中,您可以执行第 4 种情况 void func (size_t size, char str[size]),这在某些情况下可能更可取。但在 C++ 中是不可能的。
  • 我同意它不应该有两个标签,因为 C 和 C++ 是两种独立的语言。

标签: c++ arrays pointers


【解决方案1】:

所有这些变体都是相同的。 C 只允许您使用替代拼写,但即使是最后一个用数组大小​​显式注释的变体也会衰减为普通指针。

也就是说,即使使用最后一个实现,您也可以使用 any 大小的数组调用函数:

void func3(char str[10]) { }

func("test"); // Works.
func("let's try something longer"); // Not a single f*ck given.

不用说应该使用它:它可能会给用户一种错误的安全感(“哦,这个函数只接受长度为 10 的数组,所以我不需要检查我自己的长度”)。

正如Henrik所说,在C++中的正确方法是使用std::stringstd::string&std::string const&(取决于是否需要修改对象,以及是否要复制)。

【讨论】:

  • 当我尝试在一个以大小数组作为参数的函数上使用 boost::begin() 时,这让我有点吃惊。不工作。只是指针……嗯。
  • @Macke 如果这仍然相关,请查看 Morwenn 的答案,您 可以 将固定大小的数组传递给函数,尽管只能通过引用(但这绝不应该是问题)。
  • 引用标准可能有用:C99 标准 6.7.5.3,第 1 段。 7、“将参数声明为“类型数组”应调整为“限定类型指针”……”
  • @pmttavara 我们在这里讨论的是非字符串文字情况。否则,请务必使用char const*(或通过引用传递数组,即char (&arr)[N],其中N 是非类型模板参数)。在大多数其他情况下,您要么没有分配,要么成本可以忽略不计。处理分配的char 缓冲区的堆栈很少是一个好主意。
  • @RickyGonce 您可以通过引用传递数组(例如func3(char const (&x)[10])),但这仅适用于具有完全正确大小的数组。数组的大小真的相关吗?为什么不采用强类型的固定或可变大小的缓冲区/字符串(std::span/std::string_view/...)?这几乎总是更合适的策略。
【解决方案2】:

注意,在 C++ 中,如果数组的长度在编译时是已知的(例如,如果你传递了一个字符串字面量),你实际上可以得到它的大小:

template<unsigned int N>
void func(const char(&str)[N])
{
    // Whatever...
}

int main()
{
    func("test"); // Works, N is 5
}

【讨论】:

  • 这将为传递的每个新长度的文字制作func 机器代码的(调整后的)副本。
  • @Ruslan 是的,但我希望任何编译器都能在最低优化级别内联它。
  • 取决于// Whatever...的大小。
  • @Ruslan 哦,对了,我只是想用一个函数来获取数组的大小,忘记了问题的范围更广:/
【解决方案3】:

在 C++ 中,使用 void func4(const std::string&amp; str)

【讨论】:

  • void func4( std::vector&lt;char&gt; const&amp; str )
【解决方案4】:

这些在功能上都是相同的。当您将数组传递给 C 中的函数时,数组会隐式转换为指向数组第一个元素的指针。因此,这三个函数将打印相同的输出(即指向char 的指针的大小)。

void func1(char* str) {
    printf("sizeof str: %zu\n", sizeof str);
}

void func2(char str[]) {
    printf("sizeof str: %zu\n", sizeof str);
}

void func3(char str[10]) {
    printf("sizeof str: %zu\n", sizeof str);
}

此转换仅适用于数组的第一维。 char[42][13] 被转换为 char (*)[13]不是 char **

void func4(char (*str_array)[13]) {
    printf("sizeof str_array: %zu\n"
           "sizeof str_array[0]: %zu\n", sizeof str_array, sizeof str_array[0]);
}

char (*)[13]str_array 的类型。这就是您编写“指向 13 个 chars 数组的指针”的方式。这也可以写成 void func4(char str_array[42][13]) { ... },尽管 42 在功能上没有意义,正如您可以通过实验看到的,将不同大小的数组传递到 func4

在 C99 和 C11(但不是 C89 或 C++)中,您可以将指向可变大小数组的指针传递给函数,方法是将其大小连同它一起传递,并在 [square brackets] 中包含大小标识符。例如:

void func5(size_t size, char (*str_array)[size]) {
    printf("sizeof str_array: %zu\n"
           "sizeof str_array[0]: %zu\n", sizeof str_array, sizeof str_array[0]);
}

这声明了一个指向 size chars 数组的指针。请注意,您必须先取消对 指针 的引用,然后才能访问该数组。在上面的示例中,sizeof str_array[0] 计算为数组的大小,而不是第一个元素的大小。例如,要访问第 11 个元素,请使用 (*str_array)[11]str_array[0][11]

【讨论】:

    【解决方案5】:

    在 C 中,前两个定义是等价的。第三个基本相同,但它给出了关于数组大小的概念。

    如果打印str 是您的意图,那么您可以安全地使用它们中的任何一个。基本上所有三个函数都传递了一个char* 类型的参数,这正是printf() 需要打印一个字符串。以免你不知道,不管它看起来如何,C 中的所有参数传递都是在pass-by-value 模式下完成的。

    编辑: 看来我以后在 SO 上的用词选择必须非常严格。好吧,在第三种情况下,它不知道数组的大小 传递给它的函数,因为它最终被简化为类型char*,就像在前两种情况下一样。我的意思是说它有点告诉阅读它的人数组的大小是10。另外,这在 C 中并没有错/非法。但是对于程序来说,这样做就像没用一样好。它不知道传递给它的函数的数组大小。Downvoter 先生,感谢您指出这种随意的态度并且在 SO 上不能容忍疏忽。

    【讨论】:

    • 它给出了一个关于数组大小的bad概念。
    • @LightnessRacesinOrbit 在任何情况下我都不会使用第三种语法。使用它毫无意义。
    【解决方案6】:

    在一个一维数组中,编译器对它们的处理都是一样的。但是对于二维或更多维数组(例如myArray[10][10]),它很有用,因为它可用于确定数组的行/列长度。

    【讨论】:

    • 不,第三个函数传递一个固定大小的数组。这意味着这些函数之间存在 区别,但实际上没有区别。所有三个函数的类型都是相同的。如果您想提供有关预期数组大小的提示,请使用注释或文档,清楚地表明编译器不会检查大小。
    【解决方案7】:

    附加组件,以点为单位进行描述。

    1) 正如大家所说的一样。

    2) 数组在函数参数中传递时会衰减为指针。

    3) 基本问题可能是在函数中查找数组的大小。为此,我们可以使用类似宏。

       #define noOfElements(v) sizeof(v)/sizeof(0[v])
    
       int arr[100]
       myfunction ( arr, noOfElements(arr))
    

    0[v] 或 v[0] 都可以在宏中使用,其中第一个用于避免将用户定义的数据类型传递给 noOfElements。

    希望这会有所帮助。

    【讨论】:

    • 在你定义的宏中应该是 v[0]。
    • v[0],可以写成0[v],实际上转换分别是这样的*(v+0)和*(0+v)。
    • 如果您使用宏,最好定义一个宏来为您提供arr 指针和大小,您只需编写一次arr(以避免复制和粘贴错误)。例如,#define ARRAY_WITH_SIZE(x) x,sizeof(x)。然后你可以调用myfunction(ARRAY_WITH_SIZE(arr)); - 你也可以使用上面使用的相同概念来定义ARRAY_WITH_LENGTH
    猜你喜欢
    • 2013-05-17
    • 2012-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-13
    • 2021-02-23
    相关资源
    最近更新 更多