指针
说指针,就不可避免的要和*
这个运算符打交道.因此,我们首先要说的就是*
的意思.
*的意思
第一种用法:
仅仅表示a是一个指针,无其他含义
第二种用法:
*运算符,表示从指针取值.此处表示将a这个指针指向的变量的值设置为2.
编程语言中,广义的值分为左值和右值.
变量名字都是左值,意思是可以放在等号左边的值,也就是可以被赋值的值.
常量(如123)以及一些运算结果(如b+1)是右值,只能放在等号右边,不能被赋值.
此处的*a
是一个左值,可以被赋值.
编译错误中提到的lvalue
和rvalue
指左值和右值.
第三种用法:
仅表示乘法运算符.
可以看到,int * a
和 *a
中的*的意义完全不同.
指针与变量
- 所有变量都存储在内存上的某个地址中,每一个变量都有一个地址
- &运算符可以获取变量的地址, *运算符可以从地址获取值
- 地址按照字节编码(如:地址0x100的值表示内存上的第0x100个字节),而不是比特.
- 变量的值就是普通的值
- 指针的值是别的变量的地址
- 因此指针的值也可以是指针的地址(指针的指针)
- 因为指针存的是地址,因此具有特殊性,如果不初始化就访问会产生极其严重的后果,因此要么初始化为某变量地址要么初始化为NULL.
指针=地址+类型
注: 0x开头的数字是16进制表示的数字,如0x01 == 1; 0x10 == 16
一个字节是8个比特,一个16进制数是4个比特(2^4==16),因此一个16进制的两位数占用的空间大小是一个字节.
如何理解指针=地址+类型
?举个例子,假设内存中的值是这样的:
值: |
0x12 |
0x34 |
0x56 |
0x78 |
地址: |
0x100 |
0x101 |
0x102 |
0x103 |
同时,假设以下指针的值(指针中存储的地址)均为0x100:
1 2 3
| int * a; short * b; char * c;
|
则有
1 2 3
| *a == 0x12345678; *b == 0x1234; *c == 0x12;
|
因为指针=地址+类型
,以上三个指针指向的地址相同,但是因为类型不同,读取到的值也不同. int指针读取到了4个字节的值, short指针读取到了2个字节的值, char指针读取了1个字节的值.
注:以上均假设程序在运行在大端序的环境下,如果你不知道啥是大端序请忽略.
再比如,对于指针的运算,指针的类型也起到决定性作用.众所周知,对于以下代码:
data是指向数组首元素的指针, data的值是数组首元素的地址.
data+1是指向数组第2个元素的指针, data+1的值是数组第二个元素的地址.
那么,假设data=0x100, data+1等于多少?
显然,一个int类型变量占用4个字节, data[0]占用了0x100, 0x101, 0x102, 0x103这4个字节,所以data+1应该等于0x104.
这就体现了一个问题:指针运算需要知道指向的变量的大小.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int idata[10]; short sdata[10]; char cdata[10];
printf("%p\n", idata);
printf("%p\n", idata + 1);
printf("%p\n", sdata); printf("%p\n", sdata + 1);
printf("%p\n", cdata); printf("%p\n", cdata + 1);
|
无类型指针
a
是一个无类型的指针.因为没有类型,所以不知道他的大小,所以无法进行形如a+1
的运算.
同样,因为不知道类型,也无法对这个指针"取值"(文章开头提到的*的第二个用法).
(c语言中, &运算符的作用是"取地址", *运算符的作用是"取值".)
malloc函数的返回值是void*
类型,这点也很容易理解.他不知道你申请的内存要存什么类型的数据.因此,使用malloc时要进行强制类型转换:
1
| int * a = (int*)malloc(10*sizeof(int));
|