C语言——指针练习题

内容

1.sizeof与strlen计算
2.指针面试题

*1.sizeof与strlen计算

  • 32位操作系统下,不管什么类型指针都占 4 个字节的内存大小;
  • strlen(const char*);

(1) 一维数组相关计算

	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a)); //16 数组中有4个整型Int类型的元素
	printf("%d\n", sizeof(a + 0)); //4 数组名参与运算时会隐式转换为指向首元素(地址)的指针;再加减操作时指针移动的字节数;移动完仍是指针Int* 类型
	printf("%d\n", sizeof(*a)); //4 数组名隐式转化为指针 int* ,在进行解引用成 Int 类型
	printf("%d\n", sizeof(a + 1)); //4 数组名参与运算转化为指针,在加减仍为int* 类型 
	printf("%d\n", sizeof(a[1])); //4 表示数组第二个元素所占内存大小,类型位int
	printf("%d\n", sizeof(&a)); //4 int[4]数组->int(*)[4]数组指针
	printf("%d\n", sizeof(*&a)); //16 &a:int(*)[4]->再解引用得到int[4]
	printf("%d\n", sizeof(&*a)); //4 *a:inta[0]->加& 得到一个指针指向第一个元素 * a[0]
	printf("%d\n", sizeof(&a + 1)); //4 &a:int(*)[4]->再加减操作仍为指针类型
	printf("%d\n", sizeof(&a[0] + 1)); //4 &a[0]:int* +1 -> 仍为int*类型

