进阶第二节-指针的进阶

1. 字符指针

#include <stdio.h>
int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'b';
	printf("%c\n", ch);

	const char* p = "abcdef";//把字符串首字符a的地址,赋值给了p
	                   //"abcdef"为常量字符串,不能更改
	char arr[] = "abcdef";//把整个字符串存放进了arr数组
	printf("%s\n", p);
	return 0;
}

#include <stdio.h>
int main()
{
	const char* p1 = "abcdef"; //"abcdef"为常量字符串,不能更改
	const char* p2 = "abcdef";

	char arr1[] = "abcdef";
	char arr2[] = "abcdef";

	if (p1 == p2)
		printf("p1=p2\n");//p1 == p2
	else
		printf("p1!=p2\n");

	if (arr1 == arr2)
		printf("arr1=arr2\n");
	else
		printf("arr1!=arr2\n");//arr1 != arr2
	return 0;
}


//总结:C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,
//他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。

2. 指针数组 

int *arr[10]; //数组的每个元素是int*


#include<stdio.h>
int main()
{
	int arr1[4] = { 1,2,3,4 };
	int arr2[4] = { 2,3,4,5 };
	int arr3[4] = { 3,4,5,6 };

	int* parr[3] = { arr1, arr2, arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", parr[i][j]);//*(parr[i]+j)
		}
		printf("\n");
	}
	return 0;
}

3. 数组指针 

3.1 数组指针的定义

int (*p)[10];

 //解释:p先和*结合,说明p是一个指针变量,然后指向一个大小为10个整型的数组。
 //所以p是一个指向数组的指针,叫做数组指针,该数组有10个元素,每个元素的类型是int
 //这个数组指针的类型是int(*)[10]

//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p1 = arr;//取出数组首元素的地址

	int(*p2)[10] = &arr;//取出整个数组的地址

	//整型指针是指向整型的指针,用来存放整型的地址
	//字符指针是指向字符的指针,用来存放字符的地址
	//数组指针是指向整型的指针,用来存放数组的地址
	return 0;
}

3.2 &数组名VS数组名

//再次讨论数组名
#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", arr + 1);//相差4个字节

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0] + 1);//相差4个字节

	printf("%p\n", &arr);
	printf("%p\n", &arr + 1);//相差40个字节
	return;
}

//数组名通常表示的都是数组首元素的地址
//但有两个例外:
//1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
//2.&数组名,这里的数组名表示依然是整个数组,所有&数组名取出的是整个数组的地址
//单位:字节

 3.3 数组指针的使用

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*p)[10] = &arr;//[10]里面的10不能省略

	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(*p + i));//1 2 3 4 5 6 7 8 9 10
		//p是指向数组的,相当于数组的地址,*p其实就相当于数组名arr,
		//数组名又是数组首元素的地址,所以*p本质上就是数组首元素的地址
	}

	return 0;
}

#include<stdio.h>
void print1(int arr[3][5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

//二维数组arr的首元素地址是第一行的地址,第一行是一个一维数组
//所以取出的是第一行数组的地址

//数组指针int(*p)[5] = &arr
//p的类型是:int(*)[5]
//p+1 --> 跳过一个5个int元素的数组

void print2(int(*p)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", *(*(p + i) + j));
			//*(p+i) --> p[i];*(p[i]+j) --> p[i][j]
			//printf("%d ", p[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
	print1(arr, 3, 5);
	print2(arr, 3, 5);
	return 0;
}

 4. 数组参数、指针参数

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

4.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);//arr表示首元素的地址,每个元素的类型是int 
	test2(arr2);//arr2表示首元素的地址,每个元素的类型是int*

}

4.2 二维数组传参

#include<stdio.h>
void test(int arr[3][5])//ok
{}
void test(int arr[][])//err - 二维数组的行可以省略,列不能省略
{}
void test(int arr[][5])//ok
{}
void test(int* arr)//err
{}
void test(int* arr[5])//err - 这是一个指针数组
{}
void test(int(*arr)[5])//ok
{}
void test(int** arr)//err - 这是一个二级指针
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);//二维数组的数组名表示首元素的地址,其实是第一行的地址
		      //第一行是一个一维数组
}

4.3 一级指针传参

 4.4 二级指针传参

