数组类型 - 分配/用作函数参数的规则

发布于 2024-08-17 16:27:57 字数 395 浏览 2 评论 0原文

当我需要将数组传递给函数时,似乎该函数的以下所有声明都适用

void f(int arr[])  
void f(int arr[4]) // is this one correct?

int a[]={1,2,3,4};
f(a);

但是当我将一个数组分配给另一个数组时,它会失败

int a[]={1,2,3,4};
int b[4] = a; // error: array must be initialized with a brace-enclosed initializer

那么为什么将数组作为函数的参数传递是可以的,但是用在rhs上简单赋值是错误的?

when I need to pass an array to a function, it seems all the following declarations of the function will work

void f(int arr[])  
void f(int arr[4]) // is this one correct?

for this:

int a[]={1,2,3,4};
f(a);

But when I assign an array to another array, it fails

int a[]={1,2,3,4};
int b[4] = a; // error: array must be initialized with a brace-enclosed initializer

So why an array passed as an argument of a function is okay, but used on the rhs of simple assignment is wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

终陌 2024-08-24 16:27:57

为了理解差异,我们需要理解两个不同的上下文

  • 上下文中,T类型的数组的名称相当于指向T类型的指针,并且等于指向数组的第一个元素。
  • 对象上下文中,T类型的数组的名称不会简化为指针。

什么是对象上下文?

a = b; 中,a 位于对象上下文中。当您获取变量的地址时,它将在对象上下文中使用。最后,当您对变量使用 sizeof 运算符时,它会在对象上下文中使用。在所有其他情况下,变量都在值上下文中使用。

现在我们已经有了这些知识,当我们这样做时:

void f(int arr[4]);

完全相当于

void f(int *arr);

正如您所发现的,我们可以从函数声明中省略大小(上面的 4)。这意味着您无法知道传递给 f() 的“数组”的大小。稍后,当您执行以下操作时:

int a[]={1,2,3,4};
f(a);

在函数调用中,名称 a 位于值上下文中,因此它简化为指向 int 的指针。这很好,因为 f 需要一个指向 int 的指针,因此函数定义和使用相匹配。传递给 f() 的是指向 a 第一个元素的指针 (&a[0])。

在名称 b 的情况下,

int a[]={1,2,3,4};
int b[4] = a;

在对象上下文中使用,并且不会简化为指针。 (顺便说一句,a 这里位于值上下文中,并简化为指针。)

现在,int b[4]; 分配存储值4 个 int 并为其指定名称 ba 也被分配了类似的存储空间。因此,实际上,上面的赋值意味着“我想让存储位置与之前的位置相同”。这没有道理。

如果您想将a的内容复制b中,那么您可以这样做:

#include <string.h>
int b[4];
memcpy(b, a, sizeof b);

或者,如果您想要一个指针b< /code> 指向 a

int *b = a;

这里,a 位于值上下文中,并简化为指向 int 的指针,因此我们可以分配 < code>a 到 int *

最后,在初始化数组时,您可以为其指定显式值:

int a[] = {1, 2, 3, 4};

这里,a 有 4 个元素,初始化为 1、2、3 和 4。您还可以这样做:

int a[4] = {1, 2, 3, 4};

如果元素较少列表中的元素数量大于数组中的元素数量,则其余值均视为 0:

int a[4] = {1, 2};

a[2]a[3] 设置为 0 。

For understanding the difference, we need to understand two different contexts.

  • In value contexts, the name of an array of type T is equivalent to a pointer to type T, and is equal to a pointer to the array's first element.
  • In object contexts, the name of an array of type T does not reduce to a pointer.

What is object context?

In a = b;, a is in object context. When you taken the address of a variable, it's used in object context. Finally, when you use sizeof operator on a variable, it's used in object context. In all other cases, a variable is used in value context.

Now that we have this knowledge, when we do:

void f(int arr[4]);

It is exactly equivalent to

void f(int *arr);

As you found out, we can omit the size (4 above) from the function declaration. This means that you can't know the size of the "array" passed to f(). Later, when you do:

int a[]={1,2,3,4};
f(a);

In the function call, the name a is in value context, so it reduces to a pointer to int. This is good, because f expects a pointer to an int, so the function definition and use match. What is passed to f() is the pointer to the first element of a (&a[0]).

In the case of

int a[]={1,2,3,4};
int b[4] = a;

The name b is used in a object context, and does not reduce to a pointer. (Incidentally, a here is in a value context, and reduces to a pointer.)

Now, int b[4]; assigns storage worth of 4 ints and gives the name b to it. a was also assigned similar storage. So, in effect, the above assignment means, "I want to make the storage location the same as the previous location". This doesn't make sense.

If you want to copy the contents of a into b, then you could do:

#include <string.h>
int b[4];
memcpy(b, a, sizeof b);

Or, if you wanted a pointer b that pointed to a:

int *b = a;

Here, a is in value context, and reduces to a pointer to int, so we can assign a to an int *.

