//——————————指针的进阶
——————————1.字符指针 char*
//#include
//int main()
//{
// const char* p = "abcdef";
// //*p='b';//此句代码不正确,由于const(在*的左边),修饰的是指针p指向的内容
// // ,保证其不能通过指针来改变,但指针变量本身的内容可变
// printf("%s\n",p);//此处打印的结果为“abcdef”但是实际上p指向的是首字符‘a’的地址,
// //之所以全部打印,是因为printf,再打印字符串时,遇到‘\0’打印才会停止。
// return 0;
//}
// #include
//int main()
//{
// const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
//
// printf("%s\n", pstr);
// return 0;
//}
// 特别容易以为是把字符串 hello bit 放到字符指针 pstr 里了,
// 但是本质是把字符串 hello bit.首字符'h'的地址放到了pstr中。
//#include
//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不同。
————————2.指针数组(是数组,用来存放指针的数组)
举例:
//int* arr2[6];存放整型指针的数组{int* int* int* int* int* int*}
// char* arr2[5];存放字符指针的数组{char* char* char* char* char*}
//#include
//int main()
//{
// int arr1[] = { 1,2,3,4,5 };
// int arr2[] = { 2,3,4,5,6 };
// int arr3[] = { 3,4,5,6,7 };
// //引用指针数组将n个一维数组关联起来,类似于一个二维数组(本质上不是)
// int* parr[3] = {arr1,arr2,arr3};
// //数组parr[]的下标分别为0,1,2
// int i = 0;
// for (i = 0; i < 3; i++)
// {
// int j = 0;//j为arr[]的下标,分别为0,1,2,3,4
// for (j = 0; j < 5; j++)
// {
// //*(p+a)=pa
// printf("%d ",*(parr[i]+j));
// //printf("%d ",parr[i][j]);也可替换为此代码
// }
// //parr[i][j]本质上不是二维数组,因为二维数组在内存中是连续存放的
// //而arr1,arr2,arr3并不一定是连续存放的
// printf("\n");
// }
// return 0;
//}
——————————3.数组指针
一、定义:数组指针是用来存放数组的地址的
//————举例说明
1.数组指针是指针——指向数组的指针
int (*p)[10]
p是数组指针,p可以指向一个数组,该数组有10个整型元素
2.指针数组是数组——由n个指针类型元素组成的数组
int* p[10]
3.整型(字符)指针是指针——指向整型(字符型)的指针
二、&数组名VS数组名
对比arr与&arr分别代表什么意思
如代码所示
//#include
//int main()
//{
// int arr[10] = {0};
// printf("%p\n", arr);//结果为:00EFF920
// printf("%p\n", &arr);结果为:00EFF920
// printf("%p\n", arr+1);结果为:00EFF924//+1后增加4,跳过1个元素
// printf("%p\n", &arr+1);结果为:00EFF948//+1后增加40,跳过整个数组
// return 0;
//}
arr为首元素地址而&arr呢?
由代码可知,arr与&arr打印地址相同,虽然他们的值同,但其实他们的意义不同。
&arr为数组地址,并非首元素地址。&arr+1跳过了整个数组的大小,即差值为40
讨论arr的意义
情况1:&arr—>&+数组名——>数组名不是首元素地址,而表示整个数组
而&+数组名,取出的是整个数组的地址
情况2:sizeof(arr)—>sizeof(数组名)——>数组名表示整个数组
而sizeof(数组名),计算的是整个数组的大小
三、数组指针的使用
(数组指针指向的是数组,数组指针存放的是数组的地址)
//#include
//int main()
//{
// char* arr[5] = {0};
// char*(*p)[5] = &arr;//此处的*p相当于arr(数组名)
// return 0;
//}
注意:&arr:为取出数组的地址,要用指针存放。即p为指针(*p)
而数组元素的类型为char*,且为5个,所以char*(*p)[5]=&arr
// //打印数组中的每个元素
// 方法1(不常用 )
//#include
//int main()
//{
// int arr[5] = {1,2,3,4,5};
// int (*p)[5] = &arr;//此处的*p相当于arr(数组名)
// int i = 0;
// for (i = 0; i < 5; i++)
// {
// printf("%d\n",(*(*p+i)));//p指向数组//*p相当于数组名,数组名是数组首元素的地址
// }//所以*p本质上是数组首元素的地址
//
// return 0;
//}
方法2(常用)
//#include
//int main()
//{
// int arr[5] = {1,2,3,4,5};
// int* p= arr;
// int i = 0;
// for (i = 0; i < 5; i++)
// {
// printf("%d\n", *(p + i));
// }
// return 0;
//}
打印二维数组的每个元素
//#include
//void print1(int arr[3][5], int r, int c)//r:行 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");
// }
// printf("***********\n");
//}
// 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 // { // 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);//arr是数组名,数组名为首元素地址, // //而二维数组的首元素为他的第一行,即二维数组的arr表示第一行的地址 // return 0; //} ///——实参与形参的关系 数组是连续存放的,只要知道数组的首地址和数组的长度就能找到这个数组中所有的元素。 因此,要想通过实参和形参将一个数组从主调函数传到被调函数,那么只需要传递这两个信息即可。 对于一维数组来说,其数组名就表示一维数组的首地址。 所以只需要传递数组名和数组长度这两个参数就可以将数组从主调函数传入被调函数中。 当数组名作为函数的实参时,形参列表中也应定义相应的数组(或用指针变量), 且定义数组的类型必须与实参数组的类型一致,如果不一致就会出错。 ——————数组参数、指针参数 //数组传参时,可以传数组也可以传指针 //#include //void test(int arr[])//方法1:ok//"[]"内可以标记数组元素个数,也可以省略 //{} void test(int arr[10])//方法2:ok {} //void test(int* arr)//方法3:ok //{} //void test2(int* arr[])//方法1:ok //{} void test2(int* arr[20])//方法2:ok {} //void test2(int** arr)//方法3:ok//二级指针是指向一级指针的指针 //{} //二级指针是存放一级指针的地址 //int main() //{ // int arr[10] = { 0 }; // int* arr2[20] = { 0 }; // test(arr); // test2(arr2); //} //————————二维数组传参(三维数组直能省略第一维) // 1.传数组 //void test(int arr[3][5])//ok //{} void test(int arr[][])//no//形参的二维数组,行可以省略,列不可以省略(先行后列) {} //void test(int arr[][5])//ok //{} //总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。 2.传指针 //void test(int* arr)//no //一维数组的地址不能放在一级指针中,只能放在二级指针中。 //{} //void test(int* arr[5])//no//此处为指针数组,不是指针 //{} void test(int(*arr)[5])//ok//此处为数组指针 {} //void test(int** arr)//ok//一维数组的地址放在二级指针中 //{} //int main() //{ // int arr[3][5] = { 0 }; // test(arr);//二维数组的数组名,表示第一行的地址(第一行是有5个整型元素的一维数组) //} 关键在于实参是谁,传的是什么,再去考虑形参与之匹配即可 //————————一级指针传参 指针传参,传指针,就用指针接收 //#include //void print(int* p, int sz) //{ // int i = 0; // for (i = 0; i < sz; i++) // { // printf("%d\n", *(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; //} ——————思考题? 当一个函数的参数部分为一级指针的时候,函数能接收什么参数? 答:实参可以为1.整型变量的地址2.整型指针3.整型数组的数组名4.*二级指针 (只要我们传过去的值的本质是整型指针,。就可以传参) 思考题举例 //#include //void print(int* p) //{ // //} //int main() //{ // int a = 10; // int* ptr = &a; // int arr[10]; // //实参可为: // print(&a); // print(ptr); // print(arr); // return 0; //} //————————二级指针传参 //#include //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; //} //————思考题? 当函数的参数为二级指针的时候,可以接收什么参数? 答:实参可以为1.二级指针2.一级指针的地址3.指针数组4.*三级指针 //void test(char** p) //{ // //} //int main() //{ // char c = 'b'; // char* pc = &c; // char** ppc = &pc; // char* arr[10]; // test(&pc);//ok // test(ppc);//ok // test(arr);//ok // return 0; //} //——————5.函数指针(每一个函数都有自己的地址) 那我们的函数的地址要想保存起来,怎么保存? 下面我们看代码: //void test() //{ // printf("hehe\n"); //} 下面pfun1和pfun2哪个有能力存放test函数的地址? //void (*pfun1)(); //void* pfun2(); 首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针? 答:pfun1可以存放。pfun1先和* 结合,说明pfun1是指针而pfun2是指针函数。 指针pfun1指向的是一个函数,指向的函数无参数,返回值类型为void。 //#include //int Add(int x,int y) //{ // return x+y; //} //int main() //{ // int a = 10; // int* pa = &a; // *pa = 20;//通过*pa可以对&a进行打印或更改 // int arr[5] = { 0 }; // //&数组名——>取出的是数组的地址 // int(*p)[5] = &arr;//int(*p)[5]是数组指针 // //&函数名和函数名——>取出的都是函数的地址 // printf("%p\n", &Add); // printf("%p\n", Add); // int (*pf)(int, int) = &Add;//int (*pf)(int, int)为函数指针 // //可替换为//int (*pf)(int, int) = &Add; // //此处第一个int为被调函数的返回类型, // //指针(*pf)指向的是函数,所以后面跟的是"()",而函数的形参部分为int(整型), // // 所以"(int,int){有几个参数,写几个对应的类型}",也可写成(int x,int y) // //pf里存的是Add的地址,*pf就是找到了Add的函数,即(*pf)()=Add() // int ret = pf(2, 3);//2,3为传参的参数 // //可替换为 // //int ret = Add(2, 3);//pf等同于Add // //int ret = (*pf)(2, 3);//pf写成(*pf)只是为了更容易辨别指针 // //int ret = (*Add)(2, 3);//注意若使用*,必须用(*)防止Add/pf先与(2,3)结合 // printf("%d\n", ret);//通过函数指针实现函数的功能,指针还可以对地址进行更改和打印 // return 0; //} //举例 //#include //int Add(int x,int y) //{ // return x + y; //} //void Sum(int (*p)(int, int)) //{ // int a = 3; // int b = 5; // int ret = (*p)(a, b);//可换为:1.int ret = (*Add)(a, b); // //2.int ret = p(a, b);3.int ret =Add(a, b); // printf("%d\n",ret); //} //int main() //{ // Sum(Add); // return 0; //} /——————————例题 //用函数实现字符串的复制,如将字符数组a的元素复制给字符数组b //#include 函数声明 //void copy_string(char* src, char* dest) //{ // // 遍历字符数组,直到遇到空字符 '\0' // while (*src != '\0') // { // *dest = *src; // src++; // dest++; // } // // 添加空字符"\0",表示字符串的结尾 // *dest = '\0'; //} //int main() //{ // char a[20] = "Hello World!"; // char b[20]; // printf("原始字符串:%s\n", a); // // 使用指针复制字符串 // copy_string(a, b); // printf("复制后的字符串:%s\n", b); // return 0; //} 用指针实现字符串的复制,如将字符数组a的元素复制给字符数组b //#include //int main() //{ // char a[] = "Hello, World!"; // char b[20]; // int sz = sizeof(a) / sizeof(a[0]); // char* p = a; // 将a数组的指针赋给指针变量p // // 逐个复制a数组中的元素到b数组中 // int i=0; // for (i = 0; i < sz; i++) // { // while (*p != '\0') // { // b[i] = *p; // i++; // p++; // } // b[i] = '\0'; // 在b数组末尾添加空字符,表示字符串的结尾 // } // printf("原始字符串:%s\n", a); // printf("复制后的字符串:%s\n", b); // return 0; //}