5. 函数指针

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int arr[10] = { 0 };
	int (*parr)[10] = &arr;
	//&数组名 - 取出的是数组的地址
	//parr 是指向数组的指针 - 存放的是数组的地址

	//对于函数来说,&函数名和函数名都是函数的地址
	printf("%p\n", &Add);
	printf("%p\n", Add);

	//函数指针 - 存放函数地址的指针
    //&函数名 - 取到的就是函数的地址
    //pf就是一个函数指针变量
	int (*pf)(int, int) = &Add;//add == pf
	int ret = (*pf)(2, 3);//*可以省略,但使用*的话一定要加()
	printf("%d\n", ret);//5
	return 0;
}

5.1 函数指针的用法 

#include<stdio.h>
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 menu()
{
	printf("**************************\n");
	printf("**** 1. add    2. sub ****\n");
	printf("**** 3. mul    4. div ****\n");
	printf("****     0. exit      ****\n");
	printf("**************************\n");
}

//回调函数
void Calc(int (*pf)(int, int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入2个操作数>:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("ret = %d\n", ret);
}

int main()
{
	int input = 0;
	//计算器-计算整型变量的加、减、乘、除
	//a&b a^b a|b a>>b a<<b a>b

	do {
		menu();
	
		int ret = 0;
		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");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
		
	} while (input);
	return 0;
}
#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
void calc(int(*pf)(int, int))
{
	int a = 3;
	int b = 5;
	int ret = pf(a, b);
	printf("%d\n", ret);//8
}
int main()
{
	calc(Add);
	return 0;
}

阅读两段有趣的代码: 

//代码1
(*(void (*)())0)();


//调用0地址处的函数
//该函数无参,返回类型是void
//1. void(*)() - 函数指针类型
//2. (void(*)())0 - 对0进行强制类型转换,被解释为一个函数地址
//3. *(void(*)())0 - 对0地址进行了解引用操作
//4. (*(void(*)())0)() - 调用0地址处的函数

#include<stdio.h>
//typedef unsigned int uint;

typedef void(* pf_t)(int);//把void(*)(int)类型重命名为pf_t
int main()
{
	void (*signal(int, void(*)(int)))(int);//代码2

	pf_t signal(int, pf_t);
	return 0;
}

6. 函数指针数组 

#include<stdio.h>
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 (*pf)(int, int) = Add;//pf是函数指针

	int (*arr[4])(int, int) = { Add, Sub, Mul, Div };//arr是函数指针的数组
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int ret = arr[i](8, 4);
		printf("%d\n", ret);//12 4 32 2
	}
	return 0;
}

6.1转移表 

#include<stdio.h>
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 menu()
{
	printf("**************************\n");
	printf("**** 1. add    2. sub ****\n");
	printf("**** 3. mul    4. div ****\n");
	printf("****     0. exit      ****\n");
	printf("**************************\n");
}

