用试题这把“剑“帮你破除指针与数组之间的那些猫腻

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 用试题这把“剑“帮你破除指针与数组之间的那些猫腻

目录

写在前面

指针与一维数组

解析:

代码

指针与字符指针

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

解析 代码

char arr[] = "abcdef"

分析

代码

char* p = "abcdef"

分析

代码

指针与二维数组分析

代码


写在前面

在开始前我们要注意两个知识点:

1. sizeof(数组名),这里的数组名表示的是整个数组,计算的是整个数组的大小

2. &数组名,这里的数组名表示的是整个数组,取出的是整个数组的大小

3. 除此之外,其他的数组名都是首元素的地址

指针与一维数组

解析:

sizeof(a) 计算的是整个数组的大小 。

sizeof(a+0): a不是sizeof(a),也不是&a 所以他是首元素地址 +0,它是1的地址 是一个整型指针 4个字节 。

sizeof(*a): *a就是1 1是一个整型  sizeof(*a) = 4。

sizeof(a+1): a+1 是首元素的地址加一 跳过一个整型,是2的地址 还是一个整型指针 4个字节。

sizeof(a[1]): a[1]=2 2是整型 4个字节。

sizeof(&a): &a取出的是整个数组 但它本质上还是一个整型指针 4个字节。

sizeof(*&a): &a取出的是整个数组 解引用得到的是整个数组的内容 里面有4个整型。

sizeof(&a+1):  &a+1是跳过这个数组指向后面的内容 但还是一个指针 .。sizeof(&a[0]): &a[0]是取出a[0]的地址 是一个整型指针。

sizeof(&a[0]+1): &a[0]+1 是a[1]的地址 是一个整型指针.

代码

#include <stdio.h>
int main()
{
  int a[] = { 1,2,3,4 };
  printf("%d\n", sizeof(a));
  //16
  //sizeof(a) 计算的是整个数组的大小
  printf("%d\n", sizeof(a + 0));
  //4
  //a不是sizeof(a),也不是&a 所以他是首元素地址 +0
  //它是1的地址 是一个整型指针 4个字节 
  printf("%d\n", sizeof(*a));
  //4
  //*a就是1 1是一个整型  sizeof(*a) = 4
  printf("%d\n", sizeof(a + 1));
  //4
  //a+1 是首元素的地址加一 跳过一个整型
  //是2的地址 还是一个整型指针 4个字节
  printf("%d\n", sizeof(a[1]));
  //4
  //a[1]=2 2是整型 4个字节
  printf("%d\n", sizeof(&a));
  //4
  //&a取出的是整个数组 但它本质上还是一个整型指针 4个字节
  printf("%d\n", sizeof(*&a));
  //16
  //&a取出的是整个数组 解引用得到的是整个数组的内容 里面有4个整型
  printf("%d\n", sizeof(&a + 1));
  //4
  //&a+1是跳过这个数组指向后面的内容 但还是一个指针 
  printf("%d\n", sizeof(&a[0]));
  //4
  //&a[0]是取出a[0]的地址 是一个整型指针
  printf("%d\n", sizeof(&a[0] + 1));
  //&a[0]+1 是a[1]的地址 是一个整型指针
  return 0;
}

指针与字符指针

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

解析

arr 是计算整个数组的大小,6个char类型 6

arr+0  首元素地址加0 指向的还是a 它是一个地址就是指针,是指针就是4个字节

*arr arr是首元素的地址,解引用就是a 计算的就是首元素a的大小 char类型 1

arr[1] 是第二个元素,计算的是第二个元素的大小 1

&arr 取出的是整个数组 但它本质是一个char指针 计算的就是一个cha类型的指针r

&arr+1 取出整个数组的地址再加1,跳过整个数组 这是一个指向数组后一个元素的指针

&arr[0]+1 取出第一个元素的地址加1 就是第二个元素的地址,本质上还是一个指针

strlen(arr) arr是首元素地址 计算的是arr数组元素的个数,从第一个元素开始计算在遇到\0不会停止,arr数组没有\0 它是一个随机值

strlen(arr+0) arr+0是首元素的地址 从第一个元素开始计算到\0停止 arr数组没有\0 随机值

strlen(*arr) 是第一个元素是一个字符 但是strlen函数的参数需要一个指针类型 所以err

