C语言指针经典例题讲解

本文详细介绍了C语言中sizeof()和strlen()函数在处理一维数组、字符数组以及二维数组时的行为差异,特别关注了数组名在不同上下文下的含义以及指针的运算。
摘要由CSDN通过智能技术生成

C语言指针经典例题讲解

  1. sizeof( )操作符与strlen( )函数的输入输出问题
  • 一维数组
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zd ", sizeof(a));// 16-数组的总大小
	printf("%zd ", sizeof(a + 0));// 4/8-第一个元素的指针
	printf("%zd ", sizeof(*a));// 4-第一个元素元素
	printf("%zd ", sizeof(a + 1));// 4/8-第二个元素的指针
	printf("%zd ", sizeof(a[1]));// 4-第一个元素元素
	printf("%zd ", sizeof(&a));// 4/8-数组的总地址
	printf("%zd ", sizeof(*&a));// 16-数组的总大小
	//&a为数组的总地址,解引用后为整个数组,等价于sizeof(a)的数值
	printf("%zd ", sizeof(&a + 1));// 4/8-数组最后一个元素之后那个元素的地址
	printf("%zd ", sizeof(&a[0]));// 4/8-第一个元素的地址
	printf("%zd ", sizeof(&a[0] + 1));// 4/8-第二个元素的地址
	return 0}
  • 输出结果(在x64环境下):

需要注意的是,在x64环境下,指针变量的大小为8B,在x86的环境下,指针大小为4B。

总结:在sizeof( )操作符中,只有数组变量单独在括号中的时候才表示整个数组,其他情况均表示第数组中第一个元素的地址。

  • 字符数组

代码一:

  • sizeof( )操作符:
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zd\n", sizeof(arr));// 6-字符数组的大小
	printf("%zd\n", sizeof(arr + 0));// 4/8-元素a的地址
	printf("%zd\n", sizeof(*arr));// 1-第一个元素a的大小
	printf("%zd\n", sizeof(arr[1]));// 1-第一个元素a的大小
	printf("%zd\n", sizeof(&arr));// 4/8-整个数组的地址
	printf("%zd\n", sizeof(&arr + 1));// 4/8-数组最后一个元素之后那个元素的地址
	printf("%zd\n", sizeof(&arr[0] + 1));// 4/8-第二个元素b的地址
	return 0;
}

输出结果(在x64环境下):

  • strlen( )函数:
int main()
{
	//由于该字符数组后面没有\0,strlen( )函数会一直向后寻找
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zd ", strlen(arr));//随机值x
	printf("%zd ", strlen(arr + 0));//随机值x
	printf("%zd ", strlen(&arr));//随机值x
	printf("%zd ", strlen(&arr + 1));//随机值x-6———(+1跳过了整个数组的6个元素)
	printf("%zd ", strlen(&arr[0] + 1));//随机值x-1
	//注:以下为两个有问题的输入方式
	//printf("%zd ", strlen(*arr));//传入的是字符a的ASCII码值,该值作为地址过小,会导致程序崩溃
	//printf("%zd ", strlen(arr[1]));//同理,传入的是b的ASCII码值	
    return 0;
}

代码二:

  • sizeof( )操作符
int main()
{
	//使用字符串初始化字符数组,结尾存在\0,该字符串长度为7
	char arr[] = "abcdef";
	printf("%zd ", sizeof(arr));// 7-数组大小
	printf("%zd ", sizeof(arr + 0));// 4/8-表示首元素a的地址
	printf("%zd ", sizeof(*arr));// 1-表示元素a的大小
	printf("%zd ", sizeof(arr[1]));// 1-表示元素b的大小
	printf("%zd ", sizeof(&arr));// 4/8-表示整个数组的地址
	printf("%zd ", sizeof(&arr + 1));// 4/8-表示该数组最后一个元素之后,那个元素的地址
	printf("%zd ", sizeof(&arr[0] + 1));// 4/8-表示元素b的地址
	return 0;
}

输出结果(在x86环境下):

  • strlen( )函数
