百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

C语言基础之指针_c语言中的指针用法

haoteby 2025-02-21 13:26 27 浏览

概述

系统为内存的每一个字节 分配一个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 
int main(int argc, char const *argv[])
{
    int num = 0x01020304;
    char *p = (char *)#
    printf(“%#x\n”,*(p+2));
    return 0;
}

取出0203

#include 
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 
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 
int main(int argc, char const *argv[])
{
    int num = 10;
    void *p = #
    //*p;错误
    printf("%d",*(int *)p);//使用前进行强转
}

③指针变量 未初始化 不要取*

    int *p = NULL;
    *p;错误

④指针变量不要越界访问

#include 
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 
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

⑥在使用中 [] 就是 *() 的缩写

[]是* ()的缩写 []左边的值放在+的左边 []里面的值 放在+右边 整体取*

arr[6] == *(arr+6)

arr[ 5 ] [ 6 ] == * (arr[5] + 6) ==* ( * (arr + 5 ) + 6)

#include 
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 

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 

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



10、如有错误欢迎指正

相关推荐

别争了,Access数据库才是真正的低代码开发平台

Access数据库是微软公司搞出来的“奇葩”产品。...

Access开发轻松一键将 Access 全库表格导出为 Excel

hi,大家好呀!在日常工作中,Access常常是我们忠实的数据管家,默默守护着项目信息、客户列表或是库存记录。它结构清晰,录入便捷,对于许多中小型应用场景来说,无疑是个得力助手。然而,当我们需要对这...

跟我学:从零开始用Access设计一套完整的系统(一)

序言:Access是一款强大而灵活的数据库软件,可以设计和开发各种类型和规模的数据库应用程序。本文旨在为您提供从零开始设计Access数据库系统的详细指导,并通过实际案例演示如何在Access中设计和...

问卷调查管理程序 Access数据库 功能介绍和VBA代码分享

o本系统包含主要功能有:问卷管理,题目管理,问卷填写,调查结果统计,数据汇总导出o数据库系统包含:表,查询,窗体,VBA代码...

非绑定记录窗体查看管理数据 Access数据库功能模块 VBA代码编程

模块Public成绩IDnumAsLong学生成绩管理PrivateSubCommand更新_Click()DoCmd.SetWarnings(False)...

ACCESS中的DLookUp函数是如何运算的?

一、DLookUp函数介绍1.DLookUp函数的用途:可以用于从指定集合(一个域)中获取符合条件的特定字段的值。2.DLookUp函数的格式为:DLookUp(expr,domain,...

Excel常用技能分享与探讨(5-宏与VBA简介 VBA之用户窗体-一)

用户窗体(UserForm)是VBA中创建交互式界面的核心工具,可用于数据录入、设置参数或展示信息。...

【每日任务计划管理系统】Access数据库管理系统 VBA代码分享

窗体系统主页文本框,组合框,按钮,子窗体OptionCompareDatabase...

VBA高效开发:用用户窗体打造个性化数据录入工具

在日常办公中,Excel的数据录入是否总让你陷入这些困境?手动输入易错、格式混乱难追溯、重复操作耗时费力。今天,我们将突破常规,利用VBA的用户窗体(UserForm)构建一套“智能校验、流程清晰、...

VBA编程(基于Access)第1课:VBA的作用和学习方法

VBA,英文全称VisualBasicforApplications,直接翻译过来叫做“可以直接使用的VB语言”。...

Access数据库宏与VBA代码的使用(精品一)

Access数据库的宏相当于实现某一功能的一系列命令和操作,我们无需写代码,系统已经将主体代码集成一块,我们只需要做一些简单的操作即可,而VBA代码则是实实在在的代码写到程序里面,我们可以自己编写,也...

【每日任务管理系统】(2) VB 管理系统 代码 Visual Basic access数据库

窗体全部任务DimdhAsLong'存储高度差DimdwAsLong'存储宽度差...

VBA连接access数据库开发软件(vba调用数据库连接)

VBA连接access数据库开发小软件虽然VBA(包括VB)已不再流行,但是在某些场合还是比较方便的,尤其对非编程专业人员。灵活使用VBA,可以很十分方便的处理excel,access数据,提高工作效...

九章云极发布全新AI智算云平台:支持秒级生成百万级Token

6月16日,在在九章云极智能计算科技论坛上,九章云极宣布推出全新AI智算云平台“九章智算云AlayaNeWCloud2.0”,该平台基于Serverless技术架构与强化学习技术(RL)的深度融合...

浅谈基于大数据技术下的“云旅游”平台运营策略研究

云旅游体验平台是利用大数据和虚拟技术,构建虚拟旅游环境,能够改变旅游企业的营销模式和旅游者的消费模式。本文从云旅平台多维度数据信息的获取与分析,平台体验质量反馈信息数据构建,云旅游平台服务功能设计方案...