指针详解②

本文详细分析了C语言中数组和指针的关系,包括一位数组、字符数组、二维数组的sizeof()和strlen()函数的应用,并通过多个面试题实例展示了数组名、指针和地址的区别。内容涵盖了数组大小计算、指针地址操作以及字符串长度的确定等知识点。
摘要由CSDN通过智能技术生成

 9、指针和数组面试题(续)

 

https://mp.csdn.net/mp_blog/creation/editor/118405531

一、总结

A、一位数组-sizeof()

        数组名是首元素地址 
  例外:
    1、sizeof(数组名)-表示整个数组
    2、&数组名-表示整个数组 

#include<stdio.h>
int main()
{
	//数组名是首元素地址
	//例外:
	//1、sizeof(数组名)-表示整个数组
	//2、&数组名-表示整个数组 
    //一位数组
	int arr[] = { 4, 5, 6, 7 };
	printf("%d\n", sizeof(arr));//16-数组名-计算整个数组的大小
	printf("%d\n", sizeof(arr+0));//4-首元素地址
	printf("%d\n", sizeof(*arr));//4-首元素
	printf("%d\n", sizeof(arr+1));//4-第二个元素地址
	printf("%d\n", sizeof(arr[1]));//4-第二个元素
	printf("%d\n", sizeof(&arr));//4-整个数组的地址
	printf("%d\n", sizeof(*&arr));//16-整个数组
	printf("%d\n", sizeof(&arr+1));//4-下一个数组的地址
	printf("%d\n", sizeof(&arr[0]));//4-首元素地址
	printf("%d\n", sizeof(&arr[0]+1));//4-第二个元素的地址
	return 0;
}

B、字符数组-sizeof()

#include<stdio.h>
int main()
{
	//字符数组
	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第二个元素的地址
	return 0;
}
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7-数组大小+'\0'
//其余都一样

C、字符数组-strlen()

#include<stdio.h>
#include<string.h>
int main()
{
	//字符数组
	char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
	printf("%d\n", strlen(arr));//随机值-直到找到'\0'
	printf("%d\n", strlen(arr + 0));//随机值
	printf("%d\n", strlen(*arr));//error-'a'-97-非法访问内存
	printf("%d\n", strlen(arr[1]));//error-‘b’-98
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//随机值
}
#include<string.h>
int main()
{
	//字符数组
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//6-整个数组大小-直到'\0'
	printf("%d\n", strlen(arr + 0));//6-整个数组大小
	//printf("%d\n", strlen(*arr));//error-'a'-97-非法访问内存
	//printf("%d\n", strlen(arr[1]));//error-‘b’-98
	printf("%d\n", strlen(&arr));//6-整个数组大小
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//5-从第二个数直到'\0'
}

D、字符数组-sizeof()

int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));//4-指针变量p
	printf("%d\n", sizeof(p+1));//4-(p+1)为指向b的地址
	printf("%d\n", sizeof(*p));//1-字符串首元素a
	printf("%d\n", sizeof(p[0]));//1-首元素  int arr[10];arr[0]==*(arr+0);p[0]==*(p+0);
	printf("%d\n", sizeof(&p));//4-p的地址
	printf("%d\n", sizeof(&p + 1));//4-p的下一个地址
	printf("%d\n", sizeof(&p[0] + 1));//4-字符b的地址
	return 0;
}
#include<string.h>
int main()
{
	char *p = "abcdef";
	printf("%d\n", strlen(p));//6-指针变量p指向首元素地址开始直至'\0'
	printf("%d\n", strlen(p + 1));//5-(p+1)为指向b的地址开始直至'\0'
	//printf("%d\n", strlen(*p));//error-*p-字符串首元素'a'-97,非法访问内存
	//printf("%d\n", strlen(p[0]));//error-p[0]字符串首元素'a'-97,非法访问内存
	printf("%d\n", strlen(&p));//随机值
	printf("%d\n", strlen(&p + 1));//随机值
	printf("%d\n", strlen(&p[0] + 1));//5-从b开始直至'\0'
	return 0;
}

E、二维数组

sizeof(数组名)、&(数组名)才表示数组,其余都为数组首元素地址

int main()
{
	//sizeof(数组名)、&(数组名)才表示数组,其余都为数组首元素地址
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//48-二维数组大小
	printf("%d\n", sizeof(a[0][0]));//4-a[0][0]的大小
	printf("%d\n", sizeof(a[0]));//16-a[0]相当于第一行作为一维数组的数组名,
	//sizeof(a[0])把a[0]单独放在里面,所以计算的是第一行数
	printf("%d\n", sizeof(a[0]+1));//4-第一行第二个元素的地址,a[0][1]的地址
	printf("%d\n", sizeof(*(a[0] + 1)));//4-第一行第二个元素的大小,a[0][1]的大小
	printf("%d\n", sizeof(a+1));//4-第二行的地址,
	//a是首元素地址,把二维数组看成一位数组,a就是第一行地址
	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-因为sizeof并不会访问,
	//第四行数组元素(有无都不重要)的大小吧
	return 0;
}

