C语言基础之指针

指针

系统为内存的每一个字节 分配一个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 *)&num;
    printf(%#x\n”,*(p+2));
    return 0;
}

取出0203

#include <stdio.h>
int main(int argc, char const *argv[])
{
    int num = 0x01020304;
    char *p = (char *)&num;
    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 = &num;
    
    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 = &num;
    //*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+11行的行地址
*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 = &num;

​ 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列元素

10、如有错误欢迎指正

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值