int main()
{
	char arr[] = "abcdef";
	printf("%zd ", strlen(arr));// 6-该字符串的长度
	printf("%zd ", strlen(arr + 0));// 6-从元素a的地址到\0结束
	printf("%zd ", strlen(&arr));// 6-该字符串的长度
	printf("%zd ", strlen(&arr + 1));// 随机值x,因为是从数组结束之后的那个元素算起的
	printf("%zd ", strlen(&arr[0] + 1));// 5-通过+1跳过了字符a,从字符b开始算起
	//下面两个输入函数结果为a或b的ASCII码,值过小,会导致程序崩溃
	//printf("%zd ", strlen(*arr));
	//printf("%zd ", strlen(arr[1]));
	return 0;
}

输出结果:

代码三:

  • sizeof( )操作符
int main()
{
	//p为一个指针变量,存放着首元素a的地址,p的类型为const char*类型
	char* p = "abcdef";
	printf("%d\n", sizeof(p));// 4/8-为元素a的指针
	printf("%d\n", sizeof(p + 1));// 4/8-为元素b的指针
	printf("%d\n", sizeof(*p));// 1-解引用后为元素a的大小
	printf("%d\n", sizeof(p[0]));// 1-元素a的大小
	printf("%d\n", sizeof(&p));// 4/8-指针变量p的地址,为二级指针
	printf("%d\n", sizeof(&p + 1));// 4/8-为p指针变量的地址后面某个指针变量的地址
	printf("%d\n", sizeof(&p[0] + 1));// 4/8-为元素b的地址
	return 0;
}
  • strlen( )函数
//还有部分问题尚未解决
int main()
{
	char* p = "abcdef";
	char arr[] = "abcdef";
	printf("%d\n", strlen(p));// 6-该字符串的长度
	printf("%d\n", strlen(p + 1));// 5-从b开始到\0之间字符串的长度
	printf("%d\n", strlen(&p));// 
	printf("%d\n", strlen(&p + 1));
	printf("%d\n", strlen(&p[0] + 1));
	//printf("%d\n", strlen(*p));
	//printf("%d\n", strlen(p[0]));
	return 0;
}
  1. 二维数组
int main()
{
	int a[3][4] = { 0 };
	printf("%zd ", sizeof(a));// 48-表示整个数组的大小
	printf("%zd ", sizeof(a[0][0]));// 4-表示a[0][0]元素的大小
	printf("%zd ", sizeof(a[0]));
    // 16-a[0]表示该二维数组第一行的数组名,单独放在sizeof内部,表示该二维数组首元素(第一行的一维数组)的大小
	printf("%zd ", sizeof(a[0] + 1));
    // 4/8-数组名没有单独放在sizeof内部,表示a[0][0]的地址,+1后表示a[0][1]的地址
	printf("%zd ", sizeof(*(a[0] + 1)));// 4-a[0]+1表示a[0][1]的地址,解引用后表示该元素
	printf("%zd ", sizeof(a + 1));// 4/8-a表示该二维数组的地址,为数组指针,+1表示第二行的地址
	printf("%zd ", sizeof(*(a + 1)));// 16-表示第二排的大小
	printf("%zd ", sizeof(&a[0] + 1));// 4/8-为&a[0]为第一行整个的地址,+1为第二行的地址
	printf("%zd ", sizeof(*(&a[0] + 1)));// 16-为第二行数组的大小
	printf("%zd ", sizeof(*a));
    // 16-数组名没有单独存放在sizeof内部,所以为该二维数组首元素的地址,解引用后为首元素(为第一行的一维数组)
	printf("%zd ", sizeof(a[3]));// 16-该行虽然不存在,假设为该数组的“第四行”,数组名在sizeof中表示整个数组的大小
	return 0;
}

**注意:**在二维数组 arr[ i ][ j ] 中,arr[ i - 1 ]表示该数组第 i 行的数组名,一般用来表示该数组首元素的地址。

数组与指针可以相互表示,例如arr[ k ]可以用指针表示为 * ( arr + k ),arr[ m ][ n ] 可以表示为 * ( * ( arr + m ) + n )

