深入理解C语言指针(5)

深入理解指针(五)

1.sizeof和strlen的对比

sizeof 是单目操作符 不是函数!

计算变量所占空间的大小的 单位是字节

如果操作数是类型的话 那么计算的是一个类型所占空间的大小

int a = 0;
printf("%d\n",sizeof a)  如果传进去的是变量 那么不需要括号也行 
printf("%d\n",sizeof(int)) 

综上所述 sizeof不可能是函数

再看一种情况

int a = 0;
short s = 4;
printf("%d\n",sizeof(s = a + 2)); // 2
printf("%d\n",s); // 4

sizeof内的表达式是不回进行计算的

这里对于sizeof来说就是把一个int类型的东西塞到了short类型里面 所以是2

会发生截断现象 就是内存空间里的数据 被截断了一部分 只有一部分存到了short里面

那么为什么sizeof中的表达式不会计算呢?
因为C语言是编辑型语言 在编译的时候 s就被编译器认出是shorts类型 就已经被编译成2了

strlen 是一个函数 使用它需要引入头文件 <string.h>

该函数会去计算一个字符串在遇到\0之前的长度

有种情况会导致产生随机值

char arr[]  = {'1','2','3'};
int len = strlen(arr);

这种情况下 是打印出随机值的 因为这种情况并不是字符串

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

2.1数组笔试题

2.1.1 一维数组
//一维数组

