8_指针的进阶

指针的进阶

一. 字符指针

1. 形式

  • 字符指针 char*

2. 使用方式

  • 一般使用
int main()
{
 char ch = 'w';
 char *pc = &ch;
 *pc = 'w';
 return 0;
}
  • 另一种使用方式
int main()
{
 const char* pstr = "hello bit.";//这里是把一个表达式首字母的地址放到pstr指针变量里了
 printf("%s\n", pstr);
 return 0;
}

3. 一道面试题

#include <stdio.h>
int main()
{
 char str1[] = "hello bit.";
 char str2[] = "hello bit.";
 const char *str3 = "hello bit.";
 const char *str4 = "hello bit.";
 if(str1 ==str2)
 printf("str1 and str2 are same\n");
 else
 printf("str1 and str2 are not same\n");//输出
 
 if(str3 ==str4)
 printf("str3 and str4 are same\n");//输出
 else
 printf("str3 and str4 are not same\n");
 
 return 0;
}
  • 最终输出的是:
    在这里插入图片描述

  • 这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

  • 注意:&str3 不等于&str4

二.指针数组

1. 定义

  • 指针数组是一个存放指针的数组。

2. 使用

int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组

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

在这里插入图片描述

三.数组指针

1. 数组指针的定义

  • 数组指针能够指向数组的指针。
int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

2. &数组名VS数组名

#include <stdio.h>
int main()
{
 int arr[10] = {0};
 printf("%p\n", arr);
 printf("%p\n", &arr);
 return 0;
}

在这里插入图片描述

#include <stdio.h>
int main()
{
 int arr[10] = { 0 };
 printf("arr = %p\n", arr);
 printf("&arr= %p\n", &arr);
 printf("arr+1 = %p\n", arr+1);
 printf("&arr+1= %p\n", &arr+1);
 return 0;
}

在这里插入图片描述

  • 根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
  • 实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。
  • 本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型,数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40.

3. 数组指针的使用

#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,0};
 int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p**[ ]里面的大小无论什么情况都必须写(不初始化默认为0)**
 //但是我们一般很少这样写代码
 return 0;
}
  • 注意:p[i] -> *(p+i)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 一维数组正确访问姿势

  • 一个指针数组的使用

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
void print_arr2(int (*arr)[5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
int main()
{
 int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
 print_arr1(arr, 3, 5);
 //数组名arr,表示首元素的地址
 //但是二维数组的首元素是二维数组的第一行
 //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
 //可以数组指针来接收
 print_arr2(arr, 3, 5);
 return 0;
}

4. 练习

int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];

在这里插入图片描述

四.数组参数,指针参数

1. 一维数组传参

#include <stdio.h>
void test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int *arr)//ok
{}
void test2(int *arr[20])//ok
{}
void test2(int **arr)//ok(一维指针的地址,用二级指针存储)
{}
int main()
{
 int arr[10] = {0};
 int *arr2[20] = {0};
 test(arr);
 test2(arr2);
}

2.二维数组传参

void test(int arr[3][5])//ok
{}
void test(int arr[][])//no,行可以省略,列不可以省略
{}
void test(int arr[][5])//ok?
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//no
{}
void test(int* arr[5])//no
{}
void test(int (*arr)[5])//ok//数组指针
{}
void test(int **arr)//no
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}

3. 一级指针传参

#include <stdio.h>
void print(int *p, int sz)
{
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", *(p+i));//也可写成p[i]
 }
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int *p = arr;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);
 return 0;
}
  • 注意:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
    在这里插入图片描述

4.二级指针传参

#include <stdio.h>
void test(int** ptr)
{
 printf("num = %d\n", **ptr); 
}
int main()
{
 int n = 10;
 int*p = &n;
 int **pp = &p;
 test(pp);
 test(&p);
 return 0;
}
  • 注意:当函数的参数为二级指针的时候,可以接收什么参数?
void test(char **p)
{
 
}
int main()
{
 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];
 test(&pc);
 test(ppc);
 test(arr);//Ok?
 return 0;
}

5. 函数指针

