C语言----深入理解指针(3)

1.字符指针变量

复制代码
//int main()
//{
//    char ch = 'w';
//    char*pc=&ch;
//    printf("%c", *pc);
//    return 0;
//}

/*int main()
{
    //char* p = "abcdef";
    //char arr[] = "abcdef";//常量字符串 a b c d e f \0
    //char* pc = arr;//将数组首元素的地址存在p中
    const char* p = "abcdef";//这里的赋值是讲字符串中首字符的地址存在p中
    printf("%c\n", *p);//打印a

    //如何打印一整个字符串呢?
    printf("%s\n", p);//使用%s打印字符串的时候,只需要提供首字符的地址就行了

    //*p='q'------不允许修改,因为p指向的是常量字符串,常量字符串不能被修改
    //在5303行添加const可以防止*p被修改

    return 0;
}*/


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;
}

//输出结果
/*str1 and str2 are not same
str3 and str4 are same
为什么会出现这种情况呢?

str1是一个数组
str2是一个数组,只是内容相同而已

"hello bit."是一组常量字符串,是不能被修改的
内容相同的常量字符串只需要保存一份就可以
str3和str4存的都是h的地址,两个指针变量各自指向同一个地址



在这个条件语句中if (str1 == str2)
str1是数组名,首元素的地址,str2也是数组名,也是首元素的地址
str1和str2各占两份不同的空间,只是内容一样
完全不同的两个空间


在这个条件语句中if (str3 == str4)
str3和str4存放的都是h的地址,存放的是同一个地址
所以二者相同


这个代码里面的比较,不是比较字符串,比较的是地址
*/

使用%s打印字符串的时候,只需要提供首字符的地址就行了

char* p = "abcdef";//这里的赋值是讲字符串中首字符的地址存在p中

2.数组指针变量

指针数组--是数组--存放的是指针(地址)

数组指针是什么呢?

类比:字符指针--char*---指向字符的指针--字符指针变量中存放字符的地址

char ch='w';

char *pc=&ch;

整型指针---int*--指向整型的指针--整型指针变量中存放的整型变量的地址

int a=10

int *p=&a

那么数组指针就是指向数组的指针

数组指针变量存放的是数组的地址

数组的地址--&数组名

指针数组

1.是数组

2.是存放指针的数组

char* arr[6]-----char*是数组的元素类型---存放字符串指针的数组

int *arr2[5]---存放整型指针的数组

数组指针

是指针,指向数组的指针

*p--表示这是一个指针变量

这个指针指向的是数组,得有大小[10]

(*p)[10]

数组指向的元素类型是什么呢?在前面加上类型

int (*p)[10]--这里的p就是数组指针,P指向的数组有10个元素,每个元素的类型是int

p的类型是int (*)[10]

将数组的地址取出来,存放在指针变量里面,

char*(*ptr)=&arr---arr里面都是char类型的

复制代码
//指针数组
//int main()
//{
//    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//    int(*p)[10] = &arr;//取出的是数组的地址,那么数组的地址就应该放到数组指针里面
//
//    //想使用p这个数组指针访问arr数组的内容
//    //p里面放的是数组的地址,那么*p就相当于拿到了一个数组
//    for (int i = 0; i < 10; i++)
//    {//*p就是数组,那么咱们加上[i]就说明直接访问arr[i],随着i的变化,逐步打印数组内的内容
//        printf("%d", (*p)[i]);
//    }
//    return  0;
//}
//上面这种就显得有点麻烦

//int main()
//{
//    //int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//    //int(*p)[10] = &arr;//取出的是数组的地址,那么数组的地址就应该放到数组指针里面
//
//    想使用p这个数组指针访问arr数组的内容
//    p里面放的是数组的地址,那么*p就相当于拿到了一个数组
//    //for (int i = 0; i < 10; i++)
//    //{//*p就是数组,那么咱们加上[i]就说明直接访问arr[i],随着i的变化,逐步打印数组内的内容
//    //  printf("%d", (*p)[i]);
//    //}
//    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//    int* p = arr;
//    for (int i = 0; i < 10; i++)
//    {
//        printf("%d",p[i]);//p就是首元素的地址   p[i]---*(p+i)
//    }
//
//
//    return  0;
//}

3.二维数组传参的本质

不管是一位数组还是二维数组传参,形参可以写成数组,也可以写成指针

这里要对数组名做一个深入的理解

咱们要考虑到arr是数组首元素的地址

