Day16
指针变量的使用
指针变量和字符串的关系
" hello world":字符类型的数组,本质是一个地址
520:整形类型
'A':小数类型
3.14:实数类型
字符串的本质是一个地址,可以使用一个指针变量存储该地址
char *p = "hello world";
字符串常量存储在静态区的只读区域,如果对上面这个指针 p修改的话将会出现段错误
指针进阶
指针函数
指针函数本质上是一个函数,返回值为指针 的函数就是指针函数
指针函数调用的结果是一个左值
指针函数返回的必须是一个生命周期比较长的空间的地址,不能返回局部空间的地址
全局变量的空间
静态变量的空间
堆区申请的空间
主调函数以地址传入的空间
指针函数实现了将被调函数中的内存传递给主调函数使用
定义格式:返回值类型 *函数名(参数列表){函数体;return 地址;}
c
复制代码
#include <stdio.h>
//值返回函数
int fun_1(int n)
{
n += 10;
return n;
}
//地址返回函数
int *fun_2()
{
static int s = 66;
return &s;
}
int main(int argc, const char *argv[])
{
int num = 520;
//调用功能函数1
int res = fun_1(num); //值返回函数,结果为右值,只读
printf("res = %d\n", res);
// fun_1(num) = 1314; //值返回函数不能作为左值 报错
int *ptr = fun_2();
printf("*ptr = %d\n", *ptr);
*fun_2() = 777; //指针函数的返回值是一个左值
return 0;
}
函数指针
函数指针本质上是一个指针 ,用于指向函数的入口地址的指针
任何函数在被调用时系统都会为其分配运行空间,有空间就会有地址,有地址就可以用指针变量来指向他。存储函数运行空间地址的指针变量称为函数指针变量
任何一个函数的函数名都是该函数对应的入口地址,是一个地址常量
函数指针定义格式:返回值类型 (* 变量名)(参数列表);
函数指针变量的用法:和函数名用法一致
c
复制代码
#include <stdio.h>
int sum(int m, int n)
{
return m+n;
}
int judge(int m)
{
return m%2 == 0 ? 1 : 0;
}
void sort(int *arr, int n)
{
for (int i = 1; i < n; i++)
{
for (int j; j < n-i; j++)
{
if (arr[j] > arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
printf("排序成功\n");
}
int main(int argc, const char *argv[])
{
//打印三个函数的地址
printf("%p\t%p\t%p\t\n", sum, judge, sort);
//定义指针存储上面三个函数的入口地址
int (*sum_ptr)(int,int) = sum;
int (*judge_ptr)(int) = judge;
void (*sort_ptr)(int *,int) = sort;
//函数和函数指针都可以正常调用
printf("和为: %d\n", sum(1,4));
printf("和为: %d\n", sum_ptr(1,4));
if (judge(56))
{
printf("是一个偶数\n");
}else
{
printf("是一个奇数\n");
}
if (judge_ptr(56))
{
printf("是一个偶数\n");
}else
{
printf("是一个奇数\n");
}
return 0;
}
使用函数指针完成函数回调(回调函数)
函数回调:当调用某个函数时,不仅要向该函数中传递数据参数,也可以向该函数中传入一个功能
c
复制代码
#include <stdio.h>
int he(int m,int n)
{
return m+n;
}
int cha(int m, int n)
{
return m-n;
}
int ji(int m, int n)
{
return m*n;
}
//使用传入的函数指针调用别的函数
void calculate(int m, int n,int (*x)(int,int))
{
int res = x(m,n);
printf("结果为:%d\n", res);
}
int main(int argc, const char *argv[])
{
int num = 3, key = 6;
calculate(num, key, he);
calculate(num, key, cha);
calculate(num, key, ji);
return 0;
}
数组指针
数组指针的本质是一个指针 ,记录的是整个数组的地址
数组指针针对的是整个数组,每偏移一个单位,内存地址就偏移了整个数组的大小
数组指针的定义格式:数据类型 (*指针名)[数组长度];
数组指针与一维数组的关系
数组名是第一个元素的地址 ----> 变量的地址
对一维数组名,再取地址,就得到了当前数组的地址(数组的地址)
c
复制代码
#include<stdio.h>
int main(int argc, const char *argv[])
{
int arr[5] = {4,7,2,8,1}; //定义了一个长度为5的数组
int *ptr = arr; //普通元素的地址
//定义一个指针变量,存储整个数组的地址
int (*arr_ptr)[5] = NULL; //定义一个数组指针
arr_ptr = &arr; //将数组指针指向整个数组 *arr_ptr = *&arr
printf("sizeof(arr) = %ld\n", sizeof(arr)); //20
printf("ptr = %p,arr=%p, &arr[0]=%p, &arr = %p,arr_ptr=%p\n",\
ptr, arr, &arr[0], &arr, arr_ptr);
printf("ptr+1 = %p,arr+1=%p, &arr[0]+1=%p, &arr+1 = %p, arr_ptr+1=%p\n",\
ptr+1, arr+1, &arr[0]+1, &arr+1, arr_ptr+1);
//使用数组指针访问元素
printf("数组元素分别是:");
for(int i=0; i<5; i++)
{
//printf("%d\t", (*arr_ptr)[i]); // *arr_ptr ==> *(arr_ptr+0) ==> arr[0]
printf("%d\t", arr_ptr[0][i]); // *arr_ptr ==> *(arr_ptr+0) ==> arr[0]
}
return 0;
}
数组指针与二维数组的关系
二维数组的数组名本质上是一个数组指针
当数组指针与二维数组结合后,指针的偏移才有现实的意义
c
复制代码
#include<stdio.h>
int main(int argc, const char *argv[])
{
int arr[2][3] = {{1,2,3}, {4,5,6}}; //定义一个二维整型数组
//从元素的角度:定义了6个整形变量
//从数组的角度:定义了两个一位数组
//int *ptr = arr; //?不可以进行 int *ptr=arr[0]
int (*arr_ptr)[3] = arr; //定义数组指针存储二维数组名
printf("&arr[0][0]=%p, arr[0]=%p, arr=%p, arr_ptr=%p\n",\
&arr[0][0], arr[0], arr, arr_ptr);
printf("&arr[0][0]+1=%p, arr[0]+1=%p, arr+1=%p, arr_ptr+1=%p\n",\
&arr[0][0]+1, arr[0]+1, arr+1, arr_ptr+1);
//使用数组指针访问元素
//arr_ptr 表示的是第一行的整体地址
//arr_ptr+i :表示任意一行的整体地址
//*(arr_ptr+i) :表示任意一行的第一个元素的地址 --> arr_ptr[i]
//*(arr_ptr+i) +j :表示的是下标为i和j的元素的地址 --> arr_ptr[i]+j
// *(*(arr_ptr+i)+j):表示任意一个元素 --> *(arr_ptr[i]+j) -->arr_ptr[i][j]
printf("数组元素分别是:\n");
for(int i=0; i<2; i++)
{
for(int j=0; j<3; j++)
{
//printf("%d\t", arr_ptr[i][j]);
printf("%d\t", *(*(arr_ptr+i)+j));
}
printf("\n");
}
return 0;
}
指针数组
指针数组本质上是一个数组 ,但是每个元素都是一个指针变量
定义格式:数据类型 *数组名 [数组长度]
函数指针数组
函数指针数组本质上是一个数组 ,数组中的每个元素都是一个函数指针变量,都可以存储函数入口的地址
能够实现对函数的批量调用
定义格式:返回值类型 (* 数组名[数组长度])(形参列表)
二级指针
每个指针变量都占有内存空间,有空间就有地址,就可以使用指针存储他的地址
存储一级指针变量的地址的指针变量就是二级指针变量
一级指针变量的地址为二级指针
定义格式:数据类型 **指针名
二级指针变量的值为:一级指针变量的地址
二级指针指向的内存空间中的值是一级指针(也就是变量的地址)---> *二级指针变量
二级指针取值两次是变量的值 ---> **二级指针变量
二级指针变量常用于函数参数为主程序中的一级指针变量进行赋值
万能指针
万能指针就是可以接收任意地址的指针变量
定义格式:void *指针名
由于没有任何类型的限制,导致该指针变量可以接收任意类型的地址
对于万能指而言不能直接进行*
运算