菜鸟带专生学习指针进阶
前言:本篇博客是为了总结自己学习指针的笔记,也是自己第一次写博客,可能质量很低请多多包含。
1.指针是什么?
首先,要学好指针的话,就要知道指针是什么?
1.指针是内存中一个最小单元的编号,也就是地址。
2.平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
总结:
1.对于我来说 指针就是用来存变量的地址,并且通过这个地址进行操作去改变变量。
2.指针的大小在32位平台是4个字节,在64位平台是8个字节 (指针的大小与指针的类型无关)。
2.指针种类及其功能
[1]字符指针
(1)第一种用法
char * `ch='w';`
char *p=&ch;
*p='s';
(2)第二种用法
const char* pstr="hello bit.";
printf("%s\n", pstr);
对于(2) 首先这里的"hello bit."是一个常量字符串,这里的char*存放的是字符串首字符的地址也就是h的地址。
常量字符串:是不能被解引用改变的 所以要用 const 去限制不让 char*这个指针去解引用改变内容。
总结:就是一个常量字符串的首字符h的地址存放到指针变量pstr中。
例子1
#include <stdio.h>
int main()
{
char str1[] ="hello bit.";
cha rstr2[] ="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;
}
为什么str1和str2不相同,而str3和str4相同? ?首先,例子1 这里的两个if比较的都是地址
- str1和str2比较的是数组首元素地址,因为数组都会在内存开辟一块空间,所以str1和str2的地址不同。
- str3和str4是字符指针,比较的是字符串首字符的地址,这两个指针存的内容都是"hello bit.;
是同一个常量字符串,在内存中相同的常量字符串只会存一份,所以str3和str4的地址相同。
[2] 指针数组
?作用: 是一个存放指针的数组(按我的理解就是可以存放多个指针的数组) ?格式:类型 * 指针名[ ]
格式样例
int* arr1[10];
char* arr2[4];
char** arr3[5];
[3] 数组指针
?作用: 能够指向数组的指针(按我的理解就是可以指向指针数组的地址) ?格式:类型 * (指针名)[ ]
格式样例
int (*p1)[10];
char (*p2)[5];
如何区分指针数组和数组指针
?初学这里确实挺懵逼的一会这,一会那的。首先我们先写出一个指针数组和数组指针
int* arr1[10];
int(*arr2)[10];
?原理(优先级)
[ ]的优先级要高于*号的,所以名字和哪个先结合就决定是什么类型
(暴论) ? 所以我们看到带括号的一般都是指针类型
?解读一下 arr1和arr2
1.arr1是数组有十个元素每个元素是int *型 (数组的每个元素都是指针) -指针数组
2.arr2是指针指向一个数组 数组有10个元素每个元素是int型 (一个指针指向有10个整型元素的数组) -数组指针
3.&数组名是整个数组的地址要用数组指针接收 ?指针数组存放变量的地址或者数组首元素地址
&数组名和数组名区别
一般来说数组名是首元素地址
但有两个例外 数组名是数组的地址
- sizeof(数组名)
- &数组名
例子1
#include <stdio.h>
int main()
{
int arr[10]={0};
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
结果
arr+1:跳过的是一个字节的大小; &arr+1:跳过的是一个数组的大小; 所以说 &数组 —取出的是整个数组的地址而非数组首元素地址
数组指针的应用
数组指针主要运用在 二维数组
例子
#include <stdio.h>
void print_arr2(int (*arr)[5], int row, int col)
{
int i=0,j=0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[2][5] = {1,2,3,4,5,6,7,8,9,10};
print_arr2(arr, 2, 5);
return0;
}
结果 原理
二维数组传参其实传的是数组首行数组的地址 (传一个数组的地址就用 数组指针 来接收) 相当于是把二维数组截断成一维数组的地址
printf("%d "arr[i][j]);
可以这样理解 i 其实遍历的是一维数组的首地址,而内循环j就是访问一维数组第 j个元素
回顾
int* parr1[10]; '''//parr1是数组 有十个元素每个元素是int*型 指针数组'''
int (*parr2)[10]; '''//parr2是指针 指向1个数组 数组有10个元素 每个元素是int*型 数组指针'''
int (*parr3[10])[5];
'''//parr3 是一个数组 数组有10个元素 每个元素是数组指针类型而每个数组指针指向一个数组有5个元素每个元素类型为int型'''
[4]数组传参与指针传参
一维数组传参
#include <stdio.h>
void test2(int* arr[20])
{}
void test3(int** arr)
{}
int main()
{
int* arr2[20]={0};
test2(arr2);
test3(arr2);
return 0;
}
test2是可行的 指针数组传参给指针数组
test3是可行的 指针数组传参传的是指针数组首元素的地址 因为数组每个元素类型是int*型 所以要用个二级指针去接收,也就是 int** arr 。
二维数组传参
void test1(int* arr[5])
{}
void test2(int (*arr)[5])
{}
void test3(int **arr)
{}
int main()
{
int arr[3][5] ={0};
test1(arr);
test2(arr);
test3(arr);
return 0;
}
test1 和 test3 是不可行的 test2 可以
二维数组传参: 本质上是把二维数组截断看成一维数组,并把首行的一维数组的地址传过去 。 也就是传过去一个数组的地址 那么就只能用数组指针来接收.
一级指针传参
void test1(int *p)
{}
一级指针能接收什么值?
1.变量的地址 2.一级指针变量
二级指针传参
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
int* arr[10];
test(pp);
test(&p);
test(arr);
return 0;
}
二级指针能接收什么值?
1.二级指针变量 2.一级指针的地址 3.接收指针数组的首元素地址
思考如果是test(&arr) 可以吗?
不可以 因为这里传的就是整个数组的地址了 得用int *(*ptr)[10] 相当于把int* arr[10]套娃用个指针接收
[5]指针函数
作用:存放函数的地址
先看一段代码:
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
对于函数来说 函数名和&函数名是等价的 ==函数的地址
那么我们要如何存放函数的地址呢? 当然是用函数指针
例子
void test()
{
printf("hehe\n");
}
void (*pfun1)()= test;
(*pfun1)说明pfun1是指针 而()代表是无参 void则代表返回类型
试想把括号去掉 void *pfun1() 这就变成一个指针函数了 返回值是void*
"有趣"的2个小代码
------出自《剑指offer》
代码1
(*(void (*)())0)();
本质:对0强制转换成地址的函数调用
(void (*)()0) 这里的void(*)() 是一个函数指针 而((函数指针)0)就是把0这个变量强制转换成函数指针类型 而最外层的(*)()说明作函数调用,函数无返回值,无参
代码2
void (*signal(int, void(*)(int)))(int);
本质:是一个返回类型是函数指针的函数声明,该函数的参数是int类型和函数指针
signal是个函数名有两个参数一个int 一个是函数指针void (*)(int)这个函数指针有一个参数int返回类型为void 而singal的返回类型是void(*)(int) 是函数指针
[6]函数指针数组
作用:可以存放n个函数指针的数组 格式:
int (*parr1[10])();
parr1是个数组 有10个元素 每个元素是int(*)() 函数指针类型 —函数指针数组
到底有什么用——转移表
例 (计算器)
#include<stdio.h>
void menu()
{
printf(" 欢迎来到整形计算器软件 \n");
printf("*** 1.add 2.sub***\n");
printf("*** 3.mul 4.div***\n");
printf("*** 0.exit ***\n");
}
int Add(int x,int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
int x,y;
x=y=0;
int(*pff[5])(int,int) = { 0,Add,Sub,Mul,Div };
do
{
menu();
printf("请选择操作符\n");
scanf("%d", &input);
if(input >= 1 && input <= 4)
{
printf("请输入两个操作数\n");
scanf("%d%d", &x, &y);
printf("答案=%d\n", pff[input](x,y));
}
else if(input==0)
printf("退出\n");
else
printf("输入错误,重新输入\n");
} while (input);
}
[7]指向函数指针数组的指针
作用:说白了就是套娃 就是拿指针去存放函数指针数组的地址
在学这个的时候用例子来举例容易理解一些
例子:
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
void (*pfun)(const char*)=test;
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}
pfun1 可以这样理解 是一个指针可以存一个参数为const char 返回类void 型的函数 ——函数指针
pfunArr[5] 是个数组,什么数组? 函数指针数组 数组有5个元素 每个元素是函数指针类型
(*ppfunArr) 是个指针,指向一个数组,数组有5个元素,每个元素是函数指针类型void (*)(const char*) 相当于是对函数指针数组的解引用
[8]回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
个人理解(暴论)
就是假设把一个函数1 传给函数2然后这个接收的函数2用函数指针接收并通过函数指针去间接改变函数1
应用例子:qsort函数
qsort函数是什么:
qsort函数是C语言的库函数包含在<stdolib.h>中,功能是进行排序,采用的是快速排序算法
qsort函数怎么使用:
qsort函数怎么用取决与参数compar也就是我们要自己编写一个比较函数传入到qsort去 int compar(const void *p1, const void *p2); 如果compar返回值小于0(< 0),那么p1所指向元素会被排在p2所指向元素的前面 如果compar返回值等于0(= 0),那么p1所指向元素与p2所指向元素的顺序一样 如果compar返回值大于0(> 0),那么p1所指向元素会被排在p2所指向元素的后面 也就是说比较函数的返回值决定如何排序
为什么这里的两个形参要用void*型? ?因为用void*类型就可以接收任意类型的数据,不被限制与某个类型。
void * 指针有以下几种情况: ? 1.可以存放任何类型的地址 2.不能解引用操作 3.不能被进行加减整数操作
1.使用qsort对整形数据的排序(升序)
#include<stdio.h>
#include<stdlib.h>
int cmp(const void* e1,const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void print(int arr[],int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
printf("%d ",arr[i]);
}
int main()
{
int arr[5] = {5,4,3,2,1};
int arr_sz = sizeof(arr) / sizeof(arr[0]);
int el_sz = sizeof(arr[0]);
qsort(arr, arr_sz, el_sz, cmp);
print(arr,arr_sz);
return 0;
}
2.使用qsort对浮点数据进行排序(降序)
#include<stdio.h>
#include<stdlib.h>
int cmp(const void* e1, const void* e2)
{
return (int)(*(float*)e2 - *(float*)e1);
}
void print(float arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
printf("%.1f ", arr[i]);
}
int main()
{
float arr[5] = {1.0,2.0,3.0,4.0,5.0};
int arr_sz = sizeof(arr) / sizeof(arr[0]);
int el_sz = sizeof(arr[0]);
qsort(arr, arr_sz, el_sz, cmp);
print(arr,arr_sz);
return 0;
}
3.使用qsort对结构体按姓名排序(升序)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{
int age;
char name[15];
};
int cmp(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void print(struct Stu arr[], int sz)
{
int i = 0;
for(i=0;i<sz;i++)
printf("%s ", arr[i].name);
}
int main()
{
struct Stu arr[3] = { {10,"zhangsan"},{20,"lisi"},{10,"wangwu"} };
int arr_sz = sizeof(arr)/sizeof(arr[0]);
int el_sz = sizeof(arr[0]);
qsort(arr, arr_sz, el_sz, cmp);
print(arr, arr_sz);
return 0;
}
使用回调函数,模拟实现qsort(采用冒泡的方式)
自己写一个来模拟实现qsort,之前写的每个数据类型的比较函数是一样的,不同的是我们要自己实现怎么进行交换、在什么条件下进行交换。
#include<stdio.h>
struct Stu
{
int age;
char name[15];
};
int cmp_struct(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void swap(char* bf1, char* bf2, int width)
{
int i=0;
for (i = 0; i < width; i++)
{
int tmp = *bf1;
*bf1 = *bf2;
*bf2 = tmp;
bf1++;
bf2++;
}
}
void my_sort(void* base, int sz, int width, int (*cmp)( void* e1, void* e2))
{
int i;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j = 0; j < sz - i - 1; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
swap( (char*)base +j * width, (char*)base + (j + 1) * width ,width);
}
}
}
}
void test_int()
{
int i;
int arr[5] = { 5,4,3,2,1 };
int arr_sz = sizeof(arr) / sizeof(arr[0]);
int el_sz = sizeof(arr[0]);
my_sort(arr, arr_sz, el_sz, cmp_int);
for (i = 0; i < arr_sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n\n");
}
void test_struct()
{
int i = 0;
struct Stu arr[3] = { {20,"zhangsan"},{30,"lisi"},{10,"wangwu"} };
int arr_sz = sizeof(arr) / sizeof(arr[0]);
int el_sz = sizeof(arr[0]);
my_sort(arr, arr_sz, el_sz, cmp_struct);
for(i=0;i<arr_sz;i++)
printf("%d ",arr[i].age);
printf("\n\n");
}
int main()
{
test_int();
test_struct();
return 0;
}
结尾
是我的第一篇博客,不会排版,第一次用Typora,写了一半然后又导入到CSDN写的,有参考别人的
????????????????????????? 本篇完成于2022.1.24
|