C语言深入理解指针5

1.sizeof和strlen 对比

1.1sizeof

sizeof用来计算变量所占内存空间大小,单位是字节,操作数是类型的话,计算的是使用类型创建的变量所占空间的大小
sizeof只关注占用内存空间大小,不在乎内存中存放什么数据

int main()
{
	int a = 0;
	printf("z%d\n", sizeof(a));
	printf("%zd\n", sizeof(int));
	printf("%d\n", sizeof a);

	return 0;
}
  • 注:当类似是变量a的形式,就可以省略括号

1.2strlen

strlen是c语言库函数,功能是求字符串长度
函数原型

size_t strlen ( const char * str );

解释:统计的是从strlen函数的参数str中这个地址开始向后,\0之前字符串中字符的个数,strlen函数会一直向后找\0字符,直到找到为止,所以可能存在越界查找

#include <stdio.h>
int main()
{
 char arr1[3] = {'a', 'b', 'c'};
 char arr2[] = "abc";
 printf("%d\n", strlen(arr1));
 printf("%d\n", strlen(arr2));
 
 printf("%d\n", sizeof(arr1));
 printf("%d\n", sizeof(arr1));
 return 0;
}

解释:由于arr1字符数组中没有存放\0,所以strlen会一直向后查找,直到找到\0为止,arr2字符串数组中,是默认结尾存放\0的,所以可以正常打印,因为arr1是三个字符,占用3个字节,sizeof(arr1)结果是3,arr2包括abc和\0所以是4个字符,占4个字节,所以结果是4

1.3sizeof和strlen对比

sizeof

  • 1.sizeof是操作符
  • 2.sizeof计算操作数所占内存大小,单位是字节
  • 3.不关注内存中存放什么数据
    strlen
  • 1.strlen是库函数,使用需要包含头文件string. h
  • 2.strlen是求字符串长度的,统计的是\0之前字符的个数
  • 3.关注内存中是否有\0,没有就会一直找,直到找到为止

2.数组和指针练习题

2.1一维数组

//数组名是数组首元素地址
//&arr,数组名表示的是整个数组,取出的是整个数组的地址
// sizeof(arr),数组名表示整个数组,计算的是整个数组的大小,
//单位是字节
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zd\n", sizeof(a));//结果是16,sizeof(数组名)表示整个数组,a数组中有4个int类型的元素,
	printf("%zd\n", sizeof(a + 0));//结果是4/8,a表示数组首元素地址,a+0,还是数组首元素地址
	//a+0====&a[0]
	printf("%zd\n", sizeof(*a));//结果是4,a是数组首元素地址,解引用表示第一个元素
	//*a是第一个元素,a[0]
	printf("%zd\n", sizeof(a + 1)); //结果是4/8,a表示数组首元素地址,+1表示第二个元素地址
	//a+1====&a[1]
	printf("%zd\n", sizeof(a[1]));//结果是4,a[1]表示第二个元素
	printf("%zd\n", sizeof(&a));//结果是4/8,&a表示数组的地址
	printf("%zd\n", sizeof(*&a));//结果是16, *&a,两者抵消,也就是sizeof(a)
	//抵消,16,&a-int(*p)[4]=&a
	//*p访问一个数组的大小,p+1跳过一个数组
	printf("%zd\n", sizeof(&a + 1));//结果是4/8,&a表示数组地址,&a+1表示跳过整个数组的地址
	//4/8
	printf("%zd\n", sizeof(&a[0]));//结果是4/8,a[0]表示第一个元素,&a[0]表示第一个元素的地址
	printf("%zd\n", sizeof(&a[0] + 1));//结果是4/8,&a[0]表示首元素地址,+1表示第二个元素地址
	return 0;
}

2.2字符数组

练习1

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));//结果是6,sizeof(arr)表示整个数组
    printf("%d\n", sizeof(arr + 0));//结果是4/8,首元素地址
	printf("%d\n", sizeof(*arr));//结果是1,首元素地址解引用,首元素
    printf("%d\n", sizeof(arr[1]));//结果是1,第二个元素
    printf("%d\n", sizeof(&arr));//结果是4/8,数组的地址
    printf("%d\n", sizeof(&arr + 1));//结果是4/8,跳过一个数组的地址
	printf("%d\n", sizeof(&arr[0] + 1));//结果是4/8,第二个元素地址

	return 0;
}

练习2

