C语言之指针总结(2)

目录

前言

一、一维整型数组相关题

二、字符数组相关题

 三、字符串数组相关题

四、二维数组相关题

五、一些指针的笔试题


前言

本文是对指针和数组的一些题的归纳。


一、一维整型数组相关题

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4};

    printf("%d\n",sizeof(a));//16   sizeof(a)算的是数组的大小
    printf("%d\n",sizeof(a+0));//4/8   sizeof(a+0)不是sizeof(a),所以此处不属于那两种特殊情况,此处的a是数组首元素的地址,地址的大小在32位(大小为4)或64位(大小为8)平台不同
    printf("%d\n",sizeof(*a));//4   *a找到的是首元素,该数组的每个元素的类型是int,所以大小为4
    printf("%d\n",sizeof(a+1));//4/8   同理sizeof(a+1)中的a是数组首元素的地址,+1后是第一个元素的地址,地址的大小在没有特定声明的情况下是4/8
    printf("%d\n",sizeof(a[1]));//4   a[1]是数组的第一个元素,元素的大小是4
    printf("%d\n",sizeof(&a));//4/8    &a是一个数组指针(类型为int(*)[4])
    printf("%d\n",sizeof(*&a));//16    &a,取出了数组的地址,*&a访问了数组,此处计算的是数组的大小
    printf("%d\n",sizeof(&a+1));//4/8  &a是一个数组指针,+1,跳过整个数组,但还是一个数组指针
    printf("%d\n",sizeof(&a[0]));//4/8   &a[0]取出了数组首元素的地址
    printf("%d\n",sizeof(&a[0]+1));//4/8   &a[0]+1数组第一个元素的地址

    return 0;
}

二、字符数组相关题

#include <stdio.h>
#include <string.h>
int main()
{
    char arr[] = {'a','b','c','d','e','f'};

    printf("%d\n", sizeof(arr));//6    sizeof(arr)算的是数组的大小
    printf("%d\n", sizeof(arr+0));//4/8    sizeof(arr+0)不是sizeof(a),所以此处不属于那两种特殊情况,此处的arr是数组首元素的地址,地址的大小在32位(大小为4)或64位(大小为8)平台不同
    printf("%d\n", sizeof(*arr));//1   *arr找到的是首元素,该数组的每个元素的类型是char,所以大小为1
    printf("%d\n", sizeof(arr[1]));//1  arr[1]是第一个字符'b'
    printf("%d\n", sizeof(&arr));//4/8   &arr是一个数组指针(类型为char(*)[6]),大小为4/8
    printf("%d\n", sizeof(&arr+1));//4/8   &arr是一个数组指针,+1跳过一整个数组,但还是一个数组指针
    printf("%d\n", sizeof(&arr[0]+1));//4/8  &arr[0]+1是数组第一个元素的地址

    printf("%d\n", strlen(arr));//随机值   arr是数组首元素的地址,strlen()会从第一个元素的地址处开始计数,知道遇到'\0'停止计数,但是字符数组中没有'\0',所以会一直计数下去,我们也不知道在何处会遇到'\0'。
    printf("%d\n", strlen(arr+0));//随机值  同上式理
    printf("%d\n", strlen(*arr));//error   *arr访问的是数组的第一个元素'a',它的ASCII码值为97,strlen()以为97就是地址,但是它是一个非法地址
    printf("%d\n", strlen(arr[1]));//error  同上式理
    printf("%d\n", strlen(&arr));//随机值   &arr是一个数组指针,类型为char(*)[6],它会强制类型转换成(char*),同理,我们也不知道什么时候会遇到'\0',所以为随机值
    printf("%d\n", strlen(&arr+1));//随机值,与上式相差6   &arr+1跳过一整个字符数组,少算了6个元素的个数
    printf("%d\n", strlen(&arr[0]+1));//随机值,与上上式相差1   &arr[0]+1跳过一个元素的地址,与上上式相比少算了1个元素

    return 0;
}

 三、字符串数组相关题