总结:
数组名的意义:


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

2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

3.除此之外所有的数组名都表示首元素的地址。
 

笔试题:

eg1:

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

分析:

a指的是元素1的地址,a+1就是元素二的地址;

&a表示取出数组的地址,&a+1就是图示位置,所以ptr-1就是指向元素5的地址;

然后对它们进行解引用操作

代码实现:

eg2:

已知,结构体Test类型变量大小为20个字节,假设p的值为0x100000

struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//已知,结构体Test类型变量大小为20个字节
//假设p的值为0x100000
int main()
{
	p = (struct Test*)0x100000;
	//p是指向结构体的指针,初值为0x100000,
	//p + 1跳过一个结构体Test变量大小为20个字节,所以第一项输出0x00100014;
	printf("%p\n", p + 0x1);
	//强制类型转换成无符号长整型+1就是0x00100001
	printf("%p\n",(unsigned long) p + 0x1);//十进制1048576+1=1048577转换成十六进制
	//强制类型转换成无符号int*,+1跳过一个无符号整型(4个字节)就是0x00100004
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

代码实现:

eg3:

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

分析:

(&a+1)-指向a数组的下一个数组,强制类型转化为(int*),ptr1就指向数组a的下一个数组首元素,ptr1[-1]==*(ptr1-1),所以就是元素4;

a就是首元素地址,为00 00 00 01,但是在内存中为小端存储(如图所示),然后将a强制类型转换为int,((int)a + 1)再强制类型转换为(int*),等价于向后移动一个字节,指向01后面的00,指针ptr2就指向00,*ptr2就是00 00 00 02,从内存还原为02 00 00 00

代码实现:

eg4:

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

分析:

因为是数组内部为(),所以只执行最后一个元素/命令,所以数组元素为{1,3,5,0,0,0};

a[0]为数组第一行首元素地址,所以指针p是元素1的地址,p[0]==*(p+0)==*p,为1

代码实现:

eg5:

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

分析:

数组a为5行5列,a单独为首元素地址,a的类型为int(*)[5],p类型为int(*)[4],将a赋给p同指向数组首元素;

&a[4][2]就是指向数组第5行第3列的(图示红色)元素,&p[4][2]因为p数组指针指向的数组有4个元素,所以就是(图示绿色)元素;

%d:两地址相减为中间元素的个数,前面为低地址,所以为-4;

%p:将-4转化为补码存储在内存中,因为地址为无符号数,原码就是补码,所以将补码还原为十六进制为FFFFFFFC

 //-4-10000000 00000000 00000000 00000100-原码
         //   11111111 11111111 11111111 11111011-反码
         //   11111111 11111111 11111111 11111100-补码

代码实现:

eg6:

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

分析:&aa(aa为数组名),+1跳过一个数组;

*(aa+1)aa为数组首元素地址,+1把它看作一维数组就相当于跳过一行

代码实现:

 eg7:

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

分析:

a为字符指针数组,存放数组首字母地址“w”、“a”、“a”的地址,数组每个元素为char*类型,pa存放a数组首元素地址,自增,指向“a”元素地址,解引用输出字符串为“at”

 代码实现:

 eg8:

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

分析:

c数组为char*类型分别指向"ENTER"、"NEW"、"POINT"、"FIRST"首元素地址;
cp数组为char**类型,它们分别指向c数组的不同首元素地址;

cpp指针类型为char***类型指向cp数组首元素,如图所示:

①**++cpp——先执行++cpp,cpp指向c+2,再进行两次解引用操作,POINT;

②(上一步操作完成后cpp指向c+2)*--*++cpp+3——先执行++cpp操作,cpp指向c+1,再进行解引用指向c+1对应的数组c内元素地址,再进行--操作,指向c对应的数组c内元素地址,再进行解引用操作指向ENTER,+3操作,ER;

③(上一步操作完成后cpp指向c+1)*cpp[-2]+3)——cpp[-2]=*(cpp-2),为(cpp-2)指向c+3地址,**(cpp-2)指向FIRST,+3后,ST;

④cpp[-1][-1]+1——cpp[-1][-1]=*(*(cpp-1)-1),cpp-1指向c+2,*(cpp-1)指向c+2对应的数组c内元素地址,*(*(cpp-1)-1)指向NEW,+1,EW

代码实现:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值