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~

 

相关文章
|
28天前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
45 0
|
27天前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
16 2
|
28天前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
28天前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
1月前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
1月前
|
C语言
C语言指针(3)
C语言指针(3)
14 1
|
1月前
|
C语言
C语言指针(2)
C语言指针(2)
15 1
|
27天前
|
编译器 C语言
【c语言】指针就该这么学(2)
本文详细介绍了指针与数组的关系,包括指针访问数组、一维数组传参、二级指针、指针数组和数组指针等内容。通过具体代码示例,解释了数组名作为首元素地址的用法,以及如何使用指针数组模拟二维数组和传递二维数组。文章还强调了数组指针与指针数组的区别,并通过调试窗口展示了不同类型指针的差异。最后,总结了指针在数组操作中的重要性和应用场景。
19 0