C语言:详解各操作数组的含义 arr、arr[0]、&arr、&arr+1、&arr[0] + 1、*arr、*(arr+1)等,彻底摆脱被数组指针支配的恐惧!

文章详细分析了sizeof和strlen在C语言中处理数组和字符数组时的行为,包括数组名表示的含义、sizeof计算数组和元素大小的区别,以及strlen用于计算字符串长度的方法。

目录

讨论数组名

sizeof & strlen

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

sizeof(a)

sizeof(a + 0)

sizeof(*a)

sizeof(a + 1)

sizeof(a[1])

sizeof(&a)

sizeof(*&a)

sizeof(&a + 1)

sizeof(&a[0])

sizeof(&a[0] + 1)

char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'}

sizeof(arr)

sizeof(arr + 0)

sizeof(*arr)

sizeof(arr[1])

sizeof(&arr)

sizeof(&arr + 1)

sizeof(&arr[0] + 1)

♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥

strlen(arr)

strlen(arr + 0)

 strlen(*arr)

strlen(arr[1])

strlen(&arr)

strlen(&arr + 1)

strlen(&arr[0] + 1)

char arr[] = "abcdef"

sizeof(arr)

sizeof(arr + 0)

sizeof(*arr)

sizeof(arr[1])

sizeof(&arr)

sizeof(&arr + 1)

sizeof(&arr[0] + 1)

♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥

strlen(arr)

strlen(arr + 0)

strlen(*arr)

strlen(arr[1])

strlen(&arr)

strlen(&arr + 1)

strlen(&arr[0] + 1)

char *p = "abcdef"

sizeof(p)

sizeof(p + 1)

sizeof(*p)

sizeof(p[0])

sizeof(&p)

sizeof(&p + 1)

sizeof(&p[0] + 1)

♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥

strlen(p)

strlen(p + 1)

strlen(*p)

strlen(p[0])

strlen(&p)

strlen(&p + 1)

 strlen(&p[0] + 1)

int a[3][4] = {0}

sizeof(a)

sizeof(a[0][0])

sizeof(a[0])

sizeof(a[0] + 1)

sizeof(*(a[0] + 1))

sizeof(a + 1)

sizeof(*(a + 1))

sizeof(&a[0] + 1)

sizeof(*(&a[0] + 1))

sizeof(*a)

sizeof(a[3])


讨论数组名

数组名通常表示的都是数组首元素的地址
但是有2个例外:
   1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
   2. &数组名,这里的数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址

sizeof & strlen

  • sizeof只关注占用内存空间的大小,不在乎内存中放的是什么,所以\0也算一个字符(只针对字符串)
  • strlen是求字符串长度的,关注的是字符串中\0,计算的是\0之前出现的字符的个数(strlen是操作符)

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

sizeof(a)

sizeof(数组名), 数组名表示整个数组,计算的是整个数组的大小,单位是字节

printf("%d\n", sizeof(a)); // 16

sizeof(a + 0)

a不是单独放在sizeof内部,也没有取地址,所以a就是数组首元素的地址,指针地址的大小是4/8个字节,当前是64位,即8 

printf("%d\n", sizeof(a + 0)); // 8

sizeof(*a)

a代表数组首元素地址,解引用得到第一个int值,int是 4 个字节

printf("%d\n", sizeof(*a)); // 4

sizeof(a + 1)

a代表数组首元素地址,a+1,是第二个元素的地址,sizeof(a+1)就是地址的大小,4/8,当前64位,即8

printf("%d\n", sizeof(a + 1)); // 8

sizeof(a[1])

 a[1] ——> *(a+1),也就是第二个int值的大小,同样是 4 个字节

printf("%d\n", sizeof(a[1])); // 4

sizeof(&a)

&a 等同于 a+0,同样是指针地址的大小,即4/8个字节,当前是64位,即8,(如果去掉&,大小就是16) 

printf("%d\n", sizeof(&a)); // 8

sizeof(*&a)

*和& 可以看做同时抵消,只剩a,那么就是整个数组的大小(&a-->int(*)[4],&a拿到的是数组名的地址,类型是int(*)[4],是一种数组指针,数组指针解引用找到的是数组,*&a-->a)

