一、指针的运算
指针的类型:
指针有类型,地址没有类型
地址只是开始的位置,类型读取到什么位置结束
1、常量指针:
首先它是一个指针,常量只是用来修饰指针的定语。其定义如下:
char const * cp;
char a='a';
如何识别呢?根据右结合优先,先是优先,所以这个cp变量是一个指针,然后是const修饰,所以这是一个常量指针。即指向常量的指针。
cp=&a; //正常语法
*cp=a; //错误语法,因为其指向的值是一个常量
2、指针常量:
首先它是一个常量,指针是用来修饰常量的,即常量的值为一个指针地址。
char * const cp;
char a='a';
如何识别呢?根据右结合优先,先是const优先,所以这个cp变量是一个常量,然后是*修饰const,所以这是一个指针常量。
cp=&a; //错误语法,因为其地址为是一个常量
*cp=a; //正确,地址所指向的内容是一个普通字符
注意和上边区分开
3、指针数组:
首先它是一个数组,指针是用来修饰数组内容的,表示什么样的数组 :即存放指针的数组
char *arr[3] = {"1","123","345"};
如何识别,因为[]的优先级大于,所以先是定义为一个数组,而后由来修饰这个数
printf("arr0%c\n",*arr[0]);
printf("arr1%s\n",arr[1]);
4、数组指针:
首先它是一个指针,数组是修饰指针的,即指向数组的指针。
char (*p)[3]; //申明时不能同时初始化
char arr[3] = {'1','4','7'};
p=&arr; //指向数组的首地址,同时指针的类型是char * [3] 类型的,即加1操作后为sizeof(char [3])
如何识别:因为这次添加了一个显示优先,所以这次先是一个指针,而后[]修饰指针
printf("%c\n",(*p)[0]); //先取arr的首地址,再根据这个地址取数组内容
printf("%c\n",(*p)[1]);
printf("%c\n",(*p)[2]);
printf("%c\n",*((char*)p+0)); //先转换为char指针,再取值
printf("%c\n",*((char*)p+1));
printf("%c\n",*((char*)p+2));
printf("%c\n",((char*)p)[0]); //先转换为char指针,再取数组的值,和第一个类似
printf("%c\n",((char*)p)[1]);
printf("%c\n",((char*)p)[2]);
5、函数指针:
首先它是一个指针,函数是修饰指针的,即指向函数的指针。
char (*func)(void); //定义函数指针
char test(void)
{
return 'A';
}
func = test; //初始化赋值
printf("test address: %p\n",test);
printf("func address: %p\n",func);
char ch = func(); //调用
printf("%c\n", ch);
实例:
int msg(char* msg,char* title){
MessageBox(0,msg,title,0);
return 0;
}
void main(){
//msg();
printf("%#x\n",msg);
printf("%#x\n",&msg);
//函数指针
//函数返回值类型,函数指针的名称,函数的参数列表
int(*fun_p)(char* msg, char* title) = msg;
fun_p("消息内容","标题");
getchar();
}
如何识别,同数组指针一样,因()的优先级,所以这个定义首先是一个指针,而后才是对指针的描述,即一个指向函数的指针,其指向的函数也是规定的:即返回的是字符类型,不需要传入参数
6、指针函数:
首先它是一个函数,指针修饰函数的返回类型,即一个返回指针的函数
char *func(void);
如何识别,因为没有扩弧,所以的优先级没有右边的扩弧优先级高,所以先是规定了一个函数,只是修饰返回值的
char *func(void) {
char *str = "test";
return str;
}
void main() {
char *test = func();
printf("%s\n",test);
}
7、指针为什么要有类型?
指针有类型,地址没有类型
地址只是开始的位置,类型读取到什么位置结束
void main(){
int i = 89;
//int 类型的指针
int *p = &i;
double j = 78.9;
//赋值为double类型变量的地址
p = &j;
printf("double size:%d\n", sizeof(double));
printf("%#x,%lf\n",p,*p); //想通过4字节读取8字节变量的值,是不行的
getchar();
}
8、指针类型转换:
指针类型转换是个有意思的东西,你可以把一个int型的指针转换为char类型,然后再把char类型的指针转换为int型;就像普通的字符和int型之间的转换一样。但指针转换后其值没有变,唯一变的东西就是指针的步长,即进行指针运算时的计算方式。当为char指针时其运算单位均以1个字节为1个运算单位,而当为int指针时通常都是以4个字节为1个运算单位。
9、指针算术:
根据上面的指针类型转换介绍可知,不同的指针类型进行算术运算时其计算方式时不相同的,其不同之处就在于其步长的字节数不同,而具体其步长为几个字节数是以其指针类型决定的,指向char的指针步长即为1。通常的指针运算有指针与数字的加减运算,相同类型的指针的减法运算,而且还要是指向同一个数组的,不然意义不大。同理推得不同类型的指针进行运算意义更不大,甚至会报错。
下面举一个指针算术的例子,交换两个变量值不利用额外变量
毕竟new关键字还是申请了额外的内存,虽然没有申请变量,换汤未换药
int *a,*b;
a=new int(10); //给指针赋值
b=new int(20); //a=0x00030828,b=0x00030840
a=(int*)(b-a); //a=0x00000006
b=(int*)(b-int(a)); //b=0x00030828
a=(int*)(b+int(a)); //a=0x00030840
只是交换变量的话也可以:
int a = 4;
int b = 5;
*(((char*)&a)+1) = *((char*)&b);
*((char*)&b)=*((char*)&a);
*((char*)&a)=*(((char*)&a)+1);
*(((char*)&a)+1)=0;
实例:
void main(){
//数组在内存中连续存储
int ids[] = { 78, 90, 23, 65, 19 };
//数组变量名:ids就是数组的首地址
printf("%#x\n",ids);
printf("%#x\n",&ids);
printf("%#x\n",&ids[0]);
//指针变量
int *p = ids;
printf("%d\n",*p);
//指针的加法
p++; //p++向前移动sizeof(数据类型)个字节
printf("p的值:%#x\n", p);
//p--;
printf("%d\n", *p);
getchar();
}
10、NULL空指针
void main(){
int i = 9;
int *p = NULL;
//p = &i;
//空指针的默认值为0
printf("%#x\n",p);
//访问内存地址0x000000操作系统不允许
//p = 100; //操作系统不允许访问
printf("%d\n",*p);
getchar();
}
通过指针给数组赋值
void main(){
int uids[5];
//高级写法
//int i = 0;
//for (; i < 5; i++){
// uids[i] = i;
//}
//早些版本的写法
int* p = uids;
printf("%#x\n",p);
int i = 0; //i是数组元素的值
for (; p < uids + 5; p++){
*p = i;
i++;
}
getchar();
}
11、多级指针(二级指针)
指针保存的是变量的地址,保存的这个变量还可以是一个指针变量
动态内存分配给二维数组
void main(){
int a = 50;
//p1上保存的a的地址
int* p1 = &a;
//p2上保存的p1的地址
int** p2 = &p1;
//int*** p3 = &p2;
printf("p1:%#x,p2:%#x\n",p1,p2);
**p2 = 90;
printf("%d\n",a);
getchar();
}