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

C : If as I understand 0 and '\0' are the same, how does the compiler knows the size of an array when I write int my_array = {0};?

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

所以基本上它可以工作,但是如果我按如下方式初始化目标数组,它就不会工作:

int dest_array[10] = {0};

据我所知,它用 int 0 填充数组,相当于 '\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 != '[=11=]'              ;        
             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语言中,天生就不可能知道数组的长度。这是因为数组实际上只是一块连续的内存,传递给函数的值实际上只是指向数组中第一个元素的指针。因此,要真正知道函数内数组的长度,而不是声明该数组的函数,您必须以某种方式将该值提供给函数。两种常见的方法是使用指示最后一个元素的标记值(类似于 '\0' 的方式,空字符,按照惯例被解释为第一个字符,而不是 C 中字符串的一部分),或者提供另一个参数包含数组长度。

作为一个非常常见的例子:如果你写过任何使用 command-line 参数的程序,那么你肯定熟悉 int main(int argc, char *argv[]) 的常见定义,它使用上述方法通过 argc 参数提供 argv 数组的长度。

编译器有一些方法可以解决 local 变量的这个问题。例如,以下将起作用:

#include <stdio.h>

int main(){
    int nums[10] = {0};
    printf("%zu\n", 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("%zu\n", 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).

的数据类型

An example of this available on Ideone.

int* ptr1;
int* ptr2;

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

同样适用于字符数组。考虑以下:

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

您提到的检查 [=15=](或 0)的方法为您提供了位于 some_string 中的字符串的元素数量。它绝不是指 some_string 中的元素数,即 100.

要确定目的地的大小,您必须传递另一个描述其大小的参数。

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

TL/DR - 您需要将数组大小作为单独的参数传递给您的函数。 0 之类的标记值仅标记序列的 逻辑 结尾,而不是数组本身的结尾。

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

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

您将需要至少再提供一个参数给 copy 来告诉它目标缓冲区有多大,这样当您到达目标缓冲区的末尾或看到一个0 在源缓冲区中,以先到者为准。


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

C语言中有完全不完全数组的概念。您可以在初始化 arr[X] = {0} 中仅对完整数组使用语法糖。