C语言之指针详解(8)

简介: 本章重点1. 字符指针2. 数组指针3. 指针数组4. 数组传参和指针传参5. 函数指针6. 函数指针数组7. 指向函数指针数组的指针8. 回调函数9. 指针和数组面试题的解析

指针和数组笔试题解析

#include<stdio.h>
int main()
{
  //一维数组
  int a[] = { 1,2,3,4 };
  printf("%d\n", sizeof(a));
  printf("%d\n", sizeof(a + 0));
  printf("%d\n", sizeof(*a));
  printf("%d\n", sizeof(a + 1));
  printf("%d\n", sizeof(a[1]));
  printf("%d\n", sizeof(&a));
  printf("%d\n", sizeof(*&a));
  printf("%d\n", sizeof(&a + 1));
  printf("%d\n", sizeof(&a[0]));
  printf("%d\n", sizeof(&a[0] + 1));
  return 0;
}

f37800f6f57a40a79c585f03d1226a04.png


大家可以试着先分析一下结果是多少,分析错了不要紧,我逐个逐个给大家解释

首先这是一个数组,里面放的都是整型数据

数组元素的总大小是16个字节

sizeof(数组名)--数组名表示整个数组

&数组名---数组名表示整个数组

对于第一个sizeof(数组名),计算的是数组的总大小--单位是字节---16

对于第二个sizeof(a+0)结果是4,数组名在这里表示数组首元素的地址,a+0还是首元素的地址,地址的大小就是4/8个字节

对于第三个sizeof(*a)---数组名表示首元素的地址,*a就是首元素,sizeof(*a)就是4

对于第四个sizeof(a+1)--数组名在这里表示首元素的地址,a+1是第二个元素的地址,地址的大小就是4/8个字节

对于第五个sizeof(a【1】)--第二个元素的大小--4

对于第六个sizeof(&a)--&a取出的是数组的地址,但是数组的地址那也是地址,地址的大小就是4/8个字节

对于第七个sizeof(*&a)--16---&a数组的地址,数组的地址解引用访问的数组,sizeof计算的就是数组的大小--单位是字节

对于第八个sizeof(&a+1)--&a是数组的地址,&a+1虽然地址跳过整个数组,但还是地址,所以是4/8个字节

对于第九个sizeof(&a【0】)--&a【0】是第一个元素的地址

对于最后一个sizeof(&a【0】+1)--&a【0】+1是第二个元素的地址

我们把代码结果运行给大家看一下

X86平台演示的结果

2e48f805ca774b4b99c374813a96c61c.png

X64平台演示的结果

216675c0a3db404bb80997e6e20b4809.png


下面我们来看字符数组

#include<stdio.h>
int main()
{
  //字符数组
  char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", sizeof(arr));
  printf("%d\n", sizeof(arr + 0));
  printf("%d\n", sizeof(*arr));
  printf("%d\n", sizeof(arr[1]));
  printf("%d\n", sizeof(&arr));
  printf("%d\n", sizeof(&arr + 1));
  printf("%d\n", sizeof(&arr[0] + 1));
  return 0;
}

7dafeaecbd0147739498aa4477a270b5.png

我们还是一个一个来分析

对于第一个sizeof(arr)--计算的是数组的大小,6*1=6个字节

对于第二个sizeof(arr+0)--arr是首元素的地址,arr+0还是首元素的地址,地址的大小是4/8个字节

对于第三个sizeof(*arr)--arr是首元素的地址,*arr就是首元素,首元素是字符大小是一个字节

对于第四个sizeof(arr【1】)--1

对于第五个sizeof(&arr)--&arr虽然是数组的地址,但还是地址,地址大小为4/8个字节

对于第六个sizeof(&arr+1)--&arr+1是跳过整个数组后的地址,地址的大小是4/8个字节

对于最后一个sizeof(&arr【0】+1)--第二个元素的地址---4/8个字节

希望大家能够理解,我们把两个代码运行起来给大家看一下

X86平台演示的结果

fdc80d8e252a49c08ac70a5c8cb11be1.png

X64平台演示的结果

ca0a12535fac4fbfafef9571920ad265.png

我们再来看一组代码

94f357c7999c468fb5e9442b33ebf5a8.png

