C :如果据我了解 0 和 '\0' 是相同的,那么当我编写 int my_array = {0}; 时,编译器如何知道数组的大小?



我正在尝试创建一个函数,以使用指针将数组复制到另一个数组中。我想添加以下条件:如果目标数组较小,则循环必须断开。

所以基本上它正在工作,但是如果我按如下方式灌输目标数组,它就不起作用:

int dest_array[10] = {0};

据我了解,它用相当于"\0"(空字符)的 int 0 填充数组。所以这是我的问题:

在这种情况下,计算机如何知道数组大小或何时结束?

(如何比较作为参数传递的数组?

void copy(int *src_arr, int *dest_arr)
{
// The advantage of using pointers is that you don't need to provide the source array's size
// I can't use sizeof to compare the sizes of the arrays because it does not work on parameters.
// It returns the size of the pointer to the array and not of of the whole array
int* ptr1;
int* ptr2;
for(     ptr1 = source, ptr2 = dest_arr ;
*ptr1 != ''              ;        
ptr1++, ptr2++            )
{   
if(!*ptr2) // Problem here if dest_arr full of 0's
{ 
printf("Copy interrupted :n" +
"Destination array is too small"); 
break; 
}
*ptr2 = *ptr1;
}

在 C 语言中,不可能固有地知道数组的长度。这是因为数组实际上只是一个连续的内存块,而传递给函数的值实际上只是指向数组中第一个元素的指针。因此,要实际知道函数中数组的长度,而不是声明该数组的函数,您必须以某种方式向该函数提供该值。两种常见的方法是使用指示最后一个元素的哨兵值(类似于 null 字符 '\0' 按照约定被解释为第一个字符而不是 C 中字符串的一部分),或者提供另一个包含数组长度的参数。

举一个非常常见的例子:如果您编写过任何使用命令行参数的程序,那么您肯定熟悉int main(int argc, char *argv[])的常见定义,它通过argc参数提供argv数组的长度来使用上述方法中的第二种。

编译器有一些方法可以针对局部变量解决此问题。 例如,以下内容将起作用:

#include <stdio.h>
int main(){
int nums[10] = {0};
printf("%zun", sizeof(nums)/sizeof(nums[0]));

return 0;
}

哪个打印10到 STDOUT;但是,这只有效,因为sizeof操作是在本地完成的,并且编译器知道该点数组的长度。

另一方面,我们可以考虑将数组传递给另一个函数的情况:

#include <stdio.h>
int tryToGetSizeOf(int arr[]){
printf("%zu", sizeof(arr)/sizeof(arr[0]));
}
int main(){
int nums[10] = {0};
printf("%zun", sizeof(nums)/sizeof(nums[0]));

puts("Calling other function...");
tryToGetSizeOf(nums);

return 0;
}

这将最终将以下内容打印到 STDOUT:

10
Calling other function...
2

这可能不是您期望的值,但发生这种情况的原因是方法签名int tryToGetSizeOf(int arr[])在功能上等同于int tryToGetSizeOf(int *arr)。因此,您将整数指针 (int *) 的大小除以单个int的大小;而当您仍在main()的本地上下文中时(即,最初定义数组的位置),您将分配的内存区域的大小除以该内存区域分区为(int)的数据类型的大小。

在Ideone 上提供了这方面的一个例子。

int* ptr1;
int* ptr2;

将数组引用为指针时会丢失大小信息。您无法使用ptr1识别数组的大小,即元素的数量。您必须借助另一个变量,该变量将表示由ptr1(或ptr2)引用的数组的大小。

字符数组也是如此。请考虑以下事项:

char some_string[100];
strcpy(some_string, "hello");

您提到的检查(或0)的方法为您提供了驻留在some_string中的字符串一部分的元素数量。它绝不是指some_string中的元素数量,这是100

要识别目标的大小,您必须传递另一个描述其大小的参数。

还有其他方法可以识别数组的末尾,但 t 更简洁地显式传递大小,而不是使用一些指针技巧,例如将指针传递到数组的末尾或使用一些无效值作为数组中的最后一个元素。

TL/DR- 您需要将数组大小作为单独的参数传递给函数。 像0这样的哨兵值只标记序列的逻辑结束,而不是数组本身的结束。

除非它是sizeof或一元&运算符的操作数,或者是用于初始化声明中的字符数组的字符串文本,否则类型为"T的 N 元素数组"的表达式将被转换("衰减")为"指向T的指针"类型的表达式,并且表达式的值将是数组第一个元素的地址。 因此,当您将源数组和目标数组作为参数传递给copy时,函数实际接收的只是两个指针。

没有与指针关联的元数据,该指针告诉它是否指向序列中的第一个对象,或者该序列的长度为1。 像字符串中的 0 终止符这样的哨兵值只能告诉您值的逻辑序列有多长,而不是存储它们的数组的大小2

您需要至少再提供一个参数来copy告诉它目标缓冲区有多大,以便在到达目标缓冲区的末尾或在源缓冲区中看到0时停止复制,以先到者为准。


  1. 数组对象也是如此 - 数组对象中没有运行时元数据来存储大小或其他任何内容。sizeof技巧起作用的唯一原因是数组的声明在范围内。 数组对象本身不知道它有多大。
  2. 对于像strcpy这样的库函数来说,这是一个问题,它只接收每个缓冲区的起始地址 - 如果源缓冲区中的字符数超过目标的大小,strcpy将直接超过目标缓冲区的末尾并覆盖接下来的任何内容。

最新更新