用指针和数组的试题教你深度了解指针和数组

简介: C语言学习第十九弹——指针和数组的示例及解析(sizeof和strlen)

前言


发光不是太阳的权利你也可以,追逐光靠近光成为光散发光,你本来就是应该闪闪发光的人啊。看过前几篇文章都应该对数组和指针有些了解了吧,接下来我们用试题的方式来向各位展示让你更深的理解数组和指针这样就不怕难以区分啦~

提示:以下是本篇文章正文内容,下面案例可供参考

一、回顾数组和指针


数组和指针

数组 - 能够存放 - 一组相同类型元素,数组的大小取决于数组的元素个数和元素类型

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

数组是数组,指针是指针,二者不等价

数组名是数组首元素地址,这个地址就可以存放在指针变量中

我们就可以使用指针来遍历数组

数组名大部分情况下是数组首元素地址

但有两个例外:

sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小

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

二、数组例题


(1).整形类型


#include<stdio.h>
int main()
{
  int a[] = { 1,2,3,4 };
  printf("%d\n", sizeof(a));//16
  //sizeof(a)就是数组名单独放在sizeof内部,计算的是数组总大小,单位是字节
  //4个元素int整形4个字节所以4*4=16
  printf("%d\n", sizeof(a+0));//4/8
  //这里sizeof(a+0)并不是数组名单独放在sizeof内部,尽管里面加0并没有加但是它也是加上了0同时也没有&数组名所以排除两个意外
  //a是数组首元素地址,+0还是数组首元素地址,地址那就是4/8个字节
  printf("%d\n", sizeof(*a));//4
  //同理先看是不是例外中的其中一个显然不是所以a是首元素地址,也就是&a[0]
  //*a -> *&a[0](&和*抵消) -> a[0]也就是数组首元素int类型每个元素都是4个字节
  printf("%d\n", sizeof(a+1));//4/8
  //a是数组首元素地址 -- int*类型的
  //a+1 跳过一个整形,是第二个元素的地址,是地址那就是4/8个字节
  printf("%d\n", sizeof(a[1]));//4
  //a[1]也就是数组第二个元素int类型每个元素都是4个字节
  printf("%d\n", sizeof(&a));//4/8
  //&数组名 - 数组名表示整个数组,取出的是数组的地址
  //&a取出的就是数组的地址,但数组的地址也是地址,是地址大小就是4/8个字节
  //数组指针int(*pa)[4]=&a
  printf("%d\n", sizeof(*&a));//16
  //取地址(&)和解引用(*)可以抵用,所以sizeof(*&a) -> sizeof(a)
  printf("%d\n", sizeof(&a+1));//4/8
  //&a取出的是数组的地址+1跳过一个数组指向下一个数组的地址,所以还是地址也就是4/8个字节
  printf("%d\n", sizeof(&a[0]));//4/8
  //去除首元素地址也就是4/8
  printf("%d\n", sizeof(&a[0]+1));//4/8
  //第二个元素的地址所以4/8个字节
  return 0;
  //最后一定要分清&a和&a[0]是不同的一个是数组的地址一个是首元素地址所以+1跳过的一个是数组一个是元素但是都是地址所以都是4/8
}

(2).字符类型


sizeof计算的是占用内存空间的大小,单位是字节,不关注内存中到底存放的是什么,sizeof(arr) -> 6

sizoef不是函数是操作符

strlen是函数

strlen是针对字符串的求的是字符串的长度,本质上统计的是\0之前出现的字符的个数

#include<stdio.h>
int main()
{
  char arr[] = {'a','b','c','d','e','f'};
  printf("%d\n", sizeof(arr));//6
  //6个元素每个元素都是char类型的所以是6
  printf("%d\n", sizeof(arr + 0));//4/8
  //arr没有单独放在sizeof内部所以arr是数组首元素地址+0还是数组首元素地址,所以就是4/8个字节
  printf("%d\n", sizeof(*arr));//1
  //arr是数组首元素地址*arr就是首元素char类型所以就是1个字节
  printf("%d\n", sizeof(arr[1]));//1
  //数组第二个元素同理char类型所以也是1
  printf("%d\n", sizeof(&arr));//4/8
  //&arr取出数组的地址但也是地址所以4/8
  printf("%d\n", sizeof(&arr + 1));//4/8
  //&arr取出数组的地址+1跳过一个数组的地址但也是地址所以4 / 8
  printf("%d\n", sizeof(&arr[0] + 1));//4/8
  //第二个元素的地址
  return 0;
}

注意这里是strlen不是sizeof所以不存在sizeof(数组名)

strlen求字符串长度把地址传给它它从那个地址开始往后数知道\0从而求出长度

