【C语言-进阶】指针进阶(3) (笔试题)

指针笔试题①

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取出的是整个数组的地址,加一跳过整个数组,指向5末尾的那个地址处,转化为整型指针。

打印时:a是首元素地址,加一指向2,(跳过四个字节),解引用为2,后面那个因为本身是整型指针,减一向左移动4个字节,所以指向5,解引用为5,打印出5。

指针笔试题②

//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	p = (struct Test*)0x100000;

	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

 主要考察指针加减整数。test结构体占20个字节(也就是说一个结构体变量的首元素地址,与末尾的那个地址之间相差20个字节。)

printf1:p是一个struct Test*的指针变量。设定为0x100000。  +0x1,就是加1,只是16进制表示的1而已。p+1,所以跳过20个字节,所以为00100014。(前面的9用于补全)

printf2:把p转化为长整型。存储的就是0x100000(16进制)。加一就是0x100001  这个16进制数字再以%p,即地址的形式打印出来,即00100001。

printf3:p转化为整型指针,加一跳过一个整型的宽度,即跳过4个字节,再以%p打印出来即00100004

指针笔试题③

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,整个数组的地址,加一跳过整个数组,指向4后面的那个地址,转化为整型指针。

ptr1[-1] = *(ptr1-1) 即向前跳过一个整型,指向4,解引用即4

ptr2:a首元素地址,转化为整型加一,如果此时再把此整型转化为地址,其实是比原来的地址向后跳过一个字节(int四个字节)。

内存布局(小端存储):01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 则此时ptr2指向的是01后面的那个00  此时再解引用:整型指针访问四个字节,小端存储模式取出,低地址放在低字节处,高地址放在高字节处,即02 00 00 00 再以%x(16进制整数)形式打印出来,即2000000 

指针笔试题④

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

a[0],即二维数组中第一行的那个一维数组的数组名,数组名表示首元素地址,即第一行的第一个整型的地址,p[0] =   *(p+0) 即第一行的第一个整型

注意此处初始化列表中是圆括号(),里面是逗号表达式。所以相当于int a[3][2] = {1,3,5}; 即{ {1,3}, {5,0}, {0,0}}; 所以打印出来是1

 指针笔试题⑤

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

 5*5的二维数组a,指向含有四个整形的数组的指针p,p=a,a表示首元素地址,即第一行的那个一维数组的地址,这里地址减去地址,表示的是地址之间的元素个数差值(带正负)。如下图,&p[4][2] - &a[4][2] = -4。 

第一个以%p形式打印出来,即16进制形式,-4的补码为 111111111111111111111111111111111100,以16进制打印出来就是ff ff ff fc 

第二个以%d形式打印出来,就是-4

 注意p是一个指向四个整形的数组的指针,图中红框为p眼中没加一之后所指向的数组。宽度为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;
}

 ptr1存储的是10后面的那个地址

*(aa+1) 就是aa[1] 就是第二行的那个一维数组的数组名,也表示第二行的首元素地址,即6的地址

打印出来是10,5 都是向左跳过一个整型 

指针笔试题⑦

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

略,打印出来是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);
 return 0;
}

内存布局如上图所示,字符指针数组存储的是四个字符串常量的首元素地址,cp分别交错存储数组c中元素的地址,cpp存储一开始存储的是cp的首元素地址,char** 的地址为char***

1:**++cpp  假设cp数组首元素地址是0x12ff40 , 加加之后即0x12ff41,而cp数组中的元素在内存中又是连续存储的,所以++之后指向的就是cp的第二个元素,再两次解引用,第一次解引用得到cp[1],这里存储的是&c[2],再解引用得到c[2],这里存储的是常量字符串"point"的首元素地址,即p的地址,再以%s打印出来,就是point

后面的与1类似,不做详细解答了,其实可以发现不管哪次printf,里面都包含两次解引用,最后都得到某个常量字符串的首元素地址,后面如果再加一个整数,就是向后移动若干字符。

最后结果为 point,er,st,ew。 

 

