#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<string.h>
//参数是数组的形式 print1(int arr[3][5], int x, int y)//这里数组名传参,这里放数组形式 { int i = 0; int j = 0; for (i = 0; i < x; i++) { for (j = 0; j < y; j++) { printf("%d", arr[i][j]); } printf("\n"); } }
//参数数指针的形式 void print2(int(p)[5], int x, int y) { int i = 0; for (i = 0; i < x; i++) { int j = 0; for (j = 0; j < y; j++) { printf("%d ", ((p + i) + j));//等价 printf("%d", ((p + i))[j]); printf("%d", *(p[i]+j)); printf("%d", p[i][j]);
//p+i相当于这一行,j是这行第j个元素
//p+i是某一行的地址,*解应用就是那一行数组
}
printf("\n");
}
}
int Add(int x, int y) { int z = 0; z = x + y; return z; }
int Sub(int x, int y) { int z = 0; z = x - y; return z; }
int Mul(int x, int y) { int z = 0; z = x * y; return z; } int Div(int x, int y) { int z = 0; z = x / y; return z; } int Xor(int x, int y) { return x^y; }
void Calc(int(*pf)(int, int)) { int x = 0; int y = 0; printf("请输入两个操作数:>"); scanf("%d%d", &x, &y); printf("%d\n", pf(x, y)); }
void menu() { printf("\n"); printf("1.add2.sub\n"); printf("3.mul4.div\n"); printf("5.xor0.exit\n"); printf("***********************\n"); }
void print(char*str) { printf("%s\n", str); }
struct Stu { char name[20]; int age; };
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(voidbase, int sz, int width, int(cmp)(voide1, voide2)) { 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_int(const void* e1, const void* e2) { //比较两个整形值的 return ((int)e1 - (int)e2);//强制类型转换 }
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]); } }
int cmp_float(const void* e1, const void* e2) { //比较两个整形值的 //return (float)e1 - (float)e2;//强制类型转换void转换为float 最前面的是解应用 return (float)e1 - (float)e2;//其它版本会出错,所以强制类型转换 //if ((float*)e1 == (float)e2) // return 0; //else if ((float)e1 > (float)e2) //return 1; //else // return -1; }
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 j = 0; for (j = 0; j < sz; j++) { printf("%f ", f[j]); } }
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 ((struct Stu*)e1)->name - ((struct Stu*)e2)->name; }
void test3() { struct Stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} }; int sz = sizeof(s) / sizeof(s[0]); printf("name:%s\n", s->name); printf("age:%d\n", s->age); qsort(s, sz, sizeof(s[0]), cmp_stu_by_age); printf("name:%s\n", s->name);//指针用'->' printf("age:%d\n", s->age); }
int comp_up(const void a, const void b)// 由小到大排序 { return ((int)a - (int)b); }
void test4() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz, sizeof(arr[0]), cmp_int); }
void test5() { struct Stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} }; int sz = sizeof(s) / sizeof(s[0]); //bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name); bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age); }
int main() { //指针进阶 //1.字符指针 //一种写法 //char ch = 'w'; //char*pc = &ch; //pc = 'w'; //另一种写法 //char arr[] = "abcdef"; //charpc = arr; //printf("%s\n", arr);//abcdef //printf("%s\n", pc);//abcdef //printf("%c\n", *pc);//a //printf("%c\n", *arr);//a
//const char*p = "abcdef";//直接这样写则“abcdef”是一个常量字符串(不能修改),把a(首字符)的地址赋给了p
////*p = 'w';//这里不能修改 所以直接在加一个const修饰
//printf("%c\n", *p);//此时p里面存的是a的地址
//printf("%s\n", p);
//笔试题
//char*p1 = "abcdef";//最好在前面加上const
//char*p2 = "abcdef";//p1,p2是储存地址的变量
//if (p1 == p2)//因为是不能改的变量,为了节省空间,俩个地址合并
//{
// printf("hehe\n");
//}
//else
//{
// printf("haha\n");
//}
//2.数组指针 //3.指针数组 //用来存放指针 //int arr[10] = { 0 };//整型数组 //char ch[5] = { 0 };//字符数组 //intparr[4];//存放整形指针的数组--指针数组 //charpch[5]//存放字符指针的数组--指针数组
//int a = 10;
//int b = 20;
//int c = 30;
//int d = 40;
//int arr[] = { &a,&b,&c,&d };
//int i = 0;
//for (i = 0; i < 4; i++)
//{
// printf("%d", *(arr[i]));
//}
//指针数组的真实用法
//int arr1[] = { 1,2,3,4,5, };
//int arr2[] = { 1,2,3,4,5, };
//int arr3[] = { 1,2,3,4,5, };
//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("%d", *(parr[i]));
// printf("\n");
//}
//int*arr1[10];//整形指针的数组
//char*arr2[4];//一级字符指针的数组
//char**arr3[5];//二级字符指针的数组
//数组指针--指针
//4.数组传参,指针传参 //int p = NULL;//p是整形指针-指向整形的指针-可以存放整形的地址 //charpc = NULL;//pc是字符指针-指向字符的指针-可以存放整形的地址 //int arr[10] = { 0 };// arr是字符指针 - 指向数组的指针 - 可以存放数组的地址 //arr-首元素地址 //&arr[0]-首元素地址 //&arr数组的地址
//int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//int(*p)[10] = &arr;//数组的地址要存起来
////此时p就是数组指针,*不是解应用操作
//char*arr[5];
//char*(*pa)[5] = &arr;
//*说明pa是指针 pa:指针变量的名字;[5]pa指向的数组是5个元素
//char*说明pa指向的数组的元素类型是char*
//char*相当于是数组元素的类型
//&arr和arr,虽然值是一样的,但是意义应该不一样的,实际上:
//&arr表示的是数组的地址,而不是首元素的地址。 //数组的地址+1,跳过整个数组的大小,所以&arr+1相当于&arr的差值是40 //int(*p)[10] = &arr//所以我们把一个数组的地址交给一个数组指针
//数组指针的(不常用)应用
//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]);
// printf("%d ", *(*pa+i));//此时*pa相当于arr
//}
//int*p = arr;
//int i = 0;
//for (i = 0; i < 10; i++)
//{
// printf("%d ", *(p + i));
//}
//int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} }; //print1(arr,3,5);//这里arr数组名,是首元素地址(除了取地址数组名,sizeof数组名两种 ) ////把arr想象成一维数组,arr就是第一行的地址 //print2(arr, 3, 5);
//int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; //int i = 0; //int *p = arr; //for (i=0;i<10;i++) //{ // printf("%d", (p + i));//下标为i的元素 // printf("%d", (arr + i));//首元素地址向后移位 // printf("%d", arr [i]);//arr[i]==(arr+i)==(p+i)==p[i] // printf("%d", p[i]); //}
//int arr[5];//arr是一个五个元素的整形数组 //int parra[10];//parr1是1个数组,数组有10个元素,每个元素的类型是int,parr1是指针数组 //int(*parr2)[10];//parr2是一个指针,该指针指向了一个数组,数组有10个元素,每个元素的类型是int //int(*parr3[10])[5]//parr3优先和方括号结合,说明parr3是一个数组,该数组有10个元素,每个元素是一个数组指针, //该数组指针指向的数组有5个元素,每个元素是int //对称一下,int和[5]对应
//const char*p2 = "abcdef";是常量字符串,p2不能被修改,所以加const保险 //注意p2的左侧和右侧
//指针数组-存放指针的数组 //char *p2 = "abcdef";//p2存的是a的地址 // //int p3:整形指针,指向整形的指针 //charp4:指向字符的数组 //int (pa)[5]=&arr2//取出数组的地址,pa就是1个数组指针 //int()[5] = &arr2//去掉名字,剩下的就是指针数组的类型
//数组参数,指针参数
//一维数组传参 //void test(int arr[])//arr1 //void test(int arr[10])//10可以省略arr1 //void test(int *arr)//arr1对应arr2 //arr1 //void test(int *arr[20])//arr2 //void test(int **arr)//arr2 //int main() //{ // int arr[10] = {0}; // int *arr2[20] = { 0 }; // test(arr); // test(arr2); //}
//二维数组传参 //void test(int arr[3][5])//都行 //void test1(int arr[][5])// //void test3(int*arr)//此时报错,传过来是一维数组的地址,放不到整形指针里面去 //void test4(int *arr)//此时报错,二级指针放的是一级指针的变量的地址 //void test5(int(arr)[5]) //int main() //{ // int arr[3][5] = { 0 }; // test(arr); // test1(arr); // test3(arr); // test4(arr);//传的是首元素地址,是第一行的地址,是一个数组的地址 // test5(arr) //}
//void print(int*p, int sz)//指针用指针的形式接受,数组用数组的形式接收 //int *p = arr; //int sz = sizeof(arr) / sizeof(arr[0]); //print(p, sz);
//void test(int** ptr) //{ // printf("num=%d\n", ptr); //} //int main() //{ // int n = 10; // int*p = &n; // intpp = &p; // test(pp);//二级指针本身,两种都可以 // test(&p);//一级指针变量的地址 // int*arr[10]; // test(arr);//arr是首元素(地址)的地址,也可以用二级指针接收 // return 0; //}
//5.函数指针 //指向函数的指针,存放函数地址的一个指针 //int a = 10; //int b = 20; //printf("%p\n", &Add); //printf("%p\n", Add);//相同 //int(*pa)(int, int) = Add;//交代清楚函数类型就行 //printf("%d\n", (*pa)(2, 3)); //printf("%d\n", (pa)(2, 3));//这两种情况一样
//void(p)(char) = print; ////p print(函数)的地址类型 char:传的参数类型是个指针 //(*p)("hello bit"); ////*p解应用找到这个函数,传过去的是个字符指针,所以可以输入1个字符串
//void(signal(int, void()(int)) )(int);//typedef简化函数 //typedef void(*pfun_t)(int);//两句等于上面一句 //pfun_t signal(int, pfun_t);//pfun_t既是函数类型,有是函数指针 //signal函数参数有2个,第一个是int,第二个是函数指针,该函数指针指向函数的参数是int,返回类型是void
//6.函数指针数组 //int arr[5]; ////需要一个数组,这个数组可以存放4个函数的地址-函数指针的数组 //int(*parr[4])(int, int) = {Add,Sub,Mul,Div};//parr里放进去4个函数地址 //int i = 0; //for (i = 0; i < 4; i++)//i=0时,调用Add函数 //{ // printf("%d\n", parr[i](2, 3)); //}
//正确写法 int(*parr1[10])();函数指针数组
//char* my_strcpy(chardest, const charsrc); //1.写一个函数指针pf,能够指向 my_strcpy //char*(pf)(char, const char*); //2.写一个函数指针pfarr,能够存放4个my__strcpy函数的地址 //char*(pfarr[4])(char,const char*)//如果是数组,这里的[4]可以最后加
//int input = 0; //int x = 0; //int y = 0; //int(*pfarr[])(int, int) = { 0,Add,Sub,Mul,Div,Xor };//转移表 ////函数指针数组的形式可以替换switch case语句,不用写那么多case语句 //do //{ // menu(); // printf("请选择;>"); // scanf("%d", &input); // if (input >= 1 && input <= 5) // { // printf("请输入两个操作数:>"); // scanf("%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);
//do //{ // menu(); // printf("请选择;>"); // scanf("%d", &input); // switch (input) // { // case 1: // Calc(Add); // break;//回调函数:通过函数指针调用的函数--Calc // 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);
//7.指向函数指针数组的指针 //int((ppfarr)4=&pfarr //int()(int,int) //ppfarr是一个数组指针,函数指针的数组,指针指向的数组有4个元素 //int(*pf)(int, int);//函数指针 //int(pfarr[4])(int, int);////函数指针的数组 //int((*ppfarr)[4])(int, int) = &pfarr;//函数指针的数组的指针
//8.回调函数--通过函数指针调用的函数--Calc //void print(char*str) //{ // printf("hehe:%s", str); //} //void test(void(p)(char))//运行起来的时候,p里面指向的是print函数了 //{ // printf("test\n"); // p("bit");//此时调用print函数//bit相当于传参数 //} //int main() //{ // test(print);//打印的是hehe后面跟1个bit //}
//int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; //int sz = sizeof(arr) / sizeof(arr[0]); // //bubble_sort(arr, sz); //int i = 0; //for (i = 0; i < sz; i++) //{ // printf("%d", arr[i]); //}
//test1(); //test2(); //test3(); //test4(); //test5();
//qsort(arr,sz,sizeof(arr[0]),cmp_int);//排序arr数组,有sz个元素,元素字节大小,函数 //voidp = &a;//无类型的指针 可以接受任意类型的指针 //void不能进行解应用操作,不知道它是几个字节,不知道多大 //p++也不能运算
//int cmp_int(const void* e1, const void* e2, ) //{ // //比较两个整形值的 // return (int)e1 - *(int)e2;//强制类型转换 //}
//int i = 0;
//int arr[] = { 188,131,201,127,224 };
//printf("%d\n", sizeof(arr[0]));
//printf("排序前:");
//for (i = 0; i < 5; i++)
//{
// printf("%d ", arr[i]);
//}
//qsort(arr, 5, sizeof(arr[0]), comp_up);
//printf("\r\n排序后:");
//for (i = 0; i < 5; i++)
//{
// printf("%d ", arr[i]);
//}
////9.指针和数组面试题的解析 //strlen 只能求解字符数组 //一维数组 //int a[] = { 1,2,3,4 }; //printf("%d\n", sizeof(a));//16,数组名是首元素地址,俩个例外,sizeof(数组名)&(数组名) //printf("%d\n", sizeof(a+0));//4/8 首元素地址 //printf("%d\n", sizeof(a));//4 首元素的地址 //printf("%d\n", sizeof(a+1));//4/8 第二个元素地址 //printf("%d\n", sizeof(a[1]));//4 第二个元素的大小 //printf("%d\n", sizeof(&a));//4 数组的地址 //printf("%d\n", sizeof(&a));//16 &a是数组地址,解应用访问的是数组,计算的是数组的大小 //printf("%d\n", sizeof(&a+1));//4 &a是整个数组,&a+1虽然跳过整个数组,但还是地址,所以是4/8个字节 //printf("%d\n", sizeof(&a[0]));//4 第一个元素地址 //printf("%d\n", sizeof(&a[0]+1));//4 第二个元素地址 // ////字符数组 //char arr[] = { 'a','b','c','d','e','f' }; //printf("%d\n", sizeof(arr));// 6 计算的是数组大小,6个字节 //printf("%d\n", sizeof(arr+0));//4 首元素地址 //printf("%d\n", sizeof(*arr));//1 arr是首元素地址 *arr是首元素 是字符 //printf("%d\n", sizeof(arr[1]));//1 第二个元素的地址 //printf("%d\n", sizeof(&arr));//4 数组的地址也是地址 //printf("%d\n", sizeof(&arr+1));//4 虽然跳过了1个数组,但还是地址 //printf("%d\n", sizeof(&arr[0]+1));//4 第二个元素的地址 // //printf("%d\n", strlen(arr));// 随机值 从第一个地址开始取 //printf("%d\n", strlen(arr + 0));// 随机值 从第一个地址开始取 //printf("%d\n", strlen(*arr));//a的acs值是97 ,传过去一个97 地址非法访问 //printf("%d\n", strlen(arr[1]));//b的acs值是98 ,传过去一个98 地址非法访问 //printf("%d\n", strlen(&arr));//随机值 从整个数组的地址开始取,与下一个差值差6 //printf("%d\n", strlen(&arr + 1));//随机值 虽然跳过了1个数组,但还是地址 //printf("%d\n", strlen(&arr[0] + 1));//随机值 -1 // //char arr[] = { "abcdef" }; //printf("%d\n", sizeof(arr));// 7 计算的是数组大小,7个字节 //printf("%d\n", sizeof(arr + 0));//4 首元素地址 看arr是什么 //printf("%d\n", sizeof(*arr));//1 arr是首元素地址 arr是首元素 是字符 //printf("%d\n", sizeof(arr[1]));//1 第二个元素的大小 //printf("%d\n", sizeof(&arr));//4 数组的地址也是地址 //printf("%d\n", sizeof(&arr + 1));//4 虽然跳过了1个数组,但还是地址 //printf("%d\n", sizeof(&arr[0] + 1));//4 第二个元素的地址 //printf("%d\n", strlen(arr));// 6 从第一个地址开始取 //printf("%d\n", strlen(arr + 0));// 6 从第一个地址开始取 ////my_strlen(const charstr)//这里的地址不能被修改 //printf("%d\n", strlen(*arr));//a的acs值是97 ,传过去一个97 地址非法访问 //printf("%d\n", strlen(arr[1]));//b的acs值是98 ,传过去一个98 地址非法访问 //printf("%d\n", strlen(&arr));//6 从整个数组的地址开始取,也就是第一个地址 //printf("%d\n", strlen(&arr + 1));//随机值 跳过了/0 //printf("%d\n", strlen(&arr[0] + 1));//5 第二个元素的地址往后数
//char *p = "abcdef";p里面放的是a的地址 //printf("%d\n", sizeof(p));//4 a的地址 //printf("%d\n", sizeof(p+1));//4 b的地址 //printf("%d\n", sizeof(*p));// 1 *p就是a //printf("%d\n", sizeof(p[0]));//1 a //printf("%d\n", sizeof(&p));//4 是个地址 //printf("%d\n", sizeof(&p+1));//4 跳过p ,还是个地址 //printf("%d\n", sizeof(&p[0]+1));4
//char p = "abcdef";p里面放的是a的地址 //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]));//1 int arr[10]; arr[0]==(arr+0) p[0]==(p+0)=='a' //也是报错// //printf("%d\n", strlen(&p));//随机值,地址是随机的 //printf("%d\n", strlen(&p+1));//随机值 跳过p的地址 ,还是个地址 //printf("%d\n", strlen(&p[0]+1));5 第二个元素地址
//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));//4 第一行第2个元素的地址 //printf("%d\n", sizeof((a[0]+1));//4 第一行第2个元素 //printf("%d\n", sizeof(a+1);//4 a是二维数组数组名,没有sizeof(数组名),也没有&数组名 //所以a是首元素地址,而二维数组是首元素是它的第一行(把二维数组看成一维数组)地址涨16 //printf("%d\n", sizeof((a+1)));//16 第二行地址解应用 //printf("%d\n", sizeof(&a[0]+1));4 第2行地址 //printf("%d\n", sizeof(*(&a[0]+1)));4 第2行元素 //printf("%d\n", sizeof(*a));16 没有sizeof,没有&,所以是首元素地址,也是第一行地址 //printf("%d\n", sizeof(a[3]));16 第四行元素 只是数组名,但内部不进行访问
//int a[5] = { 1,2,3,4,5 }; //int ptr = (int)(&a + 1);//&a代表整个元素 //printf("%d,%d\n", *(a + 1), *(ptr - 1));//*ptr是代表首元素
//int a[4] = { 1,2,3,4 }; //int ptr1 = (int)(&a + 1); //int ptr2 = (int)((int)a + 1);//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 //printf("%x,%x", ptr1[-1], *ptr2);//ptr2存的是 00 00 00 02 取的是02 00 00 00 //在内存中最小的单位是1个字节 //内存的储存,数组元素的储存,先高后底,还有2进制小端储存
//int a[3][2] = { (0,1),(2,3),(4,5) }; //int*p; //p = a[0]; //printf("%d\n",p[0]);
//int a[5][5]; //int(*p)[4];//数组里有4个元素,强行赋值p[4][2]是第18个元素 //p = a; //printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); ////输出 fffffffc -4 2进制转16进制
//int aa[2][5] = { 1,2,3,4,5,6,7,8,9,10 }; //intptr1 = (int)(&aa + 1); //intptr2 = (int)((aa + 1));//aa[1]第2行首元素地址 解应用之后是6的地址 //printf("%d,%d", (ptr1 - 1), (ptr2 - 1));//10,5 // //int arr[10] = { 1,2,3,4,5 }; //intp = arr; ////(p+2)p[2](arr+2)==arr[2] // //char* p = "abcdef";//p里面放的是a的地址 // //char* a[] = { "work","at","alibaba" };//a里面放的是3个首字母的地址 //char**pa = a;//这里的a表示首元素地址 //pa++;//跳过1个字符串 //printf("%s\n", *pa);//*pa是a的地址 打印at
//char* c[] = { "ENTER","NEW","POINT","FIRST" };//要画图 //charcp[] = { c + 3,c + 2,c + 1,c };// //charcpp = cp; //printf("%s\n", **++cpp);//POINT //printf("%s\n", --++cpp+3);//ER //printf("%s\n", cpp[-2]+3);//cpp[-2]==(cpp-2) ST //printf("%s\n", cpp[-1][-1] + 1);//cpp[-1][-1]==((cpp-1)-1) EW
return 0;
}
//1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间 //2.指针大小是固定4/8个字节 //3.指针是有类型,指针的类型决定步长,解应用时候的权限 //4.指针的运算