4.3 KiB
Raw Blame History

2.8 高级指针

如果你爱一个人,就让他用指针,因为指针是天堂;如果你恨一个人,也让他用指针,因为指针是地狱。
指针是 C/C++ 语言中功能最强大,最具灵活性的功能之一,用好指针可以使程序更加优美简洁。指针的实质是非常简单的,每个人都很容易学会指针,但每一个 C/C++ 开发者,包括很多高手在内,都必须时刻注意指针的使用,因为一旦违反指针的使用规则,程序便极具破坏性。

2.8.1 指针的使用

void*

2.8.2 指针的实质

2.8.3 指针与数组

我们知道,数组中的元素是何种类型,我们就将这个数组称为 X型数组。例如

short arry[256];

被称作 short 数组。同样,如果数组中保存的是指针类型,我们则称这个数组为指针数组。

short* parry[256];   // 这是一个 short* 类型的数组, 数组中的每个元素都是一个指向 short 型变量的指针.

请记住,指针数组的实质仍是数组。
通过 & 符号,能够获得变量的地址,那么该如何获得数组的地址?
数组中的元素是连续排列的,如果取某个元素的地址来代表整个数组,那显然是数组的首个元素地址最具代表性。数组首个元素的地址被称作数组首地址,数组名就代表了这个地址。我们看以下的程序:

/*
 * @brief: Program 2-8-3-1
 */
short* parry[256];
void* tmp = &parry[0];

if(tmp==parry)
{
    printf("True\n");   // 这句一定会被打印出来.
}

由于数组的名字代表了数组首地址(也就是指针),那么,我们也可以把一个指向某连续内存区的指针看作数组:

unsigned char* pointer;

pointer = calloc(256);  // 分配一片长度为 256 的连续内存区, 并将这片内存区清零.

pointer[15] = 0xAA;

printf("Pointer+15=0x%x.\n", *(pointer+15)) // 输出内容为 “Pointer+15=0xaa.”

数组名这个特殊指针,与其他指针变量最大的差异在于:我们可以对指针变量进行赋值操作,但向数组名赋值是不和法的。这是因为数组是静态分配的,它的首地址是固定的,可以说数组名这个特殊指针是自带 const 属性的。因此,下面的程序将导致编译错误:

short* parry[256];
parry++;    // 这里有个错误.

2.8.4 指针与结构体/联合体

结构体名称和首个元素的地址就代表了结构体的地址:

/*
 * @brief: Program 2-8-4-1
 */
typedef struct _STA_T
{
    int a;
}STA_T;

typedef struct _STB_T
{
    STA_T b;
    int c;
}STB_T;

void fun(STA_T* st)
{
    STB_T* stb = st;

    printf("%d", stb->c);
}

int main(void)
{
    STB_T stx;

    stx.c = 5;
    fun(&stx);  // 打印内容为 “5”.
}

2.8.5 指针的类型转换

不难注意到Program 2-8-3-1 中将两种不同类型的指针直接进行了比较,这里会产生一个编译警告。我们该如何消除它?

short* parry[256];
void* tmp = &parry[0];

if(tmp==(void*)parry)
{
    printf("True\n");
}

没错,指针也是一个类型,也就是说,类型转换的概念同样适用于指针。我们可以把一种类型的指针转换成另一种类型的指针,也可以把指针转换成整数,甚至可以把整数转换成指针。还记得 2.1.11 中的例子么?

// 假设系统是从低位开始寻址.
unsigned int a = 0x11223344;
char* pa = (char*)&a;   // 通过 & 符号取变量 a 的地址. 指针 pa 的值便是变量 a 的首地址.

printf("%d.\n", *pa);   // 0x44.
pa++;

printf("%d.\n", *pa);   // 0x33.
pa++;

printf("%d.\n", *pa);   // 0x22.
pa++;

printf("%d.\n", *pa);   // 0x11.
pa++;

这是指针类型转换的一种用法,相当于把变量 a 看作长度为 4 的 char 型数组,然后我们通过 pa 指针一次访问了这个数组中的全部元素。

指针的转换同样会遇到位数问题:

char arry[256];

printf("%d", (short)arry);  // 数据将被截断.

2.8.6 指针与函数

2.8.7 多级指针

2.8.8 指针参数

2.8.9 指针与内存泄漏

数组访问的溢出 没有释放的内存 野指针

练习

1、Program 2-8-4-1 中同样有编译警告,你该如何去除这些警告?

函数指针 指针数组 数组名