注意上图中,cp数组的第二个元素起初为c+2,在进行最后一个printf打印时,不会改变c+2为c+1,它指向的仍然是c[2]。

而第二个printf,++cpp后cpp指向的是cp[2] ,再解引用,得到cp[2],这时候执行-- 操作,起初cp[2]存储的是c[1]的地址,即c+1,执行--操作后,就是c,即c数组的首元素地址,或者也可以理解为向前移动了一个元素,而这个元素是char*,即指针类型,所以向前跳过4个字节指向c数组首元素。这里是会改变cp[2]存储的内容的,这里所说的就是上上图中cp[2]元素的蓝线变成了红线

下面的代码随缘看看

//数组名是什么呢?
//数组名通常来说是数组首元素的地址
//但是有2个例外:
//1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
//2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址

int main()
{
	int a[] = { 1,2,3,4 };
	          0 1 2 3
	int (*p)[4] = &a;

	printf("%d\n", sizeof(a));//4*4 = 16
	printf("%d\n", sizeof(a + 0));//4/8 a+0是数组第一个元素的地址,是地址,大小就是4/8个字节
	printf("%d\n", sizeof(*a)); //4 a表示数组首元素的地址,*a表示数组的第一个元素,sizeof(*a)就是第一个元素的大小-4
	printf("%d\n", sizeof(a + 1));//4/8 a表示数组首元素的地址,a+1数组第二个元素的地址,sizeof(a+1)就是第二个元素的地址的大小
	printf("%d\n", sizeof(a[1]));//4 计算的是第二个元素的大小
	printf("%d\n", sizeof(&a));//4/8 &a取出的是数组的地址,数组的地址也是地址呀,是地址大小就是4/8字节
	printf("%d\n", sizeof(*&a));//16 计算的整个数组的大小 
	printf("%d\n", sizeof(&a + 1));//4/8 - &a是数组的地址,+1跳过整个数组,产生的4后边位置的地址
	printf("%d\n", sizeof(&a[0]));//4/8 取出的数组第一个元素的地址
	printf("%d\n", sizeof(&a[0] + 1));//4/8 数组第二个元素的地址

	字符数组
	char arr[] = { 'a','b','c','d','e','f' };//[a b c d e f]

	printf("%d\n", strlen(arr));//随机值,arr数组中没有\0,所以strlen函数会继续往后找\0,统计\0之前出现的字符个数
	printf("%d\n", strlen(arr + 0));//随机值,arr+0还是数组首元素的地址
	//printf("%d\n", strlen(*arr));//err - arr是数组首元素的地址,*arr是数组的首元素,‘a’-97
	//printf("%d\n", strlen(arr[1]));//err -'b' - 98
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//随机值


	printf("%llu\n", sizeof(arr));//6
	printf("%llu\n", sizeof(arr + 0));//4/8 arr + 0是数组首元素的地址
	printf("%llu\n", sizeof(*arr));//1 - *arr是首元素,首元素是一个字符,大小是一个字节
	printf("%llu\n", sizeof(arr[1]));//1 - arr[1]是数组的第二个元素,大小是1个字节
	printf("%llu\n", sizeof(&arr));//4/8 &arr是数组的地址
	printf("%llu\n", sizeof(&arr + 1));//4/8 &arr + 1是从数组地址开始向后跳过了整个数组产生的一个地址
	printf("%llu\n", sizeof(&arr[0] + 1));//4/8 &arr[0] + 1 是数组第二个元素的地址

	return 0;
}

//sizeof是一个操作符
//sizeof 计算的是对象所占内存的大小-单位是字节,size_t
//不在乎内存中存放的是什么,只在乎内存大小
//
//strlen 库函数
//求字符串长度,从给定的地址向后访问字符,统计\0之前出现的字符个数