#include<string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zd\n", strlen(arr));//随机值,字符数组中结尾没有\0
	printf("%zd\n", strlen(arr + 0));//随机值
	printf("%zd\n", strlen(*arr));//非法访问,只能传地址,传的是首字符,也就是访问97的地址,err
	printf("%zd\n", strlen(arr[1]));//非法访问,传的是第二个元素,也就是访问98的地址,err
	printf("%zd\n", strlen(&arr));//随机值,整个数组地址,起始位置地址
	printf("%zd\n", strlen(&arr + 1));//随机值,跳过一个数组的地址,
	printf("%zd\n", strlen(&arr[0] + 1));//随机值,第二个元素的地址,
	return 0;
}

练习3

#include<string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%llu\n", sizeof(arr));//结果是7,数组大小
	printf("%llu\n", sizeof(arr + 0));//结果是4/8,首元素地址
	printf("%llu\n", sizeof(*arr));//结果是1,第一个元素
	printf("%llu\n", sizeof(arr[1]));//结果是1,第二个元素
	printf("%llu\n", sizeof(&arr));//结果是4/8,数组的地址
	printf("%llu\n", sizeof(&arr + 1));//结果是4/8,跳过一个数组的地址
	printf("%llu\n", sizeof(&arr[0] + 1));//结果4/8,第二个元素地址
	return 0;
}

练习4

int main()
{
	char arr[] = "abcdef";
	printf("%lld\n", strlen(arr));//结果是6,数组首元素地址,也就是起始位置从a开始
	printf("%lld\n", strlen(arr + 0));//结果是6,首元素地址开始,也就是起始位置从a开始
	printf("%lld\n", strlen(*arr));//结果是非法访问,首元素a,也就是访问97的地址,err
	printf("%lld\n", strlen(arr[1]));//结果是非法访问,第二个元素b,也就是访问98的地址,err
	printf("%lld\n", strlen(&arr));//结果是6,数组地址,也就是起始位置从a开始
	printf("%lld\n", strlen(&arr + 1));//结果是随机值,跳过一个数组的地址
	printf("%lld\n", strlen(&arr[0] + 1));//结果是5,第二个元素地址,也就是起始位置从b开始
	return 0;
}

练习5

int main()
{
	char* p = "abcdef";
	printf("%lld\n", sizeof(p));//结果是4/8,指针变量
	printf("%lld\n", sizeof(p + 1));//结果是4/8,b的地址
	printf("%lld\n", sizeof(*p));//结果是1,首元素a
	printf("%lld\n", sizeof(p[0]));//结果是1,首元素a,p[0]===*(p+0)
	printf("%lld\n", sizeof(&p));//结果是4/8,指针变量p的地址
	printf("%lld\n", sizeof(&p + 1));//结果是4/8,跳过p变量的地址
	printf("%lld\n", sizeof(&p[0] + 1));//结果是4/8,第二个元素地址
	return 0;
}

练习6

int main()
{
	char* p = "abcdef";
	printf("%lld\n", strlen(p));//结果是6,首元素地址起始
	printf("%lld\n", strlen(p + 1));//结果是5,第二个元素b的地址开始
	printf("%lld\n", strlen(*p));//结果是非法访问,首元素a,也就是访问97的地址,err
	printf("%lld\n", strlen(p[0]));//结果是非法访问,首元素a,也就是访问97的地址,err,*(p+0)==p[0]
	printf("%lld\n", strlen(&p));//结果是随机值,p的地址,从p所占空间起始位置查找
	printf("%lld\n", strlen(&p + 1));//结果是随机值,p+1的地址,跳过p的地址位置查找
	printf("%lld\n", strlen(&p[0] + 1));//结果是5,第二个元素b的地址开始
	return 0;
}

2.3二维数组

int main()
{
	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,第一行数组名,即第一行元素的大小
	//第一行数组名单独放在sizeof内部,计算的是第一行大小
	printf("%d\n", sizeof(a[0] + 1));//结果是4/8,第一行第二个元素地址
	//a[0]是第一行数组的数组名,但是数组名并非单独放在sizeof内部,
	// 所以数组名是数组首元素地址,+1就是第一行第二个元素地址
	printf("%d\n", sizeof(*(a[0] + 1)));//结果是4,第一行第二个元素
	printf("%d\n", sizeof(a + 1));//结果是4/8,第二行地址
	//没有单独放在sizeof内部,没有&,就是首元素地址,第一行地址
	printf("%d\n", sizeof(*(a + 1)));//结果是16,第二行大小==a[1]
	printf("%d\n", sizeof(&a[0] + 1));//结果是4/8,第二行地址
	printf("%d\n", sizeof(*(&a[0] + 1)));//结果是16,第二行大小
	printf("%d\n", sizeof(*a));//结果是16,首元素
	//首元素地址解引用
	// *(a+0)==a[0]
	printf("%d\n", sizeof(a[3]));//结果是16,第四行大小,不会越界
	return 0;
}

