• C语言----深入理解指针(5)


    1.sizeof和strlen的对比

    sizeof计算变量所占内存空间的大小的,单位是字节

    sizeof只关注内存空间的大小,不在乎内存中方的什么数据

    sizeof是操作符不是函数

    sizeof会将\0算进去的

    1. // sizeof计算变量所占内存空间的大小的,单位是字节
    2. int main()
    3. {
    4. int a = 10;
    5. printf("%d\n", sizeof(int));//输出结果是4
    6. printf("%d\n", sizeof(a));//输出结果是4
    7. int arr[10] = { 0 };
    8. printf("%d\n", sizeof(arr));//输出的结果是40
    9. return 0;
    10. }//sizeof只管新占了多大的内存空间大小

    那么strlen呢?

    strlen是c语言里面的库函数,是函数

    作用的求字符串长度的,只能针对字符串

    strlen统计的是字符串\0之前的字符个数

    只能指针字符串,求字符串长度

    不能求整形数组的

    strlen直到遇到\0,不然是不会停下来的

    1. int main()
    2. {
    3. char arr1[] = { 'a','b','c' };
    4. char arr2[] = "abc";//字符串末尾隐藏着一个\0
    5. printf("%zd\n", strlen(arr1));//15---往后找\0,直到找到\0才停止--那么这个15就是随机值
    6. printf("%zd\n", strlen(arr2));//3---\0前面有三个字符
    7. printf("%zd\n", sizeof(arr1));//3---只关乎arr1占了多大的空间
    8. printf("%zd\n", sizeof(arr2));//4---放了4个字符,包括\0,总共占了4个字节
    9. //strlen是用来求字符串长度的,不能用来求整型数组
    10. return 0;
    11. }

    2.数组和指针笔试题解析

    sizeof---一维数组

    1. //恢复:数组名是数组首元素的地址
    2. //但是有两个例外
    3. //sizeof(数组名)
    4. //&数组名
    5. //除了这两种,其他的都是数组首元素的地址
    6. int main()
    7. {
    8. int a[] = { 1,2,3,4 };
    9. printf("%zd\n", sizeof(a));//16---4个元素,每个元素4个字节
    10. //数组名a单独放在sizeof内部,a表示的是整个数组,计算的是整个数组的大小,单位是字节
    11. printf("%zd\n", sizeof(a+0));//4
    12. //a是数组名,数组首元素的地址,a+0还是首元素的地址
    13. //a+i就是数组内下标为i的数组元素
    14. //这里的sizeof计算的是首元素地址的大小
    15. printf("%zd\n", sizeof(*a));//4
    16. //这里的a依然是首元素的地址,*a就是首元素,就是a[0]
    17. //因为arr[0]是一个整数,就是4个字节
    18. printf("%zd\n", sizeof(a+1));//4
    19. //因为数组名表示的是首元素的地址,那么首元素加1
    20. //就是下标为1的数字的地址,就是这个数组内第二个元素的地址
    21. //arr[1]的地址
    22. //这里计算的是地址的大小
    23. printf("%zd\n", sizeof(a[1]));//4
    24. //a[1]就是数组内第二个元素,就是4个字节的大小
    25. printf("%zd\n", sizeof(&a));//4
    26. //取出整个数组的地址
    27. //数组的地址也是一个地址啊,是地址就是4个字节的长度
    28. printf("%zd\n", sizeof(*&a));//16
    29. //取出a的地址,再进行解引用,就是整个数组,
    30. //*&抵消了,就是整个数组
    31. // sizeof(*&a)==sizeof(a)
    32. //&a是数组的地址,类型是int(*)[4]---数组指针类型
    33. //那么*&a就是访问真个数组
    34. printf("%zd\n", sizeof(&a+1));//4
    35. //&a就是整个数组的地址,地址+1就是跳过整个数组后的那个地址,
    36. // 但是仍然是地址啊
    37. //是地址就是4个字节
    38. printf("%zd\n", sizeof(&a[0]));//4
    39. //arr[0]是数组首元素的地址,再将其取出
    40. //取出数组首元素的地址
    41. printf("%zd\n", sizeof(&a[0]+1));//4
    42. //将首元素的地址取出,再+1就是第二个元素的地址
    43. return 0;
    44. }

     sizeof--字符数组

    1. int main()
    2. {
    3. char arr[] = { 'a','b','c','d','e','f' };//没有\0
    4. printf("%d\n", sizeof(arr));//6
    5. //整个数组的大小,6个字符就是6
    6. printf("%d\n", sizeof(arr+0));//4
    7. //arr是首元素的地址,arr+0还是首元素的地址,地址的字节就是4
    8. printf("%d\n", sizeof(*arr));//1
    9. //对数组首元素地址进行解引用就是数组首元素,字符就是1个字节
    10. printf("%d\n", sizeof(arr[1]));//1
    11. //数组中下标为1的字符,大小是1个字节
    12. printf("%d\n", sizeof(&arr));//4
    13. //取出整个数组的地址,仍然是地址,地址就是4个字节的大小
    14. printf("%d\n", sizeof(&arr+1));//4
    15. //取出整个数组的地址,+1跳过整个数组,还是地址,就是4个字节的大小
    16. printf("%d\n", sizeof(&arr[0]+1));//4
    17. //取出数组首元素的地址,再+1就是第二个元素的地址,地址是4个字节大小
    18. return 0;
    19. }

     strlen---大括号--一堆字符

    1. int main()
    2. {
    3. char arr[] = { 'a','b','c','d','e','f' };//没有\0
    4. printf("%zd\n", strlen(arr));//随机值
    5. //这里的arr表示的是首元素的地址
    6. //从首元素开始数字符,一直数到\0之前才停止
    7. //那么这里的值就是随机值
    8. printf("%zd\n", strlen(arr+0));
    9. //arr+0还是数组名-----首元素的地址
    10. //那么返回的值就是随机值,这个代码和上面的代码没有区别
    11. //并且这两个随机值是一样的
    12. printf("%zd\n", strlen(*arr));
    13. //arr是首元素的地址,那么对其进行解引用就是数组第一个元素,就是'a'字符
    14. //strlen(a)---'a'的ASCII的大小是97,strlen不能运行
    15. //a的值传递给strlen,strlen会认为97是地址,然后会访问内存
    16. //这个代码是有问题的
    17. printf("%zd\n", strlen(arr[1]));
    18. //arr[1]是数组下标为1的元素
    19. //strlen('b')---'b'的ASCII大小是98
    20. //这个程序也会崩溃
    21. printf("%zd\n", strlen(&arr));//随机值
    22. //将数组的整个数组的地址传过去了
    23. //strlen依然从首元素往后数
    24. //依然没有遇到\0
    25. //那么返回值就是随机值
    26. //&arr的类型是----char(*)[6]
    27. printf("%zd\n", strlen(&arr+1));//随机值
    28. //取出整个数字的地址,然后+1跳过整个数组
    29. //也是随机值,不知道什么时候遇到\0
    30. printf("%zd\n", strlen(&arr[0]+1));//随机值
    31. //arr[0]是数组首元素,+1就是b,那么strlen往后直到遇到\0才停止,那么
    32. //这个代码返回的也是随机值
    33. return 0;
    34. }

     sizeof--字符串

    1. //数组名是数组首元素的地址
    2. //两个例外:
    3. //sizeof(arr)
    4. //&arr---这里的数组名表示整个数组,取出的是数组的地址
    5. int main()
    6. {
    7. char arr[] = "abcdef";//字符串初始化
    8. //这个字符串末尾还有一个\0
    9. printf("%zd\n", sizeof(arr));//7
    10. //arr作为数组名,这里计算的是这个数组的大小
    11. //一个字符一个字节,那么这里就是7个字节
    12. //这里的数组名是单独的放在sizeof里面的
    13. printf("%zd\n", sizeof(arr + 0));//4
    14. //数组名--就是数组首元素的地址
    15. //这里的arr并不是单独的放在sizeof里面
    16. //那么这里的arr+0是首元素的地址
    17. //既然是地址,那么大小就是4个字节
    18. printf("%zd\n", sizeof(*arr));//1
    19. //这里的arr是数组名--首元素的地址
    20. //对arr进行解引用达到的就是首元素
    21. //首元素的大小是1个字节--每个字符的大小是1个字节
    22. printf("%zd\n", sizeof(arr[1]));//1
    23. //数组下标为1的元素,字符,所以大小也是1
    24. printf("%zd\n", sizeof(&arr));//4
    25. //取出整个数组的地址,地址就是4个字节
    26. printf("%zd\n", sizeof(&arr + 1));//4
    27. //取出的是整个数组的地址,再+1,得到的就是跳过整个数组
    28. //得到的也是地址,既然是地址就是4个字节的大小
    29. printf("%zd\n", sizeof(&arr[0] + 1));//4
    30. //arr[0]是数组首元素,取出数组首元素的地址再+1
    31. //得到的就是数组内的第二元素的地址,地址就是4个字节大小
    32. //首元素的地址类型是char*
    33. return 0;
    34. }

     strlen---字符串

    1. int main()
    2. {
    3. char arr[] = "abcdef";//字符串末尾放着\0
    4. printf("%d\n", strlen(arr));//6
    5. //这里的arr是首元素的地址
    6. // 从首元素开始数,数到\0之前就停止
    7. //strlen是求字符串长度的
    8. //因为末尾放着一个\0,那么strlen数的就是\0前面的字符个数
    9. //strlen是从第一个元素开始统计\0之前的字符个数
    10. printf("%d\n", strlen(arr + 0));//6
    11. //arr是首元素的地址,那么arr+0还是首元素的地址
    12. //那么把数组首元素的地址传给strlen,就是从首元素开始统计,直到遇到\0
    13. printf("%d\n", strlen(*arr));
    14. //arr是首元素的地址,那么解引用得到的就是首元素字符'a'
    15. //'a'的大小是97,将97当成地址传递给strlen,97这个地址不能被访问,这个程序就崩溃了
    16. printf("%d\n", strlen(arr[1]));
    17. //arr[1]是数组下标为1的元素,第二个元素,访问的是'b'
    18. //这个代码的道理和上面的一样,一样会崩溃的
    19. printf("%d\n", strlen(&arr));//6
    20. //取出数组的地址,将取出的地址存放至数组指针变量里面这么写
    21. //char(*pa)[7]=&arr
    22. //那么pa的类型是char(*)[7]
    23. //但是对于strlen这个库函数来说
    24. //size_t strlen (const char* str)
    25. //硬要将char(*)[7]这个地址传过去,因为char(*)[7]与const char* str不一致,会导致将传过来的指向强制转换为strlen所需要的类型
    26. //
    27. //数组的地址和数组首元素的地址是指向同一个位置的
    28. //那么strlen也是从第一个元素的位置开始向后访问
    29. //所以这个代码实现的数据是6
    30. printf("%d\n", strlen(&arr + 1));//随机值
    31. //取出数组的地址,再+1就是跳过整个数组,那么strlen遇到\0才会停止
    32. //那么这里的就是随机值
    33. printf("%d\n", strlen(&arr[0] + 1));//5
    34. //取出的是首元素的地址,那么首元素的地址+1就是数组第二个元素的地址
    35. //那么strlen就从第二个元素开始访问进行统计的
    36. //那么这里的 数据就是5
    37. return 0;
    38. }
    39. //在这些代码运行的时候
    40. //会出现很多警告
    41. //因为我们在传参的时候类型不一致
    42. //&arr的类型是char(*)[7],而strlen只能接受const char* str这个类型的指针

     sizeof---指针

    1. int main()
    2. {
    3. char* p = "abcdef";//将首字符的地址存放在p中
    4. printf("%zd\n", sizeof(p));//4
    5. //p是指针变量。那么sizeof(p)计算的就是指针变量p的大小
    6. //因为一个指针变量的大小是4个字节--x86的情况下,x64的情况下是8个字节
    7. printf("%zd\n", sizeof(p + 1));//4
    8. //p存的是首元素的地址,p的类型是char*
    9. //那么p+1得到的是b的地址,就是相当于跳过了一个字符
    10. //地址的大小就是4个字节
    11. printf("%zd\n", sizeof(*p));//1
    12. //p是字符串首元素的地址,对p进行解引用,得到的就是首元素,首元素是字符a
    13. //a的字节大小是1
    14. //因为p的类型是char*,只能访问一个字节,就是a
    15. printf("%zd\n", sizeof(p[0]));//1
    16. //p[0]-->*(p+0)---得到的就是字符a---大小为1个字节
    17. printf("%zd\n", sizeof(&p));//4
    18. //取出p的地址,&p是p的地址,对p的地址进行大小计算,地址的大小就是4个字节
    19. printf("%zd\n", sizeof(&p + 1));//4
    20. //取出p的地址进行+1操作
    21. //
    22. //
    23. // char* *ptr=&p ptr指向的p是一个类型为char*类型的指针,后面的*说明ptr是一个指针
    24. //&p的指针类型是char * *,对&p+1就是跳过一个char* * 类型的对象
    25. //举例:int *p 对p+1跳过的就是一个整型对象
    26. //&p是p的地址,&p+1是跳过了p变量,指向了p的后面
    27. //因为&p+1是地址,就是4个字节
    28. printf("%zd\n", sizeof(&p[0] + 1));//4
    29. //p[0]是*(p+0)---就是首元素a
    30. //前面加上&得到的就是a的地址,+1就是b的地址
    31. //&p[0]就是p
    32. return 0;
    33. }

     strlen---指针

    1. int main()
    2. {
    3. char* p = "abcdef";//将这个字符串首元素的地址存储在p中,末尾有\0
    4. printf("%zd\n", strlen(p));//6
    5. //p里面放的是a的地址,
    6. //strlen(p)就是从a的地址开始统计这个字符串的长度
    7. //得到的数据是6
    8. printf("%zd\n", strlen(p + 1));//5
    9. //p存的是a的地址,因为p是char*的指针,那么p+1就是指向b的地址
    10. //就是从b的地址位置开始统计这个字符串的个数
    11. //那么得到的数据就是5
    12. printf("%zd\n", strlen(*p));//程序崩溃
    13. //*p是a,a是97
    14. //97作为地址传给strlen,程序会崩溃的
    15. printf("%zd\n", strlen(p[0]));//程序崩溃
    16. //p[0]--->*(p+0)----*p---a----97
    17. //那么这个程序就会崩溃的
    18. printf("%zd\n", strlen(&p));//随机值
    19. //取出指针变量p的地址,那么strlen就从指针变量p地址开始统计,在p这块空间进行统计
    20. //那么得到的就是随机值
    21. printf("%zd\n", strlen(&p + 1));//随机值
    22. //&p是指针p的地址,+1就是跳过p的地址,开始进行统计
    23. //所以数据就是随机值
    24. printf("%zd\n", strlen(&p[0] + 1));//5
    25. //p[0]--->*(p+0)----*p---a
    26. //那么&p[0]就相当于p,
    27. //p里面存的是a的地址,+1得到的是b的地址
    28. //那么我们就从b的位置开始统计
    29. //得到的数据是5
    30. return 0;
    31. }
    32. //注意:p[0]--->*(p+0)----*p---a

     sizeof---二维数组

    1. //二维数组
    2. int main()
    3. {
    4. int a[3][4] = { 0 };//34列----每一行都输一维数组
    5. //第一行的数组名叫arr[0]---每个元素的访问a[0][i]
    6. //第一行的数组名叫arr[1]---每个元素的访问a[1][i]
    7. //第一行的数组名叫arr[2]---每个元素的访问a[2][i]
    8. printf("%d\n", sizeof(a));//48
    9. //a是二维数组的数组名,单独放在sizeof内部,a表示的是整个数组,计算的就是整个数组的大小,单位是字节
    10. //12个元素,每个元素4个字节,总共就是48个字节大小
    11. printf("%d\n", sizeof(a[0][0]));//4
    12. //a[0][0]就是第一行第一个元素,大小是4个字节
    13. printf("%d\n", sizeof(a[0]));//16
    14. //a[0]是这个二维数组的第一行的数组名
    15. //第一行的数组名单独放在sizeof内部,计算的就是整个一维数组的大小,就是4*4=16个字节的大小
    16. printf("%d\n", sizeof(a[0] + 1));//4
    17. //a[0]是第一行的数组名,但是没有单独放在sizeof里面,就说明这里的a[0]是第一行首元素的地址,那么首元素地址+1就是第一行第二个元素的地址
    18. //就是a[0][0]的地址
    19. //那么a[0] + 1--->&arr[0][0]+1--->&a[0][1]
    20. //因为这里是地址,那么就是4个字节大小
    21. printf("%d\n", sizeof(*(a[0] + 1)));4
    22. //上面说a[0]+1得到的是第一行第二个元素的地址
    23. //那么我们解引用得到的就是第一行第二个元素,是一个整型,大小是4个字节
    24. printf("%d\n", sizeof(a + 1));//4
    25. //a + 1
    26. //a是二维数组的数组名,并且没有单独放在sizeof内
    27. //说明这个a是这个二维数组的首元素地址
    28. //就是第一行的地址,那么a+1就是第二行的地址
    29. //因为a+1是第二行的地址,传给sizeof,因为是地址,所以大小是4个字节
    30. //
    31. printf("%d\n", sizeof(*(a + 1)));//16
    32. //*(a+1)--->a[1]--第二行的数组名,单独放在sizeof内部,那么sizeof计算的就是第二行的大小
    33. //因为a+1得到的是第二行的地址,那么对其进行解引用,得到的就是第二行整个数组
    34. //a+1是第二行的地址,类型是int(*)[4],数组指针,基因用访问的是这个数组,大小是16个字节
    35. printf("%d\n", sizeof(&a[0] + 1));//4
    36. //a[0]是第一行的数组名,&数组名就是第一行的地址,&a[0]就是取出了第一行的地址
    37. //那么&a[0] + 1得到的就是第二行的地址
    38. //因为是地址,那么大小就是4个字节
    39. printf("%d\n", sizeof(*(&a[0] + 1)));//16
    40. //上面说到&a[0] + 1得到的是第二行的地址,那么对第二行的地址进行解引用得到的就是第二行数组
    41. //
    42. printf("%d\n", sizeof(*a));//16
    43. //a作为数组名没有单独放在sizeof内,那么a就不是整个数组
    44. //a就是这个二维数组的首元素的地址
    45. //对其进行解引用得到的就是第一行整个数组
    46. //4*4=16
    47. //*a-->*(a+0)-->a[0]
    48. printf("%d\n", sizeof(a[3]));//16
    49. //这里是否越界
    50. //sizeof内部的表达式是不会真实计算的,也不会真实去访问的
    51. // 仅仅只是根据类型来推断长度的
    52. //a[3]是第四行的数组名---那么计算的是第四行的大小
    53. //第四行的类型是int[4]
    54. return 0;
    55. }
    56. //数组名在sizeof内单独使用,表示的就是整个数组
    57. //如果不是单独使用,我们就要考虑一下这个数组名是数组首元素的地址
    58. //sizeof内部的表达式是不会进行运算的
    59. int main()
    60. {
    61. short s = 8;//short类型的数据是2个字节
    62. int n = 12;//4个字节
    63. printf("%zd\n", sizeof(s = n + 5));//2
    64. //sizeof内部的表达式并没有进行运算
    65. //sizeof(s = n + 5)这个表达式 最终结果取决于s
    66. printf("%zd\n", s);//8
    67. return 0;
    68. }

    数组名的意义:

    1.sizeof(数组名),这里的数组名表示的是整个数组,计算的是整个数组的大小

    2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址

    3.除此之外所有的数组名都表示数组首元素的地址

    3.指针运算笔试题解析

    题目一

    1. #include <stdio.h>
    2. int main()
    3. {
    4. int a[5] = { 1, 2, 3, 4, 5 };
    5. int* ptr = (int*)(&a + 1);
    6. //&a是取出真个数组的地址,那么&a+1就是跳过了整个数组,指向的是这个数组的末尾的位置
    7. //&a+1的地址类型是数组类型的地址int(*)[5]
    8. //将这个地址强制类型转换为int*类型的
    9. //准换位int*类型的地址再赋值给ptr,那么ptr指向的也是这个数组的末尾位置
    10. printf("%d,%d", *(a + 1), *(ptr - 1));
    11. //这里的a是数组名--数组首元素的地址,那么a+1得到的就是第二个元素的地址,就是2的地址
    12. //解引用得到的就是2
    13. //因为ptr现在是整型指针,那么-1就是往回退,那么指向的就是5的位置
    14. //*(ptr - 1)得到的就是5
    15. return 0;
    16. }
    17. //&a+1就是取出整个数组再跳过这个数组,就是那么这个&a+1指向的就是这个数组的末尾
    18. //再将这个强制类型转换为int *类型的地址,再赋值给ptr
    19. //那么ptr也是指向这个数组的末尾的位置
    20. //因为ptr的类型是int *,-1就是往后退一个整型的距离,那么就是指向的5的位置

    题目二 

    1. //在X86环境下
    2. //假设结构体的⼤⼩是20个字节
    3. //程序输出的结果是啥?
    4. struct Test
    5. {
    6. int Num;
    7. char* pcName;
    8. short sDate;
    9. char cha[2];
    10. short sBa[4];
    11. }*p = (struct Test*)0x100000;
    12. //在这里括号前面的是结构体,加上*就是结构体指针类型,创建变量p
    13. //先将0x100000强制转换为结构体指针,再为b进行赋值,现在p里面放的就是0x100000这个地址
    14. int main()
    15. {
    16. printf("%p\n", p + 0x1);//00100014
    17. //指针+1和类型有关系
    18. //整数+1就是+1
    19. //0x116进制的1
    20. //因为p是一个结构体指针,因为前面说了结构体的大小是20字节
    21. // 那么p+1就是就是跳过跳过一个结构体20个字节
    22. //0x100014
    23. //因为这个是16进制的,所以加20个字节就是在这个基础上+14
    24. //4*160次方+1*161次方就是20
    25. //00100014
    26. printf("%p\n", (unsigned long)p + 0x1);//0x00100001
    27. //现在将p强制类型转换为整型,那么整型+1就是+1
    28. //那么得到的就是0x00100001
    29. printf("%p\n", (unsigned int*)p + 0x1);//00100004
    30. //将p强制类型转换为int *
    31. //+1跳过一个unsigned int*类型的指针变量,是4个字节
    32. return 0;
    33. }

    题目三 

    1. #include <stdio.h>
    2. int main()
    3. {
    4. int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    5. //仔细看,这里用的是小括号不是大括号
    6. //那么这里就成逗号表达式了
    7. //(0, 1)的结果就是1
    8. //(2, 3)的结果就是3
    9. //(4, 5)的结果就是5
    10. //int a[3][2] = { 1,3,5 };
    11. //那么这个二维数组剩下的位置就被0给填充了
    12. //1 3
    13. //5 0
    14. //0 0
    15. int* p;//指针
    16. p = a[0];
    17. //a[0]是第一行的数组名,
    18. // 没有sizeof
    19. // 没有&
    20. //
    21. // 那么表示的就是首元素的地址
    22. //那么p里面存的就是1的地址
    23. printf("%d", p[0]);
    24. //p[0]===*p(0)--指向的还是1的位置
    25. //那么打印出来的就是1
    26. return 0;
    27. }

    题目四 

    1. //假设环境是x86环境,程序输出的结果是啥?
    2. #include <stdio.h>
    3. int main()
    4. {
    5. int a[5][5];//55
    6. //每一行的数组名分别是a[0]、a[1]、a[2]、a[3]、a[4]
    7. int(*p)[4];//p是一个数组指针,p指向的数组有4个元素,每个元素是int
    8. p = a;
    9. //a是数组名,这个二维数组的数组名,就是第一行的地址,第一行整个一维数组的地址
    10. //第一行的地址的类型就是一个数组指针int(*)[5]
    11. //p的类型是int(*)[4] a的类型是int(*)[5]
    12. //不管a原来是什么类型,现在赋值给p,那么现在就是p说的算
    13. //因为a指向的是第一行的位置,那么p同样也指向那里
    14. //对于p来说,一行只有4个元素
    15. //p+1就是跳过一行,每行四个元素
    16. printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    17. //FFFFFFFC,-4
    18. //p[4][2]==*(*(p+4)+2)-----解引用p+4就相当于拿到那一行的数组名
    19. //两个地址相减是地址直接的元素个数,这中间差了4个元素
    20. //小地址减去大地址得到的是-4
    21. //第一个代码是打印地址的,因为相减的结果是-4,存在内存里面,那么直接把存放-4的地址打印出来了
    22. //-4在内存里面存的是补码
    23. //11111111111111111111111111111100
    24. //打印地址直接将内存里面的这个二进制补码直接当成地址
    25. //4个二进制的1就是一个F,最后剩下的1100就是12,转换成6进制就是C
    26. return 0;
    27. }
    28. //考察的是指针的运算,数组指针+1跳过的是多少
    29. //就是跳过一个指针类型,在这里p+1就是跳过一行

     题目五

    1. #include <stdio.h>
    2. int main()
    3. {
    4. int aa[2][5] = { 1, 2, 3, 4, 5,
    5. 6, 7, 8, 9, 10 };//两行5
    6. int* ptr1 = (int*)(&aa + 1);
    7. //&aa就是取出整个二维数组的地址,+1就是跳过整个二维数组
    8. //&aa+1得到的还是二维数组的地址,将这个地址强制类型转换为int *
    9. //因为&aa+1跳过的是整个数组,那么指向的位置就是这个二维数组的末尾
    10. //因为被强制转换为int *后再赋值给ptr1,那么赋值后ptr1指向的位置也是这个二维数组的末尾
    11. //
    12. int* ptr2 = (int*)(*(aa + 1));//aa+1--->aa[1]---第二行的数组名---第二行首元素的地址
    13. //aa是数组名,表示的是首元素的地址,就是第一行的地址
    14. //那么aa+1就是第一行的地址+1指向的就是第二行的地址
    15. //那么对其进行解引用得到的就是第二行整个一维数组
    16. //因为这个第二行的一维数组本身就是整型,那么前面的int *可以忽略
    17. //现在将(int*)(*(aa + 1))赋值给ptr2,那么ptr2也是指向的第二行的数组
    18. //aa + 1可以理解为aa[1],就是第二行的数组名,就是第二行数组首元素的地址,就是指向的6
    19. printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10,5
    20. //在这里,因为ptr1指向的是数组的末尾,并且ptr1是整型指针,那么ptr1-1指向的就是10的位置
    21. //那么对其进行解引用得到的就是10
    22. //因为ptr2指向的是6,那么ptr2-1得到的就是5的地址,对其进行解引用得到的就是5
    23. return 0;
    24. }

    题目六 

    1. #include <stdio.h>
    2. int main()
    3. {
    4. char* a[] = { "work","at","alibaba" };
    5. //正常是不能这么放的,那么就是把每个字符串的首元素地址存在这个指针数组里面了
    6. //这里定义的指针数组里面存的分别是w、a、a的地址
    7. char** pa = a;//a是数组名,表示的是首元素的地址,就是第一个元素的地址
    8. //首元素的地址是char*类型的,那么存在pa里面,那么pa就是一个二级指针
    9. //那么char**pa指向的就是首元素
    10. pa++;
    11. //pa指向的是第一个元素,那么++就是指向的第二个元素,就是跳过提个char*类型的指针
    12. //那么下面的*pa得到的就是第二个元素的地址
    13. printf("%s\n", *pa);//at
    14. //得到了地址,那么就从地址开始打印对饮的字符串
    15. return 0;
    16. }

     题目七--最难的

    1. #include <stdio.h>
    2. int main()
    3. {
    4. char* c[] = { "ENTER","NEW","POINT","FIRST" };//每个元素是char*
    5. //存放的是每个字符串首字符的地址,
    6. //分别是E、N、P、F
    7. char** cp[] = { c + 3,c + 2,c + 1,c };
    8. //c是上面的指针数组的数组名,指向的就是首元素的地址,指向的就是第一个字符串
    9. //c指向的就是"ENTER"
    10. //c+1指向的就是"NEW"
    11. //c+2指向的就是"POINT"
    12. //c+3指向的就是"FIRST"
    13. //将c里面首元素的地址存放在cp里面,那么cp的类型就是一个二级指针
    14. char*** cpp = cp;
    15. //cp是数组名,表示的是首元素的地址,将二级指针的地址放在三级指针内
    16. //cpp指向的是首元素的地址,就是c+3
    17. //下面的++和--会有副作用的,会改变指
    18. //int a=5 ++a 这个动作完了之后a的值就变为6
    19. printf("%s\n", **++cpp);//POINT
    20. //* * ++ cpp
    21. //先算++cpp,cpp本来放的是c+3的地址,那么现在进行++之后,就指向了c+2的地址
    22. // 然后解引用,因为cpp现在指向的是c+2的地址,那么第一次解引用就得到了c+2
    23. // 第二次解引用就是对c+2解引用,得到的就是"POINT"
    24. //
    25. //
    26. //
    27. printf("%s\n", *-- * ++cpp + 3);//ER
    28. //因为加法的优先级比较低,所以先算前面的
    29. // 所以先算++,再解引用,再--,再解引用
    30. //
    31. // 因为上面的cpp已经进行++操作了,指向的是c+2,那么再次++就是指向的c+1
    32. // 那么现在已经得到了c+1的地址了
    33. // 现在进行解引用通过地址找到c+1
    34. //
    35. // 原本放的是c+1,现在--就变成c了,就没有c+2了,--就是自减,c+1变成c了
    36. // c是指向的是"ENTER"这个字符串首元素E的地址,那么解引用完+3指向的就是E
    37. // //那么就是从E开始打印,所以只能打印ER
    38. //
    39. //
    40. //
    41. //
    42. //
    43. //
    44. printf("%s\n", *cpp[-2] + 3);//ST
    45. //*cpp[-2]是**(cpp+(-2))就是**(cpp-2)
    46. //
    47. // 一开始cpp指向的是原先的c+1的地址,但是c+1自减了1,就成c了,那么现在cpp指向的就是c的地址了
    48. // 那么cpp-2得到的就是c+3的地址
    49. // 这里的cpp-2不会导致cpp的改变,还是指向c
    50. //
    51. // 那么cpp-2得到的是c+2的地址,解引用得到的就是c+3
    52. // c+3指向的是这串字符串首元素字符F的地址---"FIRST"
    53. //
    54. // 最后F的地址+3就是S
    55. // 那么打印出来的就是ST
    56. //
    57. // 这里最关键的就是*cpp[-2]这个的理解
    58. //
    59. printf("%s\n", cpp[-1][-1] + 1);// EW
    60. //cpp[-1][-1] + 1
    61. //转换形式
    62. // *(*(cpp-1)-1)+1
    63. // 这里的cpp指向的是c的位置
    64. // 那么cpp-1就指向了c+2的位置
    65. //
    66. // *(cpp-1)拿到了c+2
    67. // 那么就变成下面这样了
    68. // *((c+2)-1)+1
    69. // c+2-1得到了c+1,就是现在的cpp指向的位置
    70. //
    71. // c+1指向的就是"NEW"
    72. //对c+1进行解引用就拿到了"NEW"--N的地址
    73. //然后N的地址+1得到的就是E的地址
    74. //然后打印出来的就是EW
    75. return 0;
    76. }
  • 相关阅读:
    大名鼎鼎的OceanBase居然在买Star了?
    期货开户公司想恶意滑点是做不到的
    智能照明控制系统某大楼大厅照明的应用
    Spring中Bean的作用域和生命周期
    云原生中间件 -- MongoDB Operator 篇
    中国智能家居行业市场全景调研与发展前景预测报告
    OpenHarmony开源软件供应链安全风险
    基于约束的装配设计【CadQuery】
    【数据结构】---详解二叉树--- ⌈知识点总结⌋ 和 ⌈常见力扣题目⌋ 确定不来看吗?
    磁盘占用高问题如何排查?三步教你搞定
  • 原文地址:https://blog.csdn.net/2301_80863610/article/details/139590628