strlen(arr[1]) arr[1]是一个字符 不合strlen需要的参数

strlen(&arr) 取出的是整个数组 本质上还是一个指针,等于首元素的地址,arr数组里没有\0 它是一个随机值

strlen(&arr+1) &arr+1是跳过整个数组,指向数组后面的内容  从这里开始计算,遇到\0停止 随机

strlen(&arr(&arr[0]+1) &arr[0]+1取出第二个元素的地址 从第二个元素开始计算,遇到\0停止 随机

代码

  char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", sizeof(arr));
  //6
  //arr 是计算整个数组的大小,6个char类型 6
  printf("%d\n", sizeof(arr + 0));
  //arr+0  首元素地址加0 指向的还是a 它是一个地址就是指针,是指针就是4个字节
  printf("%d\n", sizeof(*arr));
  //1
  //*arr arr是首元素的地址,解引用就是a 计算的就是首元素a的大小 char类型 1
  printf("%d\n", sizeof(arr[1]));
  //1
  //arr[1] 是第二个元素,计算的是第二个元素的大小 1
  printf("%d\n", sizeof(&arr));
  //4
  //&arr 取出的是整个数组 但它本质是一个char指针 计算的就是一个cha类型的指针r
  printf("%d\n", sizeof(&arr + 1));
  //4
  //&arr+1 取出整个数组的地址再加1,跳过整个数组 这是一个指向数组后一个元素的指针 
  printf("%d\n", sizeof(&arr[0] + 1));
  //4
  //&arr[0]+1 取出第一个元素的地址加1 就是第二个元素的地址,本质上还是一个指针 
  printf("%d\n", strlen(arr));
  //随机值
  //strlen(arr) arr是首元素地址 计算的是arr数组元素的个数,从第一个元素开始计算在遇到\0不会停止,arr数组没有\0 它是一个随机值
  printf("%d\n", strlen(arr + 0));
  //随机值 
  //strlen(arr+0) arr+0是首元素的地址 从第一个元素开始计算到\0停止 arr数组没有\0 随机值
  printf("%d\n", strlen(*arr));
  //err
  //strlen(*arr) 是第一个元素是一个字符 但是strlen函数的参数需要一个指针类型 所以err
  printf("%d\n", strlen(arr[1]));
  //err
  //strlen(arr[1]) arr[1]是一个字符 不合strlen需要的参数
  printf("%d\n", strlen(&arr));
  //随机值
  //strlen(&arr) 取出的是整个数组 本质上还是一个指针,等于首元素的地址,arr数组里没有\0 它是一个随机值
  printf("%d\n", strlen(&arr + 1));
  //随机值
  //strlen(&arr+1) &arr+1是跳过整个数组,指向数组后面的内容  从这里开始计算,遇到\0停止 随机 
  printf("%d\n", strlen(&arr[0] + 1));
  //strlen(&arr(&arr[0]+1) &arr[0]+1取出第二个元素的地址 从第二个元素开始计算,遇到\0停止 随机 

char arr[] = "abcdef"

分析

"abcdef"里面包含的有:a b c d e f \0  

arr sizeof(arr)是计算整个数组的大小 arr数组里面隐藏了\0 ,得算上\0    7

arr+0 是首元素的地址 计算的就是一个地址的大小 4

*arr 是首元素 计算的就是一个char类型 1

arr[1] 是第二个元素 计算的是一个char类型 1

&arr 取出的是整个数组 它是一个地址 计算的就是地址的大小 4

&arr+1 数组的地址+跳过一个数组 它还是一个地址 计算的就是地址 4

&arr[0]+1 是第二个元素的地址 计算的就是地址 4

arr 计算的是整个数组的个数 strlen遇到\0停止 \0不加入计算 6

arr+0 是首元素的地址 它作为给strlen的参数 从首元素开始计算到\0停止 6

*arr *arr是首元素,它不是地址 strlen的参数要指针 所以err

arr[1] 是第二个元素,它不是地址 strlen的参数要指针 所以err

&arr 取出的是整个元素的大小 但它本质上还是和首元素地址一样 所以从首元素开始计算到\0停止 6

&arr+1 就是跳过整个数组,指向后面的空间,也跳过了数组中的\0, 所以从后面的空间开始到\0 是随机的

&arr[0]+1 是第二个元素的地址 就是从第二个元素开始计算到\0停止 5

代码

  char arr[] = "abcdef";
  //里面包含的有:a b c d e f \0 
  printf("%d\n", sizeof(arr));
  //7
  //arr sizeof(arr)是计算整个数组的大小 arr数组里面隐藏了\0 ,得算上\0    7
  printf("%d\n", sizeof(arr + 0));
  //4
  //arr+0 是首元素的地址 计算的就是一个地址的大小 4
  printf("%d\n", sizeof(*arr));
  //1
  //*arr 是首元素 计算的就是一个char类型 1
  printf("%d\n", sizeof(arr[1]));
  //1
  //arr[1] 是第二个元素 计算的是一个char类型 1
  printf("%d\n", sizeof(&arr));
  //4
  //&arr 取出的是整个数组 它是一个地址 计算的就是地址的大小 4
  printf("%d\n", sizeof(&arr + 1));
  //4
  //&arr+1 数组的地址+跳过一个数组 它还是一个地址 计算的就是地址 4
  printf("%d\n", sizeof(&arr[0] + 1));
  //4
  //&arr[0]+1 是第二个元素的地址 计算的就是地址 4
  printf("%d\n", strlen(arr));
  //6
  //arr 计算的是整个数组的个数 strlen遇到\0停止 \0不加入计算 6
  printf("%d\n", strlen(arr + 0));
  //6
  //arr+0 是首元素的地址 它作为给strlen的参数 从首元素开始计算到\0停止 6
  printf("%d\n", strlen(*arr));
  //err
  //*arr *arr是首元素,它不是地址 strlen的参数要指针 所以err
  printf("%d\n", strlen(arr[1]));
  //err
  //arr[1] 是第二个元素,它不是地址 strlen的参数要指针 所以err
  printf("%d\n", strlen(&arr));
  //6
  //&arr 取出的是整个元素的大小 但它本质上还是和首元素地址一样 所以从首元素开始计算到\0停止 6
  printf("%d\n", strlen(&arr + 1));
  //随机值
  //&arr+1 就是跳过整个数组,指向后面的空间,也跳过了数组中的\0, 所以从后面的空间开始到\0 是随机的
  printf("%d\n", strlen(&arr[0] + 1));
  //5
  //&arr[0]+1 是第二个元素的地址 就是从第二个元素开始计算到\0停止 5

char* p = "abcdef"

分析

p p是字符串首元素的地址 计算的就是一个指针 4

p+1 第二个字符的地址 计算的就是一个指针 4

*p 第一个字符 计算的是一个char类型 1

p[0] ==*(p+0) 第一个字符 计算的是一个char类型 1

&p &p是第一个字符的地址的地址 一个二级指针 计算就是一个指针 4

&p+1 是二级指针加1 跳过一个char**类型 还是一个指针 计算就是一个指针 4

&p[0]+1 第二个字符的地址 是一个指针 计算的就是一个指针 4

p 首字符的地址 strlen通过第一个字符地址向后计算,到\0停止

p+1 第二个字符的地址 strlen通过第二个字符地址向后计算 到\0停止

*p 首字符 strlen参数只接收指针 err

p[0] 首字符 strlen参数只接收指针 err

&p 是首元素的地址的地址  strlen计算的是首元素的地址 相当于地址变成了它要计算的内容 里面不知道有没有\0 就是随机值

&p+1 二级指针加1 strlen计算的是首元素的地址加一  它的内容还是一个指针 随机值

&p[0]+1 第二个元素的地址 从第二个元素开始计算遇到\0停止 5

代码

  char* p = "abcdef";
  printf("%d\n", sizeof(p));
  //4
  //p p是字符串首元素的地址 计算的就是一个指针 4
  printf("%d\n", sizeof(p + 1));
  //4
  //p+1 第二个字符的地址 计算的就是一个指针 4
  printf("%d\n", sizeof(*p));
  //1
  //*p 第一个字符 计算的是一个char类型 1
  printf("%d\n", sizeof(p[0]));
  //1
  //p[0] ==*(p+0) 第一个字符 计算的是一个char类型 1
  printf("%d\n", sizeof(&p));
  //4
  //&p &p是第一个字符的地址的地址 一个二级指针 计算就是一个指针 4
  printf("%d\n", sizeof(&p + 1));
  //4
  //&p+1 是二级指针加1 跳过一个char**类型 还是一个指针 计算就是一个指针 4
  printf("%d\n", sizeof(&p[0] + 1));
  //4
  //&p[0]+1 第二个字符的地址 是一个指针 计算的就是一个指针 4
  printf("%d\n", strlen(p));
  //6
  //p 首字符的地址 strlen通过第一个字符地址向后计算,到\0停止
  printf("%d\n", strlen(p + 1));
  //p+1 第二个字符的地址 strlen通过第二个字符地址向后计算 到\0停止
  printf("%d\n", strlen(*p));
  //err
  //*p 首字符 strlen参数只接收指针 err
  printf("%d\n", strlen(p[0]));
  //err
  //p[0] 首字符 strlen参数只接收指针 err
  printf("%d\n", strlen(&p));
  //随机值
  //&p 是首元素的地址的地址  strlen计算的是首元素的地址 相当于地址变成了它要计算的内容 里面不知道有没有\0 就是随机值
  printf("%d\n", strlen(&p + 1));
  //随机值
  //&p+1 二级指针加1 strlen计算的是首元素的地址加一  它的内容还是一个指针 随机值
  printf("%d\n", strlen(&p[0] + 1));
  //5
  //&p[0]+1 第二个元素的地址 从第二个元素开始计算遇到\0停止 5

指针与二维数组

分析

a sizeof(a)是计算整个二维数组的大小 12*4
a[0][0] 第一行的第一个元素 计算一个整型大小 4

a[0] 代表了数组第一行 可以理解为第一行的数组名 sizeof(数组名)整个数组的大小 16

a[0]+1 表示第一行第二个元素的地址 一个指针 4

*(a[0]+1) 表示第一行的第二个元素 一个整型 4

a+1 a是第一行的地址+1 第二行的地址 一个指针 4

*(a+1) 表示第二行的元素 4个整型 16

&a[0]+1 &a[0]是取出第一行的地址在加一 就是第二行的地址 4

*(&a[0]+1)第二行的地址解引用 得到第二行的4个元素 16

*a a是二维数组首元素的地址 就是第一行的地址 解引用 4个整型 16

a[3]  这里我们不管a[3]越界了 我们只要知道它的类型是一个int [4] sizeof求的就是类型 16

代码

int a[3][4] = { 0 };
printf("%d\n", sizeof(a));
//48
//a sizeof(a)是计算整个二维数组的大小 12*4
printf("%d\n", sizeof(a[0][0]));
//4
//a[0][0] 第一行的第一个元素 计算一个整型大小 4
printf("%d\n", sizeof(a[0]));
//16
//a[0] 代表了数组第一行 可以理解为第一行的数组名 sizeof(数组名)整个数组的大小 16
printf("%d\n", sizeof(a[0] + 1));
//4
//a[0]+1 表示第一行第二个元素的地址 一个指针 4
printf("%d\n", sizeof(*(a[0] + 1)));
//
//*(a[0]+1) 表示第一行的第二个元素 一个整型 4
printf("%d\n", sizeof(a + 1));
//4
//a+1 a是第一行的地址+1 第二行的地址 一个指针 4
printf("%d\n", sizeof(*(a + 1)));
//16
//*(a+1) 表示第二行的元素 4个整型 16
printf("%d\n", sizeof(&a[0] + 1));
//4
//&a[0]+1 &a[0]是取出第一行的地址在加一 就是第二行的地址 4
printf("%d\n", sizeof(*(&a[0] + 1)));
//16
//*(&a[0]+1)第二行的地址解引用 得到第二行的4个元素 16
printf("%d\n", sizeof(*a));
//16
//*a a是二维数组首元素的地址 就是第一行的地址 解引用 4个整型 16
printf("%d\n", sizeof(a[3]));
//16
//a[3]  这里我们不管a[3]越界了 我们只要知道它的类型是一个int [4] sizeof求的就是类型 16

通过这些奇形怪状的试题,我们一步一步的解析,到现在想必大家对与指针与数组已经门清了儿叭!

目录
相关文章
|
1月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
37 3
|
24天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
28天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
28天前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
28天前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
53 4
|
1月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
48 2
|
1月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
39 1
|
2月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
2月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。