有关数组、指针与sizeof、strlen练习题

目录

 

一维数组练习

一维整形数组与sizeof、strlen

一维字符数组与sizeof、strlen

一维字符串数组与sizeof与strlen

常量字符串与sizeof与strlen

总结:

二维数组

总结:

指针笔试题


一维数组练习

一维整形数组与sizeof、strlen

#include<stdio.h>
int main()
{
	//一维数组
	int a[] = { 1,2,3,4 };

	printf("%d\n", sizeof(a));
	//只有数组名放到sizeof中,是代表整个数组 值=(数组内)元素个数*元素类型 4*4=16字节

	printf("%d\n", sizeof(a + 0));
	//不只有数组名了,此时a为数组首元素地址,首元素地址+0还是首元素地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节
	
	printf("%d\n", sizeof(*a));
	//不只有数组名,此时数组名为首元素地址,*首元素地址-->首元素 1是整形,所以为4个字节

	printf("%d\n", sizeof(a + 1));
	//不只有数组名,此时数组名为首元素地址,首元素地址+1,是下一个元素的地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(a[1]));
	//a[1]是元素2,类型为整形,整形为4个字节

	printf("%d\n", sizeof(&a));
	//&数组名:取出的是整个数组的地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(*&a));
	//先取出整个数组的地址,在解引用,那就是整个数组了
	//(数组内)元素个数*元素类型 4*4=16字节
	//直观理解为*&抵消了,只剩a

	printf("%d\n", sizeof(&a + 1));
	//先取出整个数组的地址,再+1,表示跳过整个数组,指向了下一个区域的地址
	//但本质上还是地址,地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(&a[0]));
	//a[0]是首元素,&a[0]为取出首元素的地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(&a[0] + 1));
	//a[0]是首元素,&a[0]为取出首元素的地址,地址+1,表示指到下一个元素的地址
	//	//地址是指针变量,在不同平台下大小不一样,4或8个字节
}

一维字符数组与sizeof、strlen

#include<stdio.h>
#include<string.h>

int main()
{
	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));
	//sizeof(数组名),代表计算整个数组的大小 6*1=6字节

	printf("%d\n", sizeof(arr + 0));
	//不只有数组名,arr便是首元素地址。首元素地址+0还是首元素地址
    //地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(*arr));
	//arr为首元素地址,*首元素地址,就是字符 'a'
	//'a'类型为char,为1个字节

	printf("%d\n", sizeof(arr[1]));
	//arr[1]<==>*(a+1)<==>'b'
	//'b'类型为char,为1个字节

	printf("%d\n", sizeof(&arr));
	//&数组名,取出的是整个数组的地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(&arr + 1));
	//&数组名,取出的是整个数组的地址,然后+1是跳过arr这个数组,指向了arr后面的地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(&arr[0] + 1));
	//&arr[0]是取出首元素的地址,地址+1下一个元素的地址
   //地址是指针变量,在不同平台下大小不一样,4或8个字节



	//strlen和sizeof不一样,它是计算从起始地址到'\0'之前所出现的字符个数的

	printf("%d\n", strlen(arr));
	//arr为首元素地址,但是该数组没有'\0',所以会是随机值

	printf("%d\n", strlen(arr + 0));
	//arr为首元素地址,+0还是指向首元素地址,同上还是随机值

	printf("%d\n", strlen(*arr));
	//strlen内应该传其实地址,这里arr为首元素地址,又解引用,就是字符'a'
	//也就是strlen(97),它就是野指针

	printf("%d\n", strlen(arr[1]));
	//与上一种情况一样,为野指针

	printf("%d\n", strlen(&arr)); 
	//&arr是取出了整个数组的地址,整个数组的地址与首元素地址相同
	//又因为没有'\0',所以会是随机值

	printf("%d\n", strlen(&arr + 1));
	//&arr取出整个数组的地址,+1跳过整个数组
	//其实地址有了,但是还是不知道'\0'在哪,所以还是随机值

	printf("%d\n", strlen(&arr[0] + 1));
	//取出首元素的地址+1,得到了第二个元素的地址
	//但是还是不知道'\0',所以还是随机值

	return 0;
}

一维字符串数组与sizeof与strlen

#include<stdio.h>
#include<string.h>