#include <stdio.h>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}
  • 输出结构
    在这里插入图片描述

输出的是两个地址,这两个地址是 test 函数的地址。 那我们的函数的地址要想保存起来,怎么保存? 下面我们看代码:

void test()
{
 printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();//正确
void *pfun2();//错误:变成函数声明了,返回值为void的函数

  • 在这里插入图片描述

  • 首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针? 答案是:

  • pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回
    值类型为void。

  • 函数解应用
    在这里插入图片描述

  • 阅读两段有趣的代码:

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
viod(*p)()指一个指针指向void函数
(void(*)())0是将0强制类型转化成上行这种类型
(*(void(*)())0())是调用这个函数并且没有传参
认为0这个地址有一个函数,调用这个函数并且没有传参

在这里插入图片描述

在这里插入图片描述

  • 代码2太复杂,如何简化:
typedef void(*pfun_t)(int);//将void(*)(int)类型重命名为pfun_t
pfun_t signal(int, pfun_t);
  • 将void(*)(int)类型重命名为pfun_t不能这么写
    在这里插入图片描述
    ,名字必须写在*旁边.
  • 平时typedef重命名名字写在后面就行
    在这里插入图片描述

6. 函数指针数组

①定义:把函数的地址存到一个数组中,那这个数组就叫函数指针数组.
int (*parr1[10])();
  • parr1先与[]结合,表示parr1是数组,数组的内容是int(*)( )类型的函数指针.
②函数指针数组的用途:转移表
  • 例子:计算器
#include<stdio.h>
void menu()
{
	printf("*******************************\n");
	printf("***  1. add        2. sub  ****\n");
	printf("***  3. mul        4. div  ****\n");
	printf("***  0. exit               ****\n");
	printf("*******************************\n");
}
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}


int main()
{
	int input = 0;
	int x = 0;
	int y = 0;

	do
	{
		menu();
		printf("请选择->");
		scanf("%d", &input);
		int ret = 0;
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("ret=%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret=%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("ret=%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("ret=%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
		default:
			printf("选择错误重新选择\n");
			break;
		}
	} while (input);


	return 0;
}

  • 使用函数指针数组的实现
void menu()
{
	printf("*******************************\n");
	printf("***  1. add        2. sub  ****\n");
	printf("***  3. mul        4. div  ****\n");
	printf("***  0. exit               ****\n");
	printf("*******************************\n");
}
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}


int main()
{
	int input = 0;
	int x = 0;
	int y = 0;

	do
	{
		menu();
		printf("请选择->\n");
		scanf("%d", &input);
		int ret = 0;
		int(*pfArr[])(int, int) = { NULL,Add,Sub,Mul,Div };//转移表
		if (0 == input)
		{
			printf("退出计算器\n");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			int ret = pfArr[input](x, y);
			printf("ret=%d\n", ret);
		}
		else
		{
			printf("选择错误,请重新选择\n");
		}
	} while (input);


	return 0;
}

7. 指向函数指针数组的指针

在这里插入图片描述

(了解即可)

void test(const char* str)
{
 printf("%s\n", str);
}
int main()
{
 //函数指针pfun
 void (*pfun)(const char*) = test;
 //函数指针的数组pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 //指向函数指针数组pfunArr的指针ppfunArr
 void (*(*ppfunArr)[5])(const char*) = &pfunArr;
 return 0;
}

8. 回调函数

在这里插入图片描述

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

  • 例子1
void menu()
{
	printf("*******************************\n");
	printf("***  1. add        2. sub  ****\n");
	printf("***  3. mul        4. div  ****\n");
	printf("***  0. exit               ****\n");
	printf("*******************************\n");
}
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}

void calc(int(*pf)(int, int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);//回调函数(Add/Sub/Mul/Div)
	printf("ret=%d\n", ret);
}

