20-06-16-指针的各种骚操作

看c语言也有一段时间了,想记录一下关于指针的各种用法。
参考书籍以及资源如下:
c语言中文教程
参考书籍:c语言入门经典(第5版)

1 指针?

什么是指针?指针就是地址。就这么理解,没错的。

指针(即地址)数据

图 1-1
图 1-1

那我们要指针干嘛呢?也就是它有什么用呢?对吧,你不能光有这个东西,没啥用怎么行。
指针就是指向数据的地址,关于数据是什么?只要合理都行。可以是整数、小数、函数、结构体、共用体等等,都行。因为指针就是一个地址啊,地址里面是什么都可以啊,对吧,挺简单的吧。

1.1 怎么表示指针?

我们不能光用文字表示指针吧,这多麻烦啊。所以需要定义指针的表示方式或者说符号。
⚠️注意:这里仅仅在c语言中,本文档没有设计c++。但是后续会重新写关于c++的部分。

【&】在变量中取地址

【*】在地址中取变量

可以参考链接01 一、初级篇——指针()、取地址(&)、解引用()与引用(&)的区别

#include<stdio.h>
void main() {
    int i = 10;//定义一个变量,并赋初始值为10
    int *p = &i;//定义一个指针变量,并赋初始值为i的地址
    *p = 199;
    printf("%d=%d", *p,i);//输出199=199
    printf("---%d---", p);//得到变量i的地址
}

简单讲就是:
--------->1 有某个数据(上面代码的例子是整数i=10)
--------->2 取这个数据的地址,也就是&i
--------->3 因为地址就是指针,所以呢,我们可以定义一个指针来等于这个地址。
--------->-- int *p =&i 在这里呢,*就是表示p是指针的符号;也就是p指向i的地址.如果这里还不能理解,请参考链接01 一、初级篇——指针()、取地址(&)、解引用()与引用(&)的区别

1.2 指针的指针

在这里插入图片描述
图1-2
我们用图来说明一下问题。
指针p1指向的是某数据的地址,指针p2指针的是p1的地址。这就是指针的指针啊。

上面是文字描述,接下来我们用符号表示。

#include<stdio.h>
int main()
{
	int i = 10;//定义一个变量,并赋初始值为10
    int *p1 = &i;//定义一个指针变量,并赋初始值为i的地址
    int *p2 = &p1; //定义一个指针变量,并赋予初始值为p1的地址
    printf("--打印p1的地址---%d", p1)
    printf("--打印p2的地址---%d", p2)
    printf("---------------------")
    printf("--打印p1的值---%d", *p1)
    printf("--打印p2的值---%d", *p2)
    printf("--打印i的值---%d", i)
}

结果:

--打印p1的地址----272632452
--打印p2的地址----272632464
---------------------
--打印p1的值---10
--打印p2的值----272632452
--打印i的值---10
Program ended with exit code: 0

我们可以发现,打印的p2的值是p1的地址哦,也就说明,p2指向的是p1的地址啊,对吧。这就是指针的指针。

1.2.1 指针的指向

上面代码中,指针指向了i的地址,i=10。这只是指针的初始值,指针是一个变量,它是可以变换的。

#include<stdio.h>
 
int main(void)
{
	int num = 7;
	int *p = &num;
	printf("数值%d所在的地址是 %p\n", num, p);
	printf("指针p所指向的地址为 %p , 该地址上所保存的值为%d\n", p, *p);
	*p = 100;
	printf("指针p所指向的地址为 %p , 该地址上所保存的值为%d\n", p, num);
	return 0;
}
----------------
数值7所在的地址是 0x7ffeefbff578
指针p所指向的地址为 0x7ffeefbff578 , 该地址上所保存的值为7
指针p所指向的地址为 0x7ffeefbff578 , 该地址上所保存的值为100
Program ended with exit code: 0

可以看到,当*p=100后,p指向的地址的数据就是100了。如果不理解,请参考链接01 一、初级篇——指针()、取地址(&)、解引用()与引用(&)的区别

1.3 指针类型

  • 字符型指针
    char *p; //定义了一个字符变量指针,只能存放字符型数据的地址编号
    -±---------------------± - -
    char ch;
    p = &ch;

  • 短整型指针
    short int *p; //定义了一个短整型的指针变量p,只能存放短整型变量的地址

  • 整型指针
    int *p;

  • 长整型指针
    long int *p;

  • float型指针
    float *p;

  • double 型指针
    double *p;

  • 数组指针

  • 什么是数组?
    数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element)。数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存。以int arr[] = { 99, 15, 100, 888, 252 };为例,该数组在内存中的分布如下图所示:
    在这里插入图片描述
    定义数组时,要给出数组名和数组长度,数组名可以认为是一个指针,它指向数组的第 0 个元素。在C语言中,我们将第 0 个元素的地址称为数组的首地址。以上面的数组为例,下图是 arr 的指向:

在这里插入图片描述
下面的例子演示了如何以指针的方式遍历数组元素:

#include <stdio.h>
int main(){
    int arr[] = { 99, 15, 100, 888, 252 };
    int len = sizeof(arr) / sizeof(int);  //求数组长度
    int i;
    for(i=0; i<len; i++){
        printf("%d  ", *(arr+i) );  //*(arr+i)等价于arr[i]
    }
    printf("\n");
    printf("----len_arr---%d\n",sizeof(arr));
    printf("---len_int---%d\n",sizeof(int));
    printf("\n");
    return 0;
}
----------
99  15  100  888  252  
----len_arr---20
---len_int---4

可能需要解释一下,数组的长度,数组的长度是元素所占的字符数,而不是元素个数。如果要求元素个数需要除以类型所占字符。

