【嵌入式C语言】图解C语言指针变量

本博文转载于 图解C语言指针变量 如有侵权,请联系我尽快删除

【嵌入式C语言】图解C语言指针变量

  • [1. 图解C语言指针变量](#1. 图解C语言指针变量)
  • [2. 指针变量与数组名](#2. 指针变量与数组名)
  • [3 . 主调函数与被调函数之间的指针传递](#3 . 主调函数与被调函数之间的指针传递)
  • 4. 数组做函数参数#

1. 图解C语言指针变量

1 指针变量的基本操作基本操作

c 复制代码
    int a,*iptr,*jptr,*kptr;
    iptr = &a;
    jptr = iptr;
    *jptr = 100;
    kptr = NULL;

图解:

1.1 己址和己空间

指针变量也是一个变量,对应一块内存空间,对应一个内存地址,指针名就是己址。这空内存空间多大?一个机器字长(machine word),32位的CPU和操作系统就是32个位,4个字节,其值域为:0x-0xFFFFFFFF。64位的CPU和操作系统就是64个位,8个字节,其值域为:0x-0xFFFFFFFFFFFFFFFF

1.2 己值、他址、他空间

指针变量的值就是其指向的空间的地址,指向的地址的空间大小就是指针变量指向类型的大小。

1.3 声明与初始化

当声明一个指针变量,没有初始化时,指针变量只获得了其自身的内存空间,而其指向还没有确定,此时指针变量解引用做左值是非法操作。如果要使用指针变量解引用做左值,有三条途径:

c 复制代码
int *ptr;
    int *ptr_2;
    int a = 1;
    ptr_2 = &a;
    // *ptr = 0;    // 非法操作,其指向其指向的内存空间还未确定
    ptr = &a;                       // ① 右值是一个变量地址
    ptr = ptr_2;                    // ② 右值是一个同类型指针,且已初始化
    ptr = (int*)malloc(sizeof(int));// ③ 右值是一个内存分配函数返回一个void指针
    *ptr = 0;       // 合法操作,ptr有了确定的指向及指向的内存空间;

1.4 函数之间指针值的传递

函数(如下例的funcForSpace())内定义局部变量(如下例的a)保存在一个函数的栈帧上,当一个函数执行完毕后,另一个函数(如下例的stackFrame_reuse())执行时,该空间会被stackFrame_reuse()重复使用,a所使用的空间将不复存在,所以当一个指针变量指向局部变量的内存空间时,其地址值传递给主调函数时,并不是一个有效值。

c 复制代码
#include <stdio.h>

void funcForSpace(int **iptr) {
    int a = 10;
    *iptr = &a;
}
void stackFrame_reuse()
{
    int a[1024] = {0};
}
int main()
{
    int *pNew;
    funcForSpace(&pNew);
    printf("%d\n",*pNew); // 10,此时栈帧还未被重复使用
    stackFrame_reuse();
    printf("%d\n",*pNew); // -858993460,垃圾值
    while(1);
    return 0;
}

可以在funcForSpace()内分配一块堆内存,传递给主调函数。

复制代码
#include <stdio.h>
#include <malloc.h>
int g(int **iptr) { // 当试图修改主调函数的一级指针变量时,被调函数的参数是一个二级指针
    if ((*iptr = (int *)malloc(sizeof(int))) == NULL)
        return -1;
}
int main()
{
    int *jptr;
    g(&jptr);
    *jptr = 10;
    printf("%d\n",*jptr); // 10
    free(jptr);
    while(1);
    return 0;
}

可以图示一下以上代码指针的传递过程:

以下图示a表示计算机内存,b表示一个函数调用时在栈(stack)上开辟的栈帧空间:

2. 指针变量与数组名

数组名在一定的上下文中会转换为指向数组首元素的地址,以方便指针的算术运算,如

c 复制代码
#include <stdio.h>

int main()
{
    int a[5] = {0}; 
    char b[20] = {0};
    *(a+3) = 10;    // a+3是指相对于地址a,偏移sizeof(int)个字节
    *(b+3) = 'x';   // b+3是指相对于地址b,偏移sizeof(char)个字节

    printf("%d, %c\n",a[3],b[3]); // 10, x
    while(1);
    return 0;
}

可以图示一下以上代码指针的偏移细节:

3 . 主调函数与被调函数之间的指针传递

看以下代码:

c 复制代码
#include <stdio.h>
void swap1(int x, int y) {
    int tmp;
    tmp = x; x = y; y = tmp;
}
void swap2(int *x, int *y) {
    int tmp;
    tmp = *x; *x = *y; *y = tmp;
}
void caller()
{
    int a = 10;
    int b = 20;
    swap1(a,b);
    printf("%d %d\n",a,b);
    swap2(&a,&b);
    printf("%d %d\n",a,b);
}
int main()
{
    caller();
    return 0;
}

以上代码可用以下图示理解:

swap1传值:

swap2传址(指针传递):

4. 数组做函数参数#

二维数组是数组的数组,n维数组是n-1维数组的数组。内存是一维的字节序列,所谓的n维数组其实只是一个逻辑意义的表示,其物理结构还是一维线性的。

n维数组的元素是一个n-1维数组。如果用指针指向一个n维数组,其指针类型必须有n-1维的长度信息,当其用作函数参数时也是如此。

c 复制代码
void g(int a[][2]) { // void g(int(*a)[2]){是相同写法
    a[2][0] = 5;
}
void caller()
{
    int a[3][2];
    int (*p)[2] = a;
    *(*(p+2)+0) = 7; // p=2表示相对于地址p偏移sizeof(*p)
    printf("%d\n",a[2][0]);  // 7
    g(a);
    printf("%d\n",a[2][0]); //  5
}

以下代码可以用以下图示辅助理解:

参考:Kyle Loudon《 Mastering Algorithms with C》

相关推荐
qq_401700412 小时前
嵌入式用Unix时间的优势及其C语言转换
服务器·c语言·unix
小奶包他干奶奶4 小时前
Webpack学习——Loader(文件转换器)
前端·学习·webpack
小奶包他干奶奶4 小时前
Webpack学习——原理理解
学习·webpack·devops
励志成为美貌才华为一体的女子4 小时前
强化学习PPO和GRPO逻辑学习
学习
tobebetter95274 小时前
How to manage python versions on windows
开发语言·windows·python
meichaoWen5 小时前
【Vue3】vue3的全面学习(一)
前端·javascript·学习
FFF团团员9095 小时前
树莓派学习笔记3:LED和Button
笔记·学习
9***P3345 小时前
PHP代码覆盖率
开发语言·php·代码覆盖率
CoderYanger5 小时前
优选算法-栈:67.基本计算器Ⅱ
java·开发语言·算法·leetcode·职场和发展·1024程序员节
jllllyuz6 小时前
Matlab实现基于Matrix Pencil算法实现声源信号角度和时间估计
开发语言·算法·matlab