在这里插入图片描述
(2)字符数组

	char a[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(a)); //6 数组a中 6 个元素都为char 类型
	printf("%d\n", sizeof(a + 0)); //4 类型为char *
	printf("%d\n", sizeof(*a)); //1 a转化char*类型->*解引用为char类型
	printf("%d\n", sizeof(a[1])); //1 求数组第二个元素内存大小,为char型
	printf("%d\n", sizeof(&a)); //4 &a:char(*)[6]指针类型
	printf("%d\n", sizeof(&a+1)); //4 &a:char(*)[6] 再+1->指针类型
	printf("%d\n", sizeof(&a[0]+1)); //4 &a[0]:char * 再+1->仍为指针
	printf("%d\n", sizeof(*&a)); //6 &a:char(*)[6] 再*解引用->为char[6]
	printf("%d\n", sizeof(&*a)); //4 *a:char -> &*a:char *指针类型

	printf("%d\n", strlen(a)); //不确定值 因为strlen函数结束标志是遇到\0结束计算,不确定遍历完数组元素之后往后哪一个位置为0/\0;
	printf("%d\n", strlen(a + 0)); //不确定值 同上传入指针进行遍历计算遇到\0结束
	printf("%d\n", strlen(*a)); //未定义行为 *a为char类型 strlen参数为 const char * 类型 
	printf("%d\n", strlen(a[1])); //未定义行为 同上
	printf("%d\n", strlen(&a)); //未定义行为 &a:char(*)[6]类型 与strlen实参const char*类型不匹配
	printf("%d\n", strlen(&a + 1)); //未定义行为 &a+1 相当于数组指针跳过数组指向后一个字节 仍然是参数不匹配
	printf("%d\n", strlen(&a[0] + 1)); //未定义行为 &a[0]+1 -> 相当于传入一个指向 'b'的char *

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
a[]={‘a’,‘b’,‘c’,‘d’,‘e’,'f};

  • strlen(a) //本身a为字符数组,不是字符串;不能使用该函数没有意义;会返回一个不确定的值;遍历完数组会继续在内存中查找直到遇到\0结束函数;
  • strlen(&a) //未定义行为,&a为char(*)[]类型的数组指针;该函数参数为const char * 参数不匹配,在C++中编译不通过,但在C语言中会把char( * )[] ->转化为 char *,相当于传入一个‘a’的地址;

(3) 字符串

	char a[] = "abcdef";
	printf("%d\n", sizeof(a)); //7 字符串常量,默认结尾有\0,故char型数组a[7]
	printf("%d\n", sizeof(a + 0)); //4 char *
	printf("%d\n", sizeof(*a)); //1 a隐式转换为char * -> 再解引用为char
	printf("%d\n", sizeof(a[1])); //1 char
	printf("%d\n", sizeof(a[1]+1)); //4 a[1]:char -> 再+1 整型提升得到 int 
	printf("%d\n", sizeof(&a)); //4 &a:char(*)[7]
	printf("%d\n", sizeof(&a + 1)); //4 char(*)[7]+1 仍为指针类型
	printf("%d\n", sizeof(&a[0] + 1)); //4 char *
	printf("%d\n", sizeof(*&a)); //7 &a:char(*)[7] -> 再解引用 char[7]
	printf("%d\n", sizeof(&*a)); //4 *a:char * -> 再& 得到char * 
	printf("%d\n", sizeof(*&a + 1)); //4 *&a得到数组 +1 -> char *
	printf("%d\n", sizeof(*&(a + 1))); //编译不过  a + 1 这个表达式不是左值, 没有在内存中创建空间, 不能 & 

	 printf("%d\n", strlen(a));  //6 计算字符串“abcdef”长度
	 printf("%d\n", strlen(a + 0));	//6 从 'a' 这个位置开始找 \0
	 //printf("%d\n", strlen(*a));    //未定义行为  *a 得到了 char, strlen 形参是 const char* 
	 //printf("%d\n", strlen(a[1]));	//未定义行为 
	 printf("%d\n", strlen(&a));	//6  理论上来讲是不应该编译通过的. &a 得到了 char(*)[7] 这个类型, strlen 要求的是 const char* 类型, 此处在 C 中会触发隐式类型转换. 地址值不变, 相当于从 'a' 往后找 '\0'
	 //printf("%d\n", strlen(&a + 1)); //未定义行为  &a + 1 导致指针跳过 整个数组, 也跳过了 \0
	 printf("%d\n", strlen(&a[0] + 1));	//5  a[0] 'a' , 再取地址, 得到了 char*, 再 + 1, 指向了 'b' 

在这里插入图片描述

  • sizeof(*&(a+1)); 编译错误!!! &必须要求左值;a+1 并没有被创建,再内存中没有地址,故不能作为左值;

(4) 字符串指针

	char* p = "abcdef";
	printf("%d\n", sizeof(p)); //4 p为char *类型
	printf("%d\n", sizeof(p + 1)); //4 仍为char *
	printf("%d\n", sizeof(*p));    //1 char
	printf("%d\n", sizeof(p[0]));  //1 p[0] -> char
	printf("%d\n", sizeof(&p));    //4 &p -> char **
	printf("%d\n", sizeof(&p + 1));//4 char ** +1成为 指针类型
	printf("%d\n", sizeof(&p[0] + 1));//4 char * +1 -> char *

	 printf("%d\n", strlen(p));	//6 指针p指向字符串常量"abcdef",计算字符长度
	 printf("%d\n", strlen(p + 1));	//5 从第二个字符向后计算长度,截止到\0
	 //printf("%d\n", strlen(*p));	//未定义行为 *p => char, strlen 要求的是 const char* 类型
	 //printf("%d\n", strlen(&p));	//未定义行为 &p => char** 
	 //printf("%d\n", strlen(&p + 1));//未定义行为 同上
	 printf("%d\n", strlen(&p[0] + 1));	//5 

在这里插入图片描述
(5) 二位数组计算

	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a)); //48 总的大小  3 * 4 * 4
	printf("%d\n", sizeof(a[0][0])); //4 a[0][0] => int
	printf("%d\n", sizeof(a[0]));	  //16  a[0] => int[4]
	printf("%d\n", sizeof(a[0] + 1));	//4  a[0] => int[4] 再 + 1, 就成了 int*
	printf("%d\n", sizeof( *(a[0] + 1) ) ); //4  a[0] => int[4], 再 + 1 得到 int*, 再解引用得到 int
	printf("%d\n", sizeof(a + 1));	    //4  a是 int[3][4] , + 1 就会隐式转成指针, int(*)[4]
	printf("%d\n", sizeof(*(a + 1)));  //16 *(a + 1) => a[1]  => int[4] 
	printf("%d\n", sizeof(&a[0] + 1));	//4  a[0] => int[4], 再来 &, 得到 int(*)[4], 再来一个 + 1 还是 int(*)[4]
	printf("%d\n", sizeof( *(&a[0] + 1) ));  //16  a[0] => int[4], 再 &, 得到 int(*)[4], 再+1, 类型还是 int(*)[4], 再来 *, int[4]
	printf("%d\n", sizeof(*a));  //16 *a => a[0]-> int[4] 