#include<stdio.h>
int main()
{
  int arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", strlen(arr));//随机值
  //strlen找到\0之前的个数但是这并不是一个字符串所以\0在哪未可知所以是随机值
  printf("%d\n", strlen(arr + 0));//随机值
  //arr首元素地址+0还是首元素地址所以还是从'a'开始往后数到\0所以还是随机值
  printf("%d\n", strlen(*arr));//error
  //*arr拿到的就是字符a,'a'本质上就是97,我们把97传给strlen,strlen就把97当成地址了开始往后数了
  //但是97所找的这块内存不是我们的也就是所说的非法访问
  printf("%d\n", strlen(arr[1]));//error
  //arr[1]就是第二个元素也就是字符b,,我们把98传给strlen,strlen就把98当成地址了开始往后数了
  但是98所找的这块内存不是我们的也就是所说的非法访问
  printf("%d\n", strlen(&arr));//随机值
  //虽然&arr取出的是数组的地址但是传给strlen还是从这个数组地址的首地址开始往后数知道\0所以就是随机值
  printf("%d\n", strlen(&arr + 1));//随机值
  //&arr+1就是跳过一个数组后的地址从这开始往后数直到到\0所以同样是随机值但是这个随机值比前面的随机值少6
  //因为跳过了a,b,c,d,e,f
  printf("%d\n", strlen(&arr[0] + 1));//随机值
  //&arr[0]+1是从第二个字符也就是b的地址开始往后数,直到\0所以也是随机值比起之前的少1
  return 0;
}
#include<stdio.h>
int main()
{
  char arr[] = "abcdef";//这种是字符串最后位隐藏着放着一个\0
  printf("%d\n", sizeof(arr));//7
  //sizeof只关注数组的大小,里面隐藏着一个\0所以总共是7
  printf("%d\n", sizeof(arr + 0));//4/8
  //首元素地址+0还是首元素地址是地址就是4/8
  printf("%d\n", sizeof(*arr));//1
  //arr首元素地址,*arr就是首元素char类型就是1
  //arr[0]    *(arr+0)都是首元素
  //int sz=sizeof(arr)/sizeof(*arr)这种也对
  printf("%d\n", sizeof(arr[1]));//1
  //数组第二个元素
  printf("%d\n", sizeof(&arr));//4/8
  //取出的是整个数组的地址但是同样是地址所以还是4/8
  printf("%d\n", sizeof(&arr + 1));//4/8
  //跳过一个数组的地址但也是地址所以是4/8
  printf("%d\n", sizeof(&arr[0] + 1));//4/8
  //指向的b的地址所以地址4/8
  return 0;
}

注意这里是strlen不是sizeof所以不存在sizeof(数组名)

strlen求字符串长度把地址传给它它从那个地址开始往后数知道\0从而求出长度

#include<stdio.h>
int main()
{
  char arr[] = "abcdef";//这种是字符串最后位隐藏着放着一个\0
  printf("%d\n", strlen(arr));//6
  printf("%d\n", strlen(arr + 0));//6
  //还是首元素地址开始往后数到\0总共6个字节
  printf("%d\n", strlen(*arr));//error
  //把a的地址给过去了
  printf("%d\n", strlen(arr[1]));//error
  //把b的地址给过去了
  printf("%d\n", strlen(&arr));//6
  //虽然取的整个数组的地址但是还是从数组首地址开始往后数到\0共6个
  //&arr取出的是char(*)[7]类型;strlen的参数类型是const char*
  printf("%d\n", strlen(&arr + 1));//随机值
  //&arr+1是跳过一个数组开始往后数而后面的数据完全不可知所以是随机值
  printf("%d\n", strlen(&arr[0] + 1));//5
  //&arr[0]+1是从第二个元素的地址开始往后数到\0是5个
  return 0;
}

三、指针例题


char* p = “abcdef”;常量字符串的首地址放在p里面去了;指针变量存放地址的

#include<stdio.h>
int main()
{
  char* p = "abcdef";//常量字符串的首地址放在p里面去了;指针变量存放地址的
  printf("%d\n", sizeof(p));//4/8
  //我们算的是p变量的大小,p是一个变量,算的是一个指针变量的大小所以是4/8个字节
  printf("%d\n", sizeof(p + 1));//4/8
  //p里面放的是a的地址是char*指针+1跳过一个字符指向了b的地址所以是4/8
  printf("%d\n", sizeof(*p));//1
  //char*的指针对它解引用访问一个字符
  printf("%d\n", sizeof(p[0]));//1
  //p[0] -- *(p+0) -- 'a'所以是一个字节
  //p里面存的a的地址解引用就是a
  printf("%d\n", sizeof(&p));//4/8
  //取地址p取得是p的地址和"abcdef"没有关系;&p是一个二级指针但还是地址所以同样4/8
  printf("%d\n", sizeof(&p + 1));//4/8
  //&p+1跳过一个p去但也是一个地址所以是4/8
  //char* p;  char** pp=&p; -- char*(这里旨在告诉我们pp指向的是一个char*类型的p) *pp(这里旨再告诉我们pp是一个指针)
  printf("%d\n", sizeof(&p[0] + 1));//4/8
  //p[0]就是a;&p[0]就是取出a的地址+1就是指向b的地址所以还是地址4/8
  return 0;
}

