0%

指针是啥

指针

说指针, 就不可避免的要和*这个运算符打交道. 因此, 我们首先要说的就是*的意思.

*的意思

第一种用法:

int * a = &b;

仅仅表示a是一个指针, 无其他含义

第二种用法:

*a = 2;

*运算符, 表示从指针取值.此处表示将a这个指针指向的变量的值设置为2.

编程语言中, 广义的值分为左值和右值.

变量名字都是左值, 意思是可以放在等号左边的值, 也就是可以被赋值的值.

常量(如123)以及一些运算结果(如b+1)是右值, 只能放在等号右边​, 不能被赋值.

此处的*a是一个左值, 可以被赋值.

编译错误中提到的lvaluervalue指左值和右值.

第三种用法:

b = 2 * 3;

仅表示乘法运算符.

可以看到, int * a*a中的*的意义完全不同.

指针与变量

  1. 所有变量都存储在内存上的某个地址中, 每一个变量都有一个地址
  2. &运算符可以获取变量的地址, *运算符可以从地址获取值
  3. 地址按照字节编码(如: 地址0x100的值表示内存上的第0x100个字节), 而不是比特.
  4. 变量的值就是普通的值
  5. 指针的值是别的变量的地址
  6. 因此指针的值也可以是指针的地址(指针的指针)
  7. 因为指针存的是地址, 因此具有特殊性, 如果不初始化就访问会产生极其严重的后果, 因此要么初始化为某变量地址要么初始化为NULL.

指针=地址+类型

注: 0x开头的数字是16进制表示的数字, 如0x01 == 1; 0x10 == 16
一个字节是8个比特, 一个16进制数是4个比特(2^4==16), 因此一个16进制的两位数占用的空间大小是一个字节.

如何理解指针=地址+类型? 举个例子, 假设内存中的值是这样的:

值: 0x12 0x34 0x56 0x78
地址: 0x100 0x101 0x102 0x103

同时, 假设以下指针的值(指针中存储的地址)均为0x100:

int * a;
short * b;
char * c;

则有

*a == 0x12345678; //从0x100开始的4个字节
*b == 0x1234; //从0x100开始的2个字节
*c == 0x12; // 从0x100开始的1个字节

因为指针=地址+类型, 以上三个指针指向的地址相同, 但是因为类型不同, 读取到的值也不同. int指针读取到了4个字节的值, short指针读取到了2个字节的值, char指针读取了1个字节的值.

注: 以上均假设程序在运行在大端序的环境下, 如果你不知道啥是大端序请忽略.

再比如, 对于指针的运算, 指针的类型也起到决定性作用. 众所周知, 对于以下代码:

int data[100];

data是指向数组首元素的指针, data的值是数组首元素的地址.

data+1是指向数组第2个元素的指针, data+1的值是数组第二个元素的地址.

那么, 假设data=0x100, data+1等于多少?

显然, 一个int类型变量占用4个字节, data[0]占用了0x100, 0x101, 0x102, 0x103 这4个字节, 所以data+1应该等于0x104.

这就体现了一个问题: 指针运算需要知道指向的变量的大小.

int idata[10];
short sdata[10];
char cdata[10];

printf("%p\n", idata);
// 注: printf %p是打印了idata的值, 也就是idata数组的首项的地址.
printf("%p\n", idata + 1);
//会发现, idada+1 比 idata 大4

printf("%p\n", sdata);
printf("%p\n", sdata + 1);
//会发现, sdada+1 比 sdata 大2

printf("%p\n", cdata);
printf("%p\n", cdata + 1);
//会发现, cdada+1 比 cdata 大1

无类型指针

void * a = NULL;

a是一个无类型的指针. 因为没有类型, 所以不知道他的大小, 所以无法进行形如a+1的运算.
同样, 因为不知道类型, 也无法对这个指针”取值”(文章开头提到的*的第二个用法).

(c语言中, &运算符的作用是”取地址”, *运算符的作用是”取值”.)

malloc函数的返回值是void*类型, 这点也很容易理解. 他不知道你申请的内存要存什么类型的数据. 因此, 使用malloc时要进行强制类型转换:

int * a = (int*)malloc(10*sizeof(int));

欢迎关注我的其它发布渠道