#include<stdio.h>
int main()
{
  char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", strlen(arr));
  printf("%d\n", strlen(arr + 0));
  printf("%d\n", strlen(*arr));
  printf("%d\n", strlen(&arr));
  printf("%d\n", strlen(arr[1]));
  printf("%d\n", strlen(&arr + 1));
  printf("%d\n", strlen(&arr[0] + 1));
  return 0;
}

我们还是给大家讲解一下


对于第一个strlen(求字符串长度)---找不到\0的位置,\0的位置不确定,所以结果打印随机值


对于第二个strlen---1--数组名---首元素的地址,和第一种情况相同---随机值


对于第三个strlen---这里传了个'a'--97过去,他就把97当作一个地址,大家有听说过吗?所以这叫非法访问,是有问题的


对于第四个strlen---同第三个,非法访问,是有问题的


对于第五个strlen----取地址arr---虽然是取出来的是数组的地址,但是数组的地址也是从'a'开始的,所以结果也是随机值


对于第六个strlen---也是随机值,但是随机值跟前面的随机值差6


对于第六个strlen---也是随机值,但是比前面的随机值差1


我们还是把代码运行起来给大家看一下


X86平台演示的结果

662062353ac3461d8df2f483500df733.png

X64平台演示的结果

c71c8bfd3d6a49c293ad8efd166d9ea6.png

我们接着来看代码


#include<stdio.h>
int main()
{
  char arr[] = "abcdef";
  printf("%d\n", sizeof(arr));
  printf("%d\n", sizeof(arr + 0));
  printf("%d\n", sizeof(*arr));
  printf("%d\n", sizeof(arr[1]));
  printf("%d\n", sizeof(&arr));
  printf("%d\n", sizeof(&arr + 1));
  printf("%d\n", sizeof(&arr[0] + 1));
  return 0;
}

b80e2900c09445618cae8022ebe1e779.png


我们还是来给大家讲解一下


第一个:计算的是数组的大小,单位是字节---7


第二个:计算的是地址的大小,arr+0是首元素的地址


第三个:*arr是首元素,计算的是首元素的大小--1


第四个:arr【1】是第二个元素,计算的是第二个元素的大小


第五个:&arr虽然是数组的地址,但也是地址,所以是4/8个字节


第六个:&arr+1是跳过整个数组后的地址,但也是地址---4/8个字节


第七个:&arr【0】+1是第二个元素的地址--4/8个字节


我们来把代码演示结果给大家展示一下


X86平台演示的结果

e246f24fa1304b689956d7ee8d0685ac.png

X64平台演示的结果

9241c8dbdfea4acd8f2ae664117eb664.png

我们来看下面的代码


#include<stdio.h>
int main()
{
  char arr[] = "abcdef";
  printf("%d\n", strlen(arr));
  printf("%d\n", strlen(arr + 0));
  printf("%d\n", strlen(*arr));
  printf("%d\n", strlen(arr[1]));
  printf("%d\n", strlen(&arr));
  printf("%d\n", strlen(&arr + 1));
  printf("%d\n", strlen(&arr[0] + 1));
  return 0;
}

fa2ed95a64a944f999077ee00a3885cd.png


还是给大家一个一个讲解一下


第一个:6,字符串的长度


第二个:6,和第一个相同


第三个:非法访问内存,访问97的这个地址,所以是有问题的


第四个:和第三个是一个道理,访问的是'b'--98的地址,属于非法访问,是有问题的


第五个:&arr---数组的地址--strlen的返回类型为const char*,我们正常使用应该使用数组指针---char(*p)【7】==&arr


第六个:&arr+1把\0也跳过去了,你也不知道后面的字符串有多长,所以结果是随机值


第七个:跳过了a这个字符从b开始往后数,所以长度为5


我们运行代码看一下结果


X86平台演示的结果

3e4358a53b9243a484d629d1ef367c8f.png

X64平台演示的结果

4688f3ae35de4ffeb8291cffd2cfd38e.png

如果大家能够理解,我们继续来看下面的代码


#include<stdio.h>
int main()
{
  char* p = "abcdef";
  printf("%d\n", sizeof(p));
  printf("%d\n", sizeof(p + 1));
  printf("%d\n", sizeof(*p));
  printf("%d\n", sizeof(p[0]));
  printf("%d\n", sizeof(&p));
  printf("%d\n", sizeof(&p + 1));
  printf("%d\n", sizeof(&p[0] + 1));
  return 0;
}

9d195956ce4a42a3a02eb88a74b20618.png


我们还是来解释一下


第一个:计算指针变量p的大小---4/8个字节