printf("%d\n", sizeof(*&a)); // 16

sizeof(&a + 1)

&a取出是数组的地址,&a+1 跳过了整个数组的字节(16)所指向的地址,但同样是指针的大小,即4/8个字节,当前是64位,即8

printf("%d\n", sizeof(&a + 1)); // 8

sizeof(&a[0])

 第一个元素的地址大小.同样是指针地址的大小,即4/8个字节,当前是64位,即8

printf("%d\n", sizeof(&a[0])); // 8

sizeof(&a[0] + 1)

第二个元素的地址大小,同样是指针地址的大小,即4/8个字节,当前是64位,即8

sizeof(&a[0] + 1)

char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'}

sizeof(arr)

printf("%d\n", sizeof(arr));         // 6

sizeof(arr + 0)

printf("%d\n", sizeof(arr + 0));     // 8 首元素地址大小,4/8

sizeof(*arr)

printf("%d\n", sizeof(*arr));        // 1 首元素地址解引用,a的字节大小

sizeof(arr[1])

printf("%d\n", sizeof(arr[1]));      // 1 第2个元素大小

sizeof(&arr)

printf("%d\n", sizeof(&arr));        // 8 取数组的地址,地址就是地址,4/8

sizeof(&arr + 1)

printf("%d\n", sizeof(&arr + 1));    // 8 跳过整个数组的地址,4/8

sizeof(&arr[0] + 1)

printf("%d\n", sizeof(&arr[0] + 1)); // 8 第2个元素的地址,4/8

♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥

strlen(arr)

printf("%d\n", strlen(arr));     // 随机值,找到 \0 才停止

strlen(arr + 0)

printf("%d\n", strlen(arr + 0)); // 随机值,arr首元素地址+0还是首元素地址,等同于上式

 strlen(*arr)

 报错, strlen接收字符指针作为参数,而 strlen(*arr) -- strlen('a') -- strlen(97),而97相当于是个野指针,并不属于a

printf("%d\n", strlen(*arr)); // error

strlen(arr[1])

 报错,相当于 strlen('b') -- strlen(61) 同上也是野指针

printf("%d\n", strlen(arr[1])); // error

strlen(&arr)

printf("%d\n", strlen(&arr)); // 随机值,取数组地址,本质也是首元素地址

strlen(&arr + 1)

printf("%d\n", strlen(&arr + 1)); // 随机值 - 6,&arr跳过整个数组取地址

strlen(&arr[0] + 1)

rintf("%d\n", strlen(&arr[0] + 1)); // 随机值 - 1, &arr[0] 第一个元素地址

char arr[] = "abcdef"

sizeof(arr)

printf("%d\n", sizeof(arr)); // 7 这里默认补 \0

sizeof(arr + 0)

printf("%d\n", sizeof(arr + 0)); // 8  a的地址+0,还是a的地址,4/8 

sizeof(*arr)

printf("%d\n", sizeof(*arr)); // 1 *arr就是元素a

sizeof(arr[1])

printf("%d\n", sizeof(arr[1])); // 1 元素b

sizeof(&arr)

printf("%d\n", sizeof(&arr)); // 8,数组地址,4/8

sizeof(&arr + 1)

printf("%d\n", sizeof(&arr + 1)); // 8 &arr + 1 移动一个数组的长度,即指向数组 arr 之后的一个内存位置,指向 arr 数组结束的位置之后的地址, 指针大小,即4/8

sizeof(&arr[0] + 1)

printf("%d\n", sizeof(&arr[0] + 1)); // 8 元素a的地址

♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥

strlen(arr)

    /* 6 截取\0之前的字符个数 */
    printf("%d\n", strlen(arr));

strlen(arr + 0)

    /* 6 同上 */
    printf("%d\n", strlen(arr + 0));

strlen(*arr)

    /* 报错,同上上上上上 */
    printf("%d\n", strlen(*arr));

strlen(arr[1])

    /* 报错,同上上上上上 */
    printf("%d\n", strlen(arr[1]));

strlen(&arr)

    /* 6 */
    printf("%d\n", strlen(&arr));

