『C语言初阶』第四章-指针(5)

1.sizeof 和strlen 的对比

1.1 sizeof

        在学习操作符的时候,我们学习了sizeof, sizefo 计算变量所占用内存空间大小,单位是字节,如果操作符是类型,计算的是使用类型创建的变量所所占内存空间的大小。

        sizefo 只关注占用内存空间的大小,不在乎内存中存放什么数据。

比如:

#include <stdio.h>
int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof a);
	printf("%d\n", sizeof(int));  
	//如果要计算的是类型的话,则必须放入括号内

	return 0;
}

1.2 strlen

        strlenC语言库函数,功能是求字符串长度。函数原型如下:

 统计的是从strlen函数的参数str 中这个地址开始向后,'\0' 之前字符串中字符的个数。

strlen函数会一直向后找'\0'字符,直到找到为止,所以可能存在越界查找。

如果要查找的字符串中没有‘\0’字符的话,strlen就会超出参数str的数组范围,直到在内存中找到一个'\0'字符为止,但这‘\0’字符会在哪找到谁也不知道,所以当str字符串中没有\0字符时,返回值将是随机值。

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

运行结果:

                        

        1.3sizeof 和strlen 的对比

sizeofstrlen
1.sizeof 是操作符1.strlen是库函数,需要包含头文件 string.h
2.sizeof计算操作数所占内存的大小,单位是字节2.strlen是求字符串长度的,统计的是‘\0’之前字符的个数
3.不关注内存中存放的数据3.关注内存中是否有\0,如果没有\0,就会持续往后找,可能会越界

           sizeof 不调类型,strlen 只针对字符串 

2.数组和指针笔试题解析

2.1 一维数组

int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));  //单独数组名,代表整个数组大小,16
	printf("%d\n", sizeof(a + 0));//不是单独数组名,仅代表数组首地址,4/8
	printf("%d\n", sizeof(*a));   //代表数组首元素 int类型所以 4
	printf("%d\n", sizeof(a + 1));  //代表数组第二个元素地址 4/8
	printf("%d\n", sizeof(a[1]));   //等价于*(a+1)  代表数组第二个元素 4
	printf("%d\n", sizeof(&a));		// 代表整个数组的地址,但是只要是地址就是4/8
	printf("%d\n", sizeof(*&a));	// 代表整个数组 16 可以理解为 *和&相互抵消了
	printf("%d\n", sizeof(&a + 1));  //代表整个数组后面的地址,4/8
	printf("%d\n", sizeof(&a[0]));  // 代表数字第一个元素的地址 4/8
	printf("%d\n", sizeof(&a[0] + 1));  //代表数组第二个元素的地址 4/8
	return 0;
}

2.2字符数组

代码1:

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));   //代表整个数组的大小 6
	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:

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));	
	//因为 strlen 函数中参数类型是const char* str, 
	//所以该次返回不是地址,但是strlen会把它当成地址,然后去访问,但是a转成整数 97 0x97是不允许用户访问的
	//所以会出错
	printf("%d\n", strlen(arr[1]));		  //同上
	printf("%d\n", strlen(&arr));		 //随机值
	printf("%d\n", strlen(&arr + 1));    // arr字符串整个地址后面一个地址,然后去找'\0',随机值,但是应该比前面小
	printf("%d\n", strlen(&arr[0] + 1));  //在第二个元素地址后面去找'\0',随机值

	return 0;
}

代码3:

int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));  //代表整个数组的大小 6+1('\0')
	printf("%d\n", sizeof(arr + 0)); // 代表数组首元素的地址 4/8
	printf("%d\n", sizeof(*arr));	//等价于 sizeof a  所以返回值 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;
}

代码4:

int main()
{
	char arr[] = "abcdef"; //字符串这样赋值最后会有一个隐形的'\0'
	printf("%d\n", strlen(arr));    //  6 
	printf("%d\n", strlen(arr + 0));  //传入数组首元素地址往后找'\0',6
	printf("%d\n", strlen(*arr));	//数组首元素 出错
	printf("%d\n", strlen(arr[1])); //出错
	printf("%d\n", strlen(&arr));   //6
	printf("%d\n", strlen(&arr + 1));  //整个数组地址之后的一个地址 随机值
	printf("%d\n", strlen(&arr[0] + 1));  //从数组第二个元素地址开始找'\0',5
	return 0;
}

代码5:

int main()
{
	char* p = "abcdef";  
	//将字符串"abcdef"的数组首地址赋给指针p
	printf("%d\n", sizeof(p));   //因为p不是数组 只是一个指针,所以返回值是4/8
	printf("%d\n", sizeof(p + 1)); //第二个元素的地址 4/8
	printf("%d\n", sizeof(*p));    // 相当于 sizeof(a) 1
	printf("%d\n", sizeof(p[0]));	// 同上 1
	printf("%d\n", sizeof(&p));		//整个数组指针的大小 4/8
	printf("%d\n", sizeof(&p + 1));	// 代表整个数组指针之后的地址 4/8
	printf("%d\n", sizeof(&p[0] + 1));	//代表数组第二个元素地址 4/8
	return 0;
}

代码6:

int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));  //6
	printf("%d\n", strlen(p + 1)); // 5
	//printf("%d\n", strlen(*p));  // 出错
	//printf("%d\n", strlen(p[0])); // 出错
	printf("%d\n", strlen(&p));   //从指针本身地址开始找'\0',所以是随机值
	printf("%d\n", strlen(&p + 1));  // 从指针本身地址之后的后移一个地址开始找,随机值
	printf("%d\n", strlen(&p[0] + 1));  // 从第二个元素地址开始找 5
	return 0;
}

2.3二维数组

int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a)); // 代表整个数组大小 48
	printf("%d\n", sizeof(a[0][0]));  // 代表数组a[0][0]元素的大小 4
	printf("%d\n", sizeof(a[0]));    
	// 我们之前说过二维数组,每个参数其实就是几个一维数组的数组名,
	//这里所以相等于数组a[0]的数组名 所以是16
	printf("%d\n", sizeof(a[0] + 1));  //相当于数组a[0]整个数组后一个地址,也就是第二个一维数组的首元素地址, 4/8  
	//单个数组名才是代表整个数组  a[0], a[1] a[2]
	printf("%d\n", sizeof(*(a[0] + 1)));   // 同上,相等于第二个一维数组的首元素 4
	printf("%d\n", sizeof(a + 1));  //这里 a相等于a[0]和上面第四个同理 4/8	
	printf("%d\n", sizeof(*(a + 1))); //*(a+1) 等价于 a[1] 所以是第二个一维数组名的数组名 16
	printf("%d\n", sizeof(&a[0] + 1));  // 相等于第二个一维数组的数组的地址 4/8
	printf("%d\n", sizeof(*(&a[0] + 1)));  // *(&((*(a+0))+1))   &和* 可以抵消相等于 *(a+1) == a[1]  就是第二个数组的数组名 16
	printf("%d\n", sizeof(*a));// 相等于*(a+0) 也就是a[0] 16
	printf("%d\n", sizeof(a[3]));  //
	// 数组虽然没有第四个一维数组,但是sizeof 并不在意我们所传入值的内从,它只管计算,a[3]位置的地址肯定有的,然后通过a本身的范围
	// 所以这里它还是以数组第四个一维数组名计算
	//数组第四个一维数组的数组名 16
	return 0;
}

数组名的意义:

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

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

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

3.指针运算笔试题解析

3.1题目1

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);  // 将a整个数组之后的数组地址强转为(int*) 赋值给ptr
	printf("%d,%d", *(a + 1), *(ptr - 1));  
	// *(a+1) 数组第二个元素, ptr-1 为 a整个数组之后的地址向前移动一个(int) 大小, 就是数组最后一个元素位置
	// 所以最后输出就是 2,5
	return 0;
}

3.2题目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);   
	// 代表 指针向后移动1个类型,Test 结构体类型大小是14,0x1就等于1
	printf("%p\n", (unsigned long)p + 0x1);  
	//将地址转成 无符号长整形+1 然后当成地址输出,
	printf("%p\n", (unsigned int*)p + 0x1);
	// 降低至转成无符号整型指针类型,然后向后移动一个类型,所以就是+4
	return 0;
}

运行结果:

                        

3.3题目3:

#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]); 
	// 因为 二维数组输入时用的()括号,而并非{}所以 这里实际上存的是 1,3,5 分别存进了a[0][0],a[0][1],a[0][2]
	//	所以这里输出是 1 本体需要细心观察

	return 0;
}

int main()
{
	int a[3][2] = { {0, 1 }, {2,3}, {4,5} };
	int* p;
	p = a[0];
	printf("%d", p[0]);  
	//p指针指向a[0]也就是a数组的第一行,然后p[0]就等价于 a[0][0] 所以是0
	return 0;
}

3.4 题目4:

//假设环境是x86环境,程序输出的结果是啥?
#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]);
	//p=a 代表p的头地址等于a的头地址,因为p是数组指针且大小为4,所以p[4][2]代表在a数组的地址上后移4次p个地址大小,然后的第2个地址
	//即 p+4*4+2 的位置,减去a[4][2]的结果输出地址,和整数(也就是之间隔多少个地址)
	//最后输出是FFFFFFFC -4的补码,内存中都是存储的补码
	// -4
	return 0;
}

3.5 题目5

#include <stdio.h>
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));   
	//prt1等于整个数组之后的一个地址,ptr-1 则是数组中最后一个元素 ——10
	//ptr2 则是二位数组中第二个一维数组的数组名 ptr2-1 则是第一个一维数组的最后一个元素——5
	// 10,5
	return 0;
}

3.6题目6

#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;  // *pa="work" **pa="w"
	pa++;  // pa++ 相等于a++ 如果要指向o的话则要 *pa++
	printf("%s\n", *pa);  // at
	return 0;
} 

3.7 题目7

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);   // *(++cpp)=  c+2  再取数之后 就是PINOINT
	// POINT
	printf("%s\n", *-- * ++cpp + 3);    //+3运算级最低,最后计算 
	// ER
	printf("%s\n", *cpp[-2] + 3);     // cpp[-2]等价于 *(cpp-2)
	// ST
	printf("%s\n", cpp[-1][-1] + 1);   // cpp[-1][-1] 等价于 *(*(cpp-1)-1)
	///EW 
	return 0;
}

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值