1->字符指针
//1.字符指针 int main() { char arr[] = "abcdef"; char* pc = arr; printf("%s\n", arr); printf("%s\n", pc); return 0; }
int main() { const char* p = "abcdef";//"abcdef"是一个常量字符串 //指向元素a //*p = 'w'; //printf("&c\n",*p); //printf("%s\n", p); return 0; }
2->指针数组
//2.指针数组 // 是数组,用来存放指针的数组 int main() { int arr[10] = { 0 };//整形数组 char ch[5] = { 0 };//字符数组 int* parr[4];//存放整形指针的数组 - 指针数组 char pch[5];//存放字符指针的数组 - 指针数组 return 0; }
int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 2,3,4,5,6 }; int arr3[] = { 3,4,5,6,7 }; int* parr[] = { arr1,arr2,arr3 };//存首元素地址 int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { printf("%d ", *(parr[i] + j)); } printf("\n"); } return 0; }
3->数组指针
//3.数组指针 // 是指针 int main() { int* p = NULL; //p是整形指针 - 指向整形的指针 - 可以存放整形的地址 char* pc = NULL; //pc是字符指针 = 指向字符的指针 - 可以存放字符的地址 //数组指针 - 指向数组的指针 - 存放数组的地址 int arr[10] = { 0 }; //arr - 首元素地址 //&arr[0] - 首元素地址 //&arr - 数组地址 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int(*p)[10] = &arr; //方块优先级高 //上面的p就是数组指针 return 0; }
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int(*pa)[10] = &arr; int i = 0; for (i = 0; i < 10; i++) { printf("%d ", (*pa)[i]); //*pa == arr } return 0; }
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);//arr - 数组名就是首元素地址 //把arr想象成一维数组 //所以数组arr有三个元素,首元素地址就是第一行地址 print2(arr, 3, 5); return 0; }
int arr[5]; // int* parr1[10]; //parr1是一个数组,数组有10个元素,每个元素都是一个int型指针, int* parr1[10]是指针数组 int (*parr2)[10]; //parr2是一个指针,该指针指向的了一个数组,数组有10个元素,每个元素是int型,int (*parr2)[10]是数组指针 int ((*parr3)[10])[5]; //parr3是一个数组,该数组有10个元素,每个元素是一个数组指针,该数组指针指向的数组有五个元素,每个元素是int
4->数组参数、指针参数
//4.数组参数、指针参数 //一维数组传参 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); return 0; }
//二维数组传参 void test(int arr[3][5]) {} void test2(int arr[][5]) {} void test3(int arr[3][]) //err,不能省略列 {} void test4(int* arr) //err,传过来的是一行数组 不能用int* {} void test5(int **arr)//err,二级指针也不能接收 一个数组地址 {} void test6(int (*arr)[5]) {} int main() { int arr[3][5] = { 0 }; test(arr);//二维数组传参 test2(arr); test3(arr); test4(arr); test5(arr); test6(arr); return 0; } //总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素 //这样才方便运算 //二级指针传参 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; }
5->函数指针
//5.函数指针 - 是指向函数的指针 - 存放函数地址的一个指针 int Add(int x, int y) { int z = 0; z = x + y; return z; } int main() { int a = 10; int b = 20; int arr[10] = { 0 }; int (*p)[10] = &arr; //printf("%d\n", Add(a, b)); //&函数名 和 函数名 都是函数的地址 /* printf("%p\n", &Add); printf("%p\n", Add); */ int (*pa)(int, int) = Add; //函数指针 printf("%d\n ", (*pa)(2, 3)); return 0; } void Print(char*str) { printf("%s\n", str); } int main() { void (*p)(char*) = Print; (*p)("hello bit"); return 0; }
//分析代码1 (*(void (*)())0)(); void(*)() - 函数指针类型 把0强制类型转换成:void(*)() - 0就是一个函数的地址 调用0地址处的该函数 //代码2 void (*signal(int, void(*)(int)))(int); void(* )(int); signal(int, void(*)(int)) signal是一个函数声明 - 参数有两个,一个是int,第二个是函数指针,该函数指针指向的函数的参数是int,返回类型是void signal函数的返回类型也是个函数指针: 该函数指针指向的函数的参数是int,返回类型是void
int Add(int x, int y) { int z = 0; z = x + y; return z; } int main() { int a = 10; int b = 20; int (*pa)(int, int) = Add; //函数指针 //以下三种都正确 printf("%d\n ", (*pa)(2, 3)); printf("%d\n ", pa(2, 3)); printf("%d\n ", Add(2, 3)); return 0; }
6->函数指针数组
//6.函数指针数组 - 存放函数的地址的数组 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* arr[5]; //需要一个数组,这个数组可以存放4个函数的地址 - 函数指针的数组 int (*pa)(int, int) = Add; //Sub/Mul/Div int (*parr[4])(int, int) = {Add,Sub, Mul,Div};//函数指针的数组 int i = 0; for (i = 0; i < 4; i++) { printf("%d\n",parr[i](2, 3)); } return 0; }
//测试题 char* my_strcpy(char* dest, const char* src); //1.写一个函数指针 pf,能够指向my_strcpy char* (*pf)(char*, const char*); //2.写一个函数指针数组pfArr, 能够存放4个my_strcpy函数的地址 char* (*pfArr[4])(char*, const char*);
//函数指针案例 //计算器 void menu() { printf("1.add\n2.sub\n3.Mul\n4.div\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 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); switch(input) { case 1: printf("请输入两个操作数:>"); scanf("%d%d", &x, &y); printf("%d\n", Add(x, y)); break; case 2: printf("请输入两个操作数:>"); scanf("%d%d", &x,&y); printf("%d\n", Sub(x, y)); break; case 3: printf("请输入两个操作数:>"); scanf("%d%d", &x, &y); printf("%d\n", Mul(x, y)); break; case 4: printf("请输入两个操作数:>"); scanf("%d%d", &x, &y); printf("%d\n", Div(x, y)); break; case 0: printf("退出\n"); break; default: printf("选择错误\n"); break; } } while (input); } //方法2.利用函数指针数组 void menu() { printf("1.add\n2.sub\n3.Mul\n4.div\n5.Xor"); } 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 Xor(int x, int y) { return x ^ y; } int main() { int input = 0; int x = 0; int y = 0; //pfArr 是一个函数指针数组 - 用途:转移表 int (*pfArr[])(int, int) = { 0,Add,Sub,Mul,Div,Xor}; do { menu(); printf("请选择:>"); scanf_s("%d", &input); if (input >= 1 && input <= 5) { printf("请输入操作数:>"); scanf_s("%d%d", &x, &y); int ret = pfArr[input](x, y); printf("%d\n", ret); } else if (input == 0) { printf("退出\n"); } else { printf("选择错误!\n"); } } while (input); }
7->指向函数指针数组的指针
//7.指向函数指针数组的指针 int Add(int x, int y) { return x + y; } int main() { int arr[10] = { 0 }; int (*p)[10] = &arr; //取出数组的地址 int (*pfArr[4])(int, int);//pfArr - 是一个数组 -函数指针的数组 //ppfArr是一个指向 函数指针数组 的指针 int(*(*ppfArr)[4])(int, int) = &pfArr; // //ppfArr 是一个数组指针,指针指向的数组有4个元素 //指向的数组的每个元素的类型是一个函数指针 int(*)(int, int) // return 0; }
8->回调函数
//8.回调函数 - 通过函数指针调用的函数 void print(const char *str) { printf("hehe:%s", str); } void test(void (*p)(char*)) { printf("test\n"); p("bit"); } int main() { test(print); return 0; }
//void类型的指针 int main() { int a = 10; //int* pa = &a; //char* pc = &a; //char ch = 'w'; void* p = &a; //无类型指针 - 可以接收任意类型的地址 - 不能进行解引用操作 - 不能进行加减整数的操作 p = &ch; return 0; }
//qsort - 库函数 - 排序 - 可以排序任意类型的数据 //quick sort //void qsort( // void* base, //目标数组首元素地址 // size_t num, //待排序数组元素个数 // size_t width, //每个元素的大小-单位是字节 // int (*cmp)(const void* e1, const void* e2) //函数指针,比较两个元素的所用函数的地址 - 这个函数使用者自己实现 - 函数指针的两个参数是:待比较的两个元素的地址 //); //对arr进行排序,排成升序 //冒泡排序函数只能排序整形数组 //bubble_sort(arr, sz);//冒泡排序函数 //void bubble_sort(int arr[], int sz) { //确定冒泡排序的趟数 int i = 0; for (i = 0; i < sz - 1; i++) { int flag = 1;//假设这一趟要排序的数据已经有序 //每一趟冒泡排序 int j = 0; for (j = 0; j < sz - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; flag = 0; } } if (flag == 1) { break; } } } struct stu { char name[20]; int age; }; int cmp_int(const void* e1, const void* e2) //void* - 可以接收任意类型的地址 { //比较两个整形值的 return *(int*)e1 - *(int*)e2; } int cmp_float(const void* e1, const void* e2) { //比较两个浮点型 return ((int)(*(float*)e1 - *(float*)e2)); } 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) { //比较名字就是比较字符串 //字符串比较不能直接用><=来比较,应该用strcmp函数 return strcmp(((struct Stu*)e1)->name ,((struct Stu*)e2)->name); } void test1() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), cmp_int); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } } void test2() { float f[] = { 9.0, 8.0, 7.0, 6.0, 5.0, 4.0 }; int sz = sizeof(f) / sizeof(f[0]); qsort(f, sz, sizeof(f[0]), cmp_float); int i = 0; for (i = 0; i < sz; i++) { printf("%f ", f[i]); } } void test3() { struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_age); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", s[i]); } } void test4() { struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_name); int i = 0; for (i = 0; i < sz; i++) { printf("%s ", s[i]); } } //实现bubble_sort函数的程序员,他是否知道未来排序的数据类型-不知道 //那程序员也不知道,待比较的两个元素的类型 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++; } } 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 j = 0; for (j = 0; j < sz - 1 - i; j++) { //两个元素的比较 if (cmp((char*)base+j*width,(char*)base+(j+1)*width) > 0) { //交换 Swap((char*)base + j * width, (char*)base + (j + 1) * width, width); } } } } int cmp_stu_by_age(const void* e1, const void* e2) { //比较两个年龄 return ((struct stu*)e1)->age - ((struct stu*)e2)->age; } void test5() { struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} }; int sz = sizeof(s) / sizeof(s[0]); //使用bubble_sort的程序员一定知道自己排序的是什么数据 //就应该知道如何比较待排序数组中的元素 bubble_sort(s, sz, sizeof(s[0]),cmp_stu_by_age); } int main() { //通过qsort排序 test1(); //排序整型数组 test2(); //排序浮点型数组 test3(); //排序- 通过结构体数组的年龄 test4(); //排序- 通过结构体数组的名字 test5(); //仿照qsort来修改冒泡函数实现能够排序任意类型 return 0; }
9->指针和数组面试题的解析
//9.指针和数组面试题的解析 int main() { //数组名是首元素地址 //例外 //1.sizeof(数组名) - 数组名表示整个数组 //2.&数组名 - 数组名表示整个数组 // //一维数组 int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); //sizeof(数组名) - 计算的是数组总大小 - 单位是字节 -16 printf("%d\n", sizeof(a + 0));// 4/8 - 数组名这里表示首元素的值,a+0还是首元素地址,地址的大小就是4/8个字节 printf("%d\n", sizeof(*a)); //4 - 数组名表示首元素地址,*a就是首元素 printf("%d\n", sizeof(a + 1));// 4/8 - 第二个元素的地址,地址的大小就是4/8个字节 printf("%d\n", sizeof(a[1])); //4 - 第2个元素的大小 printf("%d\n", sizeof(&a)); // 4/8 - &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; }
//字符数组 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 printf("%d\n", strlen(arr)); // 随机值 printf("%d\n", strlen(arr + 0));// 随机值 //printf("%d\n", strlen(*arr)); // err //printf("%d\n", strlen(arr[1])); // err printf("%d\n", strlen(&arr)); // 随机值 printf("%d\n", strlen(&arr + 1)); //随机值-6 printf("%d\n", strlen(&arr[0] + 1)); //随机值-1 return 0; } int main() { 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)); //err //printf("%d\n", strlen(arr[1])); //err printf("%d\n", strlen(&arr)); // 6 printf("%d\n", strlen(&arr + 1));//随机值 printf("%d\n", strlen(&arr[0] + 1));// 5 return 0; } int main() { const 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 p[0] == *(p + 0) == 'a' 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)); // err //printf("%d\n", strlen(p[0])); // err p[0] == *(p + 0) == 'a' printf("%d\n", strlen(&p)); // 随机值 printf("%d\n", strlen(&p + 1)); // 随机值 printf("%d\n", strlen(&p[0] + 1));// 5 return 0; }
//二维数组 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 a[0]相当于第一行做为一维数组的数组名 == sizeof(数组名) printf("%d\n", sizeof(a[0] + 1));// 4/8 a[0]是第一行首元素地址 a[0]+1,第一行第二个元素的地址 printf("%d\n", sizeof(*(a[0] + 1)));//4 printf("%d\n", sizeof(a + 1)); //4/8 a是二维数组的首元素地址,而二维数组的首元素是第一行,及把二位数组看作了一维数组,a+1就是第二行的地址 printf("%d\n", sizeof(*(a + 1)));//16 printf("%d\n", sizeof(&a[0] + 1));//4/8 第二行地址 printf("%d\n", sizeof(*(&a[0] + 1)));//16 printf("%d\n", sizeof(*a)); //16 *a = 第一行地址解引用 printf("%d\n", sizeof(a[3])); //16 表示第四行,sizeof不参与真实计算 return 0; }
//指针笔试题 int main() { int a[5] = { 1,2,3,4,5 }; int* ptr = (int*)(&a + 1); //&a + 1 为5后面的地址 printf("%d, %d\n", *(a + 1), *(ptr - 1)); //2 5 return 0; } struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p; //结构体指针 p //假设p 的值为ox100000,如下表表达式的值分别为多少? //已知, 结构体Test类型的变量大小是20个字节 int main() { p = (struct Test*)0x100000; printf("%p\n", p + 0x1); //0x00100014 printf("%p\n", (unsigned long)p + 0x1); //0x00100001 printf("%p\n", (unsigned int*)p + 0x1); //0x00100004 return 0; } 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 0x02000000 return 0; } int main() { int a[3][2] = { (0,1),(2,3),(4,5) };//逗号表达式 1,3,5 int* p; p = a[0]; //第一行 1, 3 printf("%d", p[0]); //1 return 0; } int main() { int a[5][5]; int(*p)[4]; p = a;//int (*)[4] ---- int (*)[5] printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//0xfffffffc , 4 return 0; } 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) == aa[1] printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1)); // 10 5 return 0; } int main() { char* a[] = { "work","at","alinbaba" }; char** pa = a; pa++; printf("%s\n", *pa);//at return 0; }