int main()
{
	int input = 0;
	//计算器-计算整型变量的加、减、乘、除
	//a&b a^b a|b a>>b a<<b a>b

	do {
		menu();

		//pfArr就是函数指针数组
        //转移表
		int (*pfArr[])(int, int) = { NULL, Add, Sub, Mul, Div };//NULL写成0也行
		int x = 0;
		int y = 0;
		int ret = 0;
		printf("请选择:>");
		scanf("%d", &input);//2

		if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数>:");
			scanf("%d %d", &x, &y);
			ret = (pfArr[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if(input == 0)
		{
			printf("退出程序\n");
			break;
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

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

#include<stdio.h>
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 (*pf)(int, int) = Add;//pf是函数指针

	int (*arr[4])(int, int) = { Add, Sub, Mul, Div };//arr是函数指针的数组

	//指向【函数指针数组】的指针
	int (*(*parr)[4])(int, int) = &arr;
	return 0;
}

8. 回调函数


//void* 的用法

#include <stdio.h>
int main()
{
	int a = 10;
	char* pa = &a;//&a的类型是int*

	void* pv = &a;//void*是无具体类型的指针,可以接受任意类型的地址
	              //void*是无具体类型的指针,所以不能解引用操作,也不能+-整数
	return 0;
}
//库函数qsort - 这个函数可以排序任意类型的数据

//void qsort(void* base, //base中存放的是待排序数据中第一个对象的地址
//			size_t num, //排序数据元素的个数
//			size_t size,//排序数据中一个元素的大小,单位是字节
//			int (*cmp)(const void* e1, const void* e2)//是用来比较待排序数据中的2个元素的函数
//			);                                        //cmp函数是一个比较函数的地址
													  //e1,e2是要比较的两个元素的地址 
//写一个回调函数实现冒泡排序,排序字符串

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

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

int cmp_int(const void* e1, const void* e2)
{
	return (*(int*)e1 - *(int*)e2);//降序的话把e1,e2调换
}

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

int sort_by_name(const void* e1, const void* e2)
{
	//strcmp --> >0 ==0 <0
	return strcmp((((struct Stu*)e1)->name), (((struct Stu*)e2)->name));
}

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, int width)
{
	//一个元素有几个字节就交换几次
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

//模仿qsort实现一个冒泡排序的通用算法
void bubble_sort(void* base,int sz,int width,int (*cmp)(const void*e1, const void*e2) )
{
	int i = 0;
	//趟数
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;//假设数组是排好序的
		//一趟的排序
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//两个元素比较
			//arr[j] arr[j+1]
			//int类型进行比较,width=4
			if (cmp( (char*)base+j*width, (char*)base+(j+1)*width )>0)
			{
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

void test3()
{
	//整形数据的排序
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//排序
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	//打印
	print_arr(arr, sz);
}

void test4()
{
	//使用qsort函数排序结构体数据
	struct Stu s[3] = { {"zhangsan", 30},{"lisi", 34},{"wangwu", 20} };

	int sz = sizeof(s) / sizeof(s[0]);
	//按照年龄来排序
	bubble_sort(s, sz, sizeof(s[0]), sort_by_age);
	//按照名字来排序
	bubble_sort(s, sz, sizeof(s[0]), sort_by_name);
}

int main()
{
	test3();
	test4();
	return 0;
}

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

//sizeof(数组名) - 数组名表示整个数组-计算的是整个数组的大小
//&数组名 - 数组名表示整个数组,取出的是整个数组的地址
//除此之外,所有的数组名都是数组首元素的地址

//strlen是求字符串长度的,关注的是字符串中的\0,计算的是\0之前出现的字符的个数
//strlen是库函数,只针对字符串
//sizeof只关注占用内存空间的大小,不在乎内存中放的是什么
//sizeof是操作符

#include<stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//16 - sizeof(数组名),数组名表示整个数组,计算的是整个数组a的大小,单位是字节
	printf("%d\n", sizeof(a + 0));//4/8  
	//a不是单独放在sizeof内部,也没有取地址,所以a和a + 0是首元素的地址,
	//sizeof(a + 0)计算的是首元素地址的大小
	printf("%d\n", sizeof(*a));//4 
	//*a中的a是数组首元素的地址,*a就是对数组首元素地址进行解引用
	//sizeof(*a)计算的是首元素的大小
	printf("%d\n", sizeof(a + 1));//4/8 
	//a是数组首元素的地址,a + 1是第二个元素的地址,sizeof(a + 1)计算的是数组第二个元素地址的大小
	printf("%d\n", sizeof(a[1]));//4 - sizeof(a[1])计算的是数组第二个元素的大小,a[1]等价于*(a+1)

	printf("%d\n", sizeof(&a)); //4/8 - &a取出的是数组的地址,但也是地址,sizeof(&a)计算的是数组地址的大小
	printf("%d\n", sizeof(* &a));//16 
	//&a --> int(*)[4]
	//&a取出的是数组a的地址,类型是int(*)[4],是一个数组指针
	//数组指针解引用找到的就是数组,*&a --> a,*和&互相抵消
	//sizeof(* &a)计算的是整个数组a的大小
	printf("%d\n", sizeof(&a + 1));//4/8 
	//&a取出的是数组a的地址
	//&a -->  int(*)[4]
	//&a+1 的地址是向后跳过了(有4个int类型元素)数组a的地址
	//&a + 1 是数组a后面的空间的地址,sizeof(&a + 1)计算的是数组a后面的空间地址的大小
	printf("%d\n", sizeof(&a[0]));//4/8 - 数组a首元素的地址
	printf("%d\n", sizeof(&a[0] + 1));//4/8 - 数组a第二个元素的地址,等价于&a[1]


	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };//数组里面没有\0

	//int my_strlen(const char* str) 参数传的是一个地址

	printf("%d\n", strlen(arr));//随机值1号 - strlen计算的是字符串的长度(不计算\0),arr指的是首元素的地址
	                            //strlen(arr)代表从首元素的地址开始向后计算字符直到遇到\0为止
	printf("%d\n", strlen(arr + 0));//随机值1号 - strlen(arr + 0)代表从首元素的地址开始向后计算字符直到遇到\0为止
	printf("%d\n", strlen(*arr));//err -  strlen(*arr)把首字符'a'的ascii值(97)传入strlen()中
	printf("%d\n", strlen(arr[1]));//err - strlen(arr[1])把第二个字符'b'的ascii值(98)传入strlen()中
	printf("%d\n", strlen(&arr));//随机值1号 - &arr的类型是char(*)[6],代表整个数组的地址;
	                             //但传到strlen后类型变成char*,变成了数组首字符的地址
	                             //strlen(&arr)代表从首元素的地址开始向后计算字符直到遇到\0为止
	printf("%d\n", strlen(&arr + 1));//随机值1号-6 - //strlen(&arr+1)代表从数组后面空间的字符串长度('f'-\0之间)
	printf("%d\n", strlen(&arr[0] + 1));//随机值1号-1 - //strlen(&arr[0]+1)代表从'b'的地址开始向后计算字符直到遇到\0为止

	printf("%d\n", sizeof(arr));//6 - sizeof(arr)计算的是整个数组的大小
	printf("%d\n", sizeof(arr + 0));//4/8 - arr + 0是首元素的地址,sizeof(arr + 0)计算的是首元素地址的大小
	printf("%d\n", sizeof(*arr));//1 
	//*arr就是数组首元素,*arr --> *(arr+0) --> arr[0]
	//sizeof(*arr)计算的是首元素的大小
	printf("%d\n", sizeof(arr[1]));//1 -  sizeof(arr[1])计算的是第二个元素的大小
	printf("%d\n", sizeof(&arr));//4/8 - &arr是数组的地址,sizeof(&arr)计算的是整个数组地址的大小
	printf("%d\n", sizeof(&arr + 1));//4/8 - &a + 1 是数组后面空间的地址,sizeof(&a + 1)计算的是数组后面空间地址的大小
	printf("%d\n", sizeof(&arr[0] + 1));//4/8 - sizeof(&arr[0] + 1)计算的是数组第二个元素地址的大小



	char arr[] = "abcdef";
	//[a b c d e f \0]
	printf("%d\n", strlen(arr));//6 - strlen(arr)代表从首元素的地址开始向后计算字符直到遇到\0为止
	printf("%d\n", strlen(arr + 0));//6 - strlen(arr + 0)代表从首元素的地址开始向后计算字符直到遇到\0为止
	printf("%d\n", strlen(*arr));//err -  strlen(*arr)把首字符'a'的ascii值(97)传入strlen()中
	printf("%d\n", strlen(arr[1]));//err - strlen(*arr)把第二个字符'b'的ascii值(98)传入strlen()中
	printf("%d\n", strlen(&arr));//6 - &arr的类型是char(*)[6],代表整个数组的地址;
	                             //但传到strlen后类型变成char*,变成了数组首字符的地址
	                             //strlen(&arr)代表从首元素的地址开始向后计算字符直到遇到\0为止
	printf("%d\n", strlen(&arr + 1));//随机值 - strlen(&arr+1)代表从数组后面空间的字符串长度
	printf("%d\n", strlen(&arr[0] + 1));//5 - strlen(&arr[0]+1)代表从第二个字符'b'的地址开始向后计算字符直到遇到\0为止

	//[a b c d e f \0] 
	printf("%d\n", sizeof(arr));//7 - sizeof(arr)计算的是整个数组的大小
	printf("%d\n", sizeof(arr + 0));//4/8 - sizeof(arr + 0)计算的是数组首元素地址的大小
	printf("%d\n", sizeof(*arr));//1 - sizeof(*arr)计算的是数组首元素的大小
	printf("%d\n", sizeof(arr[1]));//1 - sizeof(arr[1])计算的是数组第二个元素的大小
	printf("%d\n", sizeof(&arr));//4/8 - sizeof(&arr)计算的是整个数组地址的大小,类型是char(*)[7]
	printf("%d\n", sizeof(&arr + 1));//4/8 - sizeof(&arr + 1)计算的是数组后面空间地址的大小,类型是char(*)[7]
	printf("%d\n", sizeof(&arr[0] + 1));//4/8 - sizeof(&arr[0] + 1)计算的是数组第二个元素地址的大小


	char* p = "abcdef";//把首字符'a'的地址放到p里面

	printf("%d\n", strlen(p));//6 - strlen(p)代表从首元素的地址开始向后计算字符直到遇到\0为止,p指向'a'的地址
	printf("%d\n", strlen(p + 1));//5 - strlen(p+1)代表从字符'b'的地址开始向后计算字符直到遇到\0为止
	printf("%d\n", strlen(*p));//err - strlen(*p)把字符'a'的ascii值(97)传入strlen()中
	printf("%d\n", strlen(p[0]));//err - strlen(p[0])把字符'a'的ascii值(97)传入strlen()中
	printf("%d\n", strlen(&p));//随机值 - 从p的空间开始计算字符直到遇到\0为止
	printf("%d\n", strlen(&p + 1));//随机值 - 从p后面的空间开始计算字符直到遇到\0为止
	printf("%d\n", strlen(&p[0] + 1));//5 - strlen(&p[0]+1)代表从字符'b'的地址开始向后计算字符直到遇到\0为止

	printf("%d\n", sizeof(p));//4/8 - sizeof(p)计算的是数组的指针变量的大小,p代表的是a的地址
	printf("%d\n", sizeof(p + 1));//4/8 - sizeof(p + 1)计算的是字符'b'地址的大小
	printf("%d\n", sizeof(*p));//1 - sizeof(*p)计算的是字符'a'的大小
	printf("%d\n", sizeof(p[0]));//1 - sizeof(p[0]计算的是字符'a'的大小,p[0]等价于*(p+0)
	printf("%d\n", sizeof(&p));//4/8 - &p取出的是p的地址,是一个二级指针
	printf("%d\n", sizeof(&p + 1));//4/8 - &p+1取出的是p后面空间的地址
	printf("%d\n", sizeof(&p[0] + 1)); //4/8 - sizeof(&p[0] + 1)计算的是字符'b'的地址的大小


	int a[3][4] = { 0 };

	printf("%d\n", sizeof(a));//48 = 3*4*sizeof(int),sizeof(a)计算的是整个数组的大小
	printf("%d\n", sizeof(a[0][0]));//4 - a[0][0]是第一行首元素,sizeof(a[0][0])计算的是第一行首元素的大小
	printf("%d\n", sizeof(a[0]));//16 - 数组名a[0]是第一行的数组名,单独放在sizeof内部,
	                             //代表着整个第一行的地址,sizeof(a[0])计算的是数组第一行的大小
	printf("%d\n", sizeof(a[0] + 1));//4/8 - a[0]作为第一行的数组名并没有单独放在sizeof内部,也没取地址,
	                                 //所以a[0]就是第一行首元素的地址,等价于&a[0][0]+1
									 //a[0]+1,就是第一行第二个元素的地址
	                                 //sizeof(a[0] + 1)计算的是数组第一行第二个元素地址的大小
	printf("%d\n", sizeof(*(a[0] + 1)));//4 - *(a[0] + 1)是第一行第二个元素
	                                    //sizeof(*(a[0] + 1))计算的是数组第一行第二个元素的大小
	printf("%d\n", sizeof(a + 1));//4/8 - a是二维数组的数组名,并没有取地址,也没有单独放在sizeof内部,
	                              //所以a就表示二维数组首元素的地址,即:第一行的地址
	                              //a + 1就是二维数组第二行的地址,sizeof(a + 1)计算的是数组第二行地址的大小
	printf("%d\n", sizeof(*(a + 1)));//16 - a+1是第二行的地址,所以*(a+1)表示第二行,等价于第二行的数组名a[1]
	                                 //所以sizeof(*(a + 1))计算的就是第二行的大小
	printf("%d\n", sizeof(&a[0] + 1));//4/8 - a[0]是第一行的数组名,&a[0]取出的就是第一行的地址,
	                                  //&a[0]+1 就是第二行的地址,sizeof(&a[0] + 1)计算的是数组第二行地址的大小
	printf("%d\n", sizeof(*(&a[0] + 1)));//16 - &a[0]+1 就是第二行的地址,*(&a[0]+1) 就是第二行
	                                     //所以sizeof(*(&a[0] + 1))计算的第二行的大小
	printf("%d\n", sizeof(*a));//16 - a作为二维数组的数组名,没有&,没有单独放在sizeof内部
	                           //a就是首元素的地址,即第一行的地址,所以*a就是第一行,sizeof(*a)计算的是第一行的大小
	printf("%d\n", sizeof(a[3]));//16 - a[3]其实是第四行的数组名(如果有的话)
	                             //所以虽然其实不存在,但也能通过类型计算大小的

	return 0;
}

10. 指针笔试题

//笔试题1:

#include<stdio.h>
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

#include<stdio.h>
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);
	//意思是结构体指针变量p+1跳过一个结构体变量:0x100000+20=0x100014
	printf("%p\n", (unsigned long)p + 0x1);
	//意思是结构体指针变量p强制类型转换为unsigned long类型
	//(unsigned long)p + 1 = 1048576+1 = 1048577 = 0x100001
	printf("%p\n", (unsigned int*)p + 0x1);
	//意思是结构体指针变量p强制类型转换为unsigned int*指针类型
	//(unsigned int*)p + 1就是跳过一个无符号的整型变量:0x10000+4=0x100004
	return 0;
}
//笔试题3

#include<stdio.h>
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

#include <stdio.h>
int main()
{
    int a[5][5];
    int(*p)[4];
    p = a;//int (*)[5]
    printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    //%p是打印内存中的地址,跟%u打印无符号数类似
    //FFFFFFFC,-4
    return 0;
}

//笔试题6


#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));//aa[1]是第二行的数组名,表示第二行首元素的地址
    printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10,5
    return 0;
}

 

//笔试题7

#include <stdio.h>
int main()
{
	char* p = "abcd";
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	//*pa 得到的是a[0]的地址,也就是"work"首元素字符'w'的地址
	//pa++ 得到的是a[1]的地址,也就是"at"首元素字符'a'的地址
	printf("%s\n", *pa);//at - %s需要的是地址
	printf("%s\n", p);//abcd
	return 0;
}

 

//笔试题8

#include <stdio.h>
int main()
{
	//把各个元素的首元素的地址传到c数组中
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;

	printf("%s\n", **++cpp);//POINT
	//cpp原来是cp首元素的地址,++cpp是cp第二个元素的地址
	//*++cpp拿到的是cp第二个元素的内容(c+3),而cp第二个元素的内容又是c第三个元素的地址(&c[2])
	//**++cpp拿到的是c第三个元素的内容(c[2]),c第三个元素的内容是POINT首元素'P'的地址

	printf("%s\n", *-- * ++cpp + 3);//ER
	//++cpp是cp第三个元素的地址,* ++cpp拿到的是cp第三个元素的内容(c+1)
	//而cp第三个元素的内容又是c第二个元素的地址;
	//-- * ++cpp把cp第三个元素的内容由'c+1'改成了'c',而这个内容又是c第一个元素的地址(&c[0])
	//*-- * ++cpp拿到的是c第一个元素的内容(c[0]),c第一个元素的内容是ENTER首元素'E'的地址
	//*-- * ++cpp + 3 是ENTER首元素'E'的地址向后跳过三个字符

	printf("%s\n", *cpp[-2] + 3);//ST - *(*(cpp-2))+3
	//cpp-2是cp首元素的地址,cpp-2不会改变cpp,*(cpp-2)拿到的是cp首元素的内容(c+3)
	//cp首元素的内容又是c第四个元素的地址(&c[3]);
	//*(*(cpp-2))拿到的是c第四个元素的内容(c[3]),c第四个元素的内容又是FIRST首元素'F'的地址
	//*(*(cpp-2))+3 是FIRST首元素'F'的地址向后跳过三个字符

	printf("%s\n", cpp[-1][-1] + 1);//EW - *(*(cpp-1)-1)+1
	//cpp-1是cp第二个元素的地址,*(cpp-1)拿到的是cp第二个元素的内容
	//*(cpp-1)-1把cp第二个元素的内容由'c+2'改成了'c+1',而这个内容又是c第二个元素的地址(&c[1])
	//*(*(cpp-1)-1)拿到的是c第二个元素的内容(c[1]),c第二个元素的内容又是NEW首元素'N'的地址
	//*(*(cpp-1)-1)+1 NEW首元素'N'的地址向后跳过一个字符
	return 0;
}

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值