strlen(&arr + 1)

    /* 随机值 跳过整个数组,\0也会跳过,它不知道所指向的下个字符数组的地址个数有几个元素 */
    printf("%d\n", strlen(&arr + 1));

strlen(&arr[0] + 1)

    /* 5 */
    printf("%d\n", strlen(&arr[0] + 1));

char *p = "abcdef"

sizeof(p)

    /*  8 这里是指针变量的大小 */
    printf("%d\n", sizeof(p));

sizeof(p + 1)

    /* 8 b的地址 */
    printf("%d\n", sizeof(p + 1));

sizeof(*p)

    /* 1 */
    printf("%d\n", sizeof(*p));

sizeof(p[0])

    /* 1 p[0] == *(p + 0) */
    printf("%d\n", sizeof(p[0]));

sizeof(&p)

    /* 8 指针变量大小 */
    printf("%d\n", sizeof(&p));

sizeof(&p + 1)

    /* 8 */
    printf("%d\n", sizeof(&p + 1));

sizeof(&p[0] + 1)

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

♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥

strlen(p)

    /* 6 */
    printf("%d\n", strlen(p));

strlen(p + 1)

    /* 5 */
    printf("%d\n", strlen(p + 1));

strlen(*p)

    /* 报错,*p == 96 */
    printf("%d\n", strlen(*p));

strlen(p[0])

    /* 6 */
    printf("%d\n", strlen(p[0]));

strlen(&p)

    /* 随机值,p本身就是地址,再取地址,相当于取指针变量p的地址,两个地址之间毫无关系 */
    printf("%d\n", strlen(&p));

strlen(&p + 1)

    /* 随机值 */
    printf("%d\n", strlen(&p + 1));

 strlen(&p[0] + 1)

    /* 5 p[0] == 元素a,&a+1即5 */
    printf("%d\n", strlen(&p[0] + 1));

int a[3][4] = {0}

sizeof(a)

    /* 48 */
    printf("%d\n", sizeof(a));

sizeof(a[0][0])

    /* 4 */
    printf("%d\n", sizeof(a[0][0]));

sizeof(a[0])

a[0]相当于第一个数组,只有一个数组名,计算当前数组的大小

printf("%d\n", sizeof(a[0])); // 16

sizeof(a[0] + 1)

 arr[0]是表示首元素的地址,本质是数组名(又等于第一行数组首元素的地址),等同于第一个数组的arr+1。也就是第一行第二个元素的地址

printf("%d\n", sizeof(a[0] + 1)); // 8

sizeof(*(a[0] + 1))

第一行第二个元素的大小

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

sizeof(a + 1)

a虽然是二维数组的地址,但是并没有单独放在sizeof内部,也没取地址,a表示首元素的地址,二维数组的首元素是它的第一行,a就是第一行的地址,+1跳过一行的数组,表示第二行的地址

printf("%d\n", sizeof(a + 1)); // 8

sizeof(*(a + 1))

第二行数组地址的解引用,拿到了整个第二行,sizeof内只有数组名,求整个第二行数组的大小

printf("%d\n", sizeof(*(a + 1))); // 16

sizeof(&a[0] + 1)

&a[0]对第一行的数组名取地址,拿出的是第一行的地址,&a[0]+1 得到的是第二行的地址

printf("%d\n", sizeof(&a[0] + 1)); // 8

sizeof(*(&a[0] + 1))

第二行的地址解引用,得到第二行的数组名arr,sizeof内部只有arr,即计算整个第二行数组的大小 

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

sizeof(*a)

解引用整个二维数组的地址,即解引用二维数组首元素第一行数组的地址,再解引用第一行的地址得到arr,只有arr,计算整个第一行数组大小 

printf("%d\n", sizeof(*a)); // 16

sizeof(a[3])

超界不会真正访问第四行的,其会根据int类型,就可以推算到,等同于a[0]

printf("%d\n", sizeof(a[3])); // 16

