优秀的编程知识分享平台

网站首页 > 技术文章 正文

C指针漫谈(c 指针的指针)

nanyue 2024-08-25 10:37:06 技术文章 5 ℃

菜鸟A给另一菜鸟B一个单纯的内存地址,让他去内存基地获得需要的信息,菜鸟B兴冲冲地去了,一到却傻眼了,看到一排开关,线性排列,满腹疑云。应该取多少个字节?是数据类型还是代码类型?提取后应该按什么编码方案解释?菜鸟B只得打电话问老鸟C,老鸟告诉他,一个单纯的地址不行,要有类型,类型提供了字节长度信息、编码方案信息,让A给你一个指针吧,指针是具有类型信息的地址,不是一个单纯的内存地址。

1 计算机的内存可随机访问,可寻址;

2 程序运行前的加载阶段,要将代码和代码中定义的全局、局部静态数据加载内存;

3 程序运行后,操作系统会为每个运行的程序(进程)提供一定的栈空间和堆空间;

4 数据类型决定了内存空间的大小和编码、解码方式(如整型的补码、IEEE754浮点编码方案、ASCII字符编码方案等);

5 不同的CPU有不同的代码集(CPU的抽象);

6 函数是编程语言程序编写的基本模块抽象,函数名是是代码块的命名,可以赋值给一个函数指针,函数指针的类型由返回值类型,参数个数和类型指示;

7 指针是一类特殊的内存地址,特殊性在于其有类型,数据类型,函数类型或类型暂定的void类型。所以指针可以区分为三类:

数据指针;

函数指针;

void指针;

严格意义上的指针并不是指针变量,指针变量是一个左值,而指针特值可以赋值给一个指针变量的右值。通常,在上下文中,指针变量简称为指针。

所以,地址、指针、指针变量并不是相同概念,而是相关概念。

7.1 数据指针,不同的数据类型具有不同的内存长度,指针指向不同的数组元素或栈空间的不同部分时,称为指针的移动,移动可以由指针的算术运算完成,如有指针p、q指向某一数据,整数n,通常的算术运算有:

① p++、p--、++p、--p;
② p+=n、p-=n;
③ n = p-q;
④ bool flag = p>q;

第①、②类运算实现指针的移动,移动是为了指向某一数据元素的首地址,取出其类型所指引的字节数,按其类型所指定的编码、解码方式解析数据,按其类型所指定的操作对数据进行操作。虽然内存按字节编址,但这里按单纯的字节数移动没有意义,需要以类型规定的字节数为步长才有意义,步长的计算有编译器隐式完成:

① p++、p--、++p、--p; // 步长为sizeof(*p)、前后移动一个步长,指向前、后数据元素
② p+=n、p-=n; // 步长为sizeof(*p),整体偏移sizoef(*p)*n个字节,指向第n个元素
③ n = p-q; // 两个指针间的元素个数(而不是字节数,当然,字符的元素个数与字节数是一致的);
④ bool flag = p>q;  // 比较两个地址的前后关系,如实现memmove()函数时

CPU的寄存器IP也可以实现移动到下一条指令或跳转到某一条指令(某一个地址)的操作。

对于链式存储,可以由具体的指向自身的指针赋值实现移动:

typedef struct ListNode{
    int data;
    struct ListNode *next;
}ListNode,*ListPtr;
void printList(ListPtr head)
{
    ListPtr curr = head;
    while(curr)
    {
        printf("%d ",curr->data);
        curr = curr->next; // 自指自指针赋值实现移动
    }
}

7.2 函数指针,函数也是有类型的,函数的返回值、函数的参数类型和数量整体决定了函数指针的类型。

#include <stdio.h>
void demo()
{
    printf("hi!\n");
}
int main()
{
    demo();
    void (*funcPtr)() = demo;
    funcPtr();
    (*funcPtr)();
    getchar();
    return 0;
}

7.3 void指针,称为泛型(generic)指针,一种数据类型暂定的指针。当获取某一内存地址,但类型暂定时,可以用void指针来声明和定义。当然,解引用时需要类型确定,需要做强制类型转换,转换为具体的操作类型;

void print(void *p, int t){
if (t == 1) 
      printf("%d\n", *(int *) p);
else if (t == 2) 
      printf("%0.3lf\n", *(double *) p);
else 
      printf("error: unknown type\n");
}

8 指针类型转换可以实现强制类型类型解释字符编码

bool endian()
{
    int d = 0x01020304;
    char* p = (char*)&d;
    if(*p == 1)// 判断高位,32或64位系统,高位的字节都是统一的
    {
        printf("首地址(低址)匹配高位,是大端!\n");
        return false;
    }
    else
    {
        printf("首地址(低址)匹配低位,是小端!\n");
        return true;
    }
}

9 指针的己型、己址、己值、他型、他址、他址

10 指针类型与目标类型

看以下动态内存声明:

 int (*darrp)[COL] = (int(*)[COL])malloc(sizeof(int)*row*col);

darrp指针的指针类型是:int(*)[COL];

darrp指针的指针类型是:int[COL];

demo:

#include <stdio.h>
#include <stdlib.h>

const int ROW = 3;
const int COL = 4;
int arr[ROW][COL] = {1,2,3,4,5,6,7,8,9,10,11,12};// 静态二维数组

void input(int(*arrp)[COL])
{
    for(int i=0;i<ROW;i++)
        for(int j=0;j<COL;j++)
            arrp[i][j] =arr[i][j];
}

void input2(int**pp,int r,int c)
{
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
            pp[i][j] =arr[i][j];
}

void output(int(*arrp)[COL])
{
    for(int i=0;i<ROW;i++)
        for(int j=0;j<COL;j++)
            printf("%d ",arrp[i][j]);
    printf("\n");
}

void output2(int **pp,int r,int c)
{
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
            printf("%d ",pp[i][j]);
    printf("\n");
}

void demo(){
    int i,j;
    output(arr);
    int * parr[COL];// 静态指针数组(行数是常量)
    for(i=0;i<ROW;i++) 
        parr[i] = arr[i];
    output2(parr,ROW,COL);
    int (*arrp)[COL] = arr;// 静态指针数组(列数是常量)
    output(arrp);

    int row = 3;
    int col = 4;
    int (*darrp)[COL] = (int(*)[COL])malloc(sizeof(int)*row*col);// 动态态指针数组(列数是常量)
    input(darrp);
    output(darrp);
    free(darrp);


    int **darr =(int**)malloc(sizeof(int*)*row); // 动态指针数组
    for(i=0;i<row;i++)
        darr[i] = (int*)malloc(sizeof(int)*col);
    input2(darr,row,col);
    output2(darr,row,col);
    for(i=0;i<row;i++)
        free(darr[i]);
    free(darr);
}

int main()
{
    demo();
    getchar();
    return 0;
}
/*
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12

*/

11 指针适用场合

ref:

C|指针核心概念:目标类型、左值右值、移动操作、右左原则

C|指针的10种经典应用场合

-End-

Tags:

最近发表
标签列表