* 的几种用途
- 乘法运算符
int x = 1;
int y = 2;
int z = x*y;
- 定义新的类型
char x;
char* x1;
char** x2;
- 取值运算符
格式:
* + 指针类型的变量
int* a = (int*)1;
printf("%x \n",*a);
这里举一个例子分析一下:
#include <stdio.h> //头文件
#include <windows.h>
void main() //程序入口
{
int* a = (int*)1; //定义一个值为1的指针类型a
printf("%x \n",*(a+1)); //把这个指针类型+1的地址里的值打印到控制台
return; //程序结束
}
这个程序运行是会报错的:
原因很简单,我们去反汇编看一下问题出在哪里:
这是它的汇编指令,我们简单分析一下:
00401028 mov dword ptr [ebp-4],1 //把1赋值给ebp-4,也就是第一个变量
0040102F mov eax,dword ptr [ebp-4] //把ebp-4的值存到eax里
00401032 mov ecx,dword ptr [eax+4] //这里出问题了,把eax+4这个地址里的值取出来存到ecx里
00401035 push ecx //ecx压栈
00401036 push offset string "%x \n" (0042201c) //压入字符串
0040103B call printf (00401070) //调用printf()函数输出结果
00401040 add esp,8 //平衡堆栈
很明显,eax+4的地址就是0x00000005,这个地址是不存在的,所以程序会报错,这里对应的就是这行代码:
pinrf("%x \n",*(a+1));
这里的a+1并不是真的+1,在反汇编中也看到了,是+4,原因就是指针变量加减的多少,取决于去掉* 以后的数据类型的宽度是多少,这里是int,自然就+4,如果是a+2,那就是+8了。
探测 *指针类型 的类型
这个标题可能有点绕,意思就是看一下当编译器遇到赋值操作的时候是怎么处理的,看一下这个例子:
#include <stdio.h>
#include <windows.h>
int*** a;
int*** b;
int*** c;
void mian()
{
int x = *(a);
return;
}
看一下它的报错:
这里的int自认就是int x,那这个int就是我们定义的指针int* a,编译器在这里把int*** a当作了int**类型来处理,在原先的基础上减少了一个*,那我们定义五个 *来验证一下:
#include <stdio.h>
#include <windows.h>
int***** a;
int*** b;
int*** c;
void mian()
{
int x = *(a);
return;
}
看一下报错,跟我们推断的一样:
如果代码是这样就不会报错了:
int* a;
void main()
{
int x = *(a);
return;
}
总结:*加上一个指针类型如 *(a) ,结果的类型就是原来的指针类型减去一个 *。
取值运算举例
这里这两个例子注释的已经比较详细了,这里就不过多分析,感兴趣的可以自己去看一下反汇编,并不难理解。
例1:
#include <stdio.h> //头文件
#include <windows.h>
void mian() //入口函数
{
int x = 1; //把1赋值给x
int* p = &x; //把x的地址赋值给p
printf("%x %x\n",p,*(p)); //以16进制输出x的地址与x的值
*(p) = 2; //把p指向的内存的值改为2,也就是x的值
printf("%d \n",x); //输出x的值
return; //程序结束
}
例2:
#include <stdio.h> //头文件
#include <windows.h>
int x = 1; //把1赋值给x
void mian() //程序入口
{
int* p = &x; //把x的地址存到p里
int** p2 = &p; //把p的地址存到p2里
int*** p3 = &p2 //把p2的地址存到p3里
int r = *(*(*(p3))); //把x的值赋给r,第一层输出p2,第二层输出p,第三层输出x(这里都是把指针变量当作地址,取地址里的值,与上面的嵌套一一对应)
printf("%d \n",r); //输出r
return; //程序结束
}