【C语言】进阶指针(三)—>指针与数组笔试真题详解(上)

简介: 【C语言】进阶指针(三)—>指针与数组笔试真题详解(上)

前言:

本篇会列出全部有关数组和指针含义的内容、实例及分析,并引入八道笔试真题进行实操练习,干货满满。

贯穿本篇的核心知识就是数组名的意义

1、sizeof(数组名),这里的数组名表示整个数组,sizeof计算的是整个数组的大小,注意括号中必须为数组名才满足这一意义,比如sizeof(arr+1)就不是整个数组的大小了,&数组名同理。

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

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

一、一维数组

int main()
{
  //一维数组
  int a[] = { 1,2,3,4 };
  printf("%d\n", sizeof(a));        //16 
    //a为数组名,sizeof(数组名)计算的是整个数组的大小,即4*4=16  
  printf("%d\n", sizeof(a + 0));    //4/8
    //a为数组名,代表首元素地址,a+0还是首元素地址,只要是地址,大小即为4/8  
  printf("%d\n", sizeof(*a));     //4
    //*a为数组中的元素1,为int类型,sizeof(int)为4
  printf("%d\n", sizeof(a + 1));    //4/8
    //a+1指向数组中的元素2,为int*,只要是指针类型,大小即为4/8
  printf("%d\n", sizeof(a[1]));   //4
  printf("%d\n", sizeof(&a));     //4/8
    //&数组名取出的是整个数组的地址,但是仍为地址,所以大小为4/8
  printf("%d\n", sizeof(*&a));    //16
    //*&a相当于a,sizeof(数组名)计算的是整个数组的大小,为16
  printf("%d\n", sizeof(&a + 1));   //4/8
    //&a+1指向4后面一个元素,但是&a+1是指针,所以大小为4/8
  printf("%d\n", sizeof(&a[0]));    //4/8
    //首元素地址,大小为4/8
  printf("%d\n", sizeof(&a[0] + 1));//4/8
    //&a[0]+1指向元素2,但他也是指针,所以大小为4/8
  return 0;
}

经过对一维数组的分析, 我们可以知道:

1、在判断时,只要是指针,那么他的大小即为4/8,32位为4,64位为8,可以加快我们做题判断的速度。

2、指针的步长取决于指针的类型,指针的类型为int,那么指针加+1就是跳过一个int类型,指针类型为数组,那么指针+1就是跳过整个数组。

二、字符数组

字符数组的分析除了sizeof我们再引入strlen库函数进行更深入的分析。

strlen函数的参数为const char*,功能为统计'\0'前的元素个数。

int main()
{
  //字符数组
  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
  printf("%d\n", strlen(arr));        //随机
    //该字符数组不是字符串,'\0'的位置不确定,所以为随机值
  printf("%d\n", strlen(arr + 0));    //随机
  printf("%d\n", strlen(*arr));       //err
    //strlen需要的参数是指针,而*arr是字符'a'。ASCII值为97
    //字符'a'的ASCII值为97,strlen会从97这个地址开始计算字符串长度
    //会造成非法访问
  printf("%d\n", strlen(arr[1]));     //err
  printf("%d\n", strlen(&arr));       //随机
  printf("%d\n", strlen(&arr + 1));   //随机
  printf("%d\n", strlen(&arr[0] + 1));//随机
  char arr[] = "abcdef";
  printf("%d\n", sizeof(arr));        //7
  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
  printf("%d\n", strlen(arr));        //6
  printf("%d\n", strlen(arr + 0));    //6
  printf("%d\n", strlen(*arr));       //err
  printf("%d\n", strlen(arr[1]));     //err
  printf("%d\n", strlen(&arr));       //6
  printf("%d\n", strlen(&arr + 1));   //随机
    //该指针步长为该字符串,+1后指针指向'\0'的后一个位置,所以值为随机
  printf("%d\n", strlen(&arr[0] + 1));//5
    //该指针步长为一个字符,+1后指针指向字符'b',所以计算值为5
  char* p = "abcdef";
    //经过前面的学习我们知道p存放的是该字符串首个字符的地址
  printf("%d\n", sizeof(p));          //4/8
  printf("%d\n", sizeof(p + 1));      //4/8
  printf("%d\n", sizeof(*p));         //1
  printf("%d\n", sizeof(p[0]));       //1
  printf("%d\n", sizeof(&p));         //4/8
  printf("%d\n", sizeof(&p + 1));     //4/8
    //思考:这里指针指向了哪里??
  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));         //随机
  printf("%d\n", strlen(&p + 1));     //随机
  printf("%d\n", strlen(&p[0] + 1));  //5
  return 0;
}

