关于指针,你不可以错过的练习(c/c++)

文章详细讲解了C语言中数组名在sizeof运算符中的行为,以及指针如何影响数组元素的地址和大小计算。通过实例演示了一维、字符数组、二维数组的sizeof和指针操作,强调了数组名与首元素地址的区别以及指针级别的计算
摘要由CSDN通过智能技术生成

前言:

除了sizeof()单独放数组名和&数组名,其他的数组名都仅仅代表首元素地址,

接下来我们就靠一些练习来巩固一下对指针的学习吧.

1.练习1 :一维数组

int main()
{
	//一维数组
	int a[] = { 1,2,3,4 };//4个元素,每个元素是int类型
	printf("%d\n", sizeof(a));    
    //sizeof里面直接放数组名,计算的是整个数组的大小,所以是16
	printf("%d\n", sizeof(a + 0));//这里数组名表示的是首元素地址,地址都是4或者8个字节
	printf("%d\n", sizeof(*a));  //4个字节,相当于a[0]
	printf("%d\n", sizeof(a + 1));//表示第二个元素的地址,所以是4个或者8个字节
	printf("%d\n", sizeof(a[1]));//第二个元素的大小,4个字节
	printf("%d\n", sizeof(&a));//取到的是整个数组的地址,由于是地址,所以是4/8个字节
	printf("%d\n", sizeof(*&a)); //16 对数组指针解引用访问一个数组的大小,单位是字节,等价于                   
    sizeof(a)

	printf("%d\n", sizeof(&a + 1));//由于&a是一个指针数组类型,+1跳过一个数组,指向最后数组的后一个元素的地址,所以是4/8
	printf("%d\n", sizeof(&a[0]));//取出的是第一个元素的地址,地址的大小是4/8
	printf("%d\n", sizeof(&a[0] + 1));//取出的是第二个元素的地址,地址的大小是4/8
    return 0;
}

2.练习2:字符数组

	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));  //数组的大小,6个字节
	printf("%d\n", sizeof(arr + 0));//首元素地址+1,是地址就是4/8个字节
	printf("%d\n", sizeof(*arr));//首元素的大小,类型是char,所以是1个字节
	printf("%d\n", sizeof(arr[1]));//第二个元素的大小,一个字节
	printf("%d\n", sizeof(&arr));//整个数组的地址,4/8字节
	printf("%d\n", sizeof(&arr + 1));//跳过整个数组后面的地址,地址就是4/8字节
	printf("%d\n", sizeof(&arr[0] + 1));//跳过第一个元素的地址,地址就是4/8字节

	//strlen是求字符串长度的,统计的是\0之前所有的元素个数
	printf("%d\n", strlen(arr));    //arr是首元素地址,因为字符数组中没有手动添加\0,所以我们不知道后面到什么地方才会有\0出现,所以大小是随机值 大于等于6
	printf("%d\n", strlen(arr + 0)); //随机值,同上
	printf("%d\n", strlen(*arr));  //  *arr是首元素,等价于字符a->97,strlen就把97当做一个地址
	//97作为地址直接进行访问就是非法访问
	printf("%d\n", strlen(arr[1])); //字符b,同样是非法访问
	printf("%d\n", strlen(&arr));   //把数组指针传给了char*,类型发生变化但是值没变,随机值
	printf("%d\n", strlen(&arr + 1)); //随机值
	printf("%d\n", strlen(&arr[0] + 1));//随机值

3.练习3:字符数组2

char arr[] = "abcdef";
	printf("%d\n", sizeof(arr)); //加上\0,一共7个字节
	printf("%d\n", sizeof(arr + 0));//地址的大小就是4/8字节
	printf("%d\n", sizeof(*arr));//a的大小是1个字节
	printf("%d\n", sizeof(arr[1]));//b大小也是一个字节
	printf("%d\n", sizeof(&arr));//取出整个数组的地址,地址就是4/8字节
	printf("%d\n", sizeof(&arr + 1));//跳过数组,也是4/8字节
	printf("%d\n", sizeof(&arr[0] + 1));//地址,4/8字节

	printf("%d\n", strlen(arr)); //6字节
	printf("%d\n", strlen(arr + 0));//同上
	printf("%d\n", strlen(*arr)); //传进去的是97会报错
	printf("%d\n", strlen(arr[1]));//同上
	printf("%d\n", strlen(&arr));//6字节
	printf("%d\n", strlen(&arr + 1));//跳过了整个数组,所以是随机值
	printf("%d\n", strlen(&arr[0] + 1));//从第二个元素开始,所以是5字节