int main1()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));// 16  这里表示的是整个数组的地址 也指向首元素地址 但是+1 是跳过整个数组的字节
	printf("%d\n", sizeof(a + 0));// 8 // a是首元素的地址 类型是int* 因为这里是 a+0 地址大小就是4/8
	printf("%d\n", sizeof(*a));//4   *a是首元素  *a = a[0] = 8(a + 0)
	printf("%d\n", sizeof(a + 1));//8 a + 1是第二个元素的地址 是int* 类型  
	printf("%d\n", sizeof(a[1]));//4  这里就是第二个元素 4个字节
	printf("%d\n", sizeof(&a));// 8 &a是数组的地址 地址就是8个字节
	printf("%d\n", sizeof(*&a));// 16 这里就是首元素的地址 但是是整个数组的地址 类型是 int (*)[4]  那就是4 * int
	printf("%d\n", sizeof(&a + 1));// 8  这里是野指针
	printf("%d\n", sizeof(&a[0]));// 8
	printf("%d\n", sizeof(&a[0] + 1));// 8
	return 0;
}
2.1.2 字符数组
//字符数组
int main2()
{
	
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));// 6
	printf("%d\n", sizeof(arr + 0));// 8  地址就是8/4
	printf("%d\n", sizeof(*arr));// 1
	printf("%d\n", sizeof(arr[1]));// 1
	printf("%d\n", sizeof(&arr));// 8
	printf("%d\n", sizeof(&arr + 1));// 8
	printf("%d\n", sizeof(&arr[0] + 1));// 8  &arr = &arr[0]
	printf("%d\n", strlen(arr));// 首元素地址传进去 数组中没有 \0 结果是随机的
	printf("%d\n", strlen(arr + 0));//  这里还是地址
	//printf("%d\n", strlen(*arr));// 这里传进去的不是地址 无法执行函数 这里传进去的实际上是字符所对应的ASCII码
	 我们之前也学习过了 strlen函数的参数是 size_t strlen(const char* s)
	//printf("%d\n", strlen(arr[1]));// 无法执行函数
	printf("%d\n", strlen(&arr));// 随机值
	printf("%d\n", strlen(&arr + 1));// 随机值
	printf("%d\n", strlen(&arr[0] + 1));// 随机值
	
	
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr)); // 7  还有一个\0
	printf("%d\n", sizeof(arr + 0));// 8 地址做整数+-法之后 还是地址 大小是4/8
	printf("%d\n", sizeof(*arr));// 1
	printf("%d\n", sizeof(arr[1]));// 1
	printf("%d\n", sizeof(&arr));// 8
	printf("%d\n", sizeof(&arr + 1));// 8
	printf("%d\n", sizeof(&arr[0] + 1));// 8
	printf("%d\n", strlen(arr));// 6
	printf("%d\n", strlen(arr + 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));// 随机值  这里&arr取出的是整个元素的地址  + 1 之后 是一个野指针 是从这个字符串的最后开始寻找\0
	//但是根本找不到一点 所以是随机值
	printf("%d\n", strlen(&arr[0] + 1));// 随机值  &arr = &arr[0]
	

	
	char* p = "abcdef";
	printf("%d\n", sizeof(p));// 8
	printf("%d\n", sizeof(p + 1));// 8
	printf("%d\n", sizeof(*p));// 1  这里的p的类型是 const char* *p就是char 1个字节
	printf("%d\n", sizeof(p[0]));// 1
	printf("%d\n", sizeof(&p));// 8  对于一级指针取地址 接受它的是二级指针 相当于 char* *q = p
	printf("%d\n", sizeof(&p + 1));// 8  相当于q + 1  位移量是char* 8个字节 现在指向的是p变量的后面
	printf("%d\n", sizeof(&p[0] + 1));// 8 这里是取出首元素的地址 +1就是第二个元素的地址
	printf("%d\n", strlen(p));// 6
	printf("%d\n", strlen(p + 1));// 5
	//printf("%d\n", strlen(*p));// 错误 传进去是a
	//printf("%d\n", strlen(p[0]));//错误 
	printf("%d\n", strlen(&p));// 随机值  这里就是传入了一个二级指针 指向的是p变量的地址 和字符串就没有关系了
	// 相当于从p变量的地址开始往后数 \0的存在  
	printf("%d\n", strlen(&p + 1));// 随机值
	printf("%d\n", strlen(&p[0] + 1));// 5 从第二个字符向后数 相当于p+1
	return 0;
	
}
2.1.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 相当于放了第一个一维数组进去
	
	printf("%d\n", sizeof(a[0] + 1));// 8 a[0] 相当于第一个一维数组的地址 但是a[0]并没有单独放在sizeof里面 
	//相当于是一个首元素的地址 那么一维数组的首元素地址  也就是 a[0][0] 的地址  那么+1过后就是a[0][1]的地址  而地址的大小是8/4
	
	printf("%d\n", sizeof(*(a[0] + 1)));// 4 这里相当于解引用a[0][1]的地址 是一个整形 4个字节
	
	printf("%d\n", sizeof(a + 1));// 8  a并没有单独放在sizeof里面 因此 这里相当于首元素的地址 而二维数组的元素是一维数组
	//那么这里+ 1 就是指向第二个一维数组的地址 这里地址进行运算后还是地址 那么就是8
	
	printf("%d\n", sizeof(*(a + 1)));// 16 解引用整个二维数组的地址相当于一个一维数组的地址
	
	printf("%d\n", sizeof(&a[0] + 1));// 8 &a[0]是取出了第一行数组的地址 + 1相当于 指向了第二个一维数组的地址 地址的大小就是8/4
	
	printf("%d\n", sizeof(*(&a[0] + 1)));// 这里相当于解引用第二个一维数组的地址 那么就是把第二个一维数组传入 就是16
	
	printf("%d\n", sizeof(*a));// 16  a没有单独放到sizeof里面  这里是二维数组的首元素地址 也就是第一个一维数组的地址
	// 解引用之后就是 把第一个一维数组传进去 那么就是16  
	// 在sizeof里面 这三个是等价的 *a == *(a + 0)== a[0]
	
	printf("%d\n", sizeof(a[3]));// 16 其实这里是越界的 但是sizeof并不计算 只知道这个a[3]是一个什么类型
	// 就是拥有四个整数的一维数组 
	// a[3]是第四行的数组名 单独放在sizeof里面 计算的是第四行的大小
	return 0;
}

2.2 指针笔试题

1.第一题

int main4()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);// 为什么要强制转换呢
	// 因为这里的&a取出的是整个数组的地址 +1后 还是数组指针  类型是 int (*)[5]
	printf("%d,%d", *(a + 1), *(ptr - 1));// 2 5   -1 是因为越界了 
	return 0;
}
2.第二题
//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n", p + 0x1);// 0x100020  这里+1 跳过一个结构体
	printf("%p\n", (unsigned long)p + 0x1);// 0x100021整型值+1 就是+1   这里不是指针+1  这里没有*号
	printf("%p\n", (unsigned int*)p + 0x1);// 10x10004  这里把p强制转化成int*类型了 + 1就是4个字节的大小
	return 0;
}
3.第三题
#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) }; // 注意了这里不是大括号
	// 小的括号就变成逗号表达式了 所以  a[3][2] = {1,3,5,0,0,0};
	int* p;
	p = a[0];// 这里其实是首元素的地址 这里本来是第一个一维数组的地址   所以这里其实是a[0][0] 的地址
	printf("%d", p[0]);// 1 p[0] 相当于 *(p + 0) == *p 所以访问的就是 a[0][0] 的地址 就是1
	return 0;
}

