【嵌入式学习2】指针 - 数组

目录

## 概述

## 指针

### 指针特点

## 指针变量

### 指针变量特点

## 区别

## 指针变量的使用

定义指针变量时:

使用指针变量时:

## 通过指针间接修改变量的值

## 指针大小

指针大小与数据类型无关:无论指针指向什么类型的数据(int、char、double等),指针本身的大小只取决于系统的位数(32位或64位)。

## 指针步长

### 指针步长的计算方式

## 空指针和野指针

## 多级指针

## 指针与常量

## 函数参数传递内容

1、参数传值

2、参数传址

## 函数指针

1、函数名

2、函数指针

3、回调函数


## 概述

内存:房间

地址:房间号

## 指针

  • 指针是一种数据类型,用来存储某个变量或对象的内存地址
  • 指针的类型决定了它可以指向的数据类型。例如,int*表示指向int类型数据的指针,char*表示指向char类型数据的指针
  • 指针可以间接修改变量的值
### 指针特点
  • 指针本身也有一个地址和一个值。指针的值是它所指向的变量的地址
  • 指针可以指向任何类型的变量(包括数组、结构体等),也可以指向函数。
int a = 10;
int* p = &a;  // p是一个指针,存储变量a的地址

## 指针变量

  • 指针变量是一个具体的变量,具有指针类型。指针变量用来存储某个变量的地址,并且可以通过指针变量间接访问该变量的值
  • 指针变量本身也有一个地址和一个值,它的值是它所指向的变量的地址
  • 指针变量指向谁,就把谁的地址赋值给指针变量
### 指针变量特点
  • 指针变量是存储指针的变量,它占用一定的内存空间。
  • 指针变量可以被赋值(即可以指向不同的变量),也可以被修改。
  • 指针变量可以为空(NULLnullptr),表示它不指向任何有效的内存地址。
int a = 10;
int* p = &a;  // p是一个指针变量,存储变量a的地址
int** q = p;   // q也是一个指针变量,它存储了p的值(即a的地址)

## 区别

#include 

int main() {
    int a = 10;          // a是一个整型变量,值为10
    int* p = &a;         // p是一个指针变量,存储变量a的地址
    int** q = &p;        // q是一个指针变量,存储变量p的地址

    printf("a的值:%d\n", a);          // 输出a的值
    printf("a的地址:%p\n", &a);       // 输出a的地址
    printf("p的值:%p\n", p);          // 输出p的值(即a的地址)
    printf("p的地址:%p\n", &p);       // 输出p的地址
    printf("q的值:%p\n", q);          // 输出q的值(即p的地址)
    printf("q的地址:%p\n", &q);       // 输出q的地址
    printf("通过p访问a的值:%d\n", *p); // 通过指针变量p访问a的值
    printf("通过q访问p的值:%p\n", *q); // 通过指针变量q访问p的值
    printf("通过q访问a的值:%d\n", **q); // 通过指针变量q访问a的值

    return 0;
}

## 指针变量的使用

【嵌入式学习2】指针 - 数组_第1张图片

类型 变量;
类型 * 指针变量 = &变量;

& 取地址:返回操作数内存地址

定义指针变量时:
  • 表示所声明变量为指针类型 【指针变量的类型比这个变量的类型多一个*】
使用指针变量时:
  • *解引用,可以操作(读取、修改)指针所指向的变量的值
  • * 号可以用来操作指针所指向的内存空间
#include 

int main() {

    // 定义一个int类型的变量,同时赋值为10
    int a = 10;
    // 打印变量的地址
    printf("&a = %p\n", &a);
    // 定义一个指针变量,int *保存int的地址
    // int *代表是一种数据类型,int *指针类型,p才是变量名
    int* p;
    // 指针指向谁,就把谁的地址赋值给这个指针变量
    p = &a;
    // 打印p, *p, p指向了a的地址,*p就是a的值
    printf("p = %p, *p = %d\n", p, *p);

    return 0;
}

## 通过指针间接修改变量的值

#include 

