指针与数组笔试题解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
简介: 指针与数组笔试题解析

一、关于数组名

数组名是数组首元素的地址,但是有2个例外:

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

2. &数组名 - 数组名也表示整个数组,取出的是整个数组的地址

除了这个2个例外,你见到的所有的数组名都表示首元素的地址

二、一维数组

 
  int a[] = { 1,2,3,4 };
  printf("%d\n", sizeof(a));//16,a作为数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节
  printf("%d\n", sizeof(a + 0));//a并非单独放在sizeof内部,也没有&,所以数组名a就是数组首元素的地址
  //a+0还是数组首元素的地址,是地址大小就是 4/8 个字节
  printf("%d\n", sizeof(*a));//a是首元素的地址,*a就是首元素,sizeof(*a)就算的就是首元素的大小 - 4
  //a  - int*
  //*a - int
  printf("%d\n", sizeof(a + 1));//a是首元素的地址,a+1是第二个元素的地址,sizeof(a+1)计算的是指针的大小 - 4/8
  //a - int*
  //a+1, 跳过一个int
  printf("%d\n", sizeof(a[1]));//a[1]就是数组的第二个元素,sizeof(a[1])的大小 - 4个字节
  printf("%d\n", sizeof(&a));//&a取出的数组的地址,数组的地址,也是地址呀,sizeof(&a)就是 4/8 个字节
  printf("%d\n", sizeof(*&a));//&a是数组的地址,是数组指针类型,*&a是都数组指针解引用,访问一个数组的大小
  //16字节
  //sizeof(*&a) ==> sizeof(a)  =16
  printf("%d\n", sizeof(&a + 1));//&a数组的地址,&a+1跳过整个数组,&a+1还是地址,是 4/8 个字节
  printf("%d\n", sizeof(&a[0]));//a[0]是数组的第一个元素,&a[0]是第一个元素的地址,是 4/8 个字节
  printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一个元素的地址,&a[0]+1就是第二个元素的地址,是 4/8 个字节
  //&a[0] - int*
  //&a[0]+1 -> &a[1]

三、字符数组

关于sizeof 与 strlen


sizeof 是计算对象或者类型创建的对象所占内存空间的大小,单位是字节


sizeof 是操作符,不是函数


strlen 求字符串长度的,计算的是字符串中\0之前出现的字符的个数


统计到\0为止,如果没有看到\0,会继续往后找


strlen 是库函数 , 访问的实质是地址


ps:地址就是指针,指针就是地址

  char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", strlen(arr));//随机值,arr是数组名,但是没有放在sizeof内部,也没&,arr就是首元素的地址
  //strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到直到\0,但是arr数组中没有\0,arr内存的后边是否有\0,在什么位置
  //是不确定的,所以\0之前出现了多少个字符是随机的。
 
  printf("%d\n", strlen(arr + 0));//arr是数组首元素的地址,arr+0还是首元素的地址,随机值
  
  printf("%d\n", strlen(*arr));//arr是数组首元素的地址,*arr 是首元素 - ‘a’ - 97
  //strlen就把‘a’的ASCII码值 97 当成了地址
  //err 会非法访问内存
     
  printf("%d\n", strlen(arr[1]));//arr[1] - 'b' - 98 - err
  
  printf("%d\n", strlen(&arr));//随机值,&arr是数组的地址,数组的地址也是指向数组起始位置,和第一个案例一样
  printf("%d\n", strlen(&arr + 1));//随机值
  printf("%d\n", strlen(&arr[0] + 1));//随机值
  
  
  printf("%d\n", sizeof(arr));//arr是数组名,并且是单独放在sizeof内部,计算的是数组总大小,单位是字节 - 6
  printf("%d\n", sizeof(arr + 0));//arr是数组名,并非单独放在sizeof内部,arr表示首元素的地址,arr+0还是首元素的地址
  //是地址大小就是4/8
    
  printf("%d\n", sizeof(*arr));//arr是首元素的地址,*arr就是首元素,sizeof计算的是首元素的大小,是1字节
  printf("%d\n", sizeof(arr[1]));//arr[1]是数组的第二个元素,sizeof(arr[1])计算的是第二个元素的大小,1个字节
  printf("%d\n", sizeof(&arr));//&arr- 取出的是数组的地址,sizeof(&arr))计算的是数组的地址的大小,是地址就是4/8字节
  printf("%d\n", sizeof(&arr + 1));//&arr是数组的地址,&arr+1跳过整个数组,指向'f'的后边,&arr+1的本质还是地址,是地址就是4/8字节
  printf("%d\n", sizeof(&arr[0] + 1));//&arr[0]是‘a’的地址,&arr[0]+1是'b'的地址,是地址就是4/8字节
