C 语言基础六: 指针

10 阅读6分钟

C 语言基础六: 指针

1. 访问内存地址

  • 指针变量的数据类型和指向变量的类型需保持一致

  • 每个变量都有一个内存地址,使用&运算符访问

    int a = 15;
    int *p;
    p = &a;
    printf("a 指针地址: %p\n",p); //a 指针地址: 0x7ff7bfeff288

2. 如何使用指针

   int var = 20;
   int *ip;
   ip = &var;
   
   printf("var 变量的地址:%p\n",&var);  //var 变量的地址:0x7ff7bfeff288
   printf("var 变量存储指针地址:%p\n",ip);  //var 变量存储指针地址:0x7ff7bfeff288
   printf("var 变量的值:%d\n",*ip); //var 变量的值:20

3.空指针

  • 变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋值为NULL
   int *var = NULL;
   printf("var 变量的地址:%p\n",var);  //var 变量的地址:0x0

4. 打印指针占用的字节

  • 指针变量占用内存的大小,跟数据类型无关,跟编译器有关系;
  int c = 15;
   int* c1 = &c;
   
   char a = '0';
   char* a1 = &a;
   
   long long b = 400;
   long long* b1 = &b;
   
   printf("int : %zu \n",sizeof(c1));  // 8
   printf("char : %zu \n",sizeof(a1));  // 8
   printf("long long : %zu \n",sizeof(b1));  // 8
   

5. 指针的用法

  • 作用一: 操作其他函数中的变量 - 交换变量
#include <stdio.h>
void swrp(int *num1,int *num2);

int main(void){

   int a = 15;
   int b = 20;
   printf("变化前: a %d b %d \n",a,b);  // 变化前: a 15 b 20
   swrp(&a, &b);
   printf("变化后: a %d b %d \n",a,b);  //变化后: a 20 b 15
   
   return 0;
}

void swrp(int *num1,int *num2){
   int temp = *num1;
   *num1 = *num2;
   *num2 = temp;
}

  • 作用二: 函数返回多个值-定义一个函数求数组的最大/最小值
#include <stdio.h>

void getMaxAndMin(int arr[],int len,int *max,int *min);

int main(void){

   int arr[]= {1,2,3,4,5,6};
   int len = sizeof(arr)/sizeof(int);
   int max = arr[0];
   int min = arr[1];
   getMaxAndMin(arr, len, &max, &min);
   
   printf("max:%d  min:%d\n",max,min);  //max:6  min:1
   
  
   return 0;
}

void getMaxAndMin(int arr[],int len,int *max,int *min){
   for (int i = 0;i < len; i++) {
       if(arr[i] > *max){
           *max = arr[i];
       }
   }
   
   for (int i = 0;i < len; i++) {
       if(arr[i] < *min){
           *min = arr[i];
       }
   }
}

*作用三 : 函数的结果和计算状态分开 - 定义一个函数,获取余数

#include <stdio.h>

int getYs(int n1,int n2,int *res);

int main(void){
  
   int a = 11;
   int b = 6;
   int res = 0;
   int result = getYs(11, 6, &res);
   if(!result){
       printf("余数是%d \n",res);  //余数是5
   }

   return 0;
}

// 返回值代表的正常不正常
int getYs(int n1,int n2,int *res){
   if(n2 == 0){
       return 1;
   }
   *res = n1 % n2;
   return 0;
}

6. static小知识点

#include <stdio.h>
int* methods(void);

int main(void){

   int *p = methods();
   
   printf("坐下延时\n");
   printf("坐下延时\n");
   printf("坐下延时\n");
   
   //不加static修饰  -147765566,methods结束后,函数里的变量随之消失
   //加static修饰,正常打印
    printf("%d\n",*p);
   return 0;
}

int* methods(void){
   static int a = 10;  //此时的变量一直到程序结束 
   return &a;
}

7.指针运算(步长)

  • 步长:指针移动一次,走了多少字节.和指针类型有关系.int移动4个,char移动1个
  • 有意义的操作: 指针跟整数的加/减操作(每次移动多少个步长); 指针和指针进行操作(间隔步长)

