C语言中数组和指针的sizeof和strlen详解

2023-12-31 00:47:10

????????数组和指针是常用的数据类型,了解它们的sizeof和strlen的使用是编写高效程序的关键。本文将通过一系列示例代码详细解释数组和指针在不同场景下的sizeof和strlen的计算方式。

一维数组

首先,我们来看一维数组的情况:

int main() {
    int a[] = {1, 2, 3, 4};
    
    printf("%d\n", sizeof(a));        // 16
    printf("%d\n", sizeof(a + 0));    // 4
    printf("%d\n", sizeof(*a));       // 4
    printf("%d\n", sizeof(a + 1));    // 4
    printf("%d\n", sizeof(a[1]));     // 4
    printf("%d\n", sizeof(&a));       // 4
    printf("%d\n", sizeof(&a + 1));   // 4
    printf("%d\n", sizeof(&a[0] + 1)); // 4

    return 0;
}

在这个例子中,a是一个包含4个整数的数组。下面是对上述代码各行的详细解释:

  • sizeof(a):返回整个数组所占的字节数,这里是4个整数 * 4字节/整数 = 16字节。
  • sizeof(a + 0):数组名加0等于数组本身,但在这里被解释为数组指针,所以返回指针大小,通常是4字节。
  • sizeof(*a):返回数组的第一个元素的大小,这里是4字节,因为a是int类型数组。
  • sizeof(a + 1):数组名加1表示指向数组下一个元素的指针,所以返回指针大小,通常是4字节。
  • sizeof(a[1]):返回数组的第二个元素的大小,同样是4字节,因为a[1]是int类型。
  • sizeof(&a):返回数组的地址的大小,通常是4字节。
  • sizeof(&a + 1):数组地址加1,跳过整个数组,返回地址大小,通常是4字节。
  • sizeof(&a[0] + 1):数组第一个元素的地址加1,同样返回地址大小,通常是4字节。

这里需要注意的是,sizeof(a)计算的是整个数组的大小,而sizeof(&a)计算的是数组的地址的大小。

字符数组

接下来,我们看看字符数组的情况:

int main() {
    char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};

    printf("%d\n", sizeof(arr));       // 6
    printf("%d\n", sizeof(arr + 0));   // 4
    printf("%d\n", sizeof(*arr));      // 1
    printf("%d\n", sizeof(arr[1]));    // 1
    printf("%d\n", sizeof(&arr));      // 4
    printf("%d\n", sizeof(&arr + 1));  // 4
    printf("%d\n", sizeof(&arr[0] + 1));// 4

    return 0;
}

在这个例子中,arr是一个包含6个字符的数组。下面是对上述代码各行的详细解释:

  • sizeof(arr):返回整个字符数组所占的字节数,这里是6个字符 * 1字节/字符 = 6字节。
  • sizeof(arr + 0):数组名加0等于数组本身,但在这里被解释为数组指针,所以返回指针大小,通常是4字节。
  • sizeof(*arr):返回字符数组的第一个元素的大小,这里是1字节,因为arr是char类型数组。
  • sizeof(arr[1]):返回字符数组的第二个元素的大小,同样是1字节,因为arr[1]是char类型。
  • sizeof(&arr):返回字符数组的地址的大小,通常是4字节。
  • sizeof(&arr + 1):数组地址加1,跳过整个数组,返回地址大小,通常是4字节。
  • sizeof(&arr[0] + 1):数组第一个元素的地址加1,同样返回地址大小,通常是4字节。

这里需要注意的是,sizeof(arr)计算的是整个字符数组的大小,而sizeof(&arr)计算的是数组的地址的大小。

字符串数组

接下来,我们看看字符串数组的情况:

int main() {
    char arr[] = "abcd";

    printf("%d\n", strlen(arr));        // 4
    printf("%d\n", strlen(arr + 0));    // 4
    // 下面两行是非法的,因为strlen函数需要一个指向字符串的指针作为参数
    // printf("%d\n", strlen(*arr));
    // printf("%d\n", strlen(arr[1]));
    printf("%d\n", strlen(&arr));       // 随机值,传递整个地址给strlen
    printf("%d\n", strlen(&arr + 1));   // 随机值,跳到下一个数组
    printf("%d\n", strlen(&arr[0] + 1)); // 随机值,第一个元素跳到第二个元素

    return 0;
}