#include <stdio.h>
#include <string.h>
int main()
{
    char arr[] = "abcdef";
   
    printf("%d\n", sizeof(arr));//7  字符串后面还有一个'\0'
    printf("%d\n", sizeof(arr+0));//4/8  sizeof(arr+0)不是sizeof(a),所以此处不属于那两种特殊情况,此处的arr是数组首元素的地址,地址的大小在32位(大小为4)或64位(大小为8)平台不同
    printf("%d\n", sizeof(*arr));//1  *arr访问的是数组首元素,大小为1
    printf("%d\n", sizeof(arr[1]));//1  arr[1]代表数组的第一个元素,大小为1
    printf("%d\n", sizeof(&arr));//4/8   &arr是一个数组指针
    printf("%d\n", sizeof(&arr+1));//4/8   &arr+1跳过了一个数组,但还是一个数组指针
    printf("%d\n", sizeof(&arr[0]+1));//4/8  &arr[0]+1第1个元素的地址

    printf("%d\n", strlen(arr));//6  arr是数组首元素的地址,strlen()不计算'\0'的长度
    printf("%d\n", strlen(arr+0));//6   arr+0还是数组首元素的地址,strlen()不计算'\0'的长度
    printf("%d\n", strlen(*arr));//error
    printf("%d\n", strlen(arr[1]));//error
    printf("%d\n", strlen(&arr));//6   
    printf("%d\n", strlen(&arr+1));//随机值   &arr+1跳过了一个数组,连'\0'也跳过去了
    printf("%d\n", strlen(&arr[0]+1));//5   从第一个元素的地址开始计算,直到遇到'\0'。

    return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
    char *p = "abcdef";

    printf("%d\n", sizeof(p));//4/8  p是'a'的地址
    printf("%d\n", sizeof(p+1));//4/8   p+1是'b'的地址
    printf("%d\n", sizeof(*p));//1    *p访问的是'a'
    printf("%d\n", sizeof(p[0]));//1     p[0]代表'a'
    printf("%d\n", sizeof(&p));//4/8    取出了p的地址
    printf("%d\n", sizeof(&p+1));//4/8
    printf("%d\n", sizeof(&p[0]+1));//4/8   找到'b'的地址

    printf("%d\n", strlen(p));//6   p是'a'的地址,从该地址开始计算数组长度
    printf("%d\n", strlen(p+1));//5   p+1是'b'的地址,从该地址开始计算数组长度
    printf("%d\n", strlen(*p));//error
    printf("%d\n", strlen(p[0]));//error
    printf("%d\n", strlen(&p));//随机值  
    printf("%d\n", strlen(&p+1));//随机值
    printf("%d\n", strlen(&p[0]+1));//5   &p[0]+1是'b'的地址,从该地址开始计算数组长度

    return 0;
}

 

 注意:这两个随机值之间没有大小关系,不一定相差4,因为'\0'的位置未知

四、二维数组相关题

#include <stdio.h>
int main()
{
    int a[3][4] = {0};

    printf("%d\n",sizeof(a));//48   计算的是整个数组的大小:3*4*4
    printf("%d\n",sizeof(a[0][0]));//4  第0行,第0列的元素,大小为4
    printf("%d\n",sizeof(a[0]));//16  a[0]是第0行的数组名,sizeof(数组名)算的是数组的大小,所以第0行有4个元素,每个元素的大小是4个字节,因此4*4
    printf("%d\n",sizeof(a[0]+1));//4/8   a[0]+1第一行第二个元素的地址
    printf("%d\n",sizeof(*(a[0]+1)));//4   访问第一行第二个元素的值
    printf("%d\n",sizeof(a+1));//4/8     a+1是第一行的地址
    printf("%d\n",sizeof(*(a+1)));//16    a+1是第一行的地址,*(a+1)访问的是第一行的元素
    printf("%d\n",sizeof(&a[0]+1));//4/8  &a[0] + 1就是第一行的地址,第一行的地址的类型为int(*)[4]
    printf("%d\n",sizeof(*(&a[0]+1)));//16   &a[0]+1是第一行的地址,*(&a[0]+1))访问的是第一行的元素
    printf("%d\n",sizeof(*a));//16   a是第0行的地址,*a访问的是第0行的元素
    printf("%d\n",sizeof(a[3]));//16   第三行的数组名,尽管数组越界了,但是sizeof括号里面的表达式是不参与运算的,所以此处的16是根据a[0]或a[1]或a[2]推演出来的

    return 0 ;
}

总结: 

1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。

五、一些指针的笔试题

1、

#include <stdio.h>
int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);//&a+1的类型是int(*)[5],强制转换成int*

    printf( "%d %d", *(a + 1), *(ptr - 1));//2   5
    //解析:
    //①a是数组首元素的地址,a+1是数组第一个元素的地址,*(a+1)访问的是数组的第一个元素2
    //②&a + 1跳过了一个数组的大小,*(ptr - 1)等价于ptr[-1],意思是向前偏移一个单位,访问到的元素是5

    return 0; 
}

2.

#include <stdio.h>
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;

    p = a[0];
    printf( "%d", p[0]);//1
    //解析:
    //逗号表达式,所以放进去数组的只有1  3  5

    return 0; 
}

3.

#include <stdio.h>
int main()
{
    char* a[] = {"work","at","alibaba"};
    char**pa = a;

    pa++;
    printf("%s\n", *pa);//at

    return 0; 
}

解析如下:

4.

int main()
{
   char* c[] = {"ENTER","NEW","POINT","FIRST"};
   char** cp[] = {c + 3, c + 2, c + 1, c};
   char*** cpp = cp;

   printf("%s\n", **++cpp);//POINT
   printf("%s\n", *--*++cpp + 3);//ER
   printf("%s\n", *cpp[-2] + 3);//ST
   printf("%s\n", cpp[-1][-1] + 1);//EW

   return 0; 
}

 解析如下:

 它们之间的关系如左图所示:

(1)**++cpp:++cpp后,cpp便指向了c+2处,*++cpp相当于拿到的是c+2的内容,c+2指向的是③,相当于拿到了③的地址,再解引用,相当于拿到③的内容,③指向"POINT"的"P",所以相当于拿到的是"P"的地址,所以最终可以打印出POINT。

(2)*--*++cpp + 3:++cpp后,cpp便指向c+1处,*++cpp相当于拿到的是c+1的内容,c+1指向的是②,相当于拿到了②的地址,--*++cpp,注意:此时c+1变成c,并且它不再指向②,它指向①,如右图所示。现在我们相当于拿到了①的地址,再解引用,我们找到了①的内容,①指向"NNTER"的"E",所以相当于拿到的是"E"的地址,*--*++cpp + 3,此时我们拿到的是"E"的地址,所以最终可以打印出ER

(3)*cpp[-2] + 3:cpp[-2]相当于*(cpp-2)的意思是想前偏移2个单位,此时cpp指向的是c+3处(注意:此处没有副作用的产生),访问的是c+3处的内容,c+3指向的是④,相当于拿到了④的地址,解引用,找到的是④的内容,而④指向的是"FIRST"的"F",所以相当于拿到"F"的地址,*cpp[-2] + 3,此时我们拿到的是"S"的地址,所以最终可以打印出ST。

(4)cpp[-1][-1] + 1:注意:由于(3)中没有产生副作用,所以此时cpp指向c处,如右图所示。cpp[-1][-1]相当于*(*(cpp-1)-1),cpp-1,所以此时cpp指向的是c+2处,*(cpp-1)相当于拿到的是c+2的内容,c+2指向的是③,相当于拿到了③的地址,*(cpp-1)-1,此时c+2指向的是②,相当于拿到了②的地址,再解引用,相当于拿到②的内容,而②指向的是"NEW"的"N",所以相当于拿到"N"的地址,cpp[-1][-1] + 1,此时我们拿到的是"E"的地址,所以最终可以打印出EW。

5.

#include <stdio.h>
struct Test
{
 int Num;
 char* pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p;
//该结构体大小为20byte
int main()
{
   p = (struct Test*)0x100000;

   printf("%p\n", p + 0x1);//00100014
   //0x1就是16进制的1,此处跳过一个结构体 0x100000+20=0x100014
   printf("%p\n", (unsigned long)p + 0x1);//00100001
   //p不再是结构体,而是一个整数,整数+1就是+1,所以00100001
   printf("%p\n", (unsigned int*)p + 0x1);//00100004
   //p强制类型转换为一个整型指针,+1,跳过4byte。所以00100004

   return 0; 
}

6.

#include <stdio.h>
int main()
{
    int a[4] = { 1, 2, 3, 4 };
    int* ptr1 = (int *)(&a + 1);
    int* ptr2 = (int *)((int)a + 1);
    //举个例子:0x0012ff40转换为10进制整数:1244992
    //1244992+1=1244993  1244993转换为16进制为0x0012ff41
    //(int *)((int)a + 1);可见此处地址加1

    printf( "%x %x", ptr1[-1], *ptr2);//4  2000000
                    
    return 0; 
}

解析如下: 

 

7.

#include <stdio.h>
int main()
{
    int a[5][5];//类型:int(*)[5]
    int(*p)[4];//类型:int(*)[4]

    p = a;
    printf( "%p %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//FFFFFFFC -4
    //指针与指针相减得到的是它们之间的元素个数
    //%p--打印地址(16进制),地址没有原反补码的概念,把-4的补码当成原码打印出来
    //-4的原码:10000000000000000000000000000100
    //    反码:11111111111111111111111111111011
    //    补码:1111 1111 1111 1111 1111 1111 1111 1100
    //          F    F    F    F     F    F    F    C  

    return 0; 
}

题的图如下:

8.

#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));//10 5
    //解析:
    //10  如图
    //5   aa是数组首元素的地址即a[0],aa+1即数组第一行的地址,解引用,得到的是数组第一行第0个元素:6,ptr2指向6,ptr2-1,指向的是前一个元素的地址,即5的地址,所以*(ptr2 - 1)访问的是5

    return 0; 
}

如图:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值