C语言指针精简版(三)

简介: C语言指针精简版(三)

字符指针变量

我们先看一行代码:

const char* pstr = "hello bit.";//这⾥是把⼀个字符串放到pstr指针变量⾥了吗?

事实上它的本质是把⼀个字符串常量hello bit的⾸字符 h 的地址存放到指针变量 pstr 中。

剑指offer中经典题:

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

会出现以上的结果是因为:C/C++会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

数组指针变量

学习数组指针变量之前请先记住以下两点:

数组指针  !=  指针数组

指针数组是存放指针的数组,数组指针是存放整个数组的地址,而非数组首元素地址

格式:数组类型(*数组指针变量名)[数组元素个数] = &数组名

!!!一定要加上(),否则就会变成int* p[10],这是指针数组而不是数组指针!!!

作用:存放整个数组的地址

实例:

Int arr[10];

Int(*p)[10] = &arr;

       加上()是因为[]的优先级要⾼于*号,所以必须加上()来保证p先和*结合形成(*p),此时p为数组指针变量,指向的是一个元素个数为10个的整型数组

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    int(*p)[10] = &arr;
    return 0;
}

调试也能看到数组指针变量p存储了整型数组arr的地址,且二者的类型也是一样的......

⼆维数组传参的本质

过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:

#include <stdio.h>
void test(int a[3][5], int r, int c)
{
 int i = 0;
 int j = 0;
 for(i=0; i<r; i++)
 {
 for(j=0; j<c; j++)
 {
 printf("%d ", a[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}};
 test(arr, 3, 5);
 return 0;
}

上述代码的写法是实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?是否可以利用之前学过的数组指针变量的知识来写?

关于第二种传参方式的书写思路如下:

①⼆维数组的⾸元素即第⼀⾏的⼀维数组

②由于数组名是数组⾸元素的地址,故⼆维数组的数组名表⽰的就是第一个⼀维数组首元素的地址

③我们用一个数组指针变量指向二维数组的数组名,这样就可以遍历二维数组中的每一个元素

#include <stdio.h>
void test(int (*p)[5], int r, int c)
{
 int i = 0;
 int j = 0;
 for(i=0; i<r; i++)
 {
     for(j=0; j<c; j++)
     {
         printf("%d ", *(*(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}};
 test(arr, 3, 5);
 return 0;
}

结论:⼆维数组传参,形参的部分可以写成数组或指针的形式

函数指针变量

格式:函数类型(指针变量)(函数形参类型) =  函数名

只需要写函数形参类型即可,不需要再写形参名

我们先来看一段代码:

#include <stdio.h>
void test()
{
  printf("hehe\n");
}
int main()
{
  printf("test: %p\n", test);
  printf("&test: %p\n", &test);
  return 0;
}

通过上述代码我们可以发现:函数是有地址的,函数名就是函数的地址

       与之前的数组指针变量类似,函数指针变量应该是⽤来存放函数地址的,后续可以通过地址来调⽤函数的:

#include <stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int(*pf3)(int, int) = Add;
  printf("%d\n", (*pf3)(2, 3));
  printf("%d\n", pf3(3, 5));
  return 0;
}

上述代码就是对函数指针变量的使用了......

《C陷阱和缺陷》中有这样一段代码:

Void (* signal(int ,void(*)(int) ) (int)

       首先我们可以理解的是signal是一个函数名,(int , void(*)(int))是signal函数的两个参数,一个是int型,另一个是函数指针类型,该函数指针类型指向的函数参数是int型,返回类型是void,signal(int,void(*)(int))就相当于对signal函数的声明,如果我们把这个函数声明的整体假设为m,那么这行代码就会变成void(* m)(int),*m就是一个新的函数指针,该函数指针指向的函数参数是int型,返回的类型为void型。

typedef关键字

作用:类型重命名的,可以将复杂的类型简单化

⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:

typedef unsigned int uint;
//将unsigned int 重命名为uint

同样的我们也可以将指针类型重命名,将 int* 重命名为 ptr_t ,这样写:

Typedef  int* ptr_t;

但是对于数组指针和函数指针稍微有点区别,⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

typedef int(*parr_t)[5]; //新的类型名必须在*的右边

函数指针类型的重命名也是⼀样的,⽐如将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:

typedef void(*pfun_t)(int);//新的类型名必须在*的右边

函数指针数组

什么是函数指针数组?

函数指针数组就是把多个函数的地址存放在一个数组中

格式:返回类型(*数组名[函数个数])(形参类型,形参类型) = (函数名,函数名)

int (*parr1[3])(int,int) = (Add,Sub);

       其中,parr1 [3]表示一个有三个元素的数组,数组类型就是 int (*)(int,int) ,指向(int,int)返回值是int型的函数指针变量,数组元素为Add 和 Sub

为什么要使用函数指针数组?

当函数的返回值一样时为了简化代码可以将函数指针放在指向同一个指针类型的数组中,比如:

#include <stdio.h>
int Add(int x, int y)
{
        return x + y;
}
int Sub(int x, int y)
{
        return x - y;
}
int main()
{        
        int* arr[10];
        int (*pArr[4])(int, int) = { Add,Sub };
        return 0;
}

转移表

计算器的⼀般实现:

#include <stdio.h>
int add(int a, int b)
{
        return a + b;
}
int sub(int a, int b)
{
        return a - b;
}
int mul(int a, int b)
{
        return a * b;
}
int div(int a, int b)
{
        return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
        {
        printf("*************************\n");
        printf(" 1:add 2:sub \n");
        printf(" 3:mul 4:div \n");
        printf(" 0:exit \n");
        printf("*************************\n");
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
          {
             case 1:
                printf("输⼊操作数:");
                scanf("%d %d", &x, &y);
                ret = add(x, y);
                printf("ret = %d\n", ret);
                break;
             case 2:
                printf("输⼊操作数:");
                scanf("%d %d", &x, &y);
                ret = sub(x, y);
                printf("ret = %d\n", ret);
                break;
             case 3:
                printf("输⼊操作数:");
                scanf("%d %d", &x, &y);
                ret = mul(x, y);
                printf("ret = %d\n", ret);
                break;
             case 4:
                printf("输⼊操作数:");
                scanf("%d %d", &x, &y);
                ret = div(x, y);
                printf("ret = %d\n", ret);
                break;
             case 0:
                printf("退出程序\n");
                break;
             default:
                printf("选择错误\n");
                break;
          }
       } while (input);
return 0;
}

使用函数指针实现:

//使⽤函数指针数组的实现:
#include <stdio.h>
int add(int a, int b)
{
        return a + b;
}
int sub(int a, int b)
{
       return a - b;
}
int mul(int a, int b)
{
        return a*b;
}
int div(int a, int b)
{
        return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[])(int x, int y) = { 0, add, sub, mul, div }; //转移表
        do
        {
        printf("*************************\n");
        printf(" 1:add 2:sub \n");
        printf(" 3:mul 4:div \n");
        printf        (" 0:exit \n");
        printf("*************************\n");
        printf( "请选择:" );
        scanf("%d", &input);
        if ((input <= 4 && input >= 1))
                {
                        printf( "输⼊操作数:" );
                        scanf( "%d %d", &x, &y);
                        ret = p[input](x, y);
                        printf( "ret = %d\n", ret);
                }
        else if(input == 0)
                {
                        printf("退出计算器\n");
                }
        else
                {
                        printf( "输⼊有误\n" );
               }
         }while (input);  
 return 0;
}

~over~

 

相关文章
|
25天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
78 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
25天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
49 9
|
25天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
42 7
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
126 13
|
29天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
29天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
103 3
|
1月前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
29天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
39 1
|
1月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
1月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。