wordpress上传大视频,专业网站seo推广,wordpress和帝国区别,住房和城乡建设报名网站文章目录 一、sizeof和strlen的对⽐1.sizeof2.strlen3.sizeof与strlen对比 二、数组和指针笔试解析1.一维数组2.字符、字符串数组和字符指针代码1代码2代码3代码4代码5代码6 3.二维数组4.总结 三、指针运算笔试题解析代码1代码2代码3代码4代码5代码6 一、sizeof和strlen的对⽐ … 文章目录 一、sizeof和strlen的对⽐1.sizeof2.strlen3.sizeof与strlen对比 二、数组和指针笔试解析1.一维数组2.字符、字符串数组和字符指针代码1代码2代码3代码4代码5代码6 3.二维数组4.总结 三、指针运算笔试题解析代码1代码2代码3代码4代码5代码6 一、sizeof和strlen的对⽐
1.sizeof 在学习操作符的时候我们学习了 sizeof sizeof 计算变量所占内存内存空间大小的单位是字节如果操作数是类型的话计算的是使用类型创建的变量所占内存空间的大小它并不在意内存中存放什么数据 sizeof格式上有一个特点就是如果计算的是变量的大小可以省略小括号当然还是建议都写上小括号这样可以增加代码的可读性现在我们使用sizeof举一下例
#include stdio.hint main()
{int a 0;printf(%zd\n, sizeof(int));printf(%zd\n, sizeof(a));printf(%zd\n, sizeof a);return 0;
}运行结果
2.strlen strlen 是C语⾔库函数功能是求字符串⻓度。函数原型如下
size_t strlen ( const char * str );统计的是从 strlen 函数的参数 str 中这个地址开始向后 \0 之前字符串中字符的个数strlen 函数会⼀直向后找 \0 字符直到找到为止所以可能存在越界查找 我们来看一个例子来看看它的运行结果应该是什么
#include stdio.h
#include string.h
int main()
{char arr1[3] { a, b, c };char arr2[] abc;printf(%d\n, strlen(arr1));printf(%d\n, strlen(arr2));printf(%zd\n, sizeof(arr1));printf(%zd\n, sizeof(arr2));return 0;
}首先我们来看前两个strlen我们刚刚提到strlen会从当前地址慢慢往后找直到找到\0数组arr1和arr2的最大区别就是arr1存放的就只是3个字符并没有\0所以strlen就会一直往后找直到找到\0我们也不知道什么时候能找到\0所以会打印一个随机数 而arr2数组存放的是一个字符串虽然看不出来有没有\0但是实际上在字符串末尾会默认添加一个\0所以实际上arr2数组存放的就是abc\0然后strlen就可以正常帮我们计算字符串中字符的个数3 接下来我们来看看后面的两个sizeof我们上面已经提到了数组arr1和数组arr2的区别就是一个后面没有\0一个有\0所以在计算大小时\0会被算上所以sizeof(arr1)结果为3sizeof(arr2)结果为4 我们来看看运行结果看看我们分析的是否正确
3.sizeof与strlen对比 二、数组和指针笔试解析
1.一维数组
我们来看一组代码然后一个一个解析它们
int a[] {1,2,3,4};
1.printf(%zd\n,sizeof(a));
2.printf(%zd\n,sizeof(a0));
3.printf(%zd\n,sizeof(*a));
4.printf(%zd\n,sizeof(a1));
5.printf(%zd\n,sizeof(a[1]));
6.printf(%zd\n,sizeof(a));
7.printf(%zd\n,sizeof(*a));
8.printf(%zd\n,sizeof(a1));
9.printf(%zd\n,sizeof(a[0]));
10.printf(%zd\n,sizeof(a[0]1));1我们之前讲过sizeof(数组名)其中数组名代表整个数组会计算整个数组的大小也就是16个字节 2给首元素地址加上整数0虽然还是首元素地址但是并不能看作sizeof(数组名)所以这里算的是首元素地址是地址大小就为4或8个字节32位机器上就是4个字节64位机器上就是8字节这里解释一下下面不再做解释了 3a是数组首元素地址解引用就拿到了第一个元素由于这是整型数组每个元素都是整型所以大小应该是4字节 4a是首元素地址对它加一就是跳过一个元素到下一个元素的地址但是本质上还是地址所以大小为4或8个字节 5a[1]是数组第二个元素是整型所以大小为4个字节 6这个题有一点坑很容易做错我们主要是要注意数组名是拿到整个数组的地址它也是地址啊所以大小是4或8字节 7对数组名再解引用相当于和*相互抵消了最后又变成了数组名也就是sizeof(a)所以这里的a代表整个数组的大小为16个字节 8这里a拿到整个数组加一后就是跳过整个数组我们主要是要明白一点地址±整数还是地址所以这里a1是一个地址大小为4或8个字节 9a[0]是首元素a[0]就是取出首元素地址是一个地址所以大小为4或8个字节 10a[0]拿到首元素地址加一后拿到第二个元素的地址还是一个地址所以大小为4或8个字节 我们来看看代码运行结果64位机器
2.字符、字符串数组和字符指针 通过上面的练习我们基本可以找到一些做题的规律这里我们做一下有关字符、字符串数组的练习首先是字符数组练习
代码1
char arr[] { a,b,c,d,e,f };
1.printf(%zd\n, sizeof(arr));
2.printf(%zd\n, sizeof(arr 0));
3.printf(%zd\n, sizeof(*arr));
4.printf(%zd\n, sizeof(arr[1]));
5.printf(%zd\n, sizeof(arr));
6.printf(%zd\n, sizeof(arr 1));
7.printf(%zd\n, sizeof(arr[0] 1));1sizeof(数组名)此时数组名代表整个数组算出的是整个数组的大小应该是6个字节 2数组名这里0过后虽然感觉上和数组名差不多但是这里就只代表首元素地址是一个地址所以大小为4或8个字节 3arr是首元素地址对它解引用拿到的就是首元素是一个字符型元素所以大小为1个字节 4arr[1]是数组第二个元素也是一个字符型元素大小为1个字节 5arr拿到整个数组的地址但是也是地址所以大小为4或8字节 6arr1就是跳过整个arr数组但是得到的也是一个地址所以大小为4或8字节 7arr[0]是首元素地址1后得到的是第二个元素的地址地址的大小为4或8字节 我们来看看在64位机器上的运行结果
代码2
char arr[] {a,b,c,d,e,f};
1.printf(%zd\n, strlen(arr));
2.printf(%zd\n, strlen(arr0));
3.printf(%zd\n, strlen(*arr));
4.printf(%zd\n, strlen(arr[1]));
5.printf(%zd\n, strlen(arr));
6.printf(%zd\n, strlen(arr1));
7.printf(%zd\n, strlen(arr[0]1));这段代码与上面唯一区别就是把sizeof换成了strlen我们之前也对它们做过对比接下来我们开始分析 1strlen的计算方式是去找字符串中的\0没有遇到\0就会一直往后越界找直到碰到了\0才会结束返回结果而这里的arr数组是字符数组并不是一个字符串本身最后并没有\0所以strlen会一直往后找返回的也是一个随机值 2原理同1会返回一个随机值 3之前讲到strlen的时候我们讲过strlen的参数应该是一个字符指针但是这里*arr却是拿到了一个字符a而不是一个地址我们讲过字符在存储时是存储的是它的ascll码值所以这里strlen会把a的ascll码值97当作一个地址但是97这个编号的地址可能不属于该程序所以会非法访问也就是这个代码跑不通 4同3的原理strlen会把字符b的ascll码值98当作地址但是由于该地址可能不属于该程序所以会非法访问跑不通 5这里arr得到的是一个类型为char ( * )[6]的数组指针但是由于strlen接收的是字符指针所以这里的数组指针会被强制类型转换成字符指针由于arr其实打印出来是首元素地址只是±整数要跳过数组所以这里强制类型转换后相当于还是首元素地址此时就和12一致打印随机值 6arr1还是一个数组指针会强制类型转换成字符指针然后从那个位置一直往后数碰到\0才结束所以也是随机值 7这里相当于拿到第二个元素的地址往后数还是因为没有\0所以会打印随机值 这个部分稍微有点难涉及到了strlen的参数以及strlen的应用可以自己多做两遍现在我们来看看运行结果64位机器
代码3
从这里开始我们就开始练习字符串数组如下
char arr[] abcdef;
1.printf(%zd\n, sizeof(arr));
2.printf(%zd\n, sizeof(arr0));
3.printf(%zd\n, sizeof(*arr));
4.printf(%zd\n, sizeof(arr[1]));
5.printf(%zd\n, sizeof(arr));
6.printf(%zd\n, sizeof(arr1));
7.printf(%zd\n, sizeof(arr[0]1));1由于字符串中默认会包含一个\0所以我们在计算整个数组大小时需要把它也算上所以大小就是7个字节 2这里算的是数组arr首元素地址的大小为4或8个字节 3这里算的是数组arr首元素的大小为1个字节 4这里算的是数组arr第二个元素的大小为1个字节 5这里arr拿到整个数组的地址是一个地址大小为4或8个字节 6arr1就是跳过整个arr数组但是得到的也是一个地址大小也是4或8字节 7这里拿到的是第二个元素的地址大小为4或8个字节 我们来看看代码运行结果64机器
代码4
char arr[] abcdef;
1.printf(%zd\n, strlen(arr));
2.printf(%zd\n, strlen(arr0));
3.printf(%zd\n, strlen(*arr));
4.printf(%zd\n, strlen(arr[1]));
5.printf(%zd\n, strlen(arr));
6.printf(%zd\n, strlen(arr1));
7.printf(%zd\n, strlen(arr[0]1));1由于字符串后面会默认添加一个\0所以strlen可以正常计算字符串中字符的个数为6 2同1算出字符串中字符的个数为6 3这里跟上面代码2中的3一样会异常访问跑不通 4同代码2中的4 5这里会把数组指针强制转换成字符指针然后就变成首元素地址在这里字符串中有\0可以正常计算出6 6这里arr1会跳过整个arr数组然后往后面数这时候就没有\0了会一直往后数所以会算出一个随机值 7这里拿到第二个元素的地址可以正常从第二个元素计算走最后算出5 我们来看看代码运行结果64机器
代码5
从这里开始我们练习字符指针如下
char *p abcdef;
1.printf(%zd\n, sizeof(p));
2.printf(%zd\n, sizeof(p1));
3.printf(%zd\n, sizeof(*p));
4.printf(%zd\n, sizeof(p[0]));
5.printf(%zd\n, sizeof(p));
6.printf(%zd\n, sizeof(p1));
7.printf(%zd\n, sizeof(p[0]1));1我们讲过如果把一个字符串常量赋给一个字符指针实际上就是把字符串常量的第一个字符的地址传给这个字符指针这个式子中p就是第一个字符的地址大小为4或8个字节 2p1后拿到第二个字符的地址还是一个地址大小为4或8个字节 3对p进行解引用后就拿到了字符a它的大小就是1个字节 4p[0]这个表达也是拿到这个字符串的第一个字符相当于它也可以当做数组使用所以大小为1个字节 5p本身就是一个地址再对它取地址就是二级指针也是一个地址大小为4或8个字节下面是p的图解
6对一个二级指针1后还是一个地址所以大小为4或8个字节下面是p1的图解
7这里p[0]相当于拿到了字符串第一个字符的地址相当于就是p1后就是第二个字符的地址所以大小为4或8个字节 我们来看看代码运行结果64机器
代码6
char *p abcdef;
1.printf(%zd\n, strlen(p));
2.printf(%zd\n, strlen(p1));
3.printf(%zd\n, strlen(*p));
4.printf(%zd\n, strlen(p[0]));
5.printf(%zd\n, strlen(p));
6.printf(%zd\n, strlen(p1));
7.printf(%zd\n, strlen(p[0]1));1这里p是第一个字符的地址然后字符串中默认有\0所以strlen可以正常使用算出字符个数6 2p1变成第二个字符的地址所以strlen从第二个字符往后面数可以算出字符个数5 3同代码2的3 4同代码2的4 5这里拿到一个二级指针指向一级字符指针p会强制类型转换成一级字符指针但是这里原本拿到的是p的地址谁也不知道在哪里后面什么时候碰到\0也是不确定的所以最后strlen会一直往后面找直到随机值出现\0然后中止所以会返回一个随机值 6这里对二级指针1后会跳过一级指针p后面会碰到什么也是随机的多久碰到\0也是随机的所以会返回一个随机值如下图 7这里p[0]相当于拿到了字符串第一个字符的地址相当于就是p1后就是第二个字符的地址然后正常往后计算得到结果5 我们来看看代码运行结果64机器
3.二维数组 相信做了上面的题对一维数组有了很清晰的认识接下来我们来练习一段二维数组的题
int a[3][4] {0};
1.printf(%zd\n,sizeof(a));
2.printf(%zd\n,sizeof(a[0][0]));
3.printf(%zd\n,sizeof(a[0]));
4.printf(%zd\n,sizeof(a[0]1));
5.printf(%zd\n,sizeof(*(a[0]1)));
6.printf(%zd\n,sizeof(a1));
7.printf(%zd\n,sizeof(*(a1)));
8.printf(%zd\n,sizeof(a[0]1));
9.printf(%zd\n,sizeof(*(a[0]1)));
10.printf(%zd\n,sizeof(*a));
11.printf(%zd\n,sizeof(a[3]));1二维数组也是一样这里的数组名代表整个数组计算的是整个数组的大小大小应该是3 * 4 * 4结果为48 2这里a[0][0]得到的是二维数组a的第一行第一列的元素是一个整型所以大小为4个字节 3这里a[0]相当于这个二维数组的第一行的数组名计算的是整个第一行的大小为16个字节 4这里a[0]相当于是第一行的数组名也就是第一行第一个元素的地址1后变成第二个元素的地址是一个地址所以大小为4或8个字节 5a[0]1相当于就是第一行第二个元素的地址解引用后就拿到这个元素是一个整型大小为4个字节 6这里a作为二维数组的数组名没有单独放在sizeof中所以代表的是这个二维数组的第一个元素也就是相当于整个二维数组第一行的地址1后变成第二行的地址是一个地址所以大小为4或8个字节 7这里相当于对二维数组的第二行解引用拿到整个第二行大小就是16个字节 8这里a[0]相当于就是拿到整个第一行的地址1后跳过整个第一行变成第二行的地址是一个地址所以大小为4或8个字节 9这里a[0]1相当于就是整个第二行的地址也就是a[1]解引用后变成了a[1]相当于拿到第二行的数组名计算整个第二行的大小大小为16个字节 10这里a相当于就是二维数组的第一行解引用后拿到第一行所以算出来是16个字节 11这里a[3]越界访问了相当于二维数组的第4行这个二维数组本身没有第四行但是程序还是会帮我们越界去访问拿到与前三行相同结构的一行所以最后还是会计算出来一行的大小为16字节 我们来看看代码运行结果64机器
4.总结
数组名的意义
sizeof(数组名)这⾥的数组名表示整个数组计算的是整个数组的大小数组名这里的数组名表示整个数组取出的是整个数组的地址除此之外所有的数组名都表示首元素的地址
三、指针运算笔试题解析
代码1
#include stdio.h
int main()
{int a[5] { 1, 2, 3, 4, 5 };int *ptr (int *)(a 1);printf( %d,%d, *(a 1), *(ptr - 1));return 0;
}
//程序的结果是什么我们主要来看看整型指针ptr是什么首先a是取出整个数组的地址1后跳过了整个a数组然后将这个地址强制类型转换成了int*我们要知道强制类型转换前后的区别强制类型转换后±整数跳过的单位是整型而之前是以整个数组为单位跳过 接着来看*(a1)a在这里是首元素地址1后变成第二个元素的地址解引用后就拿到第二个元素也就是2 最后就是 * (ptr-1)我们知道ptr指向的是a数组的下一个数组的首元素地址那么现在ptr是整型指针-1后不就成了a数组的最后一个元素的地址解引用就拿到最后一个元素也就是5如图 最后来看看运行结果
代码2
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥
#include stdio.hstruct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p (struct Test*)0x100000;int main()
{printf(%p\n, p 1);printf(%p\n, (unsigned long)p 1);printf(%p\n, (unsigned int*)p 1);return 0;
}首先我们来看p1可以看到p是一个结构体指针并且被赋值为0x100000所以1后会跳过整个结构体而整个结构体的大小是20个字节所以不难猜到p1的结果为100020 随后我们来看(unsigned long)p 1这个就比较难了由于这里p被强制类型转换成了无符号长整型所以此时的p变成了一个数字1就是1变成了数字的加减法所以最后打印结果应该是100001 我们来看最后一个表达式(unsigned int*)p 1这里就比较简单了相当于还是被转换成了整型指针1后就跳过4个字节打印结果为100004 最后来看看运行结果
代码3
#include stdio.h
int main()
{int a[3][2] { (0, 1), (2, 3), (4, 5) };int *p;p a[0];printf( %d, p[0]);return 0;
}这里我们主要要看出来(0,1)这种表达式是什么含义是不是给它第一行的两个元素初始化为0和1呢很明显不是因为如果是这样应该使用大括号{}而不是小括号() 那它是什么呢它只是一个被小括号括起来的逗号表达式第一个逗号表达式结果为1第二个为3第三个为5所以这个二维数组最后应该长这个样子 然后这里说把a[0]也就是二维数组第一行的数组名赋值给p现在p就相当于二维数组第一行的数组名所以p[0]就是第一行第一个元素也就是1 我们来看看代码运行结果
代码4
//假设环境是x86环境程序输出的结果是啥
#include stdio.h
int main()
{int a[5][5];int(*p)[4];p a;printf( %p,%d\n, p[4][2] - a[4][2], p[4][2] - a[4][2]);return 0;
}这里的p是一个元素个数为4的整型数组指针而这里a是首元素的地址也就是代表了二维数组第一行的地址类型为int (*)[5]这里把a赋给p就会发生类型转换 相当于就是p接收了a存放的地址但是它变得一次只能跳过4个元素了这个题我们最好画图解决我们先分别找到a[4][2]和p[4][2]如图 其中蓝色四个方块代表p红色5个方块代表a我们可以看到p[4][2]和a[4][2]相隔了4个元素而p[4][2]比a[4][2]小所以减出来是-4 当它以%p打印时会转换成补码并且以16进制形式打印而%d形式则会直接打印-4这里就不再演示-4转换为补码等等步骤有兴趣可以自行操作 最后我们来看看代码执行结果
代码5
#include stdio.h
int main()
{int aa[2][5] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int *ptr1 (int *)(aa 1);int *ptr2 (int *)(*(aa 1));printf( %d,%d, *(ptr1 - 1), *(ptr2 - 1));return 0;
}这个题和我们的代码1有点类似只是这里变成二维数组了 首先我们来看ptr1aa是拿到整个二维数组的地址1后跳过整个二维数组然后再将其转换为整型指针如图 然后我们来看ptr2这里aa相当于第一行的地址1后拿到第二行的地址相当于就是aa[1]所以解引用后相当于拿到了第二行的数组名aa[1]此时它就代表第二行的首元素地址所以ptr2在如图位置 所以ptr1-1和ptr2-1的位置如图 所以它们分别代表的值为10和5我们来看看运行结果
代码6
#include stdio.h
int main()
{char *a[] {work,at,alibaba};char**pa a;pa;printf(%s\n, *pa);return 0;
}我们首先来看a数组这里a数组是一个字符数组指针存放的分别是三个常量字符串的首字符地址 然后再来看pa这里的a代表首元素的地址也就是字符串第一个字符w的地址把w的地址存放进了二级指针pa中如图 然后*pa就相当于at的第一个字符a的地址然后以%s的形式打印就会打印出来字符串at运行结果如下