提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
本文继上一篇指针和数组笔试题,本文主要内容为字符串赋给指针变量和二维数组的一些经典例题,预祝读者学有所成!
提示:以下是本篇文章正文内容,下面案例可供参考
一、字符串赋给指针变量
例题1
代码如下(示例):
int main()
{
const char*p = "abcdef";
//把字符串首元素地址赋给p
printf("%d\n", sizeof(p));
//p本身是一个指针变量,它是用来存放地址的,地址大小4或8(取决于当前环境是32位还是64位)
printf("%d\n", sizeof(p+1));
//p是a的地址,地址加1是跳到下一个字符b的地址,地址大小4或8
printf("%d\n", sizeof(*p));
//p是a的地址,*p即对p解引用得到字符a,a的大小是1个字节
printf("%d\n", sizeof(p[0]));
//p[0]也就是*(p+0)
printf("%d\n", sizeof(&p));
//p是a的地址,&p也就是地址的地址,它仍然是个地址,大小4或8
printf("%d\n", sizeof(&p+1));
//同上知&p是一个地址,&p+1是跳过一个char*型变量的大小(本质是指针大小为4)到一个地址,仍然是地址,大小4或8
printf("%d\n", sizeof(&p[0]+1));
//p[0]即*(p+0)也就是*p,我们知道p是a的地址,*p就是a,所以&p[0]是&a,还是p,所以p+1仍是地址,大小4或8
return 0;
}
ps1:把abcdef赋给指针p,并不是整个字符串赋给指针p,而是把字符串首元素地址赋给了p,这里因为是常量字符串,我们就限定了这块空间里只能是abcdef,为了防止这块空间由于指针被修改,我们用const修饰*p是最合适的
ps2: C 语言的数组在很多时候会隐式转成指针, 比如函数传参, 或者参与运算. C 语言的指针支持 [ ] 取下标操作, p[n] 本质上等价于 * (p+n)
例题2
代码如下(示例):
#include<stdio.h>
#include<string.h>
int main()
{
char*p = "abcdef";//f后面会自动跟一个\0
printf("%d\n", strlen(p));
//p传了a的地址,strlen从a开始往后找\0,共6个字符
printf("%d\n", strlen(p+1));
//p+1是b的地址,strlen从b开始往后走\0,共5个字符
printf("%d\n", strlen(*p));
//注意!!!这里是一个野指针,会报错
//p是a的地址,*p是对a地址解引用得到a,因为是字符a,ascll码本质是97,计算机会从97这个地址开始找
printf("%d\n", strlen(p[0]));
//p[0]即*(p+0),也就是a,和上一行代码一样是野指针
printf("%d\n", strlen(&p));
//p是a的地址,&p就是地址的地址,从这块地址往后找我们并不知道什么时候能找到\0,所以会是个随机值
printf("%d\n", strlen(&p+1));
//由上一行代码知,&p是一个地址,地址+1仍然是地址,但对于这个地址的前后有什么东西我们是未知的,这里也会出现一个随机值
printf("%d\n", strlen(&p[0]+1));
//p[0]即*(p+0),p是a的地址,*p即a,&a即p,p+1是b的地址,所以往后找\0是5个字符
return 0;
}
ps:关于&p,因为它是访问了地址的地址,对该地址也是未知的,这里其实也是一个野指针,那为什么这里是打印随机值?当访问非法内存的时候, 出现的是 “未定义行为”,意味着你看到的结果是啥样, 是不可预期的,具体你的程序崩溃不崩溃, 和你的编译器, 操作系统, 运行环境都有很大关系。笔者这里的运行环境允许了&p没有崩溃,但在其他环境下就说不准了,重要的是我们要意识到,&p也是一个野指针
二、二维数组
例题
对于二维数组我们要意识到,它本质仍然是数组,除了sizeof(数组名)以及&数组名这两种情况数组名表示整个数组,其他的的时候数组名都是数组首元素的地址
我们先来看一下简单的二维数组a[3][4]在内存中的摆放
代码如下(示例):
#include<stdio.h>
#include<string.h>
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));
//这里计算二维数组的大小,数组a共有12个元素,每个元素都是int型占4字节,合计48字节
printf("%d\n", sizeof(a[0][0]));
//a[0][0]即0,类型为int型,占4字节
printf("%d\n", sizeof(a[0]));
//a[0]是数组a第一行的数组名,这里计算的就是第一行数组的大小,4个元素,每个元素占4个字节,合计16
printf("%d\n", sizeof(a[0]+1));
//由上知a[0]是一个数组名,但这里是a[0]+1,a[0]是没有单独放sizeof内部的
//所以这里a[0]是第一行数组的首元素地址,+1是跳过一个数组元素,是第一行第二个元素地址,地址大小4或8
printf("%d\n", sizeof(*(a[0]+1));
//由上一行代码知a[0]+1是一个地址,对其解引用得到0,int型大小4字节
printf("%d\n", sizeof(a+1));
//a是数组名,但没有单独放sizeof内部,所以这里是作为数组首元素地址,+1就是第二行的地址,是地址就是4或8个字节
printf("%d\n", sizeof(*(a+1));
//由上一行代码知,a+1是第二行数组地址,对其解引用得到第二行数组,16字节
printf("%d\n", sizeof(&a[0] + 1));
//a[0]是第一行数组名,对其进行取地址再加1,得到第二行数组地址,大小4或8
printf("%d\n", sizeof(*(&a[0] + 1)));
//由上一行代码知&a[0]+1是第二行数组地址,对其解引用得到第二行数组,16字节
printf("%d\n", sizeof(*a));
//a表示首元素(第一行数组)的地址,对其解引用得到第一行数组,16字节
printf("%d\n", sizeof(a[3]));
//假设a[3]是存在的,即第四行数组的数组名,其单独放在sizeof内部,是计算第四行数组大小,16字节
printf("%d\n", sizeof(&a + 1));
//二维数组其实地址是一维的,二维数组是包含12个元素+1也应跳过12个元素得到一个地址,大小4或8
return 0;
}
ps:关于a[0],我们知道a[0][0]、a[0][1]、a[0][2]、a[0][3]分别是数组a的第一行的元素,我们又知道,一维数组b[x]是用来访问数组b的第x+1个元素,那我们这里可以把a[0]看做b,也就是a[0]是(数组a第一行数组的)一个数组名
ps:关于a+1,二维数组的首元素地址并不是a[0][0]的地址,是第一行数组的地址,以这里的a[3][4]为例,我们把它想象成一个一维数组,共三个元素,第一个元素是第一行的数组
ps:关于a[3],这里只是看了一下这个数组和数组元素的类型,并没有真的去访问这个空间
注意:
有时候我们遇到的题目如上图,他不是直接给你sizeof(a)或&a,但我们知道:二维数组a[3][4]中,a[0]、a[1]、a[2]分别表示第1、2、3行数组名,所以这里用sizeof(a[x])和&a[x]也是看成整个数组,而不是数组首元素
同样的,除了sizeof(a[x])和&a[x],其他情况单独出现a[x]都是表示第x+1行第一个元素的地址