4.第四题(有点难度)
int main()
{
	int a[5][5];
	int(*p)[4];// p是一个数组指针
	p = a;// a的首元素地址 是第一行数组的地址 类型 是 int (*)[5]
	// 但是p 的类型 是 int (*)[4]  这里类型不一样  会警告 但是还是可以运行
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);// p[4][2] 等价于 *(*(p+4)+2)
	// FFFFFFFFFFFFFFFC,-4   因为是小地址减去大地址 所以是-4 代表两个地址之间差了四个元素
	return 0;
}

图片解析

image-20240327121423241

5.第五题
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);
	//ptr1 实际指向了数组 a 之后的位置,ptr1[-1] 将指向数组 a 最后一个元素(值为4)。
	//*ptr2:由于 ptr2 指向 a 地址后的第一个字节,它并不指向有效的内存位置,因此会产生未定义行为。
	return 0;
}
6.第六题
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));// 这里指向第二行一维数组的首元素地址 是6
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); // 10 5
	return 0;
}

image-20240327122110166

7.第七题
int main()
{
	char* a[] = { "work","at","alibaba" };//指针数组 数组里面存放的是char*类型的指针
	char** pa = a; // 将首元素地址传给pa  就是"work"
	pa++;// pa+1 让pa指向了at
	printf("%s\n", *pa);// at  // *pa 解引用就是at
	return 0;
}

image-20240327122535477

8.第八题(有难度)
原题:
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;
}
解析:

先把各级指针的关系标出来

image-20240327142423338

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  此时的cpp = cpp + 1

	printf("%s\n", *-- * ++cpp + 3);// ER
	//先进行++cpp的计算  由于之前已经是cpp+1 了 所以这里会变成 cpp+2
	//这个时候cpp指向的就是c + 1的地址  进行解引用  拿到了c + 1的内存空间
	//接下来就是 --c + 1  那么就是 c   此时c指向的就是c数组的首元素地址  接着在 *c  拿到ENTER的内存空间
	//*c + 3  就是对ENTER的首元素地址+3  那么就是指向E  所以结果就是 ER

	printf("%s\n", *cpp[-2] + 3);// ST
	// 由于之前的两次 cpp++  现在的cpp是等于cpp + 2  而cpp[-2] 等价于  *(cpp - 2) 所以此时*(cpp + 2 - 2) 
	// 相当于对指向c + 3的指针进行解引用  这个时候拿到的就是c + 3 的内存空间 这里*cpp[-2] 现在就是 *(c + 3)
	// 那么拿到的就是FIRST 的首元素地址 +3 那么指向的就是S的地址  所以是 ST

	printf("%s\n", cpp[-1][-1] + 1);// EW
	// 由于之前的两次 cpp++  现在的cpp是等于cpp + 2 而cpp[-1] 等价于*(cpp - 1) 因此等价于 *(cpp + 2 - 1)
	// 那么此时就是指向的c+2的地址  解引用  拿到的就是c + 2的内存空间  此时的cpp[-1][-1]等价于  *(c + 2 - 1)== *(c + 1)
	// 对c + 1进行解引用  c + 1指向的是NEW的地址  解引用后拿到的就是NEW的首元素地址  + 1 后 指向的就是E的地址  所以是EW
	return 0;
}

那么拿到的就是FIRST 的首元素地址 +3 那么指向的就是S的地址 所以是 ST

printf("%s\n", cpp[-1][-1] + 1);// EW
// 由于之前的两次 cpp++  现在的cpp是等于cpp + 2 而cpp[-1] 等价于*(cpp - 1) 因此等价于 *(cpp + 2 - 1)
// 那么此时就是指向的c+2的地址  解引用  拿到的就是c + 2的内存空间  此时的cpp[-1][-1]等价于  *(c + 2 - 1)== *(c + 1)
// 对c + 1进行解引用  c + 1指向的是NEW的地址  解引用后拿到的就是NEW的首元素地址  + 1 后 指向的就是E的地址  所以是EW
return 0;

}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值