int main() {
    // 定义一个int类型变量a,同时赋值为0
    int a = 0;
    // 定义int *指针变量,同时赋值a的地址
    int *p = &a;
    // 通过指针间接修改a的值
    *p = 123;
    printf("a = %d\n", a);
    // 定义一个int类型变量b,同时赋值为5
    int b = 5;
    // p 保存 b的地址
    p = &b;
    // 通过指针间接修改b的值
    *p = 250;
    printf("b = %d\n", b);

    return 0;
}

## 指针大小

  • 指针大小与数据类型无关:无论指针指向什么类型的数据(intchardouble等),指针本身的大小只取决于系统的位数(32位或64位)。

  • 指针大小与系统架构相关

    • 在32位系统上,指针大小为4字节。

    • 在64位系统上,指针大小为8字节。

  • 使用sizeof运算符:可以通过sizeof运算符来动态获取指针的大小,这在编写跨平台代码时非常有用。

  • 指针类型与数据类型的关系:虽然指针的大小与指向的数据类型无关,但在使用指针时,仍然需要确保指针类型与它所指向的数据类型匹配,以避免类型不匹配错误。

## 指针步长

是指指针在进行加1或减1操作时,其地址值的变化量。指针步长的大小并不是固定的,而是与指针所指向的数据类型大小有关。

### 指针步长的计算方式

当对指针进行加1操作时,指针会指向下一个相同类型的数据。因此,指针步长等于它所指向的数据类型的大小。具体来说:

  • 如果指针指向char类型,步长为1字节(因为sizeof(char)为1字节)。

  • 如果指针指向int类型,步长为sizeof(int)字节(通常是4字节)。

  • 如果指针指向double类型,步长为sizeof(double)字节(通常是8字节)。

#include 

int main() {
    char charArr[] = "Hello";
    int intArr[] = {1, 2, 3, 4, 5};
    double doubleArr[] = {1.1, 2.2, 3.3, 4.4, 5.5};

    char* charPtr = charArr;
    int* intPtr = intArr;
    double* doublePtr = doubleArr;

    printf("Initial char pointer: %p\n", charPtr);
    printf("Initial int pointer: %p\n", intPtr);
    printf("Initial double pointer: %p\n", doublePtr);

    charPtr++;
    intPtr++;
    doublePtr++;

    printf("After increment:\n");
    printf("Char pointer: %p (Step size: %zu bytes)\n", charPtr, sizeof(char));
    printf("Int pointer: %p (Step size: %zu bytes)\n", intPtr, sizeof(int));
    printf("Double pointer: %p (Step size: %zu bytes)\n", doublePtr, sizeof(double));

    return 0;
}

结果:

#include 

int main() {
    int *p;
    p = 0x12345678; // 给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义
    // *p = 1000;      // 操作野指针指向未知区域,内存出问题,err
    printf("111111111111111111\n");

    int *q = NULL;  // 空指针

    return 0;
}
Initial char pointer: 0x7ffeea7b1000
Initial int pointer: 0x7ffeea7b1010
Initial double pointer: 0x7ffeea7b1030
After increment:
Char pointer: 0x7ffeea7b1001 (Step size: 1 bytes)
Int pointer: 0x7ffeea7b1014 (Step size: 4 bytes)
Double pointer: 0x7ffeea7b1038 (Step size: 8 bytes)

## 空指针和野指针

  • 野指针:赋任意数值,没有意义 → 此指针指向区域未知
  • 空指针:赋值NULL,标志该指针没有任何指向

野指针不会直接引发错误,操作野指针指向的内存区域才会出问题

#include 

int main() {
    int *p;
    p = 0x12345678; // 给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义
    // *p = 1000;      // 操作野指针指向未知区域,内存出问题,err
    printf("111111111111111111\n");

    int *q = NULL;  // 空指针

    return 0;
}

## 多级指针

#include 

int main() {

    int a = 100;

    // 一级指针
    int* p1 = &a;
    printf("&a=%p\n", &a);
    printf("p1=%p\n", p1);
    printf("&p1=%p\n", &p1);

    // 二级指针,可以存储一级指针变量的地址
    int** p2 = &p1;
    printf("p2=%p\n", p2);
    printf("&p2=%p\n", &p2);

    // 三级指针,可以存储二级指针变量的地址
    int*** p3 = &p2;
    printf("p3=%p\n", p3);
    printf("&p3=%p\n", &p3);

    printf("---------------------\n");

    // 通过一级指针访问100,打印出来
    printf("*p1=%d\n", *p1);
    
    // 通过二级指针访问100,打印出来
    printf("**p2=%d\n", **p2);
    
    // 通过三级指针访问100,打印出来
    printf("***p3=%d\n", ***p3);
    
    return 0;
}

