C语言:深入理解指针和数组

目录

一、sizeof大小的判断以及strlen长度的判断

 二、具体题目判断


 

本篇文章会以题目形式,辅助理解指针和数组(内容有点多)。

一、sizeof大小的判断以及strlen长度的判断

知识点:

*数组名是数组首元素的地址,但是有两个例外:
1.sizeof(数组名),这里的数组名是表示整个数组的,计算的是整个数组的大小,单位是字节。
2.&数组名,这里的的数组名也表示整个数组,取出的是数组的地址。
   除上面两种特殊情况外,所有的数组名都是数组首元素的地址。

注意:以下代码均在32位平台下运行(x86),地址大小均为4个字节;若在64位平台下(x64),地址大小都会变成8个字节。

1. 一维数组

	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));        //16
	printf("%d\n", sizeof(a + 0));    //4
	printf("%d\n", sizeof(*a));       //4
	printf("%d\n", sizeof(a + 1));    //4
	printf("%d\n", sizeof(a[1]));     //4
	printf("%d\n", sizeof(&a));       //4
	printf("%d\n", sizeof(*&a));      //16
	printf("%d\n", sizeof(&a + 1));   //4
	printf("%d\n", sizeof(&a[0]));    //4
	printf("%d\n", sizeof(&a[0] + 1));//4 

解析(见图):

一维数组的情况比较简单,只要抓住开头那条规则,基本上不会出错。

2. 字符数组

补充:关于strlen的知识点:

sizeof(操作符/关键字):不关注类型,只关注占用空间的大小。
strlen(库函数):只针对字符串类型,关注的是字符串中\0的位置,计算的是\0之前出现了多少个字符。
注意:strlen需要的是一个地址,从这个地址开始向后找字符。该函数的返回值为size_t,是无符号的。

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

	printf("%d\n", strlen(arr));         //随机值
	printf("%d\n", strlen(arr + 0));     //随机值
	printf("%d\n", strlen(*arr));        //报错
	printf("%d\n", strlen(arr[1]));      //报错
	printf("%d\n", strlen(&arr));        //随机值
	printf("%d\n", strlen(&arr + 1));    //随机值
	printf("%d\n", strlen(&arr[0] + 1)); //随机值

 解析(见图):

 

	char brr[] = "abcdef";
	printf("%d\n", sizeof(brr));         //7
	printf("%d\n", sizeof(brr + 0));     //4
	printf("%d\n", sizeof(*brr));        //1
	printf("%d\n", sizeof(brr[1]));      //1
	printf("%d\n", sizeof(&brr));        //4
	printf("%d\n", sizeof(&brr + 1));    //4
	printf("%d\n", sizeof(&brr[0] + 1)); //4

	printf("%d\n", strlen(brr));         //6
	printf("%d\n", strlen(brr + 0));     //6
	printf("%d\n", strlen(*brr));        //报错
	printf("%d\n", strlen(brr[1]));      //报错
	printf("%d\n", strlen(&brr));        //6
	printf("%d\n", strlen(&brr + 1));    //随机值
	printf("%d\n", strlen(&brr[0] + 1)); //5

 解析(见图):

数组中存字符串相比上面的数组中存字符,在字符串最后会自动有'\0',所以在计算数组长度的时候,能准确计算长度,如果想要在字符数组中不返回随机值,需手动在数组末尾加上'\0'。

	char* p = "abcdef";
	printf("%d\n", sizeof(p));        //4
	printf("%d\n", sizeof(p + 1));    //4
	printf("%d\n", sizeof(*p));       //1
	printf("%d\n", sizeof(p[0]));     //1
	printf("%d\n", sizeof(&p));       //4
	printf("%d\n", sizeof(&p + 1));   //4
	printf("%d\n", sizeof(&p[0] + 1));//4

	printf("%d\n", strlen(p));        //6
	printf("%d\n", strlen(p + 1));    //5
	printf("%d\n", strlen(*p));       //报错
	printf("%d\n", strlen(p[0]));     //报错
	printf("%d\n", strlen(&p));       //随机值
	printf("%d\n", strlen(&p + 1));   //随机值
	printf("%d\n", strlen(&p[0] + 1));//5

解析(见图):

字符数组总的来说不难,细心一点就不会判断错。

3. 二维数组

	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));           //48
	printf("%d\n", sizeof(a[0][0]));     //4
	printf("%d\n", sizeof(a[0]));        //16
	printf("%d\n", sizeof(a[0] + 1));    //4
	printf("%d\n", sizeof(*(a[0] + 1))); //4
	printf("%d\n", sizeof(a + 1));       //4
	printf("%d\n", sizeof(*(a + 1)));    //16
	printf("%d\n", sizeof(&a[0] + 1));   //4
	printf("%d\n", sizeof(*(&a[0] + 1)));//16
	printf("%d\n", sizeof(*a));          //16
	printf("%d\n", sizeof(a[3]));        //16

解析(见图):

 

 二维数组相较于一维数组会难判断一点,但只要记住:二维数组解引用一次就是某一行的地址,再解引用一次才某一行元素

 二、具体题目判断

题目1:

#include <stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));   //2,5
	return 0;
}

 解析(见图):

 题目2:

#include <stdio.h>

struct Test
{
	int Num;
	char* pcName;
	char cha[2];
	short sBa[4];
}*p;

//假设p的值为0x100000
int main()
{
	printf("%p\n", p + 0x1);               //0x100014
	printf("%p\n", (unsigned long)p + 0x1);//0x100001
	printf("%p\n", (unsigned int*)p + 0x1);//0x100004
	return 0;
}

解析(见图):

这道题需要计算结构体大小,后面文章会再讲到计算结构体的大小要用到结构体内存对齐规则,这里是解释指针就不对结构体部分做过多描述。

题目3:

#include <stdio.h>
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);   //4,2000000
	return 0;
}

解析(见图):

题目4:

#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
	return 0;
}

解析(见图):

这里要注意的是,初始化数组中只有3个数,因为是用()括起来的,()里相当于逗号表达式,值为最后一个表达式的值。如果要表示成初始化三行两列的形式,应该把()换成{ }。

题目5:

#include <stdio.h>
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]);   //FFFFFFFC,-4
	return 0;
}

解析(见图):

注意:要知道%p打印的是什么。

题目6:

#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
	return 0;
}

解析(见图):

这道题是二维数组的,可以和前面题目3的一维数组做类比,他们的同样的题型。

题目7:

#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);   //at
	return 0;
}

解析(见图):

题目8:

#include <stdio.h>
int main()
{
	char* c[] = { "ENTER","NEW","POINI","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);          //POINI
	printf("%s\n", *-- * ++cpp + 3);  //ER
	printf("%s\n", *cpp[-2] + 3);     //ST
	printf("%s\n", cpp[-1][-1] + 1);  //EW
	return 0;
}

解析(见图):

这道题算是比较复杂的一种题型,但是画图还是能够很好地理解并做出来的。

以上就是指针和数组的一些题目,理解透这些题目,基本上就能够比较好地运用指针和数组。再次强调:指针和数组对于C语言来说是非常重要的,要很好地理解好并运用出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蔡欣致

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值