int main()
{
	int input = 0;


	do
	{
		menu();
		printf("请选择->");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(Add);
			break;
		case 2:
			calc(Sub);
			break;
		case 3:
			calc(Mul);
			break;
		case 4:
			calc(Div);
			break;
		case 0:
			printf("退出计算器\n");
		default:
			printf("选择错误重新选择\n");
			break;
		}
	} while (input);


	return 0;
}
  • 例子2.先演示一下qsort函数的使用
    在这里插入图片描述

需要<stdlib.h>头文件

void qsort(void* base,//待排序数组第一个元素地址
           size_t num,//待排序数组的元素个数
           size_t size,//待排序数组中一个元素的大小
           int(* cmp)(const void* e1,const void* e2)//函数指针-cmp指向了一个函数,这个函数是用来比较两个元素的
           //e1和e2中存放的是需要比较的两个元素的地址
          )

在这里插入图片描述

void* 类型的指针 - 不能进行解引用操作,也不能进行加减整数的操作
void* 类型的指针是用来存放任意类型数据的地址
void* 无具体类型的指针
  • 使用

  • 1.排序数字
    在这里插入图片描述

  • 2.排序结构体

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

//测试sqort排序结构体数据
struct Stu
{
	char name[20];
	int age;
};

//1. 按照年龄比较
int cmp_stu_by_age(const void* e1, const void* e2)
{
	return (*(struct Stu*)e1).age - (*(struct Stu*)e2).age;
}

void test2()
{
	struct Stu arr[] = { {"zhangsan",20},{"lisi",30},{"wangwu",12} };
	int sz = sizeof(arr) / sizeof(arr[0]);	
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}

int main()
{
	//数据
	test2();
	return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//测试sqort排序结构体数据
struct Stu
{
	char name[20];
	int age;
};

//2. 按照名字比较
int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp((*(struct Stu*)e1).name, (*(struct Stu*)e2).name);
}

void test3()
{
	struct Stu arr[] = { {"zhangsan",20},{"lisi",30},{"wangwu",12} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}

int main()
{
	//数据
	test3();
	return 0;
}
  • 例子3.模拟qsort实现
#include<stdio.h>
#include<string.h>
//qsort 底层使用的快速排序
//void qsort( void* base, //待排序数据的起始地址
//	        size_t num,   //待排序数据的元素个数
//	        size_t size,  //待排序数据的一个元素的大小,单位是字节
//	        int (*compar)(const void*, const void*)//函数指针 - 指向了一个比较函数,这个函数是用来比较2个元素的
//          );
//

//int(*cmp)(const void* e1, const void* e2)
//e1是一个指针,存放了要比较的第一个元素的地址
//e2是一个指针,存放了要比较的第一个元素的地址
//e1指向的元素>e2指向的元素,返回>0的数字
//e1指向的元素=e2指向的元素,返回=0的数字
//e1指向的元素<e2指向的元素,返回<0的数字

void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void swap(char* buf1, char* buf2, size_t size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
		
	}
}

//目标:使用冒泡排序的算法,模拟实现一个排序函数,可以排序任意类型的数据
void bubble_sort(void* base, size_t num,size_t size,int(*cmp)(const void*e1,const void*e2))
{
	//冒泡排序的趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//一趟冒泡排序
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base+j*size,(char*)base+(j+1)*size)>0)
			{
				//交换
				swap((char*)base + j * size, (char*)base + (j + 1) * size,size);

			}
		}
	}
}


int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

void test1()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	bubble_sort(arr, sz,sizeof arr[0],cmp_int);
	print_arr(arr, sz);
}


struct Stu
{
	char name[20];
	int age;
};

int cmp_stu_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name , ((struct Stu*)e2)->name);
}

void test2()
{
	struct Stu arr[] = { {"zhangsan",20},{"lisi",30},{"wangwu",15} };
	int sz = sizeof(arr) / sizeof(arr[0]);//3
	bubble_sort(arr, sz,sizeof(arr[0]),cmp_stu_by_name);//或cmp_stu_by_age

}


int main()
{
	//整型数据/字符数据/结构体数据
	//测试bubble_sort,排序整型数据
	test1();

	//测试bubble_sort,排序整型数据
	test2();
	return 0;
}

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

