理解C指针

177 阅读2分钟

在阅读《深入理解计算机系统》时,读到理解指针这一节,写的非常好,这里对其进行学习总结。下面的内容部分摘自《深入理解计算机系统》,部分是自己的学习总结。

理解指针

指针是C语言的一个核心特色,以一种统一的方式,对不同数据结构中的元素产生引用。下面重点介绍一些指针和它们映射到机器代码的关键原则。

  • 每个指针都对应一个类型。这个类型表明该指针指向的是哪一类对象。以下面的指针声明为例:int *ip;,变量ip是一个指向int类型对象的指针,通常如果对象类型为T,那么指针的类型为T*。特殊的void *类型代表通用指针。比如说,void *malloc(size_t size)函数返回一个通用指针,然后通过强制类型转换或者赋值操作那样的隐私强制类型转换,将它转换成一个有类型的指针。指针类型不是机器代码中的一部分,是C语言提供的一种抽象,帮助程序员避免寻址错误。

可以这么理解,指针的具体地址值相当于是一个寻址开始的地址,什么时候结束是由指针类型的size大小决定何时结束,比如char*,从起始地址读一个字节就结束,int*,从起始地址读4个字节就结束。下面我们写一段C代码,验证我们对指针的理解。

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

typedef enum NodeType {
    INT,
    CHAR
} NodeType;

typedef struct Node {
    NodeType type;
} Node;

typedef struct IntNode {
    NodeType type;
    int value;
} IntNode;

typedef struct CharNode {
    NodeType type;
    char value;
} CharNode;

char* printNodeType(NodeType type) {

    switch (type)
    {
    case INT:
        return "INT";    
    case CHAR:
        return "CHAR";
    default:
        return "UNKNOW";
    }

    return NULL;
}

// 重点理解这个函数
void printNode(Node* n) {
    assert(n != NULL);

    switch (n->type)
    {
    case INT:
        {
            IntNode* in = (IntNode*)n;
            printf("type:%s value:%d \n", printNodeType(in->type), in->value);
        }
        break;
    case CHAR:
        {
            CharNode* cn = (CharNode*)n;
            printf("type:%s value:%d \n", printNodeType(cn->type), cn->value);
        }
        break;
    default:
        printf("unknow node \n");
        break;
    }
}

int main() {
    IntNode *a = (IntNode*)malloc(sizeof(IntNode));
    a->type = INT;
    a->value = 1024;
    printNode(a);

    CharNode *b = (CharNode*)malloc(sizeof(CharNode));
    b->type = CHAR;
    b->value = 'a';
    printNode(b);

    return 0;
}

运行结果:

type:INT value:1024 
type:CHAR value:97 

这个代码可以验证上面的理解。

  • 每个指针都有一个值。这个值是某个指定类型的对象的地址。特殊的NULL(0)值表示该指针没有指向任何地方。
  • 将指针从一种类型强制转换成另一种类型,只改变了它的类型,并不改变它的值。强制类型转换的一个效果是改变指针运算的伸缩。

从上面的代码示例可以验证上面这一点。

  • 指针也可以指向函数。函数指针的值是该函数机器代码表示中第一条指令的地址。