数组名+i就是跳过i个数组

arr[i]-----(arr+i)---第i行 arr[i][j]----- ((arr+i)+j)---(arr+i)是第i行首元素的地址,+j就表示下标为j的数字的地址,再解引用就能得到下标为j的数字

对于一个二维数组的话,arr是数组名,同时也是这个二维数组第一行的元素地址

arr+i就是这个二维数组第i行的地址

对于(*(arr + i))[j]的理解

*(arr + i)就是这个二维数组的第i行的地址解引用得到第i行

j\]这个就是第i行下标为j的元素 对于*(*(arr + i)+j)的理解 \*(arr + i)是第i行首元素的地址,这个首元素的地址+j就是第i行下标为j的数字的地址, \*(arr + i)+j再将其解引用得到的就是第i行下标为j的元素 //二维数组传参的本质 /*void print(int arr[3][5], int r, int c) { //两层循环,第一层循环打印行,第二层循环打印列 for (int i = 0; i < r; i++) { for (int j = 0; j < 5; j++) { printf("%d ", arr[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} }; print(arr, 3, 5);//将arr数组的内容打印出来,数组名、行和列都要传上去 return 0; }*/ //一维数组传参 /*数组名是首元素的地址 * 一维数组在传参的时候,其实传递的是首元素的地址 * * 函数的参数可以写成数组,也可以写成指针 * * 那么二维数组呢? * 二维数组的数组名该如何理解呢? * 其实二维数组的数组名也是数组首元素的地址,但是是那个数字呢? 二维数组可以理解为一维数组的数组 将二维数组里面的三个一维数组当成一个元素 二维数组有三个元素,每个元素有三个数组 二维数组的每一行可以看成是一个一维数组'二维数组的首元素就是这个二维数组的第一行 二维数组的数组名就是第一行的地址 不是第一行首元素的地址,而是第一行整个一维数组的地址 */ void print(int(*arr)[5], int r, int c)//因为传过来的是一位数组的地址,我们要用数组指针接收 //使用数组指针存放第一行的地址,传过来的是一个一维数组,那么我们就需要一个数组指针来接收 {//这个数组指针并不是指向的二维数组,而是指向的是这个二维数组的第一行 //两层循环,第一层循环打印行,第二层循环打印列 for (int i = 0; i < r; i++) { for (int j = 0; j < 5; j++) { //第一行是arr //第二行是arr+1 printf("%d ", (*(arr + i))[j]);//arr+i就是第几行,*(arr + i)就是解引用相当于直接访问数组后面的[j]就是那一行一维数组里面的第几个元素 } //printf("%d ", *(*(arr + i)+j)) //arr+i是某一行的地址,将其括起来解引用就是这一行,相当得到第i行的数组名 // *(arr+i)==arr[i],arr[i]是第i行的数组名,数组名是数组首元素的地址 // *(arr + i)数组首元素的地址+j就是这一行首元素后面第j个元素的地址,再解引用 // 再将这个地址解引用得到的就是数组首元素 // *(arr + i)一行首元素的地址再+j就是下标为j的元素了 // 对于这个二维数组,arr[i]表示的是arr[i][0],第i行第1个元素的地址 // // //*(arr + i)这个是某一行,相当于拿到这一行的数组名字 //对于二维数组arr的话 //arr[0]是第一行的数组名 //arr[1]是第二行的数组名 printf("\n");//换行 } } int main() { int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} }; print(arr, 3, 5);//将arr数组的内容打印出来,数组名、行和列都要传上去 //arr是在这个二维数组首元素的地址,是第一行的地址,第一行是一位数组 //这里传过去的是第一行的地址 return 0; } /*不管怎么写对于二维数组arr+i就是这个二维数组的第i行一维数组, * 传参时的arr是二维数组第一行, * * 数组名+i就是跳过一整个数组,对于二维数组,里面是存在好几个一维数组的 对于(*(arr + i))[j]的理解 *(arr + i)就是这个二维数组的第i行的地址解引用得到第i行 [j]这个就是第i行下标为j的元素 对于*(*(arr + i)+j)的理解 *(arr + i)是第i行首元素的地址,这个首元素的地址+j就是第i行下标为j的数字的地址, *(arr + i)+j再将其解引用得到的就是第i行下标为j的元素 arr[i]-----*(arr+i)---第i行 arr[i][j]-----*(*(arr+i)+j)---*(arr+i)是第i行首元素的地址,+j就表示下标为j的数字的地址,再解引用就能得到下标为j的数字 */ `*(*(arr+i)+j)` 是访问二维数组中第`i`行,第`j`列元素的方法。 在C语言中,二维数组可以看作是由多个一维数组组成的连续存储空间。定义一个 `int` 类型的二维数组 `arr`,用 `arr[i][j]` 表示其第 `i` 行、第 `j` 列的元素,也可以写成 `*(*(arr+i)+j)`。 解释 `*(*(arr+i)+j)` 的过程如下: 1. `arr` 是一个指向二维数组首地址的指针,`*(arr+i)` 表示访问第 `i` 个一维数组的首地址。 2. `*(arr+i)+j` 表示在第 `i` 个一维数组的基础上向后移动 `j` 个位置,即访问第 `i` 行、第 `j` 列元素的地址。 3. `*(*(arr+i)+j)` 表示访问上述地址所存储的值,即获取第 `i` 行、第 `j` 列的元素值。 `(*(arr + i))[j]` 也可以用来访问二维数组中第 `i` 行,第 `j` 列的元素。 在C语言中,二维数组可以看作是由多个一维数组组成的连续存储空间。定义一个 `int` 类型的二维数组 `arr`,用 `arr[i][j]` 表示其第 `i` 行、第 `j` 列的元素,也可以写成 `*(*(arr+i)+j)` 或 `(*(arr + i))[j]`。 解释 `(*(arr + i))[j]` 的过程如下: 1. `arr` 是一个指向二维数组首地址的指针,`*(arr + i)` 表示访问第 `i` 个一维数组的首地址。 2. `(*(arr + i))[j]` 表示使用 `[]` 运算符访问该一维数组中下标为 `j` 的元素,即获取第 `i` 行、第 `j` 列的元素值。 ## 4.函数指针变量 变量有地址,数组有地址,函数是否有地址呢? /*int Add(int x, int y) { return x + y; } int main() { printf("%p\n", &Add);//地址是00701040 printf("%p\n", Add);//地址是00701040 //这两种写法没区别 //如果想将函数名存起来怎么搞呢? int(*pf)(int,int) = &Add;//函数指针变量 //*和pf结合,表示pf是指针变量 //后面的(int,int)是指向的那个函数的参数 //前面的int是指向的那个函数的返回类型 //那么pf的指针类型是什么呢? //int(*)(int,int)---pf的类型 //交代pf指向的函数的参数个数和类型就行 //那我们能通过这个pf调用这个函数呢? int ret=(*pf)(4, 5);//pf是地址,加*解引用,然后得到函数后咱们就要提供数据,传参,在后面的括号内添加数据 printf("%d", ret);//打印的结果就是9 //int ret =Add(4,5) //还存在一种写法:int ret=pf(4, 5);---直接用过pf找到函数,因为pf里面存放的是函数的地址 //只要是函数指针变量就能这么写,可以不写* //如果要写*的话,那么就要带上括号 //通过函数的地址,也能调用函数 return 0; }*/ //对于这种复杂的代码,我们应该如何简单化呢? return 0; }*/ /*typedef unsigned int unit;//将unsigned int类型的数重定义名字为unit int main() { unsigned int num;//把一个相对长的类型简单化了 unit num2; //上面两个代码的内容是一样的 return 0; }*/ //typedef是用于类型重命名的,可以将复杂的类型简单化 //但如果是指针,能否重命名呢? //typedef int* pint_t; //int main() //{ // pint_t p2;//整形指针 // // // // return 0; //} //那么对于复杂点的数组指针和函数指针该如何命名呢? /*typedef int(*parr_t)[6]; //对于数组指针重命名时,新的名字应该放在*的旁边 int main() { int arr[6] = { 0 }; int(*p)[6]= & arr;//当我们把名字去掉,那么剩下的就是p的类型了 //p是指针变量的的名字 //在重新定义的时候,新的名字放在原本p的位置 parr_t p2; return 0; }*/ //现在对函数重命名 /*typedef int(*pf_t)(int, int);//函数的重命名还是把新名字放到*号旁边 Add(int x, int y) { return x + y; } int main() { int(*pf)(int ,int)=Add;//去掉名字就是函数指针类型 pf_t pf2 = Add; return 0; }*/ 整型指针、数组指针、函数指针 ## 5.函数指针数组 int Add(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int Mull(int x, int y) { return x * y; } int Div(int x, int y) { return x / y; } //指针数组 int main() { //int* arr1[6];整型指针数组 //char* arr2[5];字符指针数组 //arr[3][5];//每个元素是函数---函数指针数组 int (*pf1) (int ,int) = Add; int (*pf2) (int, int) = Sub; int (*pf3) (int, int) = Mull; int (*pf4) (int, int) = Div; //函数指针数组来存放这些函数的地址呢? //函数指针数组从函数指针的基础来写 int (*pf[4]) (int, int) = { Add, Sub ,Div ,Mull };//将函数指针放到函数指针数组里面 //通过下标来找数据 for (int i = 0; i < 4; i++) { int ret=pf[i](6,2);//后面的括号内放的是计算的数据 printf("%d\n", ret); } return 0; } 从函数指针的基础来写函数指针数组来写是最方便的 存放的都是相同类型的元素 ## 6.转移表 函数指针数组 创建函数指针数组来调用 //实现一个计数器 //完成加减乘除 /*int Add(int x, int y)//加法 { return x + y; } int Sub(int x, int y)//减法 { return x - y; } int Mull(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. mull 4. div *************\n"); printf("************** 0. exit *************\n"); } int main() { int input = 0; int x =0, y = 0,ret=0; do { menu();//菜单 printf("请选择"); scanf("%d", &input); switch (input)//判断输入的值 { case 1: printf("请输入两个操作数"); scanf("%d %d", &x, &y);//输入数字 //调用函数Add ret=Add(x, y); printf("%d\n", ret); break; case 2: printf("请输入两个操作数"); scanf("%d %d", &x, &y);//输入数字 //调用函数Sub ret = Sub(x, y); printf("%d\n", ret); break; case 3: printf("请输入两个操作数"); scanf("%d %d", &x, &y);//输入数字 //调用函数Mull ret = Mull(x, y); printf("%d\n", ret); break; case 4: printf("请输入两个操作数"); scanf("%d %d", &x, &y);//输入数字 //调用函数Div ret = Div(x, y); printf("%d\n", ret); break; case 0: printf("退出计算器\n"); break; default://输入的值不是这里面的任何的一个值 printf("选错了,重新选择\n"); break; } } while (input); return 0; }*/ //这种写法过去复杂,对于不同符号的计算 // //将这些函数的地址放到 一个指针数组里面 int Add(int x, int y)//加法 { return x + y; } int Sub(int x, int y)//减法 { return x - y; } int Mull(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. Mull 4. div *************\n"); printf("************** 0. exit *************\n"); } int main() { int input = 0; int x =0, y = 0,ret=0; //创建一个数组放函数 //函数指针数组---转移表 int (*pfArr[5])(int, int) = {0, Add,Sub,Mull,Div}; // 0 1 2 3 4 下标 do { menu();//菜单 printf("请选择"); scanf("%d", &input); if (input >= 1 && input <= 4) { printf("请输入两个操作数"); scanf("%d %d", &x, &y);//输入要运算的数字 ret = pfArr[input](x, y);//通过数组下标访问数组内存放的函数,再通过输入再传入数据进行计算 printf("%d\n", ret);//直接调用数组内的函数 } else if (input == 0) { printf("退出计数器\n"); } else//选择的不是这些值 { printf("选择错误,重新选择\n"); } } while (input); return 0; } //通过数组的方式把函数都调用起来,用过下标找到函数再进行调用

相关推荐
我命由我1234535 分钟前
Spring Boot 自定义日志打印(日志级别、logback-spring.xml 文件、自定义日志打印解读)
java·开发语言·jvm·spring boot·spring·java-ee·logback
徐小黑ACG2 小时前
GO语言 使用protobuf
开发语言·后端·golang·protobuf
0白露3 小时前
Apifox Helper 与 Swagger3 区别
开发语言
Tanecious.3 小时前
机器视觉--python基础语法
开发语言·python
叠叠乐4 小时前
rust Send Sync 以及对象安全和对象不安全
开发语言·安全·rust
Tttian6225 小时前
Python办公自动化(3)对Excel的操作
开发语言·python·excel
独好紫罗兰6 小时前
洛谷题单2-P5713 【深基3.例5】洛谷团队系统-python-流程图重构
开发语言·python·算法
闪电麦坤957 小时前
C#:base 关键字
开发语言·c#
Mason Lin7 小时前
2025年3月29日(matlab -ss -lti)
开发语言·matlab
DREAM.ZL8 小时前
基于python的电影数据分析及可视化系统
开发语言·python·数据分析