4.练习4:字符数组3

	char* p = "abcdef"; 
	//把字符串的首地址存在p里面
	printf("%d\n", sizeof(p));  //首元素地址,所以是4/8字节
	printf("%d\n", sizeof(p + 1)); //4/8   本质上就是地址+1,仍然是地址
	printf("%d\n", sizeof(*p));   //1 *p == 'a'
	printf("%d\n", sizeof(p[0])); //同上 
	printf("%d\n", sizeof(&p));   //4/8   p变量的起始地址 类型是char**二级指针类型
	printf("%d\n", sizeof(&p + 1));  //跳过一个char*,还是地址,4/8
	printf("%d\n", sizeof(&p[0] + 1));  //地址,4/8
	printf("%d\n", strlen(p));     //6字节
	printf("%d\n", strlen(p + 1));  //跳过首元素,所以是五个字节
	printf("%d\n", strlen(*p));    //err
	printf("%d\n", strlen(p[0]));  //err
	printf("%d\n", strlen(&p));    //char**,这个地址有没有\0不可知,所以是随机值
	printf("%d\n", strlen(&p + 1)); //随机值
	printf("%d\n", strlen(&p[0]+1)); //5

5.练习5:二维数组

	int a[3][4] = { 0 };
	printf("%zd\n", sizeof(a));      //48 数组名单独放在sizeof内部,表示整个数组,所以计算的是整 
    个数组的大小,单位是字节   48
	printf("%zd\n", sizeof(a[0][0]));//4
	printf("%zd\n", sizeof(a[0]));   //16 二维数组是数组的数组,a[0]单独放在sizeof内部,就计算的 
    是整个第一行的大小
	printf("%zd\n", sizeof(a[0] + 1)); //4/8  a[0]并非单独放在sizeof内部,所以a[0]表示数组首元 
    素的地址, 也就是第一行第一个元素的地址,a[0] <-->&a[0][0],所以 a[0]+1表示&a[0][1]

	printf("%zd\n", sizeof(*(a[0] + 1)));//4  对第一行第二个元素进行解引用
	printf("%zd\n", sizeof(a + 1));     //4/8 第二行地址
	printf("%zd\n", sizeof(*(a + 1)));  //16 第二行的大小
	printf("%zd\n", sizeof(&a[0] + 1)); //4/8 第二行的地址
	printf("%zd\n", sizeof(*(&a[0] + 1)));//16 第二行的大小
	printf("%zd\n", sizeof(*a));  //16 第一行大小
	printf("%zd\n", sizeof(a[3]));  //16 不会越界,因为编译器会根据变量的类型直接进行判断,并不会 
    真的去访问
	//表达式有两个属性,一个是值属性,一个是类型属性

6.小结

    总结:
        数组名的意义:
        1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
        2. & 数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

        其他数组名都表示首元素的地址.

7.几个关于指针的小题目 

7.1 难度: *

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}
//a这里表示首元素的地址,+就是第二个元素的地址,解引用得到2
//ptr指向数组的最后一个元素的后面的一个int的地址,-1则指向最后一个元素,解引用答案是5
//程序的结果是什么?
//2  5

7.2 难度: **

//注:默认是x86的环境
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{

	//考点:指针+1加几个字节? 取决于指针的类型
    //注:十六进制
	printf("%p\n", p + 0x1);                  //0x00100014
	printf("%p\n", (unsigned long)p + 0x1);   //0x00100001
	printf("%p\n", (unsigned int*)p + 0x1);   //0x00100004
	return 0;
}

7.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);
    //默认小端字节序
	//内存 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
	        //ptr2指向01后面的00  Ox 02 00 00 00   还原出来就是2000000
	//答案 4,2000000
	return 0;
}

7.4 难度: * 考点:逗号表达式

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

7.5 难度: ***

int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
    //类型不合适
	//a - int(*)[5]
	//p - int(*)[4]
	//画图
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    //%p打印就是把-4转成补码形式再转16进制
    //指针相减是表示两个指针之间的元素个数

	//10000000000000000000000000000100
	//11111111111111111111111111111011
	//11111111111111111111111111111100 -->地址FFFFFFFC
	//答案是FFFFFFFC,-4
	return 0;
}

7.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));
	//10   5
	return 0;
}

7.6 难度 ** 

int main()
{
	char* a[] = { "work","at","alibaba" };
	//a的每个元素是char*的,存的是首字符地址  
	//答案 at
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

7.7 难度 : ****

int main()
{
	//++的优先级高于*号
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);   //p的地址 POINT
	printf("%s\n", *-- * ++cpp + 3); //得到的是e的地址再+3打印出来就是ER
	printf("%s\n", *cpp[-2] + 3);  //ST
	printf("%s\n", cpp[-1][-1] + 1);//EW
	return 0;
}

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值