//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//16,数组名a单独放在sizeof内部,数组名表示整个数组的大小单位是字节,是16字节
printf("%d\n",sizeof(a+0));//4/8,a并非非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+0还是首元素的地址
    //是地址大小就是4/8 Byte
printf("%d\n",sizeof(*a));//4,a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址
	//*a 就是 首元素,大小就是4Byte  //*a == *(a+0) == a[0]
printf("%d\n",sizeof(a+1));//4/8,a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+1就是第二个元素的地址
	//a+1 == &a[1]  是第2个元素的地址,是地址就是4/8个字节
printf("%d\n",sizeof(a[1]));4,a[1]就是数组的第二个元素,这里计算的就是第二个元素的大小,单位是字节 - 4
printf("%d\n",sizeof(&a));4/8,&a - 是取出数组的地址,但是数组的地址也是地址,是地址就是4/8个Byte
	//数组的地址 和 数组首元素的地址 的本质区别是类型的区别,并非大小的区别
	//a  -- int*             int * p = a;
	//&a -- int (*)[4]       int (*p)[4] = &a;
printf("%d\n",sizeof(*&a));16,对数组指针解引用访问一个数组的大小,单位是字节
	//sizeof(*&a) --- sizeof(a) //16
printf("%d\n",sizeof(&a+1));4/8,&a数组的地址,&a+1还是地址,是地址就是4/8个字节&a[0]是首元素的地址, 计算的是地址的大小 4/8 个字节
printf("%d\n",sizeof(&a[0]+1));4/8,&a[0]是首元素的地址,&a[0]+1就是第二个元素的地址,大小4/8个字节
    //arr第二个元素的写法
	//&a[1]
	//&a[0]+1
	//a+1

//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));//6,数组名arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节
printf("%d\n", sizeof(arr+0));//4/8,arr是首元素的地址==&arr[0],是地址就是4/8个字节
	//char* 
	//指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节
	//指针变量是用来存放地址的,地址存放需要多大空间,指针变量的大小就是几个字节
	//32位环境下,地址是32个二进制位,需要4个字节,所以指针变量的大小就是4个字节
	//64位环境下,地址是64个二进制位,需要8个字节,所以指针变量的大小就是8个字节
	//门缝里看指针,把指针给看扁了
	//
printf("%d\n", sizeof(*arr));1,arr是首元素的地址,*arr就是首元素,大小就是1Byte
printf("%d\n", sizeof(arr[1]));1
printf("%d\n", sizeof(&arr));4/8,&arr是数组的地址,sizeof(&arr)就是4/8个字节
printf("%d\n", sizeof(&arr+1));4/8,&arr+1 是跳过数组后的地址,是地址就是4/8个字节
printf("%d\n", sizeof(&arr[0]+1));4/8,第二个元素的地址,是地址就是4/8Byte
//strlen 求字符串长度
//统计的是在字符串中\0之前出现的字符的个数
printf("%d\n", strlen(arr));//随机值,arr是首元素的地址
printf("%d\n", strlen(arr+0));//随机值arr是首元素的地址, arr+0还是首元素的地址
printf("%d\n", strlen(*arr));//err,arr是首元素的地址, *arr就是首元素 - 'a' - 97
	//站在strlen的角度,认为传参进去的'a'-97就是地址,97作为地址,直接进行访问,就是非法访问
printf("%d\n", strlen(arr[1]));//err, 'b' - 98
printf("%d\n", strlen(&arr));//随机值,
	//&arr -- char (*)[6]
	//const char*
printf("%d\n", strlen(&arr+1));//随机值,
printf("%d\n", strlen(&arr[0]+1));//随机值,

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));7
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
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));随机值
printf("%d\n", strlen(&arr[0]+1));5

char *p = "abcdef";
printf("%d\n", sizeof(p));4/8
printf("%d\n", sizeof(p+1));4/8
printf("%d\n", sizeof(*p));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
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));随机值
printf("%d\n", strlen(&p+1));随机值
printf("%d\n", strlen(&p[0]+1));5