char* p="hello world";

字符指针p存放的是该字符串首个字符的地址。

我想单独讲解一下这段代码:

int main
{
    char* p = "abcdef";
    printf("%d\n", sizeof(&p));         //4/8
    printf("%d\n", sizeof(&p + 1));     //4/8
    //思考:这里指针指向了哪里??
    return 0;
}

首先我们知道p指向的是字符'a',即p的类型为char*,在&p后,&p的类型就是char**,此时给&p+1,&p的步长应为char*,也就是指针&p+1相对于&p跳过了一个char* ,比如p的地址是0x0012ff40,如图:

也就是&p是指针的指针,所以他的步长应为一个指针的长度

其中相似原理的我并未重复注释,大家有问题的可以评论或者私信问我🌹🌹🌹

三、二维数组

首先我们需要知道:

假定一个二维数组int a[3][4],对于二维数组a来说,a[0]就是该二维数组第一行的一维数组的数组名,同理a[1]就是该二维数组第二行的一维数组的数组名,又因为数组名就是数组首元素的地址,所以a[0]就是该二维数组第一行的一维数组的首元素地址,同理a[1]就是该二维数组第二行的一维数组的首元素地址。

在了解了上面的概念后,接下来的代码就迎刃而解了。

int main()
{
  //二维数组
  int a[3][4] = { 0 };
  printf("%d\n", sizeof(a));           //48
  printf("%d\n", sizeof(a[0][0]));     //4
  printf("%d\n", sizeof(a[0]));        //16
  printf("%d\n", sizeof(a[0] + 1));    //4/8
    //即a[0]其实就是a[0][0]的地址,+1后变为a[0][1]的地址,只要是地址,大小就是4/8
  printf("%d\n", sizeof(*(a[0] + 1))); //4
  printf("%d\n", sizeof(a + 1));       //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
    //a[0]是第一行数组的数组名,所以&a[0]取出的是该一维数组的地址
    //+1后指向了第二行的一维数组,解引用后是第二行一维数组,大小为4*4=16
  printf("%d\n", sizeof(*a));          //16
  printf("%d\n", sizeof(a[3]));        //16
    //越界会影响sizeof的计算么??
  return 0;
}

sizeof是如何计算的?

我们发现最后一个题目中数组越界了,但并没有影响程序的运行,也就是说,sizeof的计算只考虑类型,不会真的访问计算,a[3]的类型是int [4],所以大小为4*4=16。

四、指针笔试题

笔试题(1)

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}
//程序的结果是什么?

答案:2,5

分析:

a代表首元素地址,指针类型为int*,步长为int,+1后指向元素'2',解引用后为元素'2',所以打印值为2。

&a拿到的是整个数组的地址,指针类型为int(*)[5],步长为int [5],+1后ptr指向元素'5'后面的位置,并将指针强制转换为int*类型,ptr指针类型为int*,步长为int,-1后ptr指向元素'5',解引用得到元素'5',所以打印值为5。


目录
相关文章
|
20天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
73 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
20天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
44 9
|
20天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
40 7
|
20天前
|
传感器 算法 安全
【C语言】两个数组比较详解
比较两个数组在C语言中有多种实现方法,选择合适的方法取决于具体的应用场景和性能要求。从逐元素比较到使用`memcmp`函数,再到指针优化,每种方法都有其优点和适用范围。在嵌入式系统中,考虑性能和资源限制尤为重要。通过合理选择和优化,可以有效提高程序的运行效率和可靠性。
64 6
|
24天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
44 5
|
23天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
24天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
78 3
|
24天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
24天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
35 1
|
28天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。