# 题目重述 `&arr` 和 `&arr + 1` 相差多少个字节?请详细解释。 --- ## 详解 我们来深入分析 `&arr` 和 `&arr + 1` 的含义以及它们之间的字节差距。 --- ### ✅ 示例代码背景 假设有一个数组: ```c int arr[10]; ``` 我们知道: - `arr` 是数组名,表示首元素地址(即 `&arr[0]`) - `&arr` 是整个数组的地址 - 虽然 `&arr` 和 `arr` 的**数值相同**,但它们的**类型不同**,这直接影响指针运算! --- ### 一、`&arr` 的类型是什么? 这是关键点! | 表达式 | 类型 | 含义 | |----------|------------------|--------------------------| | `&arr` | `int (*)[10]` | 指向“包含10个int的数组”的指针 | | `&arr[0]` 或 `arr` | `int*` | 指向单个 int 元素的指针 | 👉 所以 `&arr` 不是指向第一个元素,而是指向“整个数组”。 --- ### 二、`&arr + 1` 是什么? 当对指针进行加法运算时,移动的距离由其**所指类型的大小**决定。 公式为: $$ \text{新地址} = \text{原地址} + n \times \text{sizeof(所指类型)} $$ 对于 `&arr`: - 所指类型是:`int [10]`(一个含10个整数的数组) - 大小为:$ sizeof(int) \times 10 = 4 \times 10 = 40 $ 字节(假设 `int` 占 4 字节) 因此: $$ &arr + 1 = &arr + 1 \times sizeof(arr) = &arr + 40 $$ --- ### 三、`&arr` 和 `&arr + 1` 相差多少字节? 答案是: > 💡 **40 个字节** 因为: - `&arr` 指向数组 `arr` 的起始位置 - `&arr + 1` 指向的是“下一个同类型数组”的起始位置(逻辑上相当于跳过整个 `arr`) - 这个距离就是整个数组占用的空间:`10 * sizeof(int) = 40` 字节 --- ### 🧠 对比其他类似表达式 | 表达式 | 类型 | 加1后移动字节数 | 说明 | |---------------|-----------------|------------------|------| | `arr` 或 `&arr[0]` | `int*` | 4 字节 | 指向元素,`+1` 移动一个 int | | `&arr` | `int (*)[10]` | 40 字节 | 指向整个数组,`+1` 跳过整个数组 | | `&arr + 1` | 仍为 `int (*)[10]` | 指向 `arr` 之后 40 字节处 | 非法访问若解引用 | ⚠️ 注意:虽然可以计算 `&arr + 1`,但它指向的是数组末尾后的地址,**不能解引用**(会导致未定义行为)。 --- ### 🔍 内存布局图示(简化) ``` 地址: ... 0x1000 &rarr; 0x1028 &darr; &darr; 内存: +----+----+--- ... ---+------+ | a[0]| a[1]| ... | a[9] | +----+----+--- ... ---+------+ &uarr;_____________________&uarr; 40 字节 &arr &arr + 1 ``` - `&arr` 在 `0x1000` - `&arr + 1` 在 `0x1028`(= 0x1000 + 40) --- ### ✅ 实际验证代码 ```c #include <stdio.h> int main() { int arr[10]; printf("地址差: %td\n", (&arr + 1) - &arr); // 输出 1 printf("字节差: %zu\n", (char*)(&arr + 1) - (char*)&arr); // 输出 40 return 0; } ``` 输出: ``` 地址差: 1 字节差: 40 ``` 👉 `(char*)` 强制转为字节指针,可精确测量字节差距。 --- ## 总结 - `&arr` 是指向整个数组指针,类型为 `int (*)[10]` - `&arr + 1` 向后移动了 `sizeof(arr) = 40` 字节 - 所以 `&arr` 和 `&arr + 1` **相差 40 个字节** 这个结果体现了 C 语言中**指针运算的类型敏感性**:不是简单地加 1,而是按所指对象大小缩放。 --- # 知识点 - **指针加法规则**:`p + 1` 移动 `$sizeof(*p)$` 字节,取决于指针所指类型大小。 - **&arr 的类型**:`int (*)[10]`,表示指向整个数组指针,而非单个元素。 - **数组整体作为单位**:`&arr + 1` 跳过整个数组,偏移量为 `$n \times sizeof(type)$`。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山楂树の

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值