## 指针与常量

  • int const *p1 = &a;
  • //const *p1就是相当于把a的值给锁定了
  • int *const p2 = &a;
  • //*const p2就是相当于把a的地址给锁定了
#include 

int main() {
    int a = 1;
    int b = 2;
    // p1 可以改,*p1不能改
    const int *p1 = &a; // 等价于 int const *p1 = &a;
    // p1 = &b;    // ok
    // *p1 = 555;  // err

    // p2 不能修改,*p2可以修改
    int *const p2 = &a;
    // p2 = &b;    //err
    // *p2 = 555;  // ok

    // p3 和 *p 都不能改
    const int *const p3 = &a;
    // p3 = &b;    // err
    // *p3 = 555;  // err

    return 0;
}

记忆技巧:从左往右看,跳过类型,看修饰哪个字符

  • 如果是*p, 说明指针指向的变量内容不能改变(即:值不能改)
  • 如果是p,说明指针的指向不能改变 (即:地址不能改)

## 函数参数传递内容

1、参数传值
  • 传值是指将参数的值拷贝一份传递给函数,函数内部对该参数的修改不会影响到原来的变量
// 函数参数传值,函数内部交换2个变量的值,验证函数外部的变量有没有改变
#include 

// 函数定义
void func(int m, int n) {
    // 函数内部交换2个变量的值
    int temp = m;
    m = n;
    n = temp;
    printf("函数内部 m = %d, n = %d\n", m, n);
}

int main() {
    int a = 10;
    int b = 20;
    // 调用函数,值传递
    func(a, b);
    printf("函数外部 a = %d, b = %d\n", a, b);

    return 0;
}
2、参数传址
  • 传址是指将参数的地址传递给函数,函数内部可以通过该地址来访问原变量,并对其进行修改。
// 函数参数传地址,函数内部交换2个指针指向内存的值,验证函数外部的变量有没有改变
#include 

// 函数定义
void func(int *m, int *n) {
    // 函数内部交换2个指针指向内存的值
    int temp = *m;
    *m = *n;
    *n = temp;
    printf("函数内部 *m = %d, *n = %d\n", *m, *n);
}

int main() {
    int a = 10;
    int b = 20;
    // 调用函数,地址传递
    func(&a, &b);
    printf("函数外部 a = %d, b = %d\n", a, b);

    return 0;
}

## 函数指针

1、函数名
  • 一个函数在编译时被分配一个入口地址,这个地址就称为函数的指针,函数名代表函数的入口地址
2、函数指针
  • 指向函数的指针
返回值 (*函数指针变量)(形参列表);
//函数指针变量的定义,其中返回值、形参列表需要和指向的函数匹配
#include 

void func(int a) {
    printf("a = %d\n", a);
}

int main() {
    // 函数指针变量的定义,同时初始化
    void (*p1)(int a) = func;
    // 通过函数指针变量调用函数
    p1(10);

    // 先定义函数指针变量,后面再赋值
    void (*p2)(int);
    p2 = func;
    // 通过函数指针变量调用函数
    p2(20);

    return 0;
}
3、回调函数
  • 函数指针变量做函数参数,函数指针变量指向的函数就是回调函数
  • 回调函数可以增加函数的通用性,在不改变原函数的前提下,增加新功能
#include 

// 定义函数,函数指针做形参
int calc(int a, int b, int (*p)(int, int)){
    // 通过函数指针变量调用函数,获取返回值
    int res = p(a, b);
    
    return res;
}

// 定义加法函数
int add(int x, int y) {
    return x + y;
}

// 定义减法函数
int sub(int x, int y) {
    return x - y;
}

int main() {
    int result;

    // 回调加法函数
    result = calc(1, 2, add);
    printf("result = %d\n", result);

    // 回调减法函数
    result = calc(10, 5, sub);
    printf("result = %d\n", result);

    return 0;
}

你可能感兴趣的:(嵌入式学习,学习,笔记,嵌入式硬件,c语言)