在这里插入图片描述

*2.指针面试题

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

在这里插入图片描述

  • &a->类型为 char(*)[5] -得到数组指针里面存的是数组首元素地址;
  • 对于指针类型变量,强制类型转换后变量里的内容不变;
    在这里插入图片描述
    2>下面这段程序的输出结果为?
    已知:该结构体变量大小为20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
int main(){
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

分析:

  • //p+0x1:p指向结构体变量首地址(此处为0/null),+1 个字节 —> 指针跨过整个结构体(20字节)移动一位0->20;
  • //(unsigned long)p + 0x1):强制类型转换之后p变为数值的0;此处0+1=1;
  • //(unsigned int*)p + 0x1)://强制类型转换将 结构体 * p 转换为int* p;指针p变量指向空间从20变为4;+1操作-跨过自身变量后移;

在这里插入图片描述

3>下面这段程序的输出结果为?

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首元素地址为0x100,进行(int)强制类型转换得到 int 类型的整数 ;再+1 得到0x101;再进行强制类型转换为(int *)指针 指向0x101第一个元素第二个字节位;最终输出为int * 指针解引用(指向的4个字节内存空间数据);
    在这里插入图片描述
  • 打印类型 : %x -表示 以数值形式打印,高位的0 不计数;

在这里插入图片描述
4>下面这段程序的输出结果为?

	int main(int argc, char* argv[])
	{
		int a[3][2] = { (0, 1), (2, 3), (4, 5) };
		int* p;
		p = a[0];
		printf("%d", p[0]);
	}

在这里插入图片描述
在这里插入图片描述

5>下面这段程序的输出结果为?

int main(){
    int a[5][5];
	int(*p)[4];
	p = a;
	printf("a_ptr=%#p,p-ptr=%#p\n", &a[4][3], &p[4][3]);
	printf("%p,%d\n", &p[4][3] - &a[4][3], &p[4][3] - &a[4][3]);
}

在这里插入图片描述

  • %p打印 &p[4]p[3]-表示打印输出的是p[4][3] 的16进制地址;
  • 每次运行程序,操作系统都会随机分配给数组地址(每次位置不确定);
  • 指针-指针得到的是两个指针间隔的数据元素个数;
  • 在内存中地址由16进制数据表示;数据在内存中用补码表示; FFFFFFFF=-1;

在这里插入图片描述
在这里插入图片描述

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));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
}

在这里插入图片描述

在这里插入图片描述
7>下面这段程序的输出结果为?

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

在这里插入图片描述
在这里插入图片描述
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);
}

初始内存分析:
在这里插入图片描述
//经过 **++cpp 运算后的内存变化:
在这里插入图片描述
// * – * ++cpp +3 运算之后的内存分析:
在这里插入图片描述

// * cpp[-2] + 3 运算之后的内存变化:
在这里插入图片描述

// cpp[-1][-1] + 1 运算之后内存变化
在这里插入图片描述
在这里插入图片描述

补充知识:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值