代码中我们使用了*(arr+i)这个表达式,arr 是数组名,指向数组的第 0 个元素,表示数组首地址, arr+i 指向数组的第 i 个元素,*(arr+i) 表示取第 i 个元素的数据,它等价于 arr[i]。
±----------------------------------------------------+
如果一个指针指向了数组,我们就称它为数组指针(Array Pointer)。
datatype (*array_name) [length];
比如:int (*p2)[10];
p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。
在这里插入图片描述

  • 结构体指针

  • 什么是结构体?
    前面我们讲解了数组(Array),它是一组具有相同类型的数据的集合。但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放。

    可以使用结构体(Struct)来存放一组不同类型的数据。结构体的定义形式为:

struct 结构体名{
    结构体所包含的变量或数组
};

结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member)。请看下面的一个例子:

struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在学习小组
    float score;  //成绩
};

stu 为结构体名,它包含了 5 个成员,分别是 name、num、age、group、score。结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。

  • 结构体指针
    其实也就是地址中的数据是结构体,就叫结构体指针。
    struct <结构体名>*<变量名>
    实例:
struct data
{
    int i;
    char c;
    int k;
};
struct data *var;//声明data结构体类型指针变量"var"

需要说明的是,结构体指针,指向的是结构体变量。

可以参考链接02结构体指针

  • 函数指针

  • 什么是函数?
    函数是一段可以重复使用的代码,用来独立地完成某个功能,它可以接收用户传递的数据,也可以不接收。接收用户数据的函数在定义时要指明参数,不接收用户数据的不需要指明,根据这一点可以将函数分为有参函数和无参函数。

    将代码段封装成函数的过程叫做函数定义
    如果函数不接收用户传递的数据,那么定义时可以不带参数。如下所示:

dataType  functionName(){
    //body
}

例如,定义一个函数,计算从 1 加到 100 的结果:

int sum(){
    int i, sum=0;
    for(i=1; i<=100; i++){
        sum+=i;
    }
    return sum;
}

如果函数需要接收用户传递的数据,那么定义时就要带上参数。如下所示:

dataType  functionName( dataType1 param1, dataType2 param2 ... ){
    //body
}

数据通过参数传递到函数内部进行处理,处理完成以后再通过返回值告知函数外部。

int sum(int m, int n){
    int i, sum=0;
    for(i=m; i<=n; i++){
        sum+=i;
    }
    return sum;
}
  • 函数指针就是一个指向函数的指针
    函数指针可以像一般函数一样,用于调用函数、传递参数。

    函数指针变量的声明:

typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
#include <stdio.h>
 
int max(int x, int y)
{
    return x > y ? x : y;
}
 
int main(void)
{
    /* p 是函数指针 */
    int (* p)(int, int) = & max; // &可以省略
    int a, b, c, d;
 
    printf("请输入三个数字:");
    scanf("%d %d %d", & a, & b, & c);
 
    /* 与直接调用函数等价,d = max(max(a, b), c) */
    d = p(p(a, b), c); 
 
    printf("最大的数字是: %d\n", d);
 
    return 0;
}
  • 回调函数—就是通过指针调用的函数

    函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。

实例:

#include <stdlib.h>  
#include <stdio.h>
 
// 回调函数
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    for (size_t i=0; i<arraySize; i++)
        array[i] = getNextValue();
}
 
// 获取随机值
int getNextRandomValue(void)
{
    return rand();
}
 
int main(void)
{
    int myarray[10];
    populate_array(myarray, 10, getNextRandomValue);
    for(int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    return 0;
}

简单讲,就是array指向的是数组,我们用array就是希望调用这个数组。同理,(*getNextValue)(void)就是指向函数的指针,我们用它就是希望调用函数。
可以参考链接03菜鸟教程

1.4 指针数组

前面我们讲了数组指针,现在我们讲指针数组。
指针数组,本质上是数组。数组元素是指针。
dataType *arrayName[length];
例子:

#include <stdio.h>
int main(){
    int a = 16, b = 932, c = 100;
    //定义一个指针数组
    int *arr[3] = {&a, &b, &c};//也可以不指定长度,直接写作 int *arr[]
    //定义一个指向指针数组的指针
    int **parr = arr;
    printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);
    printf("%d, %d, %d\n", **(parr+0), **(parr+1), **(parr+2));
    printf("------------------\n");
    printf("---arr[0]d的地址--%d\n-",arr[0]);
    printf("---arr[1]d的地址--%d\n-",arr[1]);
    printf("---arr[2]d的地址--%d\n-",arr[2]);
    return 0;
}
-----------
16, 932, 100
16, 932, 100
------------------
---arr[0]d的地址---272632488
----arr[1]d的地址---272632492
----arr[2]d的地址---272632496
-Program ended with exit code: 0

arr[1]是地址,*arr[1]是地址的数据。

1.5 指针函数

前面我们讲了函数指针。
指针函数,本质是一个函数,函数返回类型是某一类型的指针。
类型标识符 *函数名(参数表) int *f(x,y);
当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需要指针或地址的表达式中。

char *cp(char *s, char *t) 
{ 
    t = s; return t; 
}
int main() 
{ 
    char s1[10] = "hello"; 
    char s2[10] = "world"; 
    printf("%s %sn", s1, s2); 
    printf("%sn",cp(s1,s2)); 
    return 0; 
}

这里返回值就是一个地址. 这里 char *cp(char *s, char t)里面的第一个我们可以看做是跟定义一个指针 char p,里面的是一个意思,就是只是表示这是一个指针而已.所以我们调用cp(s1,s2)返回的就是一个地址,printf("%s")的时候,传入的是一个地址或者字符串的名字.如果是一个 int *get(),那么获得返回值就是 printf("%dn",*get())就可以了.
关于指针函数写的不好,晚上改吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值