第二个:p+1得到的是字符b的地址,地址的大小为4/8个字节


第三个:*p就是字符串的第一个字符,也就是字符a,a为字符--1个字节


第四个:---可以等价等int arr[10]----arr[0]==*(arr+0)    p[0]==*(p+0)=='a',所以还是字符a--大小为1个字节


第五个:计算的地址,地址是4/8个字节


第六个:也是地址,大小为4/8个字节


第七个:b的地址---大小为4/8个字节


我们运行起来看一下效果


X86平台演示的结果

cf8542e00adf40669f50533b979050d8.png

X64平台演示的结果

afbd4290e43e4c9b960a39702f5d5241.png

我们再来看下一个代码


#include<stdio.h>
int main()
{
  char* p = "abcdef";
  printf("%d\n", strlen(p));
  printf("%d\n", strlen(p + 1));
  printf("%d\n", strlen(*p));
  printf("%d\n", strlen(p[0]));
  printf("%d\n", strlen(&p));
  printf("%d\n", strlen(&p + 1));
  printf("%d\n", strlen(&p[0] + 1));
  return 0;
}

e81582e68f9a495d8f5123cc4f53ff65.png


第一个:6-从a开始的字符串的长度到\0结束


第二个:5-从b开始的字符串的长度到\0结束


第三个:error--将a的值传过去了


第四个:error--同第三个代码


第五个:地址如果内存分配不同,结果也会不同(涉及大小端知识)--随机值


第六个:也是随机值---取地址加1对于后面的内存,地址的大小我们也不知道,所以是随机值


第七个:5--从第二个元素开始到\0结束


给大家运行代码看一下结果


X86平台演示的结果

cbcf25d58fc043868632664fe5d34f06.png

X64平台演示的结果

b1ca6b39b46f417f821c6a395992d32e.png

我们来看最后一个代码


#include<stdio.h>
int main()
{
  //二维数组
  int a[3][4] = { 0 };
  printf("%d\n", sizeof(a));
  printf("%d\n", sizeof(a[0][0]));
  printf("%d\n", sizeof(a[0]));
  printf("%d\n", sizeof(a[0] + 1));
  printf("%d\n", sizeof(*(a[0] + 1)));
  printf("%d\n", sizeof(a + 1));
  printf("%d\n", sizeof(*(a + 1)));
  printf("%d\n", sizeof(&a[0] + 1));
  printf("%d\n", sizeof(*(&a[0] + 1)));
  printf("%d\n", sizeof(*a));
  printf("%d\n", sizeof(a[3]));
  return 0;
}

4c57df933d8e4e93ab3138a3820a4f18.png



我们还是来给大家分析一下


第一个:48


第二个:4


第三个:16--a【0】相当于第一行作为一维数组的数组名,这里计算的是一行的大小


第四个:4---a【0】是第一行的数组名,数组名此时是首元素的地址,a【0】其实是第一行第一个元素的地址,a【0】+1就是第一行第二个元素的地址,地址大小为4/8个字节


第五个:4--*(a【0】+1)是第一行第二个元素,大小是4个字节


第六个:4--a是二维数组的数组名,所以a是首元素的地址,二维数组的首元素是他的第一行,a就是第一行(首元素)的地址,a+1就是第二行的地址


第七个:16---计算的是第二行的大小,单位是字节


第八个:第二行的地址--4


第九个:计算的是第二行的大小,单位是字节


第十个:a是首元素的地址--第一行的地址,*a就是第一行,计算的是第一行的大小


第十一个:16

给大家把代码运行起来看结果


X86平台演示的结果

78eb3c85d756494aa0a659543e2422b6.png

X64平台上演示的结果

ff11a50bb6eb489bbe4c0d55708009ee.png

指针详解的全部知识就给大家讲完了,希望大家能够有所收获


全章终!!!


相关文章
|
2月前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
57 0
|
20天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
74 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
20天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
46 9
|
20天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
40 7
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
105 13
|
24天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
24天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
82 3
|
25天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
1月前
|
存储 C语言 开发者
C 语言指针与内存管理
C语言中的指针与内存管理是编程的核心概念。指针用于存储变量的内存地址,实现数据的间接访问和操作;内存管理涉及动态分配(如malloc、free函数)和释放内存,确保程序高效运行并避免内存泄漏。掌握这两者对于编写高质量的C语言程序至关重要。
52 11
|
24天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
35 1