学会应用指针是C语言程序员的分水岭,也是C程序员级别的试金石。
变量可以分为基础变量、数组变量、指针变量,其中数组变量非常特殊,可以进一步分为基础数组变量和指针数组变量,所以暂时不考虑数组变量。假设我们在32位计算机上工作。
先看看简单的基础变量。
short var = 0x1234;
var = 0x4321;
printf("var = %#x\n", var);
printf("&var = %#x\n", &var);
// 程序输出:(为方便说明这里的地址是虚构的,实际情况很可能不一样)
// var = 0x4321
// &var = 0xABC00000
这里考虑short
为2字节大小。第一行代码是声明语句,都知道short
是变量类型,var
是变量名称,0x1234
是变量值。第二行代码是赋值语句,将变量var
的值修改为0x4321
。第三行代码是将变量var
的值输出。第四行代码是将变量var
的地址输出,C语言中&
表示取变量地址。
那变量本身和数据,计算机是怎么存储和使用的?
上图是变量在内存中的分布情况。变量声明语句会让编译器自动分配2字节大小的内存,例如:内存0xABC00000~0xABC00001
,而变量名只是给程序员使用这个内存数据的代称。比如,接下来赋值语句var = 0x4321;
就让变量var
的内存数据被修改为0x4321
。请记住这些事所有变量的特性,只是他们所存储的数据值类型有差异。
继续上面的代码,在看看简单的指针变量。
short *pvar = (short *)0x12340000;
pvar = &var;
printf("pvar = %#x\n", pvar);
printf("*pvar = %#x\n", *pvar);
// 程序输出:(接着上面一段代码)
// pvar = 0xABC00000
// *pvar = 0x4321
第一行代码是声明语句,都知道short *
是指针变量类型,pvar
是变量名称,(short *)0x12340000
是变量值。第二行代码是赋值语句,将变量pvar
的值修改为&var
(示例中是0xABC00000
)。第三行代码是将变量pvar
的值输出。第四行代码是指针变量特有的操作*
,稍后讲解。
那指针变量本身和数据,计算机是怎么存储和使用的?
上图是指针变量pvar
和普通变量var
在内存中的分布情况。在32位系统中中,编译器会为指针变量自动分配4字节大小的内存,例如:内存0xDEF00000~0xDEF00003
,而变量名是给程序员使用这个内存数据的代称。因赋值后,这个指针变量pvar
的数据值正好是变量var
的首地址,一般就称呼pvar
是指向var
的指针。此时两种类型变量就能互相转化为另一种类型变量。
继续上面的编码,看看这段互转的代码。
printf("complex var = %#x\n", *(short *)(&var));
printf("complex pvar = %#x\n", &(*pvar));
// 程序输出:(接着上面一段代码)
// complex var = 0x4321
// complex pvar = 0xABC00000
不知细心的读者发现没有,虽然变量var
占用2字节内存,但指针变量pvar
只指向var
的首地址0xABCD0000
,那*pvar
怎么知道数据是占用2字节呢?简单,声明指针变量时指定了short *
类型。那要是强行将类型转换为char *
或int *
会怎样呢?
char *pchar = (char *)&var;
int *pint = (int *)&var;
printf("converted *pchar = %#x\n", *pchar);
printf("converted *pint = %#x\n", *pint);
// 程序输出:(接着上面一段代码)
// converted *pchar = 0x21
// converted *pint = 0x24321
变量pint
可能输出其他值,或者它的高位2字节数据可能随机。
指针变量已经基本入门。继续深入下去会涉及到指针变量运算(+/-运算)、数组指针、多级指针(指针的指针)、函数指针等。