注意:本章内容很多,请耐心的看下去
指针的大名大家可能听说过,也有可能没有听过,不管有没有听过我们一起来看看这一部分的内容,指针的内容很丰富同时有一定的难度,正因为它有难度,指针的重要性也是其他东西不可比拟的。
在本章我们会了解内存和地址,指针变量和const与assert关键字,指针运算等内容,同时我们还会了解到一维与二维数组的本质,字符指针变量,数组指针变量,函数指针变量等一些指针变量类型,同时还会学习到冒泡排序与回调函数的内容。
1.内存和地址
这里大家可能不知道字节是多大,那大家一定知道GB与MB的关系
1GB=1024MB
1KB = 1024byte(字节)
1byte = 8bit(比特)
2.指针变量
了解了地址后,我们就来认识指针变量。
1.取地址操作符(&)
一个整形变量4个字节,应该有四个地址,为什么只打印一个地址呢?
2.指针变量和解引⽤操作符(*)
1.指针变量
那知道了怎么取地址,那么怎么储存呢?
这时候指针变量就来了。
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;//取出a的地址并存储到指针变量pa中
return 0;
}
2.解引⽤操作符
了解了如何储存地址那我们该如何去用呢?
这时候就需要解引用操作符*
#include <stdio.h>
int main()
{
int a = 100;
int* pa = &a;
*pa = 0;
return 0;
}
3.指针变量大小
我们知道每个变量类型大小都不一样,例如int类型4个字节大小,char类型1个字节大小,那指针变量多大呢?
3.指针变量类型的意义
1.指针的解引⽤
#include <stdio.h>
int main()
{
int n = 0x11223344;
int* pi = &n;
*pi = 0;
return 0;
}
#include <stdio.h>
int main()
{
int n = 0x11223344;
char* pc = (char*)&n;
*pc = 0;
return 0;
}
2.指针+-整数
#include <stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc + 1);
printf("%p\n", pi);
printf("%p\n", pi + 1);
return 0;
}
3.void* 指针
4.const修饰指针
1.const修饰变量
2.const修饰指针变量
5.指针运算
1.指针+- 整数应用
我们在上面知道指针+-整数代表跳过多少字节,而字节数是根据指针类型来跳的,下面我们就来看看实际应用的场景。
2.指针-指针
那指针-指针代表什么呢?
我们发现指针-指针代表这两个指针间的元素个数,而使用指针-指针的前提是它们指向的是同一块空间。
3.指针的关系运算
关系运算表示就是比较大小,而指针比较的就是地址
6.野指针
1.野指针成因
1.指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2.指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3.指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
函数是在内存中暂时开辟的一块空间,函数走完后,内存会返回给操作系统,此时*p相当于未初始化
2.如何规避野指针
1.指针初始化
#include <stdio.h>
int main()
{
int num = 10;
int* p1 = #
int* p2 = NULL;
return 0;
}
2.⼩⼼指针越界
3.对指针及时置NULL
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,67,7,8,9,10 };
int* p = &arr[0];
for (int i = 0; i < 10; i++)
{
*(p++) = i;
}
//此时p已经越界了,可以把p置为NULL
p = NULL;
//下次使⽤的时候,判断p不为NULL的时候再使⽤
//...
p = &arr[0];//重新让p获得地址
if (p != NULL) //判断
{
//...
}
return 0;
}
7.assert断言
#include<assert.h>
assert(p != NULL);
#define NDEBUG
#include <assert.h>
8.指针的使⽤和传址调⽤
1.strlen的模拟实现
我们知道库函数strlen的功能是求字符串⻓度,统计的是字符串中 \0 之前的字符的个数
语法如下:
int my_strlen(const char* str)
{
int count = 0;
assert(str);
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
int len = my_strlen("abcdef");
printf("%d\n", len);
return 0;
}
同时我们也可以利用指针-指针的思路
//指针-指针
#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;
}
2.传值调⽤和传址调⽤
我们学习了指针的知识,可能有人会觉得没什么用,那我们来做一道题
#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
Swap1(a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
我们运行一下
我们发现为什么没有交换?
#include <stdio.h>
void Swap2(int* px, int* py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
Swap2(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
9.数组名的实质
我们知道利用指针可以来访问数组,那么数组名的地址是什么呢?
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("arr = %p\n", arr);
return 0;
}
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sizeof(arr));
return 0;
}
输出的结果是:40,如果arr是数组⾸元素的地址,那输出应该的应该是4/8才对,那这不就和上面冲突了吗?其实不然,这是一种特殊情况。
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}
我们发现这三个代码运行结果都是一样的,可是我们不是上面刚说整个数组的地址和数组⾸元素的地址是有区别的吗?那arr与&arr有什么区别呢?我们来看看
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0] + 1);
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr + 1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr + 1);
return 0;
}
10.使⽤指针访问数组
利用指针访问数组,我们在指针+-整数就已经了解过了,我们这里再深入一下
int main()
{
int arr[10] = { 0 };
//输⼊
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//输⼊
int* p = arr;
for (i = 0; i < sz; i++)
{
scanf("%d", p + i);
//scanf("%d", arr+i);//也可以这样写
}
//输出
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
//输⼊
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//输⼊
int* p = arr;
for (i = 0; i < sz; i++)
{
scanf("%d", p + i);
//scanf("%d", arr+i);//也可以这样写
}
//输出
for (i = 0; i < sz; i++)
{
printf("%d ", p[i]);
}
return 0;
}
11.⼀维数组传参的本质
了解一维数组传参本质之前,我们从⼀个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给⼀个函数后,函数内部求数组的元素个数吗?
#include <stdio.h>
void test(int arr[])
{
int sz2 = sizeof(arr) / sizeof(arr[0]);
printf("sz2 = %d\n", sz2);
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz1 = sizeof(arr) / sizeof(arr[0]);
printf("sz1 = %d\n", sz1);
test(arr);
return 0;
}
void test(int* arr)//参数写成指针形式
{
printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
test(arr);
return 0;
}
12.冒泡排序
我们了解了指针的内容后,我们就可以认识一下冒泡排序,排序大家一定不陌生,就是把无序的数,排列有序的数,当然排序的方法很多,但是这里只会详细解释冒泡排序。
首先我们要先了解冒泡排序的核⼼思想:两两相邻的元素进⾏⽐较
每一趟下来会把最大的数放在后面,下面我们来实现一下。
对数据排序,我们首先就要知道数据的个数
int main()
{
int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
return 0;
}
之后我们可以把排序的过程写成一个函数在里面实现,同时最后打印出来看看
int main()
{
int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
接下来我们实现排序
我们知道冒泡排序的核⼼思想是两两相邻的元素进⾏⽐较,每一趟下来会把最大的数放在后面,我们先来实现一趟的程序
for (j = 0; j < sz-1 ; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
之后我们可以用循环的嵌套来实现整个排序
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j = 0; j < sz- i- 1; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
由于每趟下来,大的就在后面,后面就会有序,而前面还是无序的,因此 j < sz - 1 - i。
我们发现不论数据是否无序都会循环很多遍,有没有方法当数据有序是,停止循环?
其实很简单,多设置一个变量来判断
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int flag = 1;//假设这⼀趟已经有序了
int j = 0;
for (j = 0; j < sz - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
flag = 0;//发⽣交换就说明,⽆序
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
if (flag == 1)//这⼀趟没交换就说明已经有序,后续⽆序排序了
break;
}
}
最后我们把代码整合一下
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int flag = 1;//假设这⼀趟已经有序了
int j = 0;
for (j = 0; j < sz - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
flag = 0;//发⽣交换就说明,⽆序
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
if (flag == 1)//这⼀趟没交换就说明已经有序,后续⽆序排序了
break;
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
13.⼆级指针
学习了一级指针,我们来认识一下二级指针
int main()
{
int a = 10;
int* p = &a;
int** pa = &p;
printf("%p\n",p);
printf("%p\n",*pa);
printf("%d\n",**pa);
}
*pa 通过对pa中的地址进⾏解引⽤,这样找到的是 p , *pa 其实访问的就是 p.
14.指针数组
是存放指针的数组,不是指针
同理,指针数组存储的是指针
15.指针数组模拟⼆维数组
#include <stdio.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中
int* parr[3] = { arr1, arr2, arr3 };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
parr中存放的数组是arr1,arr2,arr3,的首元素地址,也就是存放着这三个数组的指针
16.字符指针变量
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'w';
return 0;
}
同时字符指针还有一种使用方法
int main()
{
const char* pstr = "hello bit.";
printf("%s\n", pstr);
return 0;
}
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
17.数组指针变量
1.数组指针和指针数组区别
数组指针变量就是一种指针变量。
指针数组是存储指针的数组
2.数组指针变量初始化
18.⼆维数组传参的本质
#include <stdio.h>
void test(int a[3][5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
#include <stdio.h>
void test(int(*p)[5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
19.函数指针变量
1.函数指针变量的创建
我们认识了数组指针,字符指针,同样我们来认识一个指针,叫函数指针。
根据我们所学的知识,我们可以推出函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。
那有人会有一个问题,函数也有地址吗?我们来看看下面这段代码
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("test: %p\n", test);
printf("&test: %p\n", &test);
return 0;
}
其实函数也是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的⽅式获得函数的地址。对于函数,&函数名和函数名都是函数的地址,不存在说什么首元素地址
而函数指针的用处就是存放函数的地址,接下来我们看看函数指针的基本形式
void test()
{
printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)()= test;
int Add(int x, int y)
{
return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的
2.函数指针变量的使⽤
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf3)(int, int) = Add;
printf("%d\n", (*pf3)(2, 3));
printf("%d\n", pf3(3, 5));
return 0;
}
3.例题
接下来我们来看看下面这两段代码
(*(void (*)())0)();
当我们拿到这个代码的时候怎么分析呢?先从内到外,我们发现void(* )()是函数指针类型,只不过指向的是空,(void(* )())是强制类型转换,接下来我们就知道,调用0地址处的函数,调用的函数,参数是无参,返回类型是void
void (*signal(int , void(*)(int)))(int);
4.typedef关键字
typedef 是⽤来类型重命名的,可以将复杂的类型,简单化。
typedef unsigned int uint;
//将unsigned int 重命名为uit
当然我们也可以给指针类型重命名
⽐如,将 int* 重命名为 ptr_t ,这样写:
typedef int* ptr_t;
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
同样函数指针也是一样的
typedef void(*pfun_t)(int);//新的类型名必须在*的右边
命名知道了怎么使用呢?其实很简单,我们可以直接把pfun_t当做一种变量类型直接使用就可以
int Add(int a)
{
return a;
}
int main()
{
int ret= 0;
scanf("%d", &ret);
pfun_t p = &Add;
printf("%d", p(ret));
return 0;
}
20.函数指针数组
根据名字我们就知道,函数指针数组就是储存函数指针的数组
1.语法:
int (*parr1[3]) ( );
储存int (*)() 类型的函数指针。
#include<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sud(int a, int b)
{
return a - b;
}
int main()
{
int (*a)(int, int)=Add;
int (*b)(int, int)=Sud;
int (*p[2])(int, int) = { Add,Sud };
return 0;
}
2.转移表
我们知道了函数指针数组后,我们就可以来实现转移表,那什么是转移表呢?我们可以理解为是一个简易计算器,接下来我们就实现一下
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
do
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);
printf("ret = %d\n", ret);
}
else if (input == 0)
{
printf("退出计算器\n");
}
else
{
printf("输⼊有误\n");
}
} while (input);
return 0;
}
这样一个简易的计算机就实现完成了。
21.回调函数
回调函数就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
大家这里可能会有一些不明白的地方,没关系,下面有关qsort函数我会带大家去理解。
22.qsort函数
1.使用方法与介绍
qsort函数的作用是对数据排序,这个数据是void类型,也就是说可以排序任何一种类型
对于qsort我们首先来看看它的参数
在这里我们只讨论qsort排序整型
#include <stdio.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
int(*cmp)(int, int) = int_cmp;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
在这里我们就可以发现,qsort中cmp为函数指针作为参数,而cmp则调用了int_cmp函数,而int_cmp函数就是回调函数。
2.qsort函数的模拟实现(采⽤冒泡的⽅式)
对于函数的模拟实现,我们首先要写出测试用例,在这里我们同样使用整形用例。
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
my_qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
接下来我们就开始实现qrsrt函数
实现函数我们首先了解函数参数,qsort函数的参数我们已经知道了
void my_qsort(void *base, int count , int size, int(*cmp )(void *, void *))
我们是利用冒泡排序完成的,冒泡排序在上面我们已经了解过两两相邻的元素进⾏⽐较
void bubble(void* base, int count, int size, int(*cmp)(void*e1, void*e2))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
;
}
}
}
下面就是最重要的部分交换了
有人可能会这样写
void bubble(void* base, int count, int size, int(*cmp)(void*e1, void*e2))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if(base[j]>base[j+1])
//交换
}
}
}
这样是不对的,我们在这里虽然是用整形数据测试,如果是结构体类型的话,就不能直接比较大小,那我们怎么比较呢,这时候第四个参数就有用处了,cmp就是来比较的
void bubble(void* base, int count, int size, int(*cmp)(void*e1, void*e2))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if(cmp()>0)
//交换
}
}
}
那cmp里的参数是什么呢?是我们要比较两个数的地址
那怎么得到这两个数的地址呢,有人可能会这样写base+j,但是base是void指针类型不能这样加减整数,那我们加上一个强制类型转换呢?(int*)base+j,这样是不对的,我们在这里虽然是用整形数据测试,如果是结构体类型的话,就不能直接这样。
那如何做呢?这个方法很巧妙我们把base强制转换成char*类型,之后加上j*size(一个元素大小),我们就可以得到了
void bubble(void* base, int count, int size, int(*cmp)(void*e1, void*e2))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
//交换
}
}
}
}
判断条件知道了,接下来就是交换了
对于交换,我们可以写个函数来解决,而这个函数我们需要把size传进去,因为交换元素是char*类型,每一次交换都只交换一个字节大小,因此要传入元素大小保证数据交换正确。
void _swap(void* p1, void* p2, int size)
{
int i = 0;
for (i = 0; i < size; i++)
{
char tmp = *((char*)p1 + i);
*((char*)p1 + i) = *((char*)p2 + i);
*((char*)p2 + i) = tmp;
}
}
void bubble(void* base, int count, int size, int(*cmp)(void*e1, void*e2))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}
}
}
}
交换完成后,我们整个程序的模拟实现就结束了。
#include<stdio.h>
void _swap(void* p1, void* p2, int size)
{
int i = 0;
for (i = 0; i < size; i++)
{
char tmp = *((char*)p1 + i);
*((char*)p1 + i) = *((char*)p2 + i);
*((char*)p2 + i) = tmp;
}
}
void bubble(void* base, int count, int size, int(*cmp)(void*e1, void*e2))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}
}
}
}
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
23.sizeof和strlen的对⽐
1.sizeof
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
return 0;
}
2.strlen
strlen 是C语⾔库函数,功能是求字符串⻓度。
size_t strlen ( const char * str );
int main()
{
char arr1[3] = { 'a', 'b', 'c' };
char arr2[] = "abc";
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr1));
return 0;
}
3.sizeof 和 strlen的对⽐
4.例题
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a + 0));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(a[1]));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(*&a));
printf("%d\n", sizeof(&a + 1));
printf("%d\n", sizeof(&a[0]));
printf("%d\n", sizeof(&a[0] + 1));
return 0;
}
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr + 0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr + 1));
printf("%d\n", sizeof(&arr[0] + 1));
return 0;
}
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr + 0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr + 1));
printf("%d\n", strlen(&arr[0] + 1));
return 0;
}
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr + 0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr + 1));
printf("%d\n", sizeof(&arr[0] + 1));
return 0;
}
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr + 0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr + 1));
printf("%d\n", strlen(&arr[0] + 1));
return 0;
}
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p + 1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p + 1));
printf("%d\n", sizeof(&p[0] + 1));
return 0;
}
int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p + 1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p + 1));
printf("%d\n", strlen(&p[0] + 1));
return 0;
}
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a[0][0]));
printf("%d\n", sizeof(a[0]));
printf("%d\n", sizeof(a[0] + 1));
printf("%d\n", sizeof(*(a[0] + 1)));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(*(a + 1)));
printf("%d\n", sizeof(&a[0] + 1));
printf("%d\n", sizeof(*(&a[0] + 1)));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a[3]));
return 0;
}
24.结语
我不知道有多少人能看到这里,希望不是直接翻到这里,不过到此指针的内容就结束了,内容很多,而且有点东西去消化的时间还很长,但是大家能看到这,非常感谢,最后,一起加油!!!