c语言学习第二十一课-指针和数组笔试题解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: c语言学习第二十一课-指针和数组笔试题解析

要想熟练的面对数组与指针方面的笔试题。首先我们先大致了解一下,关于数组与指针。

一,数组

数组的构成是 数据类型+数组名[常量表达式],数组无法直接引用其全部,只能逐一的引用数组元素,数组的表达形式是数组名[下标]。

对于数组用来传参,数组名一般代表数组首元素地址。

二,指针

首先指针就是地址,定义形式为  指针类型+变量名称,这个变量里的数值被认定为内存里的地址。对于指针,我们要详细的了解其指针的类型,指针指向的类型。

int *a;//指针的类型是int *型,指针指向的类型是int型
int **a;//指针的类型是int **型,指针指向的类型是int *型
int(*a)[3];//指针的类型是int (*)[]型,指针指向的类型是 int()[]型

数组与指针

指针->地址,指针变量->大小4/8字节

数组,大部分情况数组名就是数组首元素地址  但有两个例外

sizeof(数组名)  

&数组名-数组名表示整个数组-取出的是数组地址

这两个数组名代表的是整个数组的地址

一般的,指针与数组可以相对的表示 *a->&a[o]->a[0]

这里的考察主要通过 操作符sizeof与函数strlen两个对数组与指针的考察,但在此前,先了解一下

操作符sizeof()

sizeof()作为一个单目运算符,是用来计算对应操作数的所占空间大小,它里面的参数可以是,数组,指针,函数,类型等。不管是是什么,都是计算对应数所占空间大小,单位是字节。

对于sizeof(),sizeof表达式不会真的去访问它,只看类型.

  /*
    * int a=5;
    * short s=11;
    * printf("%d\n",sizeof(s=a+2));//2  short类型,sizeof只看类型,不计算
    * printf("%d\n",s);// 实际为7    但不计算,还是11
    * return 0
    * 
    * 对于表达式a+3有两种类型:
    * 值属性
    * 类型属性      //sizeof()只关注类型属性,不关注值属性
    */

函数strlen()

strlen()函数是用来求字符串长度

strlen只针对字符串,求的是字符串的长度。本质上是“\0”前的字符长度,其参数是指针才可以,否则会造成非法访问,若无“\0”,则长度为随机值

例题展示

现在我们来深入sizeof与strlen,举例一个整型数组,此时用sizeof 计算

     int a[] = { 1,2,3,4 };
  printf("%d\n", sizeof(a));//16
  //szieof)(a)就是数组名单独放在sizeof内部,计算数组总大小,单位是字节
  printf("%d\n", sizeof(a + 0));//4/8个字节
  //a+0其实是数组首元素地址
  printf("%d\n", sizeof(*a));//4
  //数组首元素
  //*a->&a[o]->a[0]
  printf("%d\n", sizeof(a + 1));//4/8个字节
  //a是数组首元素,a+1是第二个元素的地址
  printf("%d\n", sizeof(a[1]));//4
  printf("%d\n", sizeof(&a));//4或者8
  //&a-取出的是整个数组的地址。是一个地址
  //int (*pa)[4]=&a;int (*)[4]
  printf("%d\n", sizeof(*&a));//16
  //sizeof(a);
  //int (*)a;
  printf("%d\n", sizeof(&a + 1));//4/8
  //&a+1跳过一个数组,在该数组地址末尾
  printf("%d\n", sizeof(&a[0]));//4/8
  printf("%d\n", sizeof(&a[0] + 1));//4/8
  //第二个元素的地址

在上面中,若a单独放进sizeof中,就是正常作用,此时a代表整个数组,计算总空间大小为4*4个字节,若没有单独放,如a+0;那么a代表首元素地址,加零还是首元素地址,地址的大小为4或8个字节。其次sizeof(&a)这里取出的地址是整个数组的地址,int (*pa)[4]=&a,数组与指针的相互表示。若&a+1则表示跳过这个数组这一块空间,

a7c1ed8bf5c140f6845d5d3377e50d48.png

之后我们再以arr的字符型数组为例,用sizeof()来计算一次

    char arr[] = { 'a','b','c','d','e','f'};
    printf("%d\n", sizeof(arr));//6
  printf("%d\n", sizeof(arr + 0));//4/8
  //数组首元素的地址
  printf("%d\n", sizeof(*arr));//1
  //表示首元素地址,单独放进来的*,是首元素。
  printf("%d\n", sizeof(arr[1]));//1
  printf("%d\n", sizeof(&arr));//4/8
  //取出的是全部地址,但也是一个地址
  printf("%d\n", sizeof(&arr + 1));//4/8
  //末尾地址
  printf("%d\n", sizeof(&arr[0] + 1));//4/8
  //第二个元素地址

上面的数组类型不一样,计算的空间大小不一样,但本质都是看对于 arr   arr +0  *arr  *arr+ 1    &arr   &arr+1   a[0]   等的本质理解它是代表什么。

我们在举例关于strlen的计算,

char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", strlen(arr));//随机值
  //arr数组名,首元素地址,但不知道什么时候遇到‘\0’
  printf("%d\n", strlen(arr + 0));//随机值
  //+0还是首元素地址
  printf("%d\n", strlen(*arr));//非法访问
  //放的不是地址,*arr=a[0]是‘a'
  printf("%d\n", strlen(arr[1]));//无非访问
  //与上同理
  printf("%d\n", strlen(&arr));//随机值
  //无法确定“\0”的位置
  printf("%d\n", strlen(&arr + 1));//随机值-6
  //末尾地址,找不到’\0‘
  printf("%d\n", strlen(&arr[0] + 1));//随机值-1
  //第二个地址往后,找不到’\0‘

