【C语言】指针进阶笔试练习题

本文深入探讨了C语言中指针与数组的概念及其应用,详细解释了数组名、指针运算符、数组与指针之间的关系等内容,并通过多个实例帮助读者理解指针与数组的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

指针和数组练习题:

一维数组:

字符数组

字符串数组:

二维数组(重点):

2.指针笔试题


数组名的意义:

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

2.&数组名,这里的数组名表示整个数组,取出的的整个数组的地址。(&arr(arr是二维数组)也表示是的整个二维数组的地址)

3.除此之外所有的数组名都表示首元素的地址,在二维数组中数组名表示的是第一行的地址

指针和数组练习题:

一维数组:

int arr[]={1,2,3,4};

    printf("%d\n", sizeof(arr));         //整个数组的地址:int->4——4*4=16

    printf("%d\n", sizeof(arr + 0));  

    printf("%d\n", sizeof(&arr));

    printf("%d\n", sizeof(&arr + 1));

    printf("%d\n", sizeof(*&arr));


 printf("%d\n", sizeof(arr + 0));    //sizeof里只有arr才表示整个数组,arr+0表示一个元素的地址,所以是int*  就是4/8;

 printf("%d\n", sizeof(&arr));      //表示整个数组的地址,也是地址,所以是4/8;

 printf("%d\n", sizeof(&arr + 1));  //&arr,取出的是整个数组的地址,+1表示跳过4个整形  ,相当于arr[4]的地址(int*),所以是4/8。

printf ("\d\n",sizeof(*&arr));     //&arr表示取出整个数组的地址,在解引用,所以还是相当于sizeof(arr),所以是16;


字符数组

char arr[] = { 'a','b','c','d','e','f' };
    printf("%d\n\n", sizeof(arr));
    //6
    printf("%d\n\n", sizeof(arr+0));
    //4/8
    printf("%d\n\n", sizeof(*arr));
    //1
    printf("%d\n\n", sizeof(arr[1]));
    //1
    printf("%d\n\n", sizeof(&arr));
    //4/8
    printf("%d\n\n", sizeof(&arr + 1));
    //4/8
    printf("%d\n\n", sizeof(&arr[0]+1));
    //4/8

错误(重点):  

1.printf("%d\n\n", sizeof(arr));这里计算的是整个数组的大小,所以是1*6=6。

2.printf("%d\n\n", sizeof(arr+0));这里的arr就表示的是首元素的地址了,所以就是4/8;

3. printf("%d\n\n", sizeof(&arr));表示是整个数组的地址,所以地址就是4/8;4


strlen型:

     printf("%d\n\n", strlen(arr));              //19
    //随机值,因为没有'/0'结尾
    printf("%d\n\n", strlen(arr+0));           //19
    //随机值,因为没有'/0'结尾
    printf("%d\n\n", strlen(*arr));
    //报错,因为stlen是以指针传参,他会把a当成地址来进行计算
    printf("%d\n\n", strlen(arr[1]));
    //报错,因为stlen是以指针传参,他会把a当成地址来进行计算
    printf("%d\n\n", strlen(&arr));            //19
    //整个数组的地址,是一个数组指针,会出现警告,但也可以运行,这里的这个数组指针指向的地址也是首元素的地址,所以是随机值
    printf("%d\n\n", strlen(&arr+1));      //19-6,
    //随机值-6;表示跳过了这一个整个数组
    printf("%d\n\n", strlen(&arr[0]+1));  //19-1 
    //随机值-1;从第二个数的地址开始计算;


字符串数组:

char arr[] = "abcdef";


    printf("%d\n\n\n", sizeof(arr));        
    //7                         6+1,还有一个'\0'
    printf("%d\n\n\n", sizeof(arr+0));
    //4/8,                 这是一个地址
    printf("%d\n\n\n", sizeof(*arr));
    //1 *arr表示字符a
    printf("%d\n\n\n", sizeof(arr[1]));
    //1 *arr表示字符b
    printf("%d\n\n\n", sizeof(&arr));
    //4/8  &arr表示整个数组的地址
    printf("%d\n\n\n", sizeof(&arr+1));
    // 4/8 表示一个地址
    printf("%d\n\n\n", sizeof(&arr[0]+1));
    //4/8 表示的是字符b的地址


    printf("%d\n\n\n", strlen(arr));
    //6
    //字符串长度
    printf("%d\n\n\n", strlen(arr+0));
    //6
    //字符串长度
    printf("%d\n\n\n", strlen(*arr));
    //错误
    //strlen把a当成地址来计算

    printf("%d\n\n\n", strlen(arr[1]));
    //错误
    //strlen把a当成地址来计算

    printf("%d\n\n\n", strlen(&arr));
    //6
    //&arr表示的是数组指针,会报警告,因为地址类型不同,但地址指向相同,所以也是6
    printf("%d\n\n\n", strlen(&arr+1));
    //随机值
    //&arr+1跳过了这个数组。&arr表示的是这整个数组的地址
    printf("%d\n\n\n", strlen(&arr[0]+1));
    //5
    //arr[0]表示a,&arr[0]表示a的地址,a的地址+1;

二维数组(重点):

    int a[3][4]={0};


    printf("%d\n\n", sizeof(a));
    //虽然a是二维数组,但是在sizeof中,这里a就是整个数组,所以是3*4*4=48;

     
    printf("%d\n\n", sizeof(a[0][0]));
    //计算的是首元素的大小      4;

