指针、数组练习题

指针、数组练习题

我学习过程中总结了9道比较典型的练习题。

#define _CRT_SECURE_NO_DEPRECATE 1
#include "stdio.h"

//练习1

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int*)(&a + 1);
    printf("%d,%d\n", *(a + 1), *(ptr - 1));
    return 0;
}

//程序运行结果是什么?

//分析:&a代表整个数组的地址,其偏移量为真个数组,&a+1就会跳过真个数组,是指向5后面的位置的的地址。
//    然后把这个地址强转为int*,则这个指针的偏移量变成四个字节。
//    *(a+1)中a是数组名,既没放在sizeof内,也没在&后面,那么a就是数组的首元素地址,a+1就是数组a中第
//二个元素地址,对其解引用就是第二个元素2.
//    *(ptr-1)中ptr是指向5后面的元素的指针,ptr-1就是把指针向前偏移4个字节,就会指向5,那么解引用就
//是5.

//运行结果
//2,5
//请按任意键继续. . .


//练习2

int main()
{
    struct Test 
    {
        int Num;
        char* pcName;
        short sDate;
        char cha[2];
        short sBa[4];
    }*p;
    p =0x100000;
    printf("0x%p\n", p + 0x1); 
    printf("0x%p\n", (unsigned long)p + 0x1);
    printf("0x%p\n", (unsigned int*)p+0x1);
    return 0;
}

//假设p的值为0x100000。下面表达式的值分别是多少?
//p+0x1=0x__________?
//(unsigned long)p+0x1=0x____________?
//(unsigned int*)p+0x1=0x____________?

//分析:p+0x1是把指针p向后偏移0x1=1,而p是结构体的指针,他的偏移量是结构体的大小,计算可知是20个字节。
//      那么p的地址增加20,而由于地址用16进制表示,所以p增加0x14,即0x100014.
//      (unsigned long)p + 0x1  把p指针强转成unsigned long类型,p就不再是指针了,变成了变量,所以p+1
//就单纯加了个1,即0x100001.
//       强转成unsigned int*,指针p的偏移量就变成4,p+1则地址加4,即0x100004.

//运行结果
//0x00100014
//0x00100001
//0x00100004
//请按任意键继续. . .


//练习3

int main()
{
    int a[4] = { 1, 2, 3, 4 };
    int *ptr1 = (int*)(&a + 1);
    int *ptr2 = (int *)((int)a + 1);
    printf("%x,%x", ptr1[-1], *ptr2);
    return 0;
}

//分析:*ptr1 = (int*)(&a + 1):&a是数组a的地址,&a+1则表示表示跳过数组a指向数组的后面的元素的地址,对其强转
//   为int*则代表其偏移量有一个数组变成了4个字节。ptr1[-1]:代表对ptr1指针先向左偏移一个偏移量,再解引用。 
//   偏移后指针指向数组a中元素4,解引用就是4。
//      *ptr2 = (int *)((int)a + 1):在这里a代表数组的首元素地址,把其强转为整形,然后加一,就是给1的地址加一
//   个字节,a在内存中的存放为01 00 00 00 02 00 00 00 00 03 00 00 00 04 00 00 00.那现在就指向01后面的00 00 00 
//   02 ,由于是小端存储,所以解引用取出来后就是02 00 00 00,即2000000。

//运行结果:
//4,2000000请按任意键继续. . .



//练习4


int main(int argc,char* argv[])
{
    int a[3][2] = { (0, 1), (1, 2), (4, 5) };
    int *p;
    p = a[0];
    printf("%d", p[0]);
    return 0;
}


//分析:这个题是个坑,得仔细分析。首先数组a是个二维数组,但是明显对其赋值有问题,二维数组赋值是这样的:a[3][2]
//      ={{0,1},{1,2},{4,5}}.所以很明显题目中的赋值不是整个数组,而是只有三个元素,分别是第0行两个和第一行1个,而()
//      则是逗号表达式,简化为a[3][2]={1,2,5}.把a[0]赋给p,就是让指针p指向数组a的第0行,则p实际指向第0行首元素,p[0]
//      表示偏移0再解引用,即第0行的首元素1.

//运行结果:
//1请按任意键继续. . .



//练习5

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;
}