char arr[] = "abcdef";
  printf("%d\n", sizeof(arr));//7 arr单独放在sizeof内部,表示整个数组,计算整个数组总大小,‘\0’也要算
  printf("%d\n", sizeof(arr + 0));// 4/8 arr没有单独放在sizeof内部,表示首元素地址,+0还是首元素地址,地址就是指针
  printf("%d\n", sizeof(*arr));//1 arr没有单独放在sizeof内部,*对arr解引用,表示首元素
  printf("%d\n", sizeof(arr[1]));//1 首元素大小
  printf("%d\n", sizeof(&arr));//4/8  arr取地址,表示整个数组的地址,是指针
  printf("%d\n", sizeof(&arr + 1));//4/8  调过整个数组,但仍是指针
  printf("%d\n", sizeof(&arr[0] + 1));//4/8 表示第二个元素的地址,为指针
 
  printf("%d\n", strlen(arr));//6 字符串长度
  printf("%d\n", strlen(arr + 0));//6 arr表示首元素地址,+0仍是首元素地址,计算的仍是整个字符串的长度
  printf("%d\n", strlen(*arr));//err *arr表示首元素,将arr的ASCI值 97 当做地址,非法访问
  printf("%d\n", strlen(arr[1]));//err 同上
  printf("%d\n", strlen(&arr));//6 表示整个字符串数组的地址
  printf("%d\n", strlen(&arr + 1));//suiji 调过整个数组,但不能确保 ‘\0’位置
  printf("%d\n", strlen(&arr[0] + 1));//5  第二个元素地址
char* p = "abcdef";
  printf("%d\n", sizeof(p));//p是指针变量,存放字符串的首元素地址,指针
  printf("%d\n", sizeof(p + 1));//4/8 p+1指向第二个元素的地址,仍是指针
  printf("%d\n", sizeof(*p));//1 表示首元素a
  printf("%d\n", sizeof(p[0]));//1 p[0]->*(p+0)->*p
  printf("%d\n", sizeof(&p));//4/8 表示指针变量p的地址,地址就是指针
  printf("%d\n", sizeof(&p + 1));//4/8 表示跳过整个p
  //值得注意的是&p 与&p+1 大小没有直接关系,因为不能保证p的地址中是否含有‘\0'
  //假如p的地址为0x 0012ff40 ‘\0’表示0,那么 00就表示‘\0'
  printf("%d\n", sizeof(&p[0] + 1));//4/8
 
  printf("%d\n", strlen(p));//6
  printf("%d\n", strlen(p + 1));//5
  printf("%d\n", strlen(*p));//err
  printf("%d\n", strlen(p[0]));//err
  printf("%d\n", strlen(&p));// 随机值 表示p的地址,相当于是一个二级指针,当无法确定 '\0 '的位置
  printf("%d\n", strlen(&p + 1));//随机值
  printf("%d\n", strlen(&p[0] + 1)); //5