int main()
{
	char arr[] = "abcdef";
	//与上面的数组不一样的地方是,这个字符串后面默认隐藏了'\0'
	//相当于上面char arr[] = {'a','b','c','d','e','f','\0'};

	printf("%d\n", sizeof(arr));
	//sizeof(数组名),算整个数组的大小 7*1=7字节

	printf("%d\n", sizeof(arr + 0));
	//不只有数组名,arr代表首元素地址,+0还是首元素地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(*arr));
	//arr为首元素地址,解引用为首元素'a'
	//'a'的类型为char,大小为1个字节

	printf("%d\n", sizeof(arr[1]));
	//arr[1]<==>*(a+1)<==>'b'  大小为1个字节

	printf("%d\n", sizeof(&arr));
	//&数组名,取出的是整个数组的地址
    //地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(&arr + 1));
	//&数组名,取出的是整个数组的地址,然后+1是跳过arr这个数组,指向了arr后面的地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(&arr[0] + 1));
	//&arr[0]是取出首元素的地址,地址+1下一个元素的地址
    //地址是指针变量,在不同平台下大小不一样,4或8个字节



	printf("%d\n", strlen(arr));
	//arr为首元素地址,同时也有'\0',它们之间又6个字符

	printf("%d\n", strlen(arr + 0));
	//arr为首元素地址,+0没有跳过任何地址,所以还是六个字符

	//printf("%d\n", strlen(*arr));
	//arr为首元素地址,解引用为首元素'a'
	//strlen(97),97为野指针

	//printf("%d\n", strlen(arr[1]));
	//同上,是第二个元素'b'
	//也是野指针

	printf("%d\n", strlen(&arr));
	//&arr为取出整个数组的地址,整个数组的地址与首元素的地址一样
	//所以找到'\0'之间的元素个数为6

	printf("%d\n", strlen(&arr + 1));
	//&arr为取出整个数组的地址,+1就是跳过整个数组的地址
	//地址有了,但是不知道'\0'在哪里,所以为随机值

	printf("%d\n", strlen(&arr[0] + 1));
	//&arr[0]是取出首元素的地址,+1就是往后跳过一个元素
	//从第二个字符的地址开始,到'\0'之间的元素个数为5

	return 0;
}

常量字符串与sizeof与strlen


#include<stdio.h>
#include<string.h>

int main()
{
	char* p = "abcdef";
	//这里是一个常量字符串,常量字符串的内容是不可修改的
	//且这里的末尾也隐藏了'\0'
	//p只是保存了首元素'a'的地址

	printf("%d\n", sizeof(p));
	//p是保存字符串首元素地址的
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(p + 1));
	//p是个字符指针,+1跳过一个字符,那就是b的地址
	//地址是指针变量,在不同平台下大小不一样,4或8个字节

	printf("%d\n", sizeof(*p));
	//对于首元素地址解引用,那就是访问'a',为1字节

	printf("%d\n", sizeof(p[0]));
	//p[0]<==>*(p+0) 还是对首元素地址解引用,同上为1字节

	printf("%d\n", sizeof(&p));
	//p为char*类型,&p为char**,二级指针的大小依旧是4或8个字节

	printf("%d\n", sizeof(&p + 1));
	//&p+1为二级指针往后跳过刚才一级指针的位置,也就是跳过了p的地址
	//最后还是指向了一个地址,所以还是4或8个字节

	printf("%d\n", sizeof(&p[0] + 1));
	//&p[0]+1<==>&*(p+0)+1<==>p+1就是b的地址,所以为4或8个字节



	printf("%d\n", strlen(p));
	//p为首元素的地址,找到了地址,也有'\0',之间的元素个数为6

	printf("%d\n", strlen(p + 1));
	//首元素地址+1是跳过了第一个元素,所以此时计算的元素个数为5

	printf("%d\n", strlen(*p));
	//解引用首元素地址,那就是'a',写法不对

	printf("%d\n", strlen(p[0]));
	//p[0]<==>*(p+0),还是相当于解引用首元素地址,'a',写法不对

	printf("%d\n", strlen(&p));
	//取出存储首元素地址的地址,这就是另外一个空间了
	//有地址,但不知道'\0'何时出现,那就是随机值

	printf("%d\n", strlen(&p + 1));
	//同上还是随机值,但二者的随机值没有联系

	printf("%d\n", strlen(&p[0] + 1));
	//从'b'的地址开始计算,所以有5个字符。

	return 0;
}

总结:

一维数组数组名大多数情况下都代表首元素地址。

但是有两个例外,数组名代表整个数组

a.sizeof(数组名)    注意:()中必须只有数组名才能代表整个数组

b.&数组名    注意:虽然&数组名和首元素地址值一样。但是对于指针,我们要关注类型,类型能够决定指针+-整数跳过的步长(单位字节)和解引用访问的权限。&数组名类型就是数组指针类型。


二维数组

#include<stdio.h>
#include<string.h>