以上字符数组例子都是在数组中无法找到“\0”的,所以在计算时会出现随机值。

若改变数组再利用sizeof 与strlen 各计算一遍

char arr[] = "abcdef";//[a,b.c.d.e.f,\0]
  printf("%d\n", sizeof(arr));//7
  //计算数组大小,7个字节
  printf("%d\n", sizeof(arr + 0));//4/8
  printf("%d\n", sizeof(*arr));// 1
  //数组首元素。*arr= (*arr+0)= arr[0] 
  printf("%d\n", sizeof(arr[1]));//1
  printf("%d\n", sizeof(&arr));//4/8
  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
    //从首元素开始计算
  printf("%d\n", strlen(*arr));//无法访问
  printf("%d\n", strlen(&arr));//6
  //取出的类型不一样,但不影响
  printf("%d\n", strlen(&arr+1));//随机值
    //在末尾地址
  printf("%d\n", strlen(arr[0]+1));//5

对于arr在取地址操作时,他取出的是整个数组的地址。

此外对于一个变量指针存放数组再计算.,比较sizeof与strlen的区别。

int main()
{
  char* p = "abcdef";
  printf("%d\0", sizeof(p));//4/8
  //指针的大小
  printf("%d\0", sizeof(p + 1));//4/8
  //指向第二个元素的地址
  printf("%d\0", sizeof(*p));//1
  //表示首字符
  printf("%d\0", sizeof(p[0]));//==*(p+0)  为1
    //可以理解为一个数组
  printf("%d\0", sizeof(&p));//4/8
  //取出的是地址,开辟p时的地址,与abcdef无关
  printf("%d\0", sizeof(&p + 1));//4/8
  //跳过一个p
  //char *p;char* *p=&p;
  printf("%d\0", sizeof(&p[0]+1));//4/8
  //b的地址
  printf("%d\n", strlen(p));//6
  printf("%d\n", strlen(p+1));//5
  //跳过一个地址
  printf("%d\n", strlen(*p));//非法访问
  printf("%d\n", strlen(p[0]));//非法访问
  printf("%d\n", strlen(&p));//随机值
  //在地址里找'\0’
  //不知哪里有”\0“
  printf("%d\n", strlen(&p + 1));//随机值
  printf("%d\n", strlen(p[0] + 1));//5
}

这里用指针表示数组,效果一样,我们可以直接理解为数组放进去。除了数组名单独放,它都是一个表示首元素地址的效果(&p,代表取出整个地址),才是也是无法找到“\0”。

这里我们再用二维数组举例,再看sizeof与strlen在这里的情况。

int main()//二维数组数组名没单独放。则是第一行地址
{
  int a[3][4] = { 0 };
  printf("%d\n", sizeof(a));//a的数组名单独放在sizeof里 代表所有大小  48
  printf("%d\n", sizeof(a[0][0]));//第一行第一个元素   4
  printf("%d\n", sizeof(a[0]));//16 第一行的数组名,数组名单独放在sizeof内部。
  printf("%d\n", sizeof(a[0]+1));//4 不是单独放在sizeof里    a[0]代表首元素地址  是第一行第二个元素地址
  printf("%d\n", sizeof(*(a[0]+1)));// a[0][1]大小 4个字节
  printf("%d\n", sizeof(a+1));//a代表第一行的地址类型  加一为 第二行的地址 4/8
  printf("%d\n", sizeof( * (a + 1)));//16
  //第二行的大小  
  printf("%d\n", sizeof(&a[0]+1));//取出第一行的地址 加一  第二行的地址 4/8
  printf("%d\n", sizeof(*(&a[0] + 1)));  //第二行的元素大小  16
  printf("%d\n", sizeof(*a));//表示第一行的大小  16
  printf("%d\n", sizeof(a[3]));//第四行元素  16  四个0  
}

对于二维数组,sizeof(a)

我们知道二维数组是连续储存的,那么它的结构应该是这样。


275f1c040c194dbcab6bf80af2171f16.png

,这里的a[0]单独放表示第一行整个的所有元素,a不单独放代表的是第一行的整个地址。

且在最后一个例子当中我们发现sizeof并没有说越界访问,这是因为sizeof只看数据类型,不看值类型,在sizeof中参数表达式不会被计算。

对于sizeof(*arr+1) ,a若单独放*a=arr[0],是代表第一行的元素。 不是单独放,则为首元素地址所以这里是第二个元素。

看完以上这个,举个笔试题。

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+1中。a是全部地址在ptr中
  return 0;
}

这里我们可以看到&a是去除的是整个该数组的地址,且a表示首元素地址。

以上便是今天学习的内容了。

大家一起加油o

相关文章
|
24天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
77 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
20天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
104 14
|
24天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
48 9
|
24天前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
41 8
|
24天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
42 7
|
24天前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
244 6
|
24天前
|
存储 网络协议 算法
【C语言】进制转换无难事:二进制、十进制、八进制与十六进制的全解析与实例
进制转换是计算机编程中常见的操作。在C语言中,了解如何在不同进制之间转换数据对于处理和显示数据非常重要。本文将详细介绍如何在二进制、十进制、八进制和十六进制之间进行转换。
34 5
|
24天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
35 5
|
9天前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
37 0
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2

推荐镜像

更多