文章目录
指针
系统为内存的每一个字节 分配一个32位的地址编号
指针 就是内存的编号
指针变量:本质是变量 只是该变量 保存的是内存的地址编号(不是普通的数值)
**&**为变量取地址
1、指针变量的定义
(1)指针的定义
int num = 20;
p为20的内存地址即**&num**
*p = 20
p = &num
注意:如果对num的地址取地址,即**q == *p == 20,那么 * q == p == &num
(2)定义步骤
*修饰指针变量名( * p)
保存谁的地址 就先定义谁。
1 定义一个指针变量p 保存 int num的地址; int *p;
2 定义一个指针变量p 保存数组int arr[5]首地址; int (*p)[5]
3 定义一个指针变量p 保存函数的入口地址 int fun(int,int); int (*p)(int,int);
4 定义一个指针变量p 保存结构体变量的地址 struct stu lucy; struct stu *p;
5 定义一个指针变量p 保存指针变量int *p的地址 int **p
(3)指针变量的详解
在32位平台任何类型的指针变量 都是4字节
在64位平台任何类型的指针变量 都是8字节
p等价于&num
*p等价于num的值 表示取p保存的地址编号的内容
(4)指针使用
int num = 10;
int *p = NULL;//定义一个指针
p = & num;
(5)指针的指针
int num = 10;
int *p = #
int **q = &p;
n级指针变量 可以保存 n-1级的地址
*p---->num
**q---->num
2、指针变量的初始化
(1)指针变量在操作之前必须指向合法地址空间,指针变量如果不初始化 立即操作 会出现段错误
(2)指针变量如果没有指向合法空间 建议初始化为NULL
int *p = NULL;
(3)将指针地址变量初始化为合法地址(变量地址、动态申请的地址、函数入口地址)
int num = 10;
int *p = & num;
等价于
int num = 10, *p = & num;
3、指针变量类型
(1)指针变量自身的类型
指针变量自身的类型 一般用于赋值语句的判断
char *p ---->char *
int *p ---->int *
short *p ---->short *
float *p ---->float *
double *p ---->double *
long *p ---->long *
void test()
{
int num = 10;
int *p = #
//在使用中
//num 为int &num 为int * ‐‐‐‐>对变量名 取地址 整体类型加一个*
//p 为int * *p 为int ‐‐‐‐>对指针变量 取* 整体类型减一个*
//在使用中 &和*相遇 从右往左 依次抵消
*&p == p
}
(2)指针变量指向的类型
指针类型指向类型决定取值宽度
char *p ----->char 1B
int *p ----->int 4B
short *p ---->short 2B
float *p ---->float 4B
double *p ---->double 8B
long *p ---->long 4B
案例:
定义一个变量 num = 0x01020304 ,取出其中的02(内容的存取是倒着存倒着取)
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 0x01020304;
char *p = (char *)#
printf(“%#x\n”,*(p+2));
return 0;
}
取出0203
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 0x01020304;
char *p = (char *)#
printf(“%#x\n”,*(short *)(p+1));
return 0;
}
(3)*p等价于num
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 0;
int *p = #
scanf("%d",p);
printf("num=%d\n",num);
*p = 10;
printf("num=%d\n",num);
(*p)++;
printf("num=%d\n",num);
return 0;
}
(4)指针变量的注意事项
①void不能定义普通变量
void num;错误
②void *可以定义指针
void *p;//可以定义
p自身类型为void *,在32为平台任意类型的指针 为4B那么系统知道P开辟4B空间,所以定义成功
p就是万能的一级指针变量,可保存任何一级指针地址
万能指针一般用于函数的形参 达到算法操作多种数据类型的目的
注:不要直接对void p的指针变量 取
int num = 10;
void *p = & num;
*p;//错误 p指向的类型为void 无法确定p的取值宽度
对p取*之前对p先进行指针类型强转
强转*(int *)p
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 10;
void *p = #
//*p;错误
printf("%d",*(int *)p);//使用前进行强转
}
③指针变量 未初始化 不要取*
int *p = NULL;
*p;错误
④指针变量不要越界访问
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[5] = {1,2,3,4,5};
int *p = arr;
printf(“%d\n”,*(p+6));//越界
return 0;
}
⑤数组元素的指针变量
p = &arr[0];取第0个元素地址
p = arr;取数组首地址 等价于第0个元素地址
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[5] = {1,2,3,4,5};
int n = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i=0;i<n;i++)//遍历数组
{
printf("%d",arr[i]);
}
int *p = arr;
for(i=0;i<n;i++)//遍历数组
{
printf("%d",*(p+i));
}
for(i=0;i<n;i++)//遍历数组
{
printf("%d",*(arr+i));
}
return 0;
}
⑥在使用中 [] 就是 *() 的缩写
[]是* ()的缩写 []左边的值放在+的左边 []里面的值 放在+右边 整体取*
arr[6] == *(arr+6)
arr[ 5 ] [ 6 ] == * (arr[5] + 6) ==* ( * (arr + 5 ) + 6)
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[5] = {1,2,3,4,5,7,8,9};
int n = sizeof(arr)/sizeof(arr[0]);
printf("%d\n",arr[6]);
printf("%d\n",*(arr+6));
}
& arr[0] == & *(arr+0) == arr+0 == arr
⑦扩展
指向同一数组,两指针变量相减 等于他们间的元素个数
int *p1 =arr; int *p2 = arr+4;
p2-p1=4;
两指针变量赋值= p2=p1它们指向同一处
两指针变量判断相等 == p2==p1 他们是否相等
两指针变量判断大小 > < >= <= !=
p1>p2 p1!=p2判断位置关系
两指针不能相加(重要)
4、指针数组
指针数组:本质是数组 只是数组的每个元素为 指针。
(1)数值的指针数组
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num1 = 1;
int num2 = 2;
int num3 = 3;
int num4 = 4;
int *arr[5] = {&num1, &num2, &num3, &num4};
int n = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for (i = 0; i < n; i++)
{
printf("%d\n",*arr[i]);
}
return 0;
}
(2)字符的指针数组
#include <stdio.h>
int main(int argc, char const *argv[])
{
char *str[] = {"haha","lala","xixi","hehe"};
int n = sizeof(str)/sizeof(str[0]);
int i = 0;
for (i = 0; i < n; i++)
{
printf("%s\n",str[i]);
}
return 0;
}
字符串存放在文字常量区,数组内是每个字符串的地址,存放在栈区或全局区
(3)二维字符数组
char *arr1[4]={"hehe", "xixi", "lala", "haha"};
char arr2[4][128]={"hehe", "xixi", "lala", "haha"};
arr1是在指针数组 存放的是每个字符串的首元素的地址
arr2是二维字符数组 存放的是每个字符串
5、数组指针
数组指针本质是指针变量保存的是数组的首地址。
(1)数组元素地址 和 数组首地址
数组首元素地址:&arr[0] == arr ,+1跳过一个元素
数组的首地址:&arr ,+1跳过整个数组
数组元素地址与数组首地址相等
(2)int (*p)[5] = NULL; 数组指针
int arr[5] = {1,2,3,4,5};
int (*p)[5] = &arr;
访问第3个元素
arr[2] == (arr+2) == * ( * p+2) == * ( * (p+0)+2) ==(p[0]+2) ==p [ 0 ] [ 2 ]
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[5] = {1,2,3,4,5};
int (*p)[5] = &arr;
printf("%d\n",arr[2]);
printf("%d\n",*(arr+2));
printf("%d\n",*(*p+2));
printf("%d\n",*(*(p+0)+2));
printf("%d\n",*(p[0]+2));
printf("%d\n",p [0][2]);
return 0;
}
(6)总结
int *arr[5];//指针数组 本质是数组 每个元素为int *
int (*arr)[5];//数组指针 本质是指针变量 保存的是数组的首地址(该数组必须5个元素
每个元素为int)
6、二维数组和数组指针的关系
int arr[5][5] =
{{1,2,3,4,5},{6,7.8,9,10},{11,12,13,14,15},{16,17,18,19,20},{21,22,23,24,25}};
arr既是数组的地址也是数组首元素地址
arr+1表示第二行地址
示例:
arr[1] => *(arr+1) 第一行第0列的列地址
&arr[1] => &*(arr+1)=>arr+1 第1行的行地址
*arr+1 => 第0行第1列的列地址
arr[1]+2 =>*(arr+1)+2 =>第1行第2列的列地址
**arr ==*(*(arr+0)+0) == arr[0][0]
案例:访问第19个元素
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[5][5] = {{1,2,3,4,5},{6,7.8,9,10},{11,12,13,14,15}, {16,17,18,19,20},{21,22,23,24,25}};
printf("%d\n",arr[3][4]);
printf("%d\n",*(*(arr+3)+4));
printf("%d\n",*(arr[3]+4));
return 0;
}
7、多维数组的物理存储
不管几维数组在物理上 都是一维存储
将二维数组当成一维数组访问
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[5][5] = {{1,2,3,4,5},{6,7.8,9,10},{11,12,13,14,15}, {16,17,18,19,20},{21,22,23,24,25}};
int row = sizeof(arr)/sizeof(arr[0]);
int col = sizeof(arr[0])/sizeof(arr[0][0]);
int *p = &arr[0][0];
int i = 0;
for (i = 0; i < row * col; i++)
{
printf("%d ",p[i]);
}
printf("\n");
return 0;
}
8、指针作为函数参数
(1)指针变量作为函数的参数
如果直接将变量的值传递到函数内部修改不能改变变量的值。
int a = 1;
int b = 2;
void my_swap(int x, int y)
{
int tmp = a;
a = b;
b = tmp;
}
my_swap(a,b);//此代码不能交换a,b的值
如果想在函数内部修改外部变量的值 需要将外部变量的地址 传递给函数。
#include <stdio.h>
void set_num(int *d)
{
*d=100;
return;
}
int main(int argc, char const *argv[])
{
int num = 0;
set_num(&num);
printf("num = %d\n",num);
return 0;
}
(2)数组作为函数的参数
函数内部想操作外部数组元素,将数组名传递给函数。
#include <stdio.h>
void printf_int_array(int *p,int n)
{
printf("sizeof(arr)=%lu\n",sizeof(p));//首元素地址大小8B
printf("arr[2]=%d\n",p[2]);
}
int main(int argc, char const *argv[])
{
int arr[4] = {2,3,4,5};
int n = sizeof(arr)/sizeof(arr[0]);
printf("sizeof(arr)=%lu\n",sizeof(arr));//数组大小4*4B
printf_int_array(arr,n);
return 0;
}
**案例:**键盘输入数组,输出最大值最小值(指针数组作为函数)
#include <stdio.h>
void input_int_array(int *p,int n)
{
printf("请输入%d个int数据:",n);
int i = 0;
for ( i = 0; i < n; i++)
{
scanf("%d",p+i);
}
}
void get_max_min(int *p,int *max_array,int *min_array,int n)
{
int max= p[0],min = p[0];
int i = 0;
for ( i = 0; i < n; i++)
{
if (max < p[i])
{
max = p[i];
}
if (min > p[i])
{
min = p[i];
}
*max_array = max;
*min_array = min;
}
return;
}
int main(int argc, char const *argv[])
{
int arr[5] = {0};
int n = sizeof(arr)/sizeof(arr[0]);
int max=0,min=0;
input_int_array(arr,n);//键盘输入
get_max_min(arr,&max,&min,n);
printf("max=%d \nmin=%d\n",max,min);
return 0;
}
(3)二维数组作为函数的参数
函数内部 想操作 函数外部的二维数组 需要将二维数组名 传递给函数。
#include <stdio.h>
void look_array(int (*p)[4],int row,int col)
{
int i=0,j=0;
for ( i = 0; i < row; i++)
{
for ( j = 0; j < col; j++)
{
printf("%d ",p[i][j]);
}
printf("\n");
}
}
int main(int argc, char const *argv[])
{
int arr[3][4] = {{1,2,3,4},{2,5,6,34},{4,8,9,2}};
int row = sizeof(arr)/sizeof(arr[0]);
int col = sizeof(arr[0])/sizeof(arr[0][0]);
look_array(arr,row,col);
return 0;
}
(4)函数的返回值类型为指针类型
将函数内部的合法地址 通过返回值 返回给函数外部使用
注意:函数不要返回 普通局部变量的地址
#include <stdio.h>
int* get_add(void)
{
static int data = 10;//静态局部变量,作用于整个进程
return &data;
}
int main(int argc, char const *argv[])
{
int *p;
p = get_add();
printf("%d\n",*p);
return 0;
}
(5)函数指针作为指针类型
函数名 代表函数的入口地址;
函数指针:本质是一个指针变量 只是该变量 保存的是函数的入口地址
函数指针p只能保存 有两个int形参以及int返回值 的函数入口地址
int (*p)(int ,int) = NULL;
#include <stdio.h>
int my_add(int x,int y)
{
return x + y;
}
int main(int argc, char const *argv[])
{
int (*p)(int ,int);
p = my_add;
printf("%d\n",p(10,20));
return 0;
}
9、总结
数值指针:
int num = 10;
int *p = NULL;
p = #
p = #
*p = num;
*p + 1 ==num + 1
一维指针数组:
int arr[4] = {1,6,9,3};
int *p = NULL;
p = arr;
p = arr; //数组名等价于元素首地址
*p =arr =(arr+0)= arr[0];
*p+1 =*arr+1==arr[0]+1;
(p+1) =(arr+1)==arr[1];
arr+1 //第1个元素地址
一维数组指针:
int arr[4] = {12,45,2,78};
int (*p)[4] = NULL;
p = &arr;
p = &arr;
p //第0个元素地址,数组首地址
p+1 //跳过整个数组,跳过的地址为数组元素个数*一个数组所占字节
arr //第0个元素地址
arr+1 //第1个元素地址
*(arr+1) //第1个元素
二位指针数组:
int arr[3][4] = {{2,6,3,8},{0,12,6,89},{12,35,98,36}};
int *p = NULL;
p = &arr[0][0];
p //首元素地址
p+1 //第1个元素地址
(p+1) //第0行第1个元素(*(p+0)+1)
arr //首元素地址
*arr //首元素地址
*arr+1 //第1个元素地址
(arr+1)//跳到下一行,跳过的地址为第一行元素个数一个元素所占字节
arr+1 //跳到下一行,跳过的地址为第一行元素个数*一个元素所占字节
arr[1] //第1行元素首地址
arr[1]+3 //第1行第3列地址
二位数组指针:
int arr[3][4] = {{2,6,3,8},{0,12,6,89},{12,35,98,36}};
int (*p)[4] = NULL;
p = arr;
p , arr //首行地址
p+1 , arr+1 //第1行地址
*p+1 , *arr+1 //第1个元素地址
*(p+1) , *(arr+1) //第1行地址
arr[1] , p[1] //第1行地址
arr[1]+1 , p[1]+1 //第1行第1列地址
*arr[1] , * p[1] //第1行第0列元素
*arr[1]+1 , *p[1]+1 //第1行第0列元素+1
*(arr[1]+1) , *(p[1]+1) //第1行第1列元素