在这个例子中,arr是一个包含4个字符的字符串数组。下面是对上述代码各行的详细解释:

  • strlen(arr):返回字符串 "abcd" 的长度,结果为4。
  • strlen(arr + 0)arr + 0 等于 arr,因为它们都指向数组的起始位置,所以结果也为4。
  • strlen(*arr):非法。strlen函数需要一个指向字符串的指针作为参数,而 *arr 是一个字符,所以传递 *arr 给 strlen 是错误的。
  • strlen(arr[1]):非法。arr[1] 是一个字符,不能作为 `strlen‘的参数
  • strlen(&arr):随机值。整个地址传给 strlen,这可能会偶然在内存中找到一个 \0 字节,从而返回一个非预期的长度值。
  • strlen(&arr + 1):随机值。跳到下一个数组,同样传递整个地址给 strlen
  • strlen(&arr[0] + 1):随机值。第一个元素的地址加1,相当于跳到第二个元素,比 &arr 少一个元素。
  • 需要注意的是,strlen 函数需要一个指向字符串的指针作为参数,而 &arr&arr + 1&arr[0] + 1 在这里都是地址,而非指向字符串的指针。

字符指针

现在,我们来看字符指针的情况:

int main() {
    char* p = "abcde";

    printf("%d\n", sizeof(p));          // 随机值,通常为 4 或 8
    printf("%d\n", sizeof(p + 0));      // 随机值,通常为 4 或 8
    printf("%d\n", sizeof(*p));         // 1
    printf("%d\n", sizeof(p[0]));       // 1
    printf("%d\n", sizeof(&p));         // 随机值,通常为 4 或 8
    printf("%d\n", sizeof(&p + 1));     // 随机值,通常为 4 或 8
    printf("%d\n", sizeof(&p[0] + 1));  // 随机值,通常为 4 或 8

    printf("%d\n", strlen(p));          // 随机值,可能会找到 \0 字节返回一个非预期的长度值
    printf("%d\n", strlen(p + 0));      // 随机值,同上
    // 下面两行是非法的,因为strlen函数需要一个指向字符串的指针作为参数
    // printf("%d\n", strlen(*p));
    // printf("%d\n", strlen(p[0]));
    printf("%d\n", strlen(&p));         // 随机值,整个地址传给strlen
    printf("%d\n", strlen(&p + 1));     // 随机值,跳到下一个地址
    printf("%d\n", strlen(&p[0] + 1));  // 随机值,第一个元素的地址加1,相当于跳到第二个元素

    return 0;
}

在这个例子中,p 是一个字符指针,指向字符串 "abcde" 的起始位置。下面是对上述代码各行的详细解释:

  • sizeof(p):返回指针的大小,通常为 4 或 8 字节,具体取决于系统位数。
  • sizeof(p + 0):指针加0等于指针本身,返回指针大小。
  • sizeof(*p):返回字符的大小,这里是1字节。
  • sizeof(p[0]):同样返回字符的大小,1字节。
  • sizeof(&p):返回指针的大小,通常为 4 或 8 字节。
  • sizeof(&p + 1):指针地址加1,跳到下一个地址,返回地址大小。
  • sizeof(&p[0] + 1):第一个元素的地址加1,相当于跳到第二个元素,返回地址大小。

strlen 函数的使用情况与前面的例子相似,需要注意传递给 strlen 的参数必须是指向字符串的指针。

二维数组

最后,我们来看二维数组的情况:

int main() {
    int p[3][4] = {0};

    printf("%d\n", sizeof(p));          // 48
    printf("%d\n", sizeof(p[0][0]));    // 4
    printf("%d\n", sizeof(p[0]));       // 16
    printf("%d\n", sizeof(p[0] + 1));   // 4
    printf("%d\n", sizeof(*(p[0] + 1)));// 4
    printf("%d\n", sizeof(p + 1));      // 4
    printf("%d\n", sizeof(*(p + 1)));   // 16
    printf("%d\n", sizeof(&p[0] + 1));  // 4
    printf("%d\n", sizeof(*(&p[0] + 1)));// 16
    printf("%d\n", sizeof(*p));         // 16
    printf("%d\n", sizeof(p[3]));       // 16

    printf("---------------------\n");
    // 验证
    printf("%p ", &p[0][0]);
    printf("%p ", &p[0][1]);
    printf("%p ", p[0] + 1);

    return 0;
}

在这个例子中,p 是一个包含3行4列的二维数组。下面是对上述代码各行的详细解释:

  • sizeof(p):返回整个二维数组所占的字节数,这里是3 * 4 * 4 = 48字节。
  • sizeof(p[0][0]):返回二维数组中的单个元素的大小,这里是4字节。
  • sizeof(p[0]):返回二维数组的第一行的大小,相当于数组名,所以返回的是第一行的大小,16字节。
  • sizeof(p[0] + 1):第一行的地址加1,相当于跳到第二个元素,返回地址大小,4字节。
  • sizeof(*(p[0] + 1)):第一行的地址加1,解引用,相当于取第二个元素的大小,4字节。
  • sizeof(p + 1)p 是二维数组的数组名,这里返回的是第二行的地址大小,4字节。
  • sizeof(*(p + 1)):第二行的地址解引用,相当于取整个第二行的大小,16字节。
  • sizeof(&p[0] + 1):第一行的地址加1,相当于跳到第二行的地址,返回地址大小,4字节。
  • sizeof(*(&p[0] + 1)):第一行的地址加1,解引用,相当于取整个第二行的大小,16字节。
  • sizeof(*p)p 是二维数组的数组名,返回的是第一行的大小,16字节。
  • sizeof(p[3]):其实sizeof并不会真正去访问,只是会通过变量来推导类型,所以并不会真的去访问它。在这里a[3]其实是数组指针类型,int (*)[3],大小为4或8个字节。

接下来,通过验证部分,我们输出一些地址以验证我们的计算:

  // 验证
    printf("%p ", &p[0][0]);
    printf("%p ", &p[0][1]);
    printf("%p ", p[0] + 1);

????????这部分代码输出了一些地址,其中 &p[0][0] 是第一个元素的地址,&p[0][1] 是第一个元素的地址加1,而 p[0] + 1 是第一行的地址加1。这些输出的地址应该有助于理解地址的增长。

????????在这个例子中,p 被看作是一个包含3行4列的二维数组。需要注意的是,在计算二维数组大小时,sizeof(p) 计算的是整个数组的大小,而 sizeof(p[0]) 计算的是数组的第一行的大小,相当于数组名。此外,sizeof(p[0][0]) 计算的是数组中单个元素的大小。

????????通过这一系列的例子,我们深入了解了C语言中数组和指针在不同场景下的 sizeofstrlen 的计算方式,对于理解内存布局和地址的增长是非常有帮助的。希望这篇博客能够对C语言初学者和进阶者有所启发。

总结:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,去除的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。

文章来源:https://blog.csdn.net/2302_78381559/article/details/135310672
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。