目录
一、指针变量类型的意义
a.通过指针的解引用理解类型意义
b.void* 类型指针
c.const修饰的指针变量
c.1 const放在*号左边编辑
c.2 const放在*号右边
二、指针运算
a.指针+-整数
编辑
b.指针 - 指针
编辑
c.指针的关系运算
一、指针变量类型的意义
指针变量有不同的类型,在同一平台下,不同类型的指针变量大小是一样的,那么为什么还要分那么多种指针变量类型呢?
a.通过指针的解引用理解类型意义
对比,下面2段代码,主要在调试时观察内存的变化。
对于代码1,一次访问更改四个字节:
对于代码2,一次访问更改一个字节:
由于一个int类型数据需要四个字节大小内存空间来存放,所以访问一个整形变量时,应该一次访问四个字节才能拿到完整数据,不同的指针类型就应该一一对应不同类型变量应该决定访问空间大小。
结论:指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
b.void* 类型指针
在指针类型中有⼀种特殊的类型是 void * 类型的,可以理解为无具体类型的指针(或者叫泛型指针),这种类型的指针可以用来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进行指针的+-整数和解引用的运算。
为什么要存在 void* 类型的指针呢?主要还是作用于函数接口,一般 void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。使得⼀个函数来处理多种类型的数据。
举例如下:
struct Stu //学⽣
{char name[20];//名字int age;//年龄
};
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{test2();test3();return 0;
}
c.const修饰的指针变量
⼀般来讲const修饰指针变量,可以放在*的左边,也可以放在*的右边,意义是不一样的。
int * p;//没有const修饰
int const * p;//const 放在*的左边做修饰
int * const p;//const 放在*的右边做修饰
c.1 const放在*号左边
由例子可以看出const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本身的内容可变。
c.2 const放在*号右边
由例子可以看出const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
二、指针运算
a.指针+-整数
因为数组在内存中是连续存放的,只要知道第一个元素的地址,就能找到后面的所有元素。(逻辑上连续同样可以,链表就是很好的例子)
那么对于数组的遍历我们可以使用指针来完成:
#include <stdio.h>
//指针+- 整数
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++){printf("%d ", *(p + i));//p+i 这⾥就是指针+整数}return 0;
}
我们观察上述代码可以发现,其实 a[ i ] == *(p + i) ,而数组名又代表首元素地址(此时),那么则a[ i ] == *(a + i),事实也确实如此。
我们通过 p + i 的方式得到了数组每一个元素的地址,而数组每一个元素都是一个int类型,那么也就意味着,每次 p + i 都使得指针跳动( sizeof(int)* i )个字节,其余类型也同理,不是想当然的跳过 i 个字节。
b.指针 - 指针
指针 - 指针实际上可以得到两个指针间元素个数,当然这是在同一逻辑空间内的(数组),其他非逻辑空间没有意义(随意两指针)。
//指针-指针
#include <stdio.h>
int my_strlen(char* s)
{char* p = s;while (*p != '\0')p++;return p - s;
}
int main()
{printf("%d\n", my_strlen("abc"));return 0;
}
c.指针的关系运算
指针是可以区分大小的,高地址位的指针大于低地址位的指针,存放相同地址的指针变量的指针相同。
//指针的关系运算
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);while (p < arr + sz) //指针的⼤⼩⽐较{printf("%d ", *p);p++;}return 0;
}
上例就用了 arr + sz 作为while循环结束的条件,arr + sz 指向了数组最后一个元素的下一个元素,当 p 指向 arr + sz 时,即 p == arr + sz 时,循环停止。