有关数组,指针笔试中最常考的题
- 本文概述
-
- 基础知识概述
-
- 字符和字符串
- 指针和数组
-
- 指针基础知识
- 数组基础知识
- sizeof与strlen()
- 字符一维数组
-
- 程序及结果分析
- 重点难点及小结
- 字符串一维数组
-
- 程序及结果分析
- 重点难点及小结
- 字符串指针
-
- 程序及结果分析
- 重点难点及小结
- 字符二维数组
-
- 程序及结果分析
- 重点难点及小结
- 本文总结
本文概述
你好! 这是一篇针对 字符指针和字符串指针,字符数组和字符串数组 的深入了解。此部分内容在一些大厂的笔试面试中出现的频率相当高,如果你想学习在笔试面试中拿下高分, 可以仔细阅读这篇文章,本文基本包含了面试可能出现的所有情况,希望对你有所帮助。
基础知识概述
字符和字符串
- 字符就是单个字符,字符串就是多个字符的集合;
- 定界符不同:用双引号引起来的就是字符串,用单引号引起来的就是字符;
- 在字符串的结尾处,自动被编译器加上了'\0'这个字符,所以 字符串以\0结尾,而字符没有\0;
- 在屏幕上要打印时候,在printf函数中,字符串使用的是“%s”这个占位符,字符使用“%c” 这个占位符。
指针和数组
在 C 语言中,指针和数组之间的关系十分密切,通过数组下标所能完成的任何操作都可以通过指针来实现。一般来说,用指针编写的程序比用数组下标编写的程序执行速度快,但另一方面,用指针实现的程序理解起来稍微困难一些。
指针基础知识
- 指针是一种保存变量地址的 变量 。(存放在指针中的值都被当成地址处理)
- 指针的大小在32位平台是4个字节,在64位平台是8个字节。
- 指针的定义方式是: type + * 。
例如: char* 类型的指针是为了 存放 char 类型变量的地址。 short* 类型的指针是为了存放 short 类型变量的地址。 int* 类型的指针是为了存放int 类型变量的地址。 - 指针的类型决定了:
< 1 > 指针向前或者向后移动一步有多大(距离)。
< 2 > 对指针解引用的时候可以访问几个字节空间的内存(能操作几个字节)。 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
数组基础知识
- 数组是一组相同类型元素的集合。([ ] ,下标引用=操作符;[ ] 中要给一个常量才可以,不能使用变量)
- 数组在内存中是连续存放的,并且整体一次开辟整块内存空间。
- 数组名是数组首元素的地址。(有两个例外:sizeof (数组名) 和 & 数组名)
本文能用到的有关指针和数组的以上的内容就够了,详细的介绍我会重新开一篇去深入介绍,想要了解更多的干货知识请关注我的博客1。https://i.csdn.net/#/uc/profile
sizeof与strlen()
字符一维数组
程序及结果分析
注意:程序中的解释有程序运行结果,都是进行检验过的,由于篇幅限制就不贴程序运行结果图了。
int a[] = {
1, 2, 3, 4 };printf("%d\n", sizeof(a));//在sizeof(a)中 a代表整个数组,数组类型为int每个元素占四个字节,4(元素个数)*4(int类型)=16字节。printf("%d\n", sizeof(a + 0));//a+0:代表数组首元素地址,占4/8字节。printf("%d\n", sizeof(*a));//a表示数组首元素地址,*a为数组首元素占4字节。printf("%d\n", sizeof(a + 1));//a+1:指向数组首元素地址的指针指向下一元素的地址,占4/8字节。printf("%d\n", sizeof(a[1]));//a[1]:表示第二元素,int类型占4个字节。printf("%d\n", sizeof(&a));//&a整个数组的地址也就是首元素地址,int类型占4/8字节。printf("%d\n", sizeof(*&a));//*&a表示整个数组,与sizeof(a)等价4*4=16字节。printf("%d\n", sizeof(&a + 1));//&a+1:代表的是数组首地址加上整个数组的大小,这里数组类型是int,所以+1代表的是地址加上4x4=16;但是其地址的大小仍为4/8字节。printf("%d\n", sizeof(&a[0]));//&a[0]:首元素地址,int类型占4/8字节。printf("%d\n", sizeof(&a[0] + 1));//&a[0]+1=&a[1],int类型占4/8字节。
char a[] = {
'a', 'b', 'c', 'd', 'e', 'f' };printf("%d\n", strlen(a));//a:首元素地址,strlen(a)是随机值。(下面分析)printf("%d\n", strlen(a + 0));//a + 0:代表数组首元素地址,是随机值。(下面分析)printf("%d\n", strlen(*a));//*a:err报错。(下面分析)printf("%d\n", strlen(a[1]));//a[1]:err报错。(下面分析)printf("%d\n", strlen(&a));//&a:数组首地址,随机值。(下面分析)printf("%d\n", strlen(&a + 1));//&a + 1:跳过整个数组大小的地址,随机值 - 6。(下面分析)printf("%d\n", strlen(&a[0] + 1));//&a[0] + 1:随机值 - 1。(下面分析)
重点难点及小结
- sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。
- &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。
- strlen(char * ) 是函数,要在运行时才能计算。参数必须是字符型指针(char*)。当数组名作为参数传入时,实际上数组就退化成指针了。
- 指针的大小在32位平台是4个字节,在64位平台是8个字节。
- 难点分析
- strlen(a) ;strlen(a + 0) 由于strlen( ) 函数计算字符串长度到\0为止,且不包含\0,而字符数组不含\0所以它会一直往下读取超过了数组大小直到\0为止,所以是随机值,但是大于等于数组字符数。
- strlen( * a),strlen(a[1]) 由于strlen( char * ) 函数参数必须是字符型指针(char *),而 * a , a[0]都是首元素“a”不是地址所以会报错 。 那么接下来我们看一下报错窗口。
读取位置 0x00000061 时发生访问冲突,strlen(*a)等效于strlen(a)字符 a 的ASCII码中为 97,而 97 转为十六进制数是0x00000061恰好与报错的数一致,这是偶然吗?明显不是,是因为函数strlen( )将放入参数位置的数都看作是地址进行计算。所以strlen( char * ) 函数 参数 必须 是字符型指针(char * )。 - strlen(&a),strlen(&a + 1),strlen(&a[0] + 1) 字符数组不含\0所以它会一直往下读取超过了数组大小直到\0为止,所以是随机值但是大于等于数组字符数。但是 &a + 1 代表跳过整个数组指向数组末端下一位置所以随机值减去整个数组大小就是其真实值大小。&a[0] + 1数组第二元素随机值减一就是其真实值大小。
除此1,2两种情况之外,所有的数组名都表示数组首元素的地址。
字符串一维数组
程序及结果分析
char a[] = {
"abcdef" };printf("%d\n", sizeof(a));//a:整个数组,从首字符到 \0(含) 共有7个字符,7字节。printf("%d\n", sizeof(a + 0));//a + 0:首元素地址,4/8字节。printf("%d\n", sizeof(*a));//*a:为数组首元素,1字节。printf("%d\n", sizeof(a[1]));//a[1]:为数组第二元素,1字节。printf("%d\n", sizeof(&a));//&a:数组的地址,4/8字节。printf("%d\n", sizeof(&a + 1));//&a + 1:跳过整个数组大小的地址,4/8字节。printf("%d\n", sizeof(&a[0] + 1));//&a[0] + 1:表示第二元素地址,4/8字节。
char a[] = {
"abcdef"};printf("%d\n", strlen(a));//a:首元素地址,从首字符到 \0(不含)共有6个字符,6。printf("%d\n", strlen(a + 0));//a + 0:首元素地址,6。printf("%d\n", strlen(*a));//*a:err报错。printf("%d\n", strlen(a[1]));//a[1]:err报错。printf("%d\n", strlen(&a));//&a:数组的地址,6。printf("%d\n", strlen(&a + 1));//&a + 1:跳过整个数组大小的地址,随机值。printf("%d\n", strlen(&a[0] + 1));//&a[0] + 1:表示第二元素地址,随机值 - 1。
重点难点及小结
- 在字符串的结尾处,自动被编译器加上了'\0'这个字符;
- 数组做sizeof的参数不退化,传递给strlen就退化为指针了;
- strlen只关心存储的数据内容,不关心空间的大小和类型;
字符串指针
程序及结果分析
char *p = "abcdef";//等价于arr[]={"abcdef"}; p = &arr;printf("%d\n", sizeof(p));//字符串指针p指向字符串首地址大小为4/8字节。printf("%d\n", sizeof(p + 1));//p+1:指针指向下一字符4/8字节。printf("%d\n", sizeof(*p));//*p:指针解引用,表示指针指向的内容,即字符串首字符,char类型1字节。printf("%d\n", sizeof(p[0]));//p[0]:等价于*(p + 0),char类型1字节。printf("%d\n", sizeof(&p));//&p:整个字符串的地址,4/8字节。printf("%d\n", sizeof(&p + 1));//&p + 1:等价于&arr+1,首地址加上整个字符串长度指向下一字符地址,4/8字节。printf("%d\n", sizeof(&p[0] + 1));//&p[0] + 1:p[0]首字符,&p[0]首元素地址,&p[0] + 1等价于p + 1,4/8字节。
char *p = "abcdef";printf("%d\n", strlen(p));//p:指向首地址,6。printf("%d\n", strlen(p + 1));//p + 1:指向下一字符,5。printf("%d\n", strlen(*p));//*p:解引用,*p = arr[0],不是字符串指针,err报错。printf("%d\n", strlen(p[0]));//p[0] :等价 *(p + 0)首元素,err报错。printf("%d\n", strlen(&p));//&p:整个字符串首地址,printf("%d\n", strlen(&p + 1));// &p + 1:首地址加上整个字符串长度指向下一字符地址,随机值 //sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以'\0'结尾的printf("%d\n", strlen(&p[0] + 1));//&p[0] + 1:等价于&arr[1],第二字符地址,5。printf("%d\n", strlen(&p[0]));//&p[0]:首字符地址,6。
重点难点及小结
“指针加 1”
如果 pa 指向数组中的某个特定元素,那么,根据指针运算的定义,pa+1 将指向下一个元素,pa + i将指向 pa 所指向数组元素之后的第 i 个元素,而 pa - i 将指向 pa 所指向数组元素之前的第 i 个元素。因此,如果指针 pa 指向 a[0],那么 * (pa+1) 引用的是数组元素 a[1] 的内容,pa+i 是数组元素 a[i]的地址,*(pa+i)引用的是数组元素 a[i]的内容。
即 指针pa + i = 指针 pa + sizeof(指针的类型) x i
- p[0] 就是*p 就是p所指向的内存中的值;(另外p[0]也等价于 *(p+0))
- &p 是p这个指针本身的地址,&p的类型是int**;
- p[0] 就是p[0]的地址,也就是p;
- (&p)[0],就是 * (&p),也是p;
- *p 就是前面说的p[0]。
字符二维数组
程序及结果分析
//二维数组int a[3][4] = {
0 };printf("%d\n", sizeof(a));//a:整个数组,3*4*4=48字节。printf("%d\n", sizeof(a[0][0]));//a[0][0]:第一个元素,int类型4字节。printf("%d\n", sizeof(a[0]));//a[0]:整个第一行元素,4*4=16字节。printf("%d\n", sizeof(*a));//*a:a是首元素地址即&a[0]首行元素地址,*a表示首行元素,4*4=16字节。printf("%d\n", sizeof(a[0] + 1));//a[0] + 1 = &a[0][1],指针类型4/8字节。printf("%d\n", sizeof(*(a[0] + 1)));//*(a[0] + 1) = a[0][1]int类型,4字节。printf("%d\n", sizeof(a + 1));//a + 1 = &a[1],4/8字节。printf("%d\n", sizeof(*(a + 1)));//*(a + 1) = a[2],4*4=16字节。printf("%d\n", sizeof(&a[0] + 1));//&a[0] + 1 = &a[1],4/8字节。printf("%d\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1) = a[1],4*4=16字节。printf("%d\n", sizeof(a[3]));//4*4=16字节。
重点难点及小结
可以把二维数组看成一个特殊的一维数组,它的每个元素又是一个一维数组。二维数组名为首对象或首行的地址。二维数组名加 1 表示跳过一个对象(一行)的空间,为下一个对象(下一行)的地址。即跳过一个对象所有属性(一行中所有列元素)对应的空间,到达下一个对象(下一行)的起始位置。
二位数组存储方式
以 **a[3][4]**为例
- a:表示首个一维数组的地址。a–>0x1000
- a+i:表示第i+1个一维数组的地址。a+0–>0x1000 a+1–>0x1008
- * (a+i):表示第i+1个一维数组首个元素的地址。*(a+0)–>0x1000
- * (a+i)+j:表示第i+1个一维数组的第j+1个元素的地址。*(a+0)+1–>0x1004
- * ( * (a+i)+j) :表示第i+1个一维数组的第j+1个元素的 值 。* ( * (a+0)+0)–>0x1000
- &a:表示数组的地址。&a–>0x1000
举个例子
本文总结
strlen(…)是函数,要在运行时才能计算。参数必须是字符型指针(char*)。当数组名作为参数传入时,实际上数组就退化成指针了。数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址。sizeof() 是一种内存容量度量函数,功能是返回一个变量或者类型的大小(以字节为单位);在 C 语言中,sizeof() 是一个判断数据类型或者表达式长度的运算符。而strlen只关心存储的数据内容,不关心空间的大小和类型。
https://i.csdn.net/#/uc/profile ??