//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));48-数组名a单独放在了sizeof内存,表示整个数组,sizeof(a)计算的是数组的大小,单位是字节
printf("%d\n",sizeof(a[0][0]));4-a[0][0]是数组的第一行第一个元素,这里计算的就是一个元素的大小,单位是字节
printf("%d\n",sizeof(a[0]));16 - a[0]是第一行这个一维数组的数组名,数组名单独放在了sizeof内部
	//a[0]就表示整个第一行这个一维数组,sizeof(a[0])计算的整个第一行这个一维数组的大小
printf("%d\n",sizeof(a[0]+1));4/8 - a[0]并非单独放在sizeof内部,也没有&,所以a[0]表示第一行这个一维数组首元素的地址
	//也就是第一行第一个元素的地址
	//a[0] <---> &a[0][0]
	//a[0]+1 ---> &a[0][1]
printf("%d\n",sizeof(*(a[0]+1)));4 - a[0] + 1是第一行第二个元素的地址,*(a[0] + 1))就是第一行第二个元素
printf("%d\n",sizeof(a+1));4/8
	//a 作为二维数组的数组名,并没有单独放在sizeof内部,也没有&,a就是数组首元素的地址,也就是第一行的地址, a 的类型是 int(*)[4]
	//a+1 就是第二行的地址,类型是:int(*)[4]
printf("%d\n",sizeof(*(a+1)));16 a+1是第二行的地址,*(a+1)就是第二行,计算的就是第二行的大小
	//另外一个角度理解:*(a+1) -- a[1]
	//sizeof(a[1]) - a[1]这个第二行的数组名,单独放在了sizeof内部,计算的是第二行的大小
printf("%d\n",sizeof(&a[0]+1));4/8
	//a[0]是第一行的数组名,&a[0]取出的是数组的地址,取出的是第一行这个一维数组的地址,类型就是int(*)[4]
	//&a[0]+1 就是第二行的地址,类型就是int(*)[4]
printf("%d\n",sizeof(*(&a[0]+1)));//16,*(&a[0] + 1)得到的就是第二行,计算的就是第二行的大小
printf("%d\n",sizeof(*a));16
	//a表示数组首元素的地址,也就是第一行的地址
	//*a 就是第一行,也就相当于是第一行的数组名
	//*a--> *(a+0) -- a[0]
printf("%d\n",sizeof(a[3]));16-不会越界,
	//a[3] --    arr[0]
	//int [4]    int [4]
	

	//int a = 10;
	//sizeof(a);//sizeof(int)

	//表达式 有2个属性:值属性,类型属性
	//2+3    = 5
	//       = int

在这里插入图片描述

  • 总结: 数组名的意义:
    1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
    1. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
    1. 除此之外所有的数组名都表示首元素的地址。

10. 指针笔试题

  • 笔试题1:
int main()
{
 int a[5] = { 1, 2, 3, 4, 5 };
 int *ptr = (int *)(&a + 1);
 printf( "%d,%d", *(a + 1), *(ptr - 1));
        //2  5
 return 0;
}

  • 笔试题2:
//由于还没学习结构体,这里告知结构体的大小是20个字节x86环境
struct Test
{
 int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//注意给p赋值0x100000要强制类型转换,0x100000默认是int型
//已知,结构体Test类型的变量大小是20个字节
int main()
{
 printf("%p\n", p + 0x1);
 printf("%p\n", (unsigned long)p + 0x1);
 printf("%p\n", (unsigned int*)p + 0x1);
 return 0;
}
00100014
00100001
00100004
前两位00要打印出来,x86打印8位数字
  • 笔试题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);
       //4  2000000
 return 0;
}

在这里插入图片描述

在这里插入图片描述

  • 笔试题4:
#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
 return 0;
}

在这里插入图片描述

  • 笔试题5:
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]);
     //FFFFFFFFFFFFFFFC,-4
 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));
 printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
        //10  5
 return 0;
}

在这里插入图片描述

  • 面试题7:
#include <stdio.h>
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
        //at
 return 0;
}

在这里插入图片描述

  • 面试题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;
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值