//分析:【】【】【】【】【】 【】【】【】【】【】 【】【】【】【】【】 【】【】【】【】【】 【】【】【】【】【】
// 代表a[5][5].p是指针数组,p=a,代表p数组存储的四个指针指向数组a的0—3行,则数组p的情况可以表示为:
//【】【】【】【】【】 【】【】【】【】【】 【】【】【】【】【】 【】【】【】【】【】
//&p[4][2]-&a[4][2]代表第19个元素的地址减去第23个元素的地址等于-4.
//%d的形式打印则是-4.
//%p则是以地址的形式打印-4,整形-4的二进制:
//10000000 00000000 00000000 00000100   源码
//11111111 11111111 11111111 11111011   反码
//11111111 11111111 11111111 11111100   补码
//-4是个负数,在内存中存放其补码,但以%p打印时,是将其当作地址,是无符号数,即是正数,那他的源码就是补码。
//即
//11111111 11111111 11111111 11111100
//FF       FF       FF       F   C

//运行结果:
//FFFFFFFC,-4
//请按任意键继续. . .



//练习6

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;
}



//分析:*ptr1 = (int *)(&aa + 1):&aa表示数组aa的地址,&aa+1则表示跳过aa这个数组指向数组后面的元素的地址。
//      对其强转为int*,则把其偏移量由一个数组改为一个整形4个字节。*(ptr1 - 1)表示ptr指针向前偏移一个偏移量,
//      然后解引用。则结果就是10.
//      *ptr2 = (int *)(*(aa + 1)):aa+1表示数组aa[5][2]的首行地址加一,即指向aa[1]的地址。 *(aa+1)表示
//      aa[1][0]的地址,对其强转为int*赋给ptr1,表示ptr的偏移量为int,4个字节。*(ptr2 - 1)表示ptr指针向前
//      偏移一个偏移量,然后解引用,即aa[1][0]的前一个元素5.


//运行结果:
//10,5请按任意键继续. . .




//练习7


//解释下面的代码、
(* (void(*) ( )) 0 )( );
//1.把0强转为一个函数指针,该函数指针所指向的函数的返回类型为void,无参数。
//2.对函数指针解引用相当于函数名,所以这是个函数声明。
void(*signal(int, void(*)(int)))(int);
//1.signal是一次函数声明。
//2.signal函数的第一个参数是int,第二个参数是函数指针,该函数指针指向的函数参数为int,返回类型为
//  void。
//3.signal函数的返回类型为一个函数指针,该函数指针指向的函数的参数为int,返回类型为void。



//练习8


int main()
{
    char *a[] = { "work", "at", "alibaba" };
    char **pa = a;
    pa++;
    printf("%s\n", *pa);
    return 0;
}

//分析:
//a是一个指针数组,他的三个元素分别是指向三个字符串的指针,假如为char *p1,p2,p3,
//把a的首地址赋给二级指针pa,则pa指向p1.pa++后,pa指向p2,*pa则取出p2的内容“at”。


//运行结果:
//at
//请按任意键继续. . .



//练习9


int main()
{
    char *c[] = { "ENTER", "NEW", "POINT", "FIRST" };
    char **cp[] = { c + 3, c + 2, c + 1, c };
    char ***cpp = cp;
    printf("%s\n", **++cpp);
    printf("%s\n", *--*++cpp+3);
    printf("%s\n", *cpp[-2]+3);
    printf("%s\n", cpp[-1][-1]+1);
    return 0;
}

//分析:c是指针数组,他的元素是四个指针,假如为p1,p2,p3,p4.分别指向四个字符串,也就是指向首元素。
//      cp是二级指针数组,元素是二级指针,假如为cp1,cp2'cp3,cp4.而cp1->p4,cp2->p3,cp3->p2,cp4->
//      p1.cpp是个三级指针,cpp->cp1.
//      1.**++cpp:++cpp,cpp->cp2.解引用*++cpp,cp2->p3.再解引用**++cpp,p3->"POINT".
//      2.*--*++cpp+3:++cpp,cpp->cp3.解引用*++cpp,cp3->p2.--*++cpp,cp3->p1.再解引用
//      *--*++cpp,p1->"ENTER".*--*++cpp+3,p1+3->"ER".
//      3.*cpp[-2]+3:cpp[-2],cpp先-2再解引用,cpp->cp1,cp1->p4.解引用*cpp[-2],p4->"FIRST".*cpp[-2]+3,
//       p4+3->"ST"但cpp本身没有变cpp->cp3.
//      4.cpp[-1][-1]+1:cpp[-1],cpp先-1,cpp->cp2,再解引用找到cp2->p3.cpp[-1][-1],cp2先-1,cp2->p2
//         再解引用找到p2->"NEW".p2+1->"EW".但cpp和cp2本身没变,cpp->cp3,cp2->p3.

//运行结果:
//POINT
//ER
//ST
//EW
//请按任意键继续. . .

猜你喜欢

转载自blog.csdn.net/yulong__li/article/details/81702508