int main(void){

int a = 10;
int * p = &a;
printf("%p \n",p);  //0x7ff7bfeff288
printf("%p \n",p+1);  //0x7ff7bfeff28c  移动4个字节
printf("%p \n",p+2);   //0x7ff7bfeff290  移动8个字节

int arr[] = {0,1,2,3,4,5,6,7,8,9,10};
int * p1 = &arr[0];

//通过内存地址获取数据
printf("数组 %d\n",*p1); // 0
printf("数组 %d\n",*(p1 + 2)); // 2

//计算间隔步长
int * p2 = &arr[5];

//p2和p1的间隔步长
printf("间隔步长: %ld\n",p2 - p1); // 5


return 0;

}

8 野指针/悬空指针

  • 野指针:指向的空间未分配
  • 悬空指针: 指针指向的空间已分配,但是被释放了
#include <stdio.h>

int* methods(void);

int main(void){
   
    int a = 10;
    int * p = &a;
    printf("L:%p \n",p);  //L:0x7ff7bfeff288
    printf("L:%d \n",*p);  // L:10
    
    //野指针
    int * p1 = p + 10;
    printf("K:%p \n",p1);  //K:0x7ff7bfeff2b0
    printf("K:%d \n",*p1);  //K:0
    
    //悬空指针
    int *m = methods();
    printf("拖点时间");
    printf("拖点时间\n");
    printf("F:%p \n",m); //F:0x7ff7bfeff25c
    printf("F:%d \n",*m);  //F:123312650
     
  
    return 0;
}

int* methods(void){
    int num = 10;
    int *p = &num;
    return p;
}

9. void *指针

// void *是一种特殊的指针类型,也被称为通用指针或者无类型指针,它可以指向任何类型的数据,但需要使用的时候需进行显示类型转换
// 特点:1.可以指向任何累心变量 2. 不包含所指向数据的类型信息 3.必须转换成具体类型才能访问数据 4.不能进行指针算术运算

int main(void){
   
    // 定义两个变量
    int a = 15;
    short b = 16;
    
    //定义两个指针
    int* p1 = &a;
    short* p2 = &b;
    
    //不同类型的指针之间是不能相互赋值的
    // void可以打破上面的观念
    // void没有任何类型,好处可以接受任意类型记录的内存地址
    void* p3 = p1;
    void* p4 = p2;
    
    //运算:无法获取指针指向的数据,也不能加减运算
//    printf("%d \n",*p3); 报错
    printf("%p \n",p3+1);  //错误地址
  
    return 0;
}

10.二级指针

  • 指针数据类型:跟指向空间中,数据类型是保持一致的
  • 二级指针可以操作一级指针记录的地址
//定义变量
    int a= 15;
    int b = 20;
    //一级指针
    int* p = &a;
    //二级指针
    int** PP = &p;
    
    //作用一:利用二级指针修改一级指针里边记录的地址
    *PP = &b;
    
    printf("%p\n",&a);  //0x7ff7bfeff288
    printf("%p\n",&b);  //0x7ff7bfeff284
    printf("%p\n",p);   //0x7ff7bfeff284
    
    //作用二:利用二级指针获取到变量中记录的值
    printf("%d\n",**PP);   //20

11.利用指针遍历数组

 int arr[] = {10,20,30,40,50};
    int len = sizeof(arr)/sizeof(int);
    
    //获取数组指针
    int *p1 = arr;
    int *p2 = &arr[0];
    
    //两种方式获取到的地址一样
    printf("%p\n",p1);  //0x7ff7bfeff270
    printf("%p\n",p2);  //0x7ff7bfeff270
    
    // 修改指针
    printf("%d\n",*p1);  // 10
    printf("%d\n",*(p1+1));  //20
    printf("%d\n",*(p1+2));  // 30
    
    for (int i = 0; i < len; i++) {
        printf("%d ",*p1);
        p1++;
    }
    
    //10 20 30 40 50

12. 数组指针细节

  • arr 参与计算的时候,会退化为第一个元素的指针
  • 不会退化的情况: 1. sizeof运算的时候,不会退化,还是整体 2. &arr获取地址的时候,不会退化
    int arr[] = {10,20,30,40,50};
    //不会退化,情况1
    int len = sizeof(arr)/sizeof(int);
    //情况2: &arr
    printf("%p\n",arr);  //0x7ff7bfeff270
    printf("%p\n",&arr);  //0x7ff7bfeff270
    
    
    printf("%p\n",arr+1);  //0x7ff7bfeff274  增加4步长
    printf("%p\n",&arr+1);  //0x7ff7bfeff284  // 增加20步长, 数据类型 * 数组长度 = 20