电子商务网站建设系统功能,wordpress机械展示,做网站找那些公司,青海省住房城乡建设厅网站文章目录 1. sizeof 和 strlen1.1 sizeof1.2 strlen 2. 数组和指针结合的试题深入解析2.1 一维数组2.2 字符数组代码1代码2代码3代码4代码5代码6 2.3 二维数组 3.指针运算的试题深入解析题1题2题3题4题5题6题7 希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力… 文章目录 1. sizeof 和 strlen1.1 sizeof1.2 strlen 2. 数组和指针结合的试题深入解析2.1 一维数组2.2 字符数组代码1代码2代码3代码4代码5代码6 2.3 二维数组 3.指针运算的试题深入解析题1题2题3题4题5题6题7 希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力 本篇为指针系列的最后一篇我们将在该篇 vlog 对指针的常见表达形式的概念及技巧进行深入的解析通过该篇 vlog 可以让你以后在遇到指针时基本都能将代码转化为自己的语言去理解建议先思考后再看解析更有助于加深理解希望能够为广大读者们在初学指针时排忧解惑 1. sizeof 和 strlen
1.1 sizeof
在初学C语言时就提到过 sizeof 的概念这也是个常用的关键字想必已经大家已经烂熟于心 回顾提示sizeof类型、sizeof 表达式 sizeof 是操作符sizeof 计算操作数所占内存的大小单位是字节不关注内存中存放什么数据 这里不做过多赘述忘记的可以去看我往期的文章回顾: 传送门关于我、重生到500年前凭借C语言改变世界科技vlog.2——C语言数据类型和变量 1.2 strlen
前面在举指针的例子的时候提到过 strlen 是用来计算字符串长度的 传送门strlen-C参考 头文件为 #include string.h , strlen 是专门用于计算字符串长度的strlen 从 str 这个参数的地址开始向后统计 \0 之前的字符串个数只要没遇到 \0 就不会停止直到找到为止所以可能存在越界查找的情况
通过一个例子就能明白
#include stdio.h
int main()
{char arr1[3] {a, b, c};char arr2[] abc;printf(%d\n, strlen(arr1));printf(%d\n, strlen(arr2));printf(%d\n, sizeof(arr1));printf(%d\n, sizeof(arr2));return 0;
}运行代码后可以发现结果为 35334 字符没有 \0 所以 strlen 找不到停止的标志就会产生一个随机值
可以总结出以下几点 strlen是库函数使用需要包含头文件 string.hsrtlen是求字符串长度的统计的是 \0 之前字符的个数关注内存中是否有 \0 如果没有 \0 就会持续往后找可能会越界 2. 数组和指针结合的试题深入解析
以下代码均在 x64 环境下运行地址大小为 8 地址保持不变
2.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));解析 16 数组名放在sizeof内部表示整个数组 单位是字节8 这里 a 是数组名表示首元素地址加 0 后不变4 这里 a 是数组名表示首元素地址解引用后就是首元素即 a[0] 所以*a – *(a 0) – a[0]8 这里 a 1 是第二个元素的地址4 第二个元素类型为 int8 这里取整个数组的地址16 这里的 * 和 抵消了也就是 sizeof(a) ,访问整个数组8 这里 a 1 跳过整个数组取地址8 取第一个元素的地址8 取第二个元素的地址 2.2 字符数组
代码1
char arr[] {a,b,c,d,e,f};
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));解析 6 数组名放在sizeof内部表示整个数组8 这里 arr 是数组名表示首元素地址加 0 后不变1 这里 arr 是数组名表示首元素地址解引用后就是首元素1 表示第二个元素8 这里取整个数组的地址8 这里 a 1 跳过整个数组取地址8 取第二个元素的地址 代码2
char arr[] {a,b,c,d,e,f};
1.printf(%d\n, strlen(arr));
2.printf(%d\n, strlen(arr0));
3.printf(%d\n, strlen(*arr));
4.printf(%d\n, strlen(arr[1]));
5.printf(%d\n, strlen(arr));
6.printf(%d\n, strlen(arr1));
7.printf(%d\n, strlen(arr[0]1));解析 随机值 没有 \0随机值 没有 \0程序崩溃 访问首元素即 ‘a’ 97 会把 97 当成地址去访问程序崩溃 访问第二个元素即 ‘b’ 98 会把 98 当成地址去访问随机值 arr数组的地址没有 \0随机值 跳过整个数组取地址没有 \0随机值 取第二个元素的地址没有 \0 代码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));解析 7 数组名放在sizeof内部表示整个数组8 arr是首元素地址加 0 后不变1 访问首元素即 a1 访问第二个元素即 b8 这里是数组的地址和首元素地址一样8 跳过整个数组取地址8 取第二个元素的地址 代码4
char arr[] abcdef;
1.printf(%d\n, strlen(arr));
2.printf(%d\n, strlen(arr0));
3.printf(%d\n, strlen(*arr));
4.printf(%d\n, strlen(arr[1]));
5.printf(%d\n, strlen(arr));
6.printf(%d\n, strlen(arr1));
7.printf(%d\n, strlen(arr[0]1));解析 6 arr是首元素地址统计 \0 之前的字符长度6 arr是首元素地址统计 \0 之前的字符长度加 0 后不变程序崩溃 访问首元素即 ‘a’ 97 会把 97 当成地址去访问程序崩溃 访问第二个元素即 ‘b’ 98 会把 98 当成地址去访问6 arr是数组的地址即首元素地址统计 \0 之前的字符长度随机值 跳过整个数组取地址没有 \05 取第二个元素的地址统计 \0 之前的字符长度 代码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));解析 8 p 是指针变量计算的是指针变量的大小8 p1是第二个元素地址1 p 的大小是 char* 所以 *p 只能访问一个字节1 p[0] – *(p 0) – *p ,访问一个字节8 指针变量 p 的地址8 跳过 p 变量取后面的地址8 取第二个元素的地址 代码6
char *p abcdef;
1.printf(%d\n, strlen(p));
2.printf(%d\n, strlen(p1));
3.printf(%d\n, strlen(*p));
4.printf(%d\n, strlen(p[0]));
5.printf(%d\n, strlen(p));
6.printf(%d\n, strlen(p1));
7.printf(%d\n, strlen(p[0]1));解析 6 p 是指针变量存放字符串的地址统计 \0 之前的字符长度5 指向第二个元素的地址程序崩溃 访问首元素即 ‘a’ 97 会把 97 当成地址去访问程序崩溃 p[0] – *(p 0) – *p访问首元素即 ‘a’ 97 会把 97 当成地址去访问随机值 取指针变量 p 的地址没有 \0随机值 跳过 p 变量取后面的地址没有 \05 取第二个元素的地址统计 \0 之前的字符长度 2.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]));解析 48 数组名放在sizeof内部表示整个数组4 第一行第一个元素16 第一行数组名计算第一行大小8 a[0]1则是将这个指针向后移动一个元素的位置 所以 a[0] 1 – a[0][0] 1 – a[0][1]4 第一行第二个元素8 第二行的地址16 解引用第二行8 第二行的地址16 第二行解引用16 第一行解引用16 第四行 3.指针运算的试题深入解析
题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;
}
//程序的结果是什么解析 输出 25 1.首先a 取的是整个数组a的地址数组的地址和数组首元素的地址在数值上是相同的但类型不同数组首元素的地址类型是 int 而数组的地址类型是 int ()[5]因为a是一个包含 5 个元素的数组 2.然后a 1 表示将指针向后移动一个数组的大小由于数组 a 包含 5 个 int 类型的元素每个int类型元素占 4 个字节那么整个数组a在内存中所占字节数为 5×4 20字节所以 a 1 实际上是指向了数组 a 所占内存空间之后的下一个位置 3.最后(int *)(a 1) 将这个指向数组a之后位置的指针强制转换为 int * 类型的指针也就是将其看作是指向一个int类型元素的指针赋值给了ptr *对于 (a 1) 数组名a在大多数情况下会被隐式转换为指向数组首元素的指针所以 a 1 就是将指向首元素的指针向后移动一个元素的位置*(a 1) 则是获取这个移动后指针所指向的元素也就是数组a的第二个元素其值为 2
*对于 (ptr - 1) ptr 是指向数组a所占内存空间之后的下一个位置那么 ptr - 1 就是将这个指针向前移动一个元素的位置*(ptr - 1) 就是获取这个移动后指针所指向的元素也就是数组a的最后一个元素其值为 5
题2
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥
struct Test
{int Num;char *pcName;short sDate;char cha[2];short sBa[4];
}*p (struct Test*)0x100000;
//定义了一个指向 Test 结构体的指针 p并将其初始化为内存地址 0x100000
int main()
{printf(%p\n, p 0x1);printf(%p\n, (unsigned long)p 0x1);printf(%p\n, (unsigned int*)p 0x1);return 0;
}输出0x1000140x1000010x100004 第一次输出 将各成员所占字节数相加4 4 2 2 8 20 字节 所以当 p 0x1 时指针会按照结构体大小移动即从初始地址 0x100000 移动到 0x100000 20×1 0x100014这里输出的结果应该是 0x100014
第二次输出 这里将结构体指针 p 强制转换为 unsigned long 类型然后进行加法运算当把指针转换无符号长整型后就不再按照结构体的大小进行指针移动的运算了而是单纯的数值加法因为 p 被初始化为 0x100000将其视为无符号长整型并加上 0x1得到的结果就0x100001这里输出的结果应该是 0x100001
第三次输出 这里将结构体指针 p 强制转换为 unsigned int* 类型的指针然后进行加法运算当 unsigned int* 类型的指针进行算术运算时指针移动的步长是根据 unsigned int 类型的大小来确定的在一般情况下unsigned int 类型占 4 个字节所以当 (unsigned int*)p 0x1 时指针会从初始地址 0x100000 移动到 0x100000 4×1 0x100004这里输出的结果应该是 0x100004
题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;
}解析 输出1 a[0] 可以看作是指向二维数组 a 第一行这里将 a[0] 赋值给指针 p此时 p 就指向了数组 a 的第一行的第一个元素也就是值为 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;
}解析 输出0xFFFFFFFC,-4 int(*p)[4], p a 的图示 p[4][2] ((p4)2) 所以地址减地址得到的是元素个数又因为这里打印地址所以以补码的形式打印 题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;
}解析 输出105 int *ptr1 (int *)(aa 1) int *ptr2 (int )((aa 1)) 如图所示 首先aa 取整个二维数组 aa 的地址加一然后aa 1 指向了二维数组 aa 的第二行*(aa 1) 就是获取这个指针所指向的内容也就是二维数组 aa 的第二行 *对于 (ptr1 - 1) ptr1 是指向数组 aa 所占内存空间之后的下一个位置那么 ptr1 - 1 就是将这个指针向左移动一个元素的位置*(ptr1 - 1) 就是获取这个移动后指针所指向的元素也就是数组 aa 的最后一个元素其值为 10
*对于 (ptr2 - 1) ptr2 是指向二维数组 aa 的第二行那么 ptr2 - 1 就是将这个指针向左移动一个元素的位置*(ptr2 - 1) 就是获取这个移动后指针所指向的元素也就是二维数组 aa 的第一行的第五个元素其值为 5
题6
#include stdio.h
int main()
{char *a[] {work,at,alibaba};char**pa a;pa;printf(%s\n, *pa);return 0;
}解析 输出at char *a[ ] {“work”,“at”,“alibaba”} char**pa a 如图所示 a 数组里分别存放三个字符串的地址然后将该数组的地址存放到二级指针里即第一个字符串的地址pa 指向第二个字符串然后解引用得到 at 的地址但是这里使用 %s 占位符所以当把 *pa 作为 %s 的参数传递给 printf 函数时printf 函数会按照字符串的格式来处理它即从这个指针所指向的位置开始依次输出字符直到遇到空字符\0为止这样就输出了完整的字符串 “at”而不是它的地址 题7
#include stdio.h
int main()
{char *c[] {ENTER,NEW,POINT,FIRST};char**cp[] {c3,c2,c1,c};char***cpp cp;printf(%s\n, **cpp);printf(%s\n, *--*cpp3);printf(%s\n, *cpp[-2]3);printf(%s\n, cpp[-1][-1]1);return 0;
}解析 输出POINTEWSTNT 如图所示
第一次输出
首先cpp 会使 cpp 指针自增它现在指向 cp 数组中的第二个元素也就是原来 cp[1] 的地址。然后*cpp 会取出 cpp 所指向的元素即 cp[1]它是指向 c 2 的指针也就是指向字符串 “POINT” 的指针。最后**cpp 再次间接访问得到的就是字符串 “POINT”所以这个 printf 语句会输出 “POINT”
第二次输出
先看 cpp这会使 cpp 再次自增现在它指向 cp 数组中的第三个元素原来 cp[2] 的地址然后 *cpp 取出 cpp 所指向的元素即 cp[2]它是指向 c 1 的指针指向字符串 “NEW” 的指针接着 --*cpp 会对 cp[2] 所指向的指针也就是指向字符串 “NEW” 的指针进行自减操作此时它指向了字符串 “NEW” 中的倒数第二个字符假设字符串以 \0 结尾那么就是指向 ‘W’ 的指针最后 –cpp 3 会先取出这个新指向的字符‘W’然后再往后偏移 3 个字符此时就指向了字符串 “NEW” 中的倒数第一个字符‘W’ 往后 3 个字符也就是 ‘W’ 本身因为字符串 “NEW” 较短所以这个 printf 语句会输出 “EW”
第三次输出
cpp[-2] 相当于 *(cpp - 2)因为前面 cpp 经过两次自增现在要往回找两个位置所以 cpp[-2] 指向的是原来 cp[0] 的地址*cpp[-2] 取出 cpp[-2] 所指向的元素即 cp[0]它是指向 c 3 的指针指向字符串 “FIRST” 的指针*cpp[-2] 3 会在指向字符串 “FIRST” 的指针基础上往后偏移 3 个字符所以会指向字符串 “FIRST” 中的第 4 个字符因此这个 printf 语句会输出 “ST”
第四次输出
cpp[-1] 相当于 *(cpp - 1)因为前面 cpp 经过两次自增现在往回找一个位置所以 cpp[-1] 指向的是原来 cp[1] 的地址cpp[-1][-1] 相当于 ((cpp - 1) - 1)也就是先找到 cp[1]指向 c 2 的指针指向字符串 “POINT” 的指针然后再对这个指针进行自减操作此时它指向了字符串 “POINT” 中的倒数第二个字符cpp[-1][-1] 1 会在指向 ‘N’ 的指针基础上往后偏移 1 个字符所以会指向字符串 “POINT” 中的倒数第一个字符‘N’ 往后 1 个字符也就是 ‘T’ 的指针因此这个 printf 语句会输出 “NT” 今天的博客属实不易有些题博主也想了很久才理解也尽量用最简易易懂的方式给大家讲解每一道题希望看完这篇 vlog 以后不再害怕指针类型的题目冬天到了祝大家立冬快乐 希望读者们多多三连支持
小编会继续更新
你们的鼓励就是我前进的动力