在C语言中将字符数组传递给函数的方式有几种?

12
  • foo(char *s)
  • foo(char *s[])
  • foo(char s[])

所有这些的区别是什么?

是否有任何方法可以修改作为参数传递的数组中的元素,就像我们使用&传递intfloat并且实际参数的值被修改一样?


1
对我来说很难理解你的意思,但第二个选项是错误的。它是指向数组的指针,因此实际上是指向指针,而不是指向值的指针。第一个和第二个之间没有区别。我会使用第一个来表示我正在传递一个指向字符的指针,并使用第二个来表示我正在传递字符数组(指向表中第一个字符的指针)。对于问题的第二部分,我根本不理解。你可以使用所有这些方法修改元素。 - DawidPi
3
我的朋友 char *s[] 不是一个字符数组。 - ameyCU
char (*s)[] 是指向字符数组的指针。char *s[] 是字符指针的数组。 - zneak
感谢 @DawidPi,我是 C 语言的新手,并不知道它们之间的区别,所以可能会列举出错误的选项。 - mightyWOZ
@Balraj 请不要编辑问题并__添加__额外的问题,一旦初始问题得到回答。如果您需要更多信息,可以在答案下面使用评论部分或者提出新问题。谢谢。 - Sourav Ghosh
抱歉。已经注意到了。 - mightyWOZ
4个回答

13
在 C 语言中,无法通过值传递数组。您列出的每个有效解决方案(不幸的是,不包括 #2)都不仅会让您可以,而且会强制您修改原始数组。
由于参数衰减,foo(char* s)foo(char s[]) 是完全等价的。在这两种情况下,您都使用数组的名称来传递它:
char array[4];
foo(array); // regardless of whether foo accepts a char* or a char[]

在这两种情况下,数组都被转换为指向其第一个元素的指针。

指向数组的指针解决方案较少见。它需要以这种方式进行原型设计(注意*s周围的括号):

void foo(char (*s)[]);

没有括号,你就在请求一个字符指针的数组。

在这种情况下,为了调用函数,你需要传递数组的地址:

foo(&array);

每次访问数组元素时,您还需要取消引用来自foo的指针:

void foo(char (*s)[])
{
    char c = (*s)[3];
}

就像这样,它并不是特别方便。然而,它是唯一允许您指定数组长度的形式,您可能会发现它很有用。这是我个人最喜欢的之一。

void foo(char (*s)[4]);

如果您尝试传递的数组没有恰好4个字符,则编译器将警告您。此外,sizeof仍按预期工作。(显而易见的缺点是数组必须具有精确数量的元素。)


实际上可以通过值传递数组。请注意,当您将错误的指针类型显示为数组指针时,编译器实际上并不强制警告您不兼容的指针类型,尽管好的编译器会这样做。更好的类型安全性的替代方法可能是VLA语法:void foo(size_t n,char s [n])。这仍然被简化为对第一个元素的指针,因为C不区分常规数组和VLA的“参数衰减”。 - Lundin
1
@Lundin,如果您能向我展示如何按值传递数组(而不是包装数组的结构体),那么今天您将教会我一些东西。 - zneak
@Lundin 啊,那个古老的“实际上可以通过值传递数组”但代码实际上是“通过值传递结构体”的技巧。Maxwell Smart会为此感到自豪。 - chux - Reinstate Monica
就机器码(又称C标准之外的真实世界)而言,你的程序正在按值传递一个数组。 - Lundin

4
场景:您正在使用数组作为参数调用函数。
在这种情况下,
 foo(char *s)

并且

 foo(char s[])

considered被认为是等价的。它们都期望使用char数组进行调用。

另一方面,foo(char *s[ ])不同,因为它需要一个char *数组的地址。


一旦将 s 传递给 foo 函数,我能否像 s[] = "Hello world" 这样为 s 赋值,还是只能使用循环的方法? - mightyWOZ
@Balraj,你永远不能给数组名称赋值。循环或使用memcpy()/strcpy()是可选项。 - Sourav Ghosh
但是可以在初始化新的字符数组时完成,对吧。 - mightyWOZ
@Balraj 精确地说,在定义时使用大括号括起来的初始化器列表进行赋值初始化是可能的,所以是的。 - Sourav Ghosh

4
  • foo(char *s) is pretty straight-forward, it is just a pointer, which could potentially be a pointer to the first element of an array.

  • foo(char s[]) is a declaration of an array of char, but because of a (stupid) rule in the C standard, this gets translated to a pointer to char. Similarly, if you declare any array size, you still get a pointer to char.

    Mentioned rule can be found in C11 6.7.6.3 Function declarators:

    A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’

    It would perhaps have been much better if this strange, confusing "would-be-array" syntax was banned entirely, as it is 100% superfluous and fills no other purpose but to confuse beginners who try to join the "C club". But then C was never a rational language...

    Some will say that the the rationale is that you should never be allowed to pass arrays by value in C, for performance reasons. Well, tough luck, because you still can, despite this rule.

  • You can "hack the C standard" and pass an array by value:

    typedef struct
    {
      char array[10];
    } by_val_t;
    
    foo (by_val_t arr);  // bad practice but possible
    

    Now there's really never a reason why you would want to pass an array by value, this is very bad program design and doesn't make any sense. I merely included this example here to prove that the rule in 6.7.6.3 completely lacks any rationale.

  • You could pass an array pointer as a parameter to a function. This is where things turn advanced, as this is quite obscure and has very limited practical use:

    foo(char(*s)[])
    

    If you specify an array size here, you actually get a bit of added type safety because compilers tend to warn about casts between array pointers of different size.

    But note that array pointers are mainly just there to make the C language more consistent. There's very few reasons why you would ever want to pass an array pointer to a function (here is one somewhat complex example where I apparently found a valid use for it).


0

foo(char *s)foo(char *s[])foo(char s[]),它们之间有什么区别?

在这些函数中,foo(char *s)foo(char s[]) 都将期望作为参数的 char 数组

除此之外,foo(char *s[]) 将期望一个指向 char 类型的指针数组作为参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接