指针相关知识

本文详细介绍了C语言中的指针概念,包括指针的基本用法、指针数组、数组指针、函数指针以及函数指针数组。通过实例解析了各种指针类型的定义和使用,帮助读者深入理解指针在C语言中的作用。

指针

在c语言中,我们会频繁的使用指针,那么什么是指针?首先我们来看一段代码。

#include <stdio.h>
int main()
{
    int a = 10;//在内存中开辟一块空间
    int *p = &a;//将a的地址存放在指针变量p中
    return 0;
}

这里写图片描述

如图所示,假如创建的变量a的地址为0x0000ff11,那么指针变量指向变量a,存放的是变量a的地址。
总结:指针就是地址,指针变量是变量,指针变量是用来保存地址的变量。存放在指针变量中的值都会被当做地址来处理。在32位机器上指针变量的大小为4个字节,在64位机器上指针变量的大小为8个字节。

指针数组

指针变量也是变量,那么就可以像创建整型数组一样,创建一个指针组。首先指针数组是数组,在这个数组内的元素的类型为指针。如下所示定义了一个数组p,p的元素类型为int *,即该数组的每个元素为指向整型的指针。如下图所示。

 int *p[10];

数组指针

 int (*p)[10];

这里写图片描述
()的优先级比[]高,因此p先与*结合,p为一个指针,指向的是一个包含10个整型变量的数组,所以p是一个指向数组的指针,叫数组指针。那么数组的地址如何存放?

int arr[10] = {0};
int *p1 = &arr;
int (*p2)[10] = &arr;
int (*p3)[10] = arr; 
  • &arr表示整个数组的地址,arr表示数组首元素的地址。显然,p1为整型指针,p2、p3为数组指针。在c语言中,“=”两边的类型必须相同。
  • p1左边是整型指针,右边是指向数组的指针,两边的类型不同,变量p1不能用来存放数组的地址。
  • p2两边都是指向数组的指针,可以用来存放数组的指针。
  • p3左边为指向数组的指针,右边为指向数组首元素的指针,两边类型不同。

因此p2用来存放数组的地址比较合适。

函数指针

函数指针,从字面意思理解就是一个指针,这个指针指向的是一个函数。先来看一段代码。

#include <stdio.h>

void test()
{
    printf("test\n");
}

int main()
{
    printf("%p\n",test);
    printf("%p\n",&test);
    return 0;
}

运行结果
这里写图片描述
函数名代表地址,和&函数名相同,函数名具有只读属性。那么函数的地址怎么保存?

 char *(*pfun1)(char *p1,char *p2);
 char **pfun2(char *p1,char *p2);
 char *pfun3(char *p1,char *p2);
  • 显然,pfun3是函数名,该函数有两个char *类型的参数p1和p2,返回值为char *类型。
  • pfun2这个表达式与pfun3表达式很相似,唯一不同的是pfun2的返回值为char**类型。
  • 在这里pfun1不是函数名,它是一个指针变量,指向的是一个函数,该函数的返回值为char *类型,函数有两个参数p1和p2其类型为char*。

所以pfun1可以用来存放函数指针。现在我们来看两段比较有趣的代码。

 (*(void(*)())0)();
 void (*signal(int,void(*)(int)))(int);
  • 代码1:void(*)()为函数指针类型,(void(*)())0将0强转为函数指针,*(void(*)())0对函数指针解引用得到函数,(*(void(*)())0)()调用该函数。因此,代码1实现的是函数调用
  • 代码2:首先signal为函数;函数的参数有两个,第一个是int类型,第二个是函数指针,该函数指针指向的函数有一个整型参数,返回类型为void;signal函数的返回类型为函数指针,该指针指向的函数有一个int类型的参数,返回值为void。

代码2太过复杂,不便于阅读,我们可以对其进行简化。我们可以将重复使用的void(*)(int)函数指针类型重定义,这样可以使代码更易阅读。

 typedef void (*pfun_t)(int);
 pfun_t signal(int,pfun_t);

函数指针数组

函数指针数组是将函数的地址即函数指针存放在数组中,或者说函数指针数组的每个元素为函数指针。函数指针数组可以用作转移表。下来我们来使用一下函数指针数组,使用函数指针数组实现简单计算器的功能。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int Add(int x,int y)
{
    return x+y;
}

int Sub(int x,int y)
{
    return x-y;
}

int Mul(int x,int y)
{
    return x*y;
}

int Div(int x,int y)
{
    return x/y;
}

void calc(int (*pfun)(int,int))
{
    int x = 0;
    int y = 0;
    int ret = 0;
    printf("请输入两个操作数:");
    scanf("%d%d",&x,&y);
    ret = pfun(x,y);
    printf("ret=%d\n",ret);
}

void menu()
{
    printf("****************************\n");
    printf("*****  1.Add    2.Sub  *****\n");
    printf("*****  3.Mul    4.Div  *****\n");
    printf("*****      0.exit      *****\n");
    printf("****************************\n");
}

int main()
{
    int (*pfun[5])(int,int) = {0,Add,Sub,Mul,Div};
    int input = 0;
    do
    {
        menu();
        printf("请选择:");
        scanf("%d",&input);
        if(input>=1 && input<=4)
        {
            calc(pfun[input]);
        }
        else if(input == 0)
            printf("退出\n");
        else
            printf("选择错误\n");
    }while(input);
    return 0;
}