这样我们就可以容易理解为二维数组 arr 的第 m 个元素,数组 arr[ m ] 的第 n 个元素。

  • 总结——数组名的意义
    • sizeof ( arr ),这里面的数组名表示整个数组,计算的是整个数组的大小。
    • &arr,这里面的数组名表示整个数组,取出的是整个数组的地址。
    • 除此之外所有的数组名都表示首元素的地址,包括二维数组中arr[ i - 1 ]这种第 i 行的数组名。
  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在这里表示数组首元素的地址,+1后解引用为该数组第二个元素
//ptr原本为数组指针类型,+1后强制类型转化为int*,即ptr-1向前跳一个元素,即为5

答案:2 5

  • 题目二:
//在x86环境下,已知结构体的大小为20B,那么该程序的输出结果为多少
struct Test
{
	int num[5];
}*p = (struct Test*)0x100000;//将0x100000这个数组强制转化为结构体指针类型
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}
//题目解析:
//p+0x1为向后跳了一个结构体单位(20B),应当是0x100000+0x20进行数学运算,结果为0x100014
//p被强制转换为unsigned long类型,p+0x1为基本的数学运算,结果为0x100001
//p被强制转换为unsigned int*类型,p+0x1应当是0x100000+0x4进行数学运算,结果为0x1000004

答案:00100014 00100001 00100004

  • 题目三:
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}
//题目解析:
//首先,数组中的表达式为逗号运算,计算后数组中的数字应当为{ 1,3,5,0,0,0 }
//p为指针变量,a[0]为该二维数组第一行的数组名,表示第一行首元素的地址
//p可以表示为*(a+0),那么p[0]就可以表示为*(*(a+0)+0),即为a[0][0]。

答案:1

  • 题目四:
int main()
{
	int a[5][5] = { 0 };
	int (*p)[4];//为数组指针,类型为int (*)[4]
	p = a;//将数组a的地址赋值给p,在这里需要注意类型转化
	printf("%d,%p\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}
//题目解析;
//a为int (*)[5],而p为int (*)[4]
//数组指针在赋值时发生了强制类型转换,变量p为四列,而a为五列
//p[4][2]表示的是 *(*(p+4)+2)
//p[4]表示每次跳过四个元素,[2]表示找到该行(四个元素为一行)的第二个元素
//p[4][2]表示的是该数组中第18个小元素,而a[4][2]表示的是第22个小元素
//&p[4][2] - &a[4][2]——地址相减,表示中间的元素个数差,得到的值为-4
//-4的补码为11111111111111111111111111111100,转化为16进制数为0xFFFF FFFC

答案:-4 FFFFFFFC

  • 题目五:
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	//这里两个强制转换把数组指针转化为了int*类型的指针
	int* ptr1 = (int*)(&aa + 1);
	int* ptr2 = (int*)(*(aa + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}
//题目解析:
//ptr1中,先将该二维数组的地址取出+1后在强制类型转化为int*类型的指针
//*(ptr1 - 1)为向后跳一个元素之后对其解引用,得到结果为10
//ptr2中aa表示的是二维数组首元素的地址,+1后变为第二行的首元素的地址
//(即&aa[1][0]),*(ptr2 - 1)为向后跳一个整型变量,解引用可以得到为第一行最后一个元素a[0][4],结果为5

答案:10 5

  • 题目六:
int main()
{
	//由于每个字符串都是一个指针变量,所以此处要用指针数组
	char* a[] = { "work","at","alibaba" };
	//数组名为首元素的地址,而该数组首元素就是地址,所以这里要使用二级指针
	char** pa = a;
	pa++;
	printf("%s", *pa);
	return 0;
}
//题目解析:
//pa代表的是数组a中第一个元素的地址,pa++表示向后移动一个元素,pa就是字符串“at”的地址

答案:at

  • 题目七:
int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	//数组cp将数组c反转了一下
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	//先让cpp自增1(++在前,先加再用),然后两次解引用得到字符
	printf("%s\n", **++cpp);
	//先让cpp自增1,然后解引用一次后在自减1,然后解引用得到字符,最后在+3
	printf("%s\n", *-- * ++cpp + 3);
	//*cpp[-2]表示*(cpp-2)
	printf("%s\n", *cpp[-2] + 3);
	//cpp[-1][-1]表示*(*(cpp-1)-1)
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}
  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值