一、首先了解指针变量的概念:存放内存中数据的地址的变量
代码演示:
TIPS: “&”为取址操作符,可以将变量的地址取出;“*”是取值操作符,可以将指针变量指向的变量取 出。
下边的代码就是指针的最简单的一个实现,下面的输出语句等价于printf(“%d”,num);
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int num = 10;//内存里创建一个整型变量num
int* p = #//将num在内存里的地址存放在指针变量 p里,可以说p指向num
printf("%d", *p);
}
二、指针变量的大小
问题:在32位机和64位机里指针变量所占的内存空间分别是多少?为什么是是这么分配的?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int num = 10;//内存里创建一个整型变量num
int* p = #//将num在内存里的地址存放在指针变量 p里,可以说p指向num
printf("%zd", sizeof(p));//32位情况下输出4,64位情况下输出8
}
为什么在不同机器上指针大小会不一样呢?
首先32/64 位指的是计算机可以同时处理的二进制位数,在32位机下数据可以出现 2^32 种组合可能,要为这么多的数据编址也要对等数量的地址。故地址也一共有 2 ^32 个。32b/8=4byte。这就是为什么在32位机情况下一个指针占用4个字节空间了。
同时我们可以知道地址的大小跟指向数据的类型是无关的,因为指针与计算机的位数有关,不论指针指向什么元素他只会占4/8个字节的空间,内存的每个字节都有他自己的地址
三、指针变量+1意味着什么?
代码示例:
重点观察指针每次加一后在内存的情况
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int arr[5] = { 1,2,3,4,5 };
//指针遍历数组
for (int i = 0; i < 5; i++) {
//重点观察指针每次加一后在内存的情况
printf("%d", *(arr + i));
}
}
程序调试图:
可以很清楚的在内存里看到,指针每次加一地址都会+4,这是因为指针的基类型是int形的,而我是在32机的情况下运行的程序。一个int占4个字节,指向int 的指针变量每次加一就会跳过一个int的内存,这么做也是为了方便我们编程者的使用,举一反三,指向什么类型的数据,指针每次加一就会跳过这个类型大小的空间。
四、 void * 指针是做什么用的?怎么用?
在我看来void 的指针就像一个垃圾桶,当我们编写一个适用范围比较广的函数或者方法的时候,往往并不能确定传参时候到底传的是什么数据类型,这个时候void* 指针就排上用场了,我们可以将任意类型的地址传给他,他都可以接收。下边通过一个具体例子给大家解释到底如何使用好 void* 指针*
模拟实现qsort()函数:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
typedef struct stu {
char name[20];
int num;
} Stu;
Stu student[3] = { {"zhangsan",100} ,{"lizi",101},{"wangwu",102} };
void swap(char* buf1, char* buf2, int size) {
char temp;
for (int i = 0; i < size; i++) {
temp = *(buf1+i);
*(buf1 + i) = *(buf2+i);
*(buf2+i) = temp;
}
}
void qsort(void* base, size_t num, int size, int (*pfun)(void* e1, void* e2)) {
//注意这里数组使用void*接受的,我们并不知道数组的基类型,但是我们可以通过数组元素的大小来进行元素的使用
//我们知道char只占一个字节空间,不受32/64 位机的影响,故我们将base强制转换成char*指针就能对内存里的数据进行精确的操控了
//(char *)base +1*size ,(char *)base +2*size 这样就可以操作数组的第一个和第二个元素了,以此类推..
for (size_t i = 0; i < num - 1; i++) {
for (size_t j = 0; j < num - 1 - i; j++) {
if (pfun((char*)base + j * size, (char*)base + (j + 1) * size) > 0) {
//printf("进入了循环,size=%d\n",size);
swap((char*)base + j * size, (char*)base + (j + 1) * size, size);//交换函数
}
}
}
}
//冒泡排序byInt
int bubble_byInt(void* e1, void* e2) {
//printf("返回值是%d\n", *(int*)e2 - *(int*)e1);
return *(int*)e2 - *(int*)e1;
}
//冒泡排序byStruct:string
int bubble_byString(void* e1, void* e2) {
//printf("返回值是%d\n", strcmp(((Stu*)e1)->name, ((Stu*)e2)->name));
return strcmp(((Stu *)e1)->name, ((Stu*)e2)->name);
}
void test01() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
size_t num = sizeof(arr) / sizeof(arr[0]), size = sizeof(arr[0]);
qsort(arr, num, size, bubble_byInt);
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
}
void test02() {
qsort(student, sizeof(student)/sizeof(student[0]), sizeof(student[0]), bubble_byString);
for (int i = 0; i < 3; i++) {
printf("%s \n",(student+i)->name);
}
}
int main() {
//qsort是一个万能排序函数,它源代码采用快速排序,这里我们采用冒泡来实现底层排序逻辑,他一个非常
//重要的参数就是第四个参数传送的是自己写的判断函数,他将根据我们排序函数的返回值进行排序,这里不是重点不做过多赘述
test01();
printf("\n");
test02();
return 0;
}
使用我已经在代码的注释中详细给出,下边贴出运行结果
以上是我对指针的一些浅薄理解,希望可以帮助到更多人理解指针的用法