运行结果
这里写图片描述

指向函数指针数组的指针

指向函数指针数组的指针是一个指针,该指针指向的是一个数组,这个数组的元素类型为函数指针。这个知识点我们平时不太用,只要了解、认识即可。

void test(int x)
{
    printf("%d\n",x);
}
 int main()
 {
     //定义一个函数指针pfun,该函数的返回值类型为void,有一个参数,参数类型为整型
     void (*pfun)(int) = test;
     //定义一个函数指针数组pfunArr[10]
     void (*pfunArr[10])(int);
     //函数指针数组的第一个元素指向函数指针pfun
     pfunArr[0] = test;
     //定义一个指向函数指针数组pfunArr的指针ppfunArr
     void (*(*ppfunArr[10]))(int) = &pfunArr;
     return 0;
 }
<think>我们被要求回答指针相关的基础知识,并参考之前提供的引用内容。注意,用户的问题中引用了两篇关于指针基础的文章,我们需要从中提取关键点,并结合C语言的标准知识进行讲解。 关键点: 1. 指针变量的定义和声明 2. 指针的两个操作符:&(取地址)和*(解引用) 3. 指针的初始化(包括空指针)和野指针问题 4. 指针在函数参数传递中的应用 5. 数组与指针的关系(包括二维数组、指针数组、数组指针) 我们将按照以下结构组织回答: 1. 什么是指针? 2. 指针变量的定义 3. 指针的两个操作符:&和* 4. 指针的初始化(包括空指针)和野指针 5. 指针作为函数参数(传址调用) 6. 数组与指针的关系 7. 二维数组与指针指针数组和数组指针) 注意:使用LaTeX格式的数学表达式(虽然指针中数学表达式较少,但地址值我们可以用类似$0x7ffee$这样的形式,但实际需要用LaTeX,因为地址是十六进制数,直接写即可。重点是在需要公式的地方使用,比如指针运算中的偏移量计算可以用公式) 另外,在代码块中展示示例。 根据要求,回答末尾生成相关问题。 开始回答: </think>### C语言指针基础知识详解 #### 1. 指针的定义与本质 指针是存储内存地址的特殊变量,其类型决定了所指向数据的类型和解引用方式: ```c int* ptr; // 整型指针 char* s_ptr; // 字符指针 float* f_ptr; // 浮点指针 ``` - 内存地址:每个变量在内存中的位置标识,如$0x7ffee$ - 指针大小:固定(32位系统4字节,64位系统8字节)[^1] #### 2. 核心操作符 | 操作符 | 名称 | 示例 | 作用 | |--------|----------|----------------|--------------------------| | `&` | 取址符 | `&var` | 获取变量内存地址 | | `*` | 解引用符 | `*ptr` | 访问指针指向的内存值 | | `[]` | 下标 | `ptr[i]` | 指针偏移访问(等价`*(ptr+i)`)| **操作示例**: ```c int num = 42; int* ptr = &num; // ptr存储num的地址 printf("地址: %p\n", ptr); // 输出地址,如0x7ffee printf("值: %d\n", *ptr); // 输出42(解引用) *ptr = 100; // 修改num值为100 ``` #### 3. 指针初始化与危险操作 **安全初始化**: ```c int* p1 = NULL; // 空指针(指向0地址) int x = 10; int* p2 = &x; // 指向有效变量 ``` **危险操作——野指针**: ```c int* danger; // 未初始化(随机地址) *danger = 42; // 导致程序崩溃或数据损坏 ``` 规避方法: 1. 初始化时赋`NULL` 2. 解引用前验证非空`if(ptr != NULL)` 3. 指针使用后及时置空[^1] #### 4. 指针与函数参数 通过指针实现传址调用(修改实参值): ```c void increment(int* ptr) { (*ptr)++; // 解引用修改原值 } int main() { int a = 5; increment(&a); // 传递地址 printf("%d", a); // 输出6 } ``` #### 5. 指针与数组关系 数组名在多数场景退化为首元素指针: ```c int arr[3] = {10,20,30}; int* ptr = arr; // 等价 ptr = &arr[0] printf("%d", *ptr); // 10 printf("%d", *(ptr+1));// 20(指针算术运算) ``` **二维数组与指针**: ```c int matrix[2][3] = {{1,2,3},{4,5,6}}; int (*ptr)[3] = matrix; // 数组指针(指向含3元素的数组) printf("%d", ptr[1][2]); // 6(访问第二行第三列) ``` #### 6. 指针类型区别 | 类型 | 示例 | 特点 | |-------------|--------------------|-------------------------------| | 字符指针 | `char* s` | 常用于字符串处理 | | 指针数组 | `char* arr[5]` | 存储多个指针的数组 | | 数组指针 | `int (*ptr)[5]` | 指向整个数组的指针 | | 二级指针 | `int** pp` | 指向指针指针(多级间接访问)| > **关键理解**:`char*`和`char[]`在函数参数中可互换,因为数组作为参数时自动转为指针(退化规则)[^2]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值