注意:

  • 1.sizeof(数组名)表示整个数组,计算整个数组大小
  • 2.&数组名,表示整个数组,整个数组的地址
  • 3.除此之外,所有数组名都是首元素地址

3.指针运算练习题

练习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;
}//2 5

解释:&a+1是跳过整个数组的地址,强制转换为int*复制给ptr, (a+1)表示第二个元素,(ptr-1)表示最后一个元素
练习2

//在X86环境下
//假设结构体的⼤⼩是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
	printf("%p\n", p + 0x1);//p+1    //跳过一个结构体,结构体是20个字节,0x100014
	printf("%p\n", (unsigned long)p + 0x1); //0x100001
	// 无符号整型
	printf("%p\n", (unsigned int*)p + 0x1);//0x100004
	//整形指针+1(整型)
	return 0;
}

练习3

int main()
{
	int a[4] = { 1, 2, 3, 4 };//a数组
	int* ptr1 = (int*)(&a + 1);//ptr1存放的是跳过数组的地址
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	//ptr[-1],*(ptr-1), 向前移动一个整形,指向4,解引用得到4,十六进制打印,和十进制4一样
	//小端
    //a指向01000000,强换成int, +1就是1,在转换成int*, 指向01后面的,00,给ptr2,取四个整型, 00000002
    //取出来还原成0x02000000,即*ptr2是20000000
	return 0;
}

练习4

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式,从左向右,最后一个表达式是结果,1,3,5
	//数组元素为,1,3,5,0,0,0
	int* p;
	p = a[0];//&a[0][0]
	printf("%d", p[0]);//*(p+0) 1
	return 0;
}

练习5

int main()
{
	int a[5][5];//a数组5行5列
	int(*p)[4];//p是一个存放有4个整型元素数组的数组指针
	p = a;//p指向a
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	//&p[4][2]-&a[4][2]表示(p+4*4+2)-(a+4*5+2)=p-a-4,p和a的起始地址相同,差值为-4
	//FFFFFFFC,-4
	//跳过一个整型,指针-指针=元素个数
	//%p是输出地址,输出-4的补码0xFFFFFFFC, %d输出整数,即为-4
	return 0;
}

练习6

int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//aa是一个2行5列的数组
	int* ptr1 = (int*)(&aa + 1);//ptr1指向跳过整个数组的地址
	int* ptr2 = (int*)(*(aa + 1));//第二行地址
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10   5
	//-一个整型,10,-一个整型,5
	return 0;
}

练习7

int main()
{
	char* a[] = { "work","at","alibaba" };//字符指针数组a,分别存放指向3个字符串的首字符地址
	char** pa = a;//首元素地址
	pa++;//第二个元素地址
	printf("%s\n", *pa);//at
	return 0;
}

练习8

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	//字符指针数组.分别是指向"ENTER","NEW","POINT","FIRST"的首字符地址 
	char** cp[] = { c + 3,c + 2,c + 1,c };
	//cp存放的是{c+3, c+2, c+1, c},分别是存放,指向FIRST的地址, 指向POINT地址, 指向NEW地址,指向,ENTER的地址, 
	char*** cpp = cp;
	//cpp指向cp,即首元素地址,c+3的地址
	printf("%s\n", **++cpp);
	//++cpp, 跳过第一个元素指向cp的第二个元素c+2,
	//*++cpp表示c+2,指向POINT的首字母地址,**++cpp拿出c+2里面的内容,,从P开始打印,表示POINT,
	printf("%s\n", *-- * ++cpp + 3);
    //++cpp指向,指向cp第三个元素,指向c+1, ,*++cpp得到c+1,
   //--*++cpp,c+1, --c,得到c,指向变为ENTER
   //*--*++cpp, 拿到E的地址,
   // *--*++cpp+3指向ENTER第四个元素
   //从E开始得到ER
	printf("%s\n", *cpp[-2] + 3);
	//cpp[-2],是 *(cpp-2)
    //指向c+3, 解引用得到c+3, 
    //*cpp[-2],**(cpp-2)+3
    //解引用得到FIRST,
    //从首字母地址开始+3
    //*cpp[-2]+3, 从第四个位置开始,即ST
	printf("%s\n", cpp[-1][-1] + 1);
	//指向还是c
    //cpp[-1][-1] + 1,*(*(cpp-1)-1)+1
    //cpp-1, 指向c+2, 解引用得到c+2, 指向POINT的地址
    //*(*(cpp-1)-1),指向NEW得地址,首字母地址+1, 指向E, 从E开始EW
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值