int main()
{
	int a[3][4] = { 0 };

	printf("%d\n", sizeof(a));
	//sizeof()只有数组名,代表整个数组的大小 3*4*4=48字节

	printf("%d\n", sizeof(a[0][0]));
	//a[0][0]访问的元素为0,sizeof(0)=4

	printf("%d\n", sizeof(a[0]));
	//类比
	//一维数组              二维数组
	//a1[i]                 a2[i][j]
	//这里的a1是数组名       二维数组的a2[i]不就是恰好对应上一维数组的a1,所以a[i]就相当于一维数组名
	
	//a[0]就是这个二维数组的第一行数组名
	//数组名单独放到sizeof()就是计算第一行数组的大小
	//4*4=16

	printf("%d\n", sizeof(a[0] + 1));
	//sizeof()不仅仅有数组名,那就不是整个数组的大小了
	//此时a[0]为第一行首元素的地址,+1那就是第一行第二个元素的地址,地址为4或8个字节
	//a[0]相当于&a[0][0],&a[0][0]+1相当于&a[0][1]  

	printf("%d\n", sizeof(*(a[0] + 1)));
	//a[0]+1同上,解引用那就是访问a[0][1]的元素0,整形的字节大小为4个字节

	printf("%d\n", sizeof(a + 1));
	//a没有单独放在sizeof内部,也没有&a
	//此时这里的a就是二位数组首元素地址,二维数组首元素地址就是二维数组第一行地址
	//a+1就是跳过第一行的地址,表示第二行的地址,所以为4或8个字节

	printf("%d\n", sizeof(*(a + 1)));
	//a+1同上,解引用那就是第二行的数组,大小为4*4=16
	//还可以这样理解*(a+1)-->a[1]
	//sizeof(a[1])就是计算第二行数组的大小

	printf("%d\n", sizeof(&a[0] + 1));
	//a[0]为第一行数组名,&数组名就是取出整个第一行数组
	//+1就是跳过第一行,取出第二行整个数组的地址,为4或8个字节

	printf("%d\n", sizeof(*(&a[0] + 1)));
	//&a[0]+1同上
	//在解引用就是整个第二行的元素,4*4=16

	printf("%d\n", sizeof(*a));
	//a未放在sizeof()内部,也没有&a
	//a就是二维数组首元素地址,二维数组首元素地址就是二维数组第一行地址
	//在解引用那就是第一行的元素 4*4=16

	printf("%d\n", sizeof(a[3]));
	//a[3]应该是整个第四行数组,4*4=16
	//不存在越界访问,因为只是使用了其数组名,并未访问内部元素

	return 0;
}

总结:

二位数组的数组名与一维数组有相同之处

以int a[3][4]这个二维数组举例

对于数组名a来说,如果单独放在sizeof()或者&a的话,那就是代表整个数组

                               其他情况就是代表二维数组首元素地址

                               二维数组首元素地址就是二维数组第一行的地址,即&a[0]

对于a[0]来说,如果单独放在sizeof()或者&a[0]的话,那就是代表第一行的整个地址

                                其他情况就是代表二维数组第一行首元素地址,即&a[0][0]


指针笔试题

1.

#include<stdio.h>

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

小结:

        指针变成另一种指针时,虽然还是指向相同的地址,但是其解引用权限和步长都会发生改变


2.

#include<stdio.h>

//x86环境下,以下结构体的大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}* p = 0x00100000;

//假设p 的值为0x00100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n", p + 0x1);
	//p为结构体类型指针变量,+1就是跳过一个结构体Test
	//已知题目变量大小为20字节,而题目当中是以 %p 的形式打印,是以十六进制打印
	//十进制20转十六进制为14
    //0x00100000+0x00000014=0x00100014

	printf("%p\n", (unsigned long)p + 0x1);
	//p的类型被强制转换成无符号长整型
	//此时0x00100000为16进制的整数,再+16进制的整数0x1
	//那就是0x001000001

	printf("%p\n", (unsigned int*)p + 0x1);
	//先把p强制转化成无符号整形指针类型
	//再+1,就相当于跳过一个无符号整形的长度(4个字节)
	//0x00100000+0x00000004=0x00100004

	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\n", ptr1[-1], *ptr2);
	//ptr1[-1] --> *(ptr1-1)
	//%x 以十六进制的形式打印
	return 0;
}


4.

#include<stdio.h>

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	//注意对比int a[3][2]={ {0,1}, {2,3}, {4,5} };
	//这个才是二位数组的赋值,而 (,)是逗号表达式
	//实际相当于 int a[3][2]={1,3,5};
	int* p;
	p = a[0];
	//a[0]第一行数组名
	//a[0]既没有放到sizeof内部也没有取地址
	//a[0]就是二维数组第一行首元素的地址,即&a[0][0]
	printf("%d", p[0]);
	//p[0] --> *(p+0)
	return 0;
}


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

 


6.

#include<stdio.h>
int main()
{
	int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)(*(a + 1));
	printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}


7.

#include<stdio.h>

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


8.

#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);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}

 


有什么错误希望评论区可以指出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值