四、二维数组

  int a[3][4] = { 0 };
  
  printf("%d\n", sizeof(a));//a是二维数组的数组名,数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节
  //48
  printf("%d\n", sizeof(a[0][0]));//a[0][0]是一个整型元素,大小是4个字节
  printf("%d\n", sizeof(a[0]));//把二维数组的每一行看做一维数组的时候,a[0]是第一行的数组名,第一行的数组名单独放在sizeof内部
  //计算的是第一行的总大小,单位是字节 - 16
  printf("%d\n", sizeof(a[0] + 1));//a[0]虽然是第一行的数组名,但是并非单独放在sizeof内部
  //a[0]作为第一行的数组名并非表示整个第一行这个数组,a[0]就是第一行首元素的地址,a[0]--> &a[0][0] - int*
  //a[0]+1,跳过一个int,是a[0][1]的地址  4/8字节
  printf("%d\n", sizeof(*(a[0] + 1)));//a[0]+1是第一行第二个元素的地址,所以*(a[0]+1)就是a[0][1],大小是4个字节
  
  printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,没单独放在sizeof内部,也没有&,所以a就是数组首元素的地址
  //二维数组,我们把它想象成一维数组,它的第一个元素就是二维数组的第一行
  //a就是第一行的地址,a+1 是第二行的地址,是地址,大小就是 4/8 个字节
  //a - &a[0]
  //a+1 - &a[1]
  //a+2 - &a[2]
 
  printf("%d\n", sizeof(*(a + 1)));//a+1是第二行的地址,*(a+1) 找到的就是第二行,sizeof(*(a + 1))计算的就是第二行的大小
  //16
  //*(a+1) --> a[1]
  //sizeof(*(a + 1)) --> sizeof(a[1])
  //
  printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一行的地址,&a[0]+1就是第二行的地址,sizeof(&a[0] + 1)计算的第二行地址大小
  //单位是字节 - 4/8
 
  printf("%d\n", sizeof(*(&a[0] + 1)));//&a[0] + 1是第二行的地址,*(&a[0] + 1)拿到的就是第二行,大小就是16个字节
  //*(&a[0]+1) --> a[1]
 
  printf("%d\n", sizeof(*a));//a表示首元素的地址,就是第一行的地址 - &a[0]
  //*a - 拿到的就是第一行 - 大小就是16个字节
  //*a -> *(a+0) -> a[0]
  //
  printf("%d\n", sizeof(a[3]));//代码没问题
  //a[3]是二维数组的第4行,虽然没有第四行,但是类型能够确定,大小就是确定的。大小就是一行的大小,单位是字节 - 16
  //能够分析出 a[3]的类型是:int [4] 
  printf("%d\n", sizeof(*( &a)));//48

总结:

数组名的意义:

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

2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

3. 除此之外所有的数组名都表示首元素的地址

五、指针笔试题

笔试题一:

​​​​​​​int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int* ptr = (int*)(&a + 1);
    printf("%d,%d", *(a + 1), *(ptr - 1));//2,5  a表示首元素地址,地址加一调过一个整型,指向第二个元素地址
    //取地址a,获得整个数组地址,加一调过整个数组,指向5的后面的地址,强制类型转换(不强有警告)。ptr减一,向后移动一个元素
    return 0;
}

笔试题二:

struct Test
{
  int Num;
  char* pcName;
  short sDate;
  char cha[2];
  short sBa[4];
}*p;//p是一个结构体变量指针
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
  p = (struct Test*)0x100000;//p的值为16进制数字,需强制类型转换为指针类型
  printf("%p\n", p + 0x1);//00100014,p为结构体指针,+1跳过一个结构体类型
  printf("%p\n", (unsigned long)p + 0x1);//00100001,强制类型转换为长整型,整型加一表示数值加一
  printf("%p\n", (unsigned int*)p + 0x1);// 00100004 强制类型转化为整型,整型指针加一表示调过一个整型,加4
  return 0;
}

笔试题三:

笔试题四:

int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };//带‘()’的表达式最后一个值为表达式的解,
    //则a的实际值为:{{1,3},{5,0},{0,0}}
    int* p;
    p = a[0];
    printf("%d", p[0]);//p[0]此时表示第一行首元素地址,在sizeof中表示第一行地址
    return 0;
}

笔试题五:

int main()
{
    int a[5][5];
    int(*p)[4];//p的每行有4个元素,p[4][2]指向a中的第四行第三个元素,
    //相减得到字符个数的相反数-4
    p = a;
    printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    //指针相减得到的是中间的字符个数,将-4以地址形式打印
    return 0;
}

笔试题六:

int main()
{
    int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int* ptr1 = (int*)(&aa + 1);//获得整个数组地址,加一跳过整个数组;
    //ptr-1指针大小减一,对地址解引用指向10
    int* ptr2 = (int*)(*(aa + 1));//aa表示首行元素地址,加一表示第二行首元素地址;
    //*ptr减一向后移动一个指针,指向5所在的地址
    printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10,5
    return 0;
}

笔试题七:

int main()
{
  char* c[] = { "ENTER","NEW","POINT","FIRST" };//c为指针变量,c中的元素类型为char*
  char** cp[] = { c + 3,c + 2,c + 1,c };//cp为指针变量,cp中的元素类型为char*,cp的类型为char**
  char*** cpp = cp;//cpp为指针变量,cpp中的元素类型为char**,cpp的类型为char***
  printf("%s\n", **++cpp);//POINT
  printf("%s\n", *-- * ++cpp + 3);//ER
  printf("%s\n", *cpp[-2] + 3);//ST
  printf("%s\n", cpp[-1][-1] + 1);//EW
  return 0;
}


相关文章
|
2天前
|
C语言
【C语言】:详解函数指针变量,函数指针数组及转移表
【C语言】:详解函数指针变量,函数指针数组及转移表
8 2
|
2天前
|
C语言
【C语言】:详解指针数组,数组指针及(二维)数组传参(2)
【C语言】:详解指针数组,数组指针及(二维)数组传参(2)
6 1
|
2天前
|
Serverless C语言
【C语言】:对(一维)数组与指针的深入理解(1)
【C语言】:对(一维)数组与指针的深入理解(1)
5 1
|
1天前
|
存储 JavaScript 前端开发
JavaScript——JavaScript基础:数组 | JavaScript函数:使用、作用域、函数表达式、预解析
在JavaScript中,内嵌函数可以访问定义在外层函数中的所有变量和函数,并包括其外层函数能访问的所有变量和函数。①全局变量:不在任何函数内声明的变量(显式定义)或在函数内省略var声明的变量(隐式定义)都称为全局变量,它在同一个页面文件中的所有脚本内都可以使用。函数表达式与函数声明的定义方式几乎相同,不同的是函数表达式的定义必须在调用前,而函数声明的方式则不限制声明与调用的顺序。③块级变量:ES 6提供的let关键字声明的变量称为块级变量,仅在“{}”中间有效,如if、for或while语句等。
16 0
|
5天前
|
C语言
C语言----关于二维数组传参的本质相关的知识点(数组指针、指针数组)
C语言----关于二维数组传参的本质相关的知识点(数组指针、指针数组)
|
5天前
|
机器学习/深度学习 缓存 算法
netty源码解解析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk
netty源码解解析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk
|
7天前
|
XML Java 数据格式
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
17 3
|
6天前
|
存储 NoSQL 算法
Redis(四):del/unlink 命令源码解析
Redis(四):del/unlink 命令源码解析
|
7天前
|
XML Java 数据格式
深度解析 Spring 源码:揭秘 BeanFactory 之谜
深度解析 Spring 源码:揭秘 BeanFactory 之谜
13 1
|
16天前
|
SQL 缓存 算法
【源码解析】Pandas PandasObject类详解的学习与实践
【源码解析】Pandas PandasObject类详解的学习与实践

推荐镜像

更多