Finally, when initializing an array, you can assign to it explicit values:

int a[] = {1, 2, 3, 4};

Here, a has 4 elements, initialized to 1, 2, 3, and 4. You could also do:

int a[4] = {1, 2, 3, 4};

If there are fewer elements in the list than the number of elements in the array, then the rest of the values are taken to be 0:

int a[4] = {1, 2};

sets a[2] and a[3] to 0.

一世旳自豪 2024-08-24 16:27:57
void f(int arr[]);
void f(int arr[4]);

语法具有误导性。它们都与此相同:

void f(int *arr);

即,您传递一个指向数组开头的指针。您没有复制数组。

void f(int arr[]);
void f(int arr[4]);

The syntax is misleading. They are both the same as this:

void f(int *arr);

i.e., you are passing a pointer to the start of the array. You are not copying the array.

伪心 2024-08-24 16:27:57

C 不支持数组赋值。在函数调用的情况下,数组会衰减为指针。 C 确实支持指针赋值。几乎每天都会有人问这个问题 - 你们在读哪本没有解释这个问题的 C 教科书?

C does not support the assignment of arrays. In the case of a function call, the array decays to a pointer. C does support the assignment of pointers. This is asked here just about every day - what C text book are you guys reading that doesn't explain this?

∝单色的世界 2024-08-24 16:27:57

尝试 memcpy。

int a[]={1,2,3,4};
int b[4];
memcpy(b, a, sizeof(b));

谢谢你指出这一点,Steve,我已经有一段时间没有使用 C 了。

Try memcpy.

int a[]={1,2,3,4};
int b[4];
memcpy(b, a, sizeof(b));

Thanks for pointing that out, Steve, it's been a while since I used C.

琉璃繁缕 2024-08-24 16:27:57

为了获得对此的直觉,您必须了解机器级别上发生的情况。

初始化语义 (= {1,2,3,4}) 意味着“完全按照这种方式将其放在二进制图像上”,以便可以对其进行编译。

数组赋值会有所不同:编译器必须将其转换为循环,该循环实际上会迭代元素。 C 编译器(或 C++,就此而言)从不做这样的事情。它理所当然地期望你自己做这件事。为什么?因为你可以。所以,它应该是一个用 C 语言(memcpy)编写的子程序。这都是关于简单性和接近你的武器,这就是 C 和 C++ 的全部。

To get your intuition about it, you must understand what's going on on machine level.

The initialization semantics (= {1,2,3,4}) means "put it on your binary image exactly this way" so this can be compiled.

Array assignment would be different: the compiler would have to translate it into a loop, which would actually iterate over elements. C compiler (or C++, for that matter) never does such a thing. It rightfully expects you to do it yourself. Why? Because you can. So, it should be a subroutine, written in C (memcpy). This is all about simplicity and closeness to your weapon, which is C and C++ all about.

Smile简单爱 2024-08-24 16:27:57

请注意,int a[4] 中的a 类型为int [4]

但是TypeOf(&a) == int (*)[4] != int [4]

另请注意,avalue 类型是int *,这与上述所有类型都不同!

这是您可以尝试的示例程序:

int main() {
  // All of these are different, incompatible types!      
  printf("%d\n", sizeof (int[4]));  // 16
  // These two may be the same size, but are *not* interchangeable!
  printf("%d\n", sizeof (int (*)[4]));  // 4
  printf("%d\n", sizeof (int *));  // 4
}

Note that the type of a in int a[4] is int [4].

But TypeOf(&a) == int (*)[4] != int [4].

Also note that the type of the value of a is int *, which is distinct from all of the above!

Here's a sample program you can try:

int main() {
  // All of these are different, incompatible types!      
  printf("%d\n", sizeof (int[4]));  // 16
  // These two may be the same size, but are *not* interchangeable!
  printf("%d\n", sizeof (int (*)[4]));  // 4
  printf("%d\n", sizeof (int *));  // 4
}
述情 2024-08-24 16:27:57

我想澄清一下。答案中有一些误导性的提示...以下所有函数都可以采用整数数组:

void f(int arr[])
void f(int arr[4])
void f(int *arr) 

但是形式参数并不相同。因此编译器可以以不同的方式处理它们。从内部内存管理的意义上来说,所有的参数都指向指针。

void f(int arr[])

... f() 接受任意大小的数组。

void f(int arr[4])

... 形式参数表示数组大小。

void f(int *arr)

...您还可以传递整数指针。 f() 不知道任何有关大小的信息。

I want to clarify. There are some misleading hints in answers... All following functions can take integer arrays:

void f(int arr[])
void f(int arr[4])
void f(int *arr) 

But formal arguments are not the same. So the compiler can handle them differently. In the sense of internal memory management, all arguments lead to pointers.

void f(int arr[])

... f() takes an array of any size.

void f(int arr[4])

... The formal argument indicates an array size.

void f(int *arr)

... You can also pass an integer pointer. f() does not know anything about the size.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文