int main()
{
	char arr[] = { 'a', 'b', 'c','d', 'e', 'f' };

	char arr[] = "abcdef";
	//[a b c d e f \0]
	printf("%d\n", strlen(arr));//6
	printf("%d\n", strlen(arr + 0));//6
	//printf("%d\n", strlen(*arr));//err
	//printf("%d\n", strlen(arr[1]));//err
	printf("%d\n", strlen(&arr));//6
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//5

	printf("%d\n", sizeof(arr));//7
	printf("%d\n", sizeof(arr + 0));//4/8 arr+0是数组首元素的地址
	printf("%d\n", sizeof(*arr));//1 - *arr 数组的首元素
	printf("%d\n", sizeof(arr[1]));//1 arr[1]数组的第二个元素
	printf("%d\n", sizeof(&arr));//4/8 - &arr数组的地址,但是数组的地址依然是地址,是地址大小就是4/8
	printf("%d\n", sizeof(&arr + 1));//4/8 - &arr + 1是\0后边的这个地址
	printf("%d\n", sizeof(&arr[0] + 1));//4/8 - &arr[0] + 1是数组第二个元素的地址

	char* p = "abcdef";

	printf("%d\n", strlen(p));//6
	printf("%d\n", strlen(p + 1));//5 从b的位置开始向后数字符
	//printf("%d\n", strlen(*p));  //err
	//printf("%d\n", strlen(p[0]));//err
	printf("%d\n", strlen(&p));//随机值
	printf("%d\n", strlen(&p + 1));//随机值
	printf("%d\n", strlen(&p[0] + 1));//5  从b的位置开始向后数字符

	printf("%d\n", sizeof(p));//4/8  p是指针变量,计算的是指针变量的大小
	printf("%d\n", sizeof(p + 1));//4/8 p+1是'b'的地址
	printf("%d\n", sizeof(*p)); //1  - *p 其实就是'a'
	printf("%d\n", sizeof(p[0]));//1 - p[0]-> *(p+0)-> *p
	printf("%d\n", sizeof(&p));//4/8 &p 是指针变量p在内存中的地址
	printf("%d\n", sizeof(&p + 1));//4/8 - &p+1是跳过p之后的地址
	printf("%d\n", sizeof(&p[0] + 1));//4/8 &p[0]是‘a’的地址,&p[0]+1就是b的地址


	二维数组
	int a[3][4] = { 0 };

	printf("%d\n", sizeof(a));//计算的是整个数组的大小,单位是字节3*4*4 = 48
	printf("%d\n", sizeof(a[0][0]));//4 第1行第一个元素的大小
	printf("%d\n", sizeof(a[0]));//16 - a[0]是第一行的数组名,sizeof(a[0])就是第一行的数组名单独放在sizeof内部,计算的是第一行的大小
	printf("%d\n", sizeof(a[0] + 1));//4/8 a[0]作为第一行的数组名,并没有单独放在sizeof内部,也没有被取地址
	所以a[0]就是数组首元素的地址,就是第一行第一个元素的地址,a[0]+1就是第一行第二个元素的地址

	printf("%d\n", sizeof(*(a[0] + 1)));//4 - *(a[0] + 1))表示的是第一行第二个元素
	printf("%d\n", sizeof(a + 1));//4/8 - a表示首元素的地址,a是二维数组,首元素的地址就是第一行的地址
	所以a表示的是二维数组第一行的地址,a+1就是第二行的地址
	printf("%d\n", sizeof(*(a + 1)));//16 对第二行的地址解引用访问到就是第二行
	*(a+1) -> a[1]
	sizeof(a[1])
	
	printf("%d\n", sizeof(&a[0] + 1));//4/8 - a[0]是第一行的数组名,&a[0]取出的就是第一行的地址
	&a[0] + 1 就是第二行的地址

	printf("%d\n", sizeof(*(&a[0] + 1)));//16 - 对第二行的地址解引用访问到就是第二行
	printf("%d\n", sizeof(*a));//16 - a就是首元素的地址,就是第一行的地址,*a就是第一行
	*a - > *(a+0) -> a[0]

	printf("%d\n", sizeof(a[3]));//16 int [4]

	int a = 10;
	printf("%d\n", sizeof(a));//4
	printf("%d\n", sizeof(int));//4

	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值