&p+1跳过的是p变量的地址;而和数组没有关系,p变量是指针的话就是4个字节意思就是把这个4个字节的变量跳过去了

#include<stdio.h>
int main()
{
  char* p = "abcdef";//常量字符串的首地址放在p里面去了
  printf("%d\n", strlen(p));//6
  //p里面放的a的地址所以从a开始往后数到\0;6个
  printf("%d\n", strlen(p + 1));//5
  //p里面放的a的地址+1指向b的地址所以从b开始往后数到\0;5个
  printf("%d\n", strlen(*p));//error
  //*p也就是a,同之前一样
  printf("%d\n", strlen(p[0]));//error
  //p[0]--*(p+0),同样是a
  printf("%d\n", strlen(&p));//随机值
  //&p取到的是p的地址至于p里面存的地址值是什么后面有没有遇到\0完全位置所以是随机值
  printf("%d\n", strlen(&p + 1));//随机值
  //同理p+1我们也不知道所以也是随机值;但是这个随机值和上面那个随机值没有半毛钱关系因为我们不知道有没有\0
  //所以没有类似之前的少5少1的关系
  printf("%d\n", strlen(&p[0] + 1));//5
  //从b的地址向后数到\0也就是5个
  return 0;
}

四、二维数组例题


(1).二维数组注意点一:


谈起二维数组我们就要来说一下数组名a[0] a[1] a[2]分别代表第一第二第三行的数组名

#include<stdio.h>
int main()
{
  int a[3][4] = { 0 };
  printf("%d\n", sizeof(a));//48
  //二维数组数组名单独放在sizeof内部表示整个数组,计算整个数组的大小
  //4*3*4=48
  printf("%d\n", sizeof(a[0][0]));//4
  //第一行第一个元素大小四个字节
  printf("%d\n", sizeof(a[0]));//16
  //a[0]第一行的数组名,这时数组名单独放在sizeof内部计算的是第一行数组的总大小
  //4*4=16(一行四个元素)
  printf("%d\n", sizeof(a[0]+1));//4/8
  //这里是arr[0]+1所以不是单独放在sizeof内部所以a[0]只表示首元素的地址--第一行第一个元素的地址
  //+1就是第一行第二个元素的地址 -- &a[0][1]所以是4/8
  printf("%d\n", sizeof(*(a[0] + 1)));//4
  //这个就是在上面的那个基础上加上*所以就是第一行第二个元素int类型4
  printf("%d\n", sizeof(a + 1));//4/8
  //a作为二维数组的数组名并没单独放在sizeof内部,所以表示首元素地址
  //二维数组的首元素是第一行,所以这里的a就是第一行的地址 --- int(*)[4]
  //a+1的时候是跳过第一行指向第二行的地址 --- &a[1]
  printf("%d\n", sizeof(*(a+1)));//16
  //同理这个同样是在上面代码的基础上解引用拿到的就是第二行的大小
  //一行4个元素int类型$*4=16
  //*(a+1)-->a[1]
  printf("%d\n", sizeof(&a[0]+1));//4/8
  //a[0]第一行数组,&a[0]取出第一行的地址+1就是第二行的地址
  printf("%d\n", sizeof(*(&a[0] + 1)));//16
  //同理在上面代码的基础上解引用得到的就是第二行
  //4*4=16
  printf("%d\n", sizeof(*a));//16
  //*a,a没有单独放在sizeof内部所以表示首元素地址也就是第一行地址解引用也就是第一行元素
  //4*4=16
  //*a -- *(a+0) --- a[0]
  printf("%d\n", sizeof(a[3]));//16
  //数组下标从0开始这个数组总共就三行那是不是越界了呢
  //答案是不是的因为sizeof并不会真的去访问只是计算所以这也是第四行的元素
  //4*4=16
  printf("%d\n", sizeof(*a+1));//4/8
  //*a是第一行的元素+1---*a+1并没有单独放在sizeof内部所以*a---a[0]也就是第一行首元素地址
  //+1也就是第一行第二个元素的地址
  return 0;
}

(2).二维数组注意点二:


注意:

下面这段代码并没有越界

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

数组下标从0开始这个数组总共就三行那是不是越界了呢

答案是不是的因为sizeof并不会真的去访问只是计算所以这也是第四行的元素;4*4=16

#include<stdio.h>
int main()
{
  int a = 5;
  short s = 11;
  printf("%d\n", sizeof(s = a + 2));
  //a和2都是int类型但计算结果要放到short类型里面所以会发生截断最终结果short说了算
  //short的大小是2字节
  printf("%d\n", s);
  //按理来说s=a+2=7所以这里s打印出来是7但运行结果确实11没有改变为什么呢
  //因为sizeof内部的表达式并没有真的计算
  return 0;
}

总结


Ending,今天的指针和数组内容就到此结束啦~,如果后续想了解更多,就请关注我吧,在接下来的茫茫探索路上我们一起前进。


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