**************************重点!!!!()**********************
    printf("%d\n\n", sizeof(a[0]));
    //数组名单独放在sizeof内部计算的是整个数组,所以在二维数组中,arr[0]表示第一行的数组名,所以sizeof(a[0])这表示计算这一行的大小,所以是4*4=16。


    printf("%d\n\n,", sizeof(a[0] + 1));
    //sizeof中a[0]没有单独放到sizeof内部,所以a[0]这行首元素的地址,即第一行第一个元素的地址,所以a[0]+1就是首元素+1,所以a[0]+1表示第一行第二个元素的地址,所以是4/8。


    printf("%d\n\n", sizeof(*(a[0] + 1)));
    //a[0]+1,表示第一行第二个元素的地址,所以再解引用,就是第一行第二个元素,所以是4。


    printf("%d\n\n", sizeof(a + 1));
    //a表示第一行的地址,+1就是下一行,所以sizeof测的是第二行的地址,所以是4/8;


    printf("%d\n\n", sizeof(*(a + 1)));
    //对第二行的地址解引用,就表示的是整个第二行!!!!所以是4*4=16


    printf("%d\n\n", sizeof(&a[0] + 1));
    //a[0]表示第一行首元素的地址,再&表示这一整行的地址,+1表示第二整行的地址,所以是4/8;


    printf("%d\n\n", sizeof(*(&a[0] + 1)));
    //对第二整行解引用,所以是整个第二行的地址,所以是4*4=16


    printf("%d\n\n", sizeof(*a));
    //a表示整个第一行的地址,解引用就拿到了这整个数组,所以是4*4=16


    printf("%d\n\n", sizeof(a[3]));
    //a[3]单独放在sizeof中,计算整个数组的大小,所以是4*4=16;sizeof根本不会去访问          他,只是去会计算。

2.指针笔试题

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

这里&a拿出整个数组,是一个数组指针,+1表示跳过这一整个数组,再将int(*)[5]型的指针强行转化为整形指针类型赋给ptr,最后输出的结果为2,5。a表示首元素的地址,+1指向第二个元素;ptr不是数组指针,所以-1是指向5;



struct Test
{
	int num;
	char* pcname;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//这个结构体大小是20个字节。
//p的初始值为0x000000;
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

下面中注释我假设是100000,其实实际上是0x000000,但影响不大。

 16行:结构体指针+1,跳过一个此般大小的结构体,这个结构体大小是20字节,所以+1是跳过20个字节,因为%p是以地址的形式打印,所以是以16进制显示,那20就是14(16进制)

 17行:将结构体指针转化为无符号整形,所以无符号整形+1,就是+1,即输出100000+1.

18行:这里将p转化为无符号整形指针,整形指针+1跳过4个字节,所以这里+1,实际上是+4。


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

ptr1应该没什么问题,我们来主要看看ptr2,我们将a数组首元素的地址强制类型转换为整形,我们进行+1,就是整形+1,我们再转换为int*型给ptr,所以ptr指向的是a首元素地址加上相当于一个char指针大小的地址,就看作是ptr2是a首元素向后指向了一个字节的大小,上图绿色的部分就是ptr2指向的内容再内存中的存放形式,我们再解引用,就打印出结果了。


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

 

 这是一个逗号表达式,不要被蒙骗了。


%p打印:

%p是打印地址的形式,他将地址以16进制的形式打印出来并在前面补上0,给一个地址就打打印出来,%p只是打印的一种方式,他不会主动去内存中找一个数的地址。

%x也是也是打印地址,但是他不会补齐前面多余的零。%#x可以在打印的时候加上0x;

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[4][2]和p[4][2]不相同,我们可以模拟数组的存放样式来分析他们的对应关系。指针减指针求的是元素的个数,所以&p[4][2] - &a[4][2]会得出4,又因为数组中地址随下标的增长而增长,即p[4][2]的地址低于a[4][2],所以&p[4][2] - &a[4][2]会最终得出结果为-4,内存中-4会以补码的形式存放在内存中存放,所以%p形式打印会以为内存中存的是地址,会直接打印补码的内容。

 而%d形式打印-4,则会将补码转化为原码进行打印,所以会正常打印出-4;


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

这里&arr,表示取出整个aa数组的地址,+1即跳过整个数组。

这里aa表示第一行地址,+1指向第二行,再解引用表示整个第二行,我们再强行转换为int*型赋给ptr2,所以ptr2指向aa第二行的首元素。


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

a里面存放的是3串字符串的首元素的地址,我们再用ppa这个二级指针指向a数组的首元素,所以我们将ppa+1后ppa指向a数组的第二个元素,我们再将ppa解引用后即找到了a数组第二个元素的地址,再用%s打印,即可打印at这个单词。


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

这道题只要把图画好,每条语句从变量名开始下手,基本就可以把这题写出来,我们先来了解一些优先级顺序,++、--的优先级大于*号,【】的优先级大于*号和++ 。

这些笔试题就分享到这了,希望能加深大家对指针的理解,指针的篇章也就到这就结束了,接下来准备开始更新字符串函数和结构体的相关内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Brant_zero2022

素材免费分享不求打赏,只求关注

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值