一、指针

1、指针概念

a.指针 — 地址 —内存单元编号 //值
b.指针 — 数据类型 —指针类型 //类型

不同语境:

定义一个指针?//指针类型的变量
打印某个变量的指针? //指针 –地址

2、指针变量的定义

基类型 * 变量名

a.基类型 — > 表示 指针变量 指向的目标的 数据类型
数据类型
基本数据类型
数组类型
指针类型
b. *//定义时候的 * 表示定义的是一个 指针类型的变量
c. 变量名

eg1:
int a,b,*p,*q; //p和q都是指针类型的变量 *是修饰指针变量名的

int *p,q; //p是指针变量 q int型变量

eg2:
int a = 10;
float b = 1.23;
int *p = &a; //指针变量 p 指向 了 int型变量a

p变量的数据类型 int * //指针类型

&b –地址值 —类型?
float
b有一块内存空间 — 放的是float类型的数据
&b —得到了一块 存放着 float类型数据的 空间的地址

float *p1 = &b;’

float b = 10;

&b — > 地址值的类型 –>float *
b的数据类型 — 决定了这块空间放的的数据类型
&b — 获得了 这块空间 的地址编号

float * //这种类型的地址数据

float *p1 = &b;

注意:
1.指针变量的 大小 64位(8字节) 32位(4字节)
2.指针类型 — 存储的是 地址 这种特殊的数据

指针变量的给值:
int *p; //野指针 — 随机值 —被当做了地址
//避免野指针 — 一般初始化为NULL –0号地址(不能访问NULL更不能给NULL赋值)

3、通过地址实现对指针变量的间接访问

* 指针运算符
* 单目运算
运算数必须是指针类型的值(地址)

*p 过程
1.首先拿出p指针变量中的值(地址) 到内存中定位
2.从定位处开始,偏移出sizeof(基类型)大小的一块空间
3.把这块空间当做一个 基类型的 变量来看

*p 整体就是一个基类型的变量

eg:
int a = 0;
int *p = &a;
a = 20;//直接访问给变量a赋值20
*p = 20;//间接访问给变量a赋值20

int a = 0x12345678;
char* p = &a;
*p –> 此时只能访问到0x78; //因为从定位处开始,偏移出sizeof(基类型)大小的一块空间

4、核心用途(80%): 被调修改主调

被调修改主调的过程:
1.想修改谁,就把谁的地址传过去
2.必须要做*运算(间接访问),实现修改

扩展:
指针 操作 一维整型数组

int a[10];

int *p = a;

//1.数组本身的特点 (连续性,单一性,有序性)
//2.p+1— 偏移了一个基类型

通过指针访问到数组元素:
*(p+i) int型的变量 a[i] p[i] *(a + i)

5、指针运算

&
*
p+1
p++
p-1
p–

关系运算
> >= < <= == !=
p>q

p-q
前提: 同一类型的指针
表示之间差了几个基类型

p+q //指针不能做加法运算

#include#if 0//逆序指针实现void reverse(int *p1,int *p2){int t = 0;while(p1 < p2){t = *p1;*p1 = *p2;*p2 = t;p1++;p2--;}}#endif#if 0//二分查找指针形式void binary_search(int *p1,int *p2,int n){int *mid = NULL;while(p1  n){p2 = mid - 1;}else if(*mid < n){p1 = mid + 1;}else{break;}}if(p1  p2){returnNULL;}if(*mid > n){p2 = mid - 1;ret = binary_search(p1,p2,n);}else if(*mid < n){p1 = mid + 1;ret = binary_search(p1,p2,n);}else{ret = mid;}return ret;}#endif#if 0//选择排序指针形式void line(int *begin,int *end){int t = 0;int* i = NULL;int* j = NULL;for(i = begin;i < end;i++){for(j = i+1;j  *j){t = *i;*i = *j;*j = t;}}}}#endif#if 0//冒泡排序指针形式void line(int* begin,int *end){int i = 0;int len = end - begin;int* j = NULL;int t = 0;for(i = 0;i < len;i++){for(j = begin;j  *(j+1)){t = *j;*j = *(j+1);*(j+1) = t;}}}}#endif#if 0//插入排序,指针实现void line(int *begin,int *end){int *i = NULL;int *j = NULL;int t = 0;for(i = begin;i  begin && *(j-1) > t){*j = *(j - 1);j--;}*j = t;}}#endif #if 0int main(){int s1[10] = {10,9,8,7,6,5,4,3,2,1};int i = 0;line(s1,s1+9);//int n = 0;//scanf("%d",&n);//reverse(s1,s1+9);//line(s1,s1+9);//int t *ret = binary_search(s1,s1+9,n);//printf("%p\n",ret);//printf("%d",NALL); -->errorfor(i = 0;i < 10;i++){printf("%d ",s1[i]);}return 0;}

二、const前缀

int puts(const char *s);
const char * s;
const int a; //只读变量
int a = 10;
const int *p = &a; //表示 基类型 为只读
p — 指针变量 –本身的类型 int *
a — int型变量 –本身类型 int

p = &a;

const int *p = &a;
int const *p = &a; //就近原则 — 离谁近,就限定谁的

int *const p = &a; //限定p为只读
const int * const p = &a; //p不能被修改,指向的目标类型不能被修改
//(是不能通过*p)

int puts(const char *s)
{
}//const char *s –在函数里面 不能通过*s修改到外面的数据

好处:
1.可以接收 字符数组名//char *
也可以接收 字符串常量 //const char *
提高了参数的适用性
2.避免了 可能出现的修改的操作
可以将 运行时的错误,提前到 编译时发现

const char * p 可以用来保存字符串常量的地址

作业:

1.实现
strlen
strcat
strcpy
strcmp

#include#if 0int my_strlen(char* p){int count = 0;while(*p != '\0'){count++;p++;}return count - 1;}#endif#if 0void my_strcpy(char* p1,char* p2){while(*p2 != '\0'){*p1 = *p2;p1++;p2++;}*p1 = '\0';}#endif#if 0void my_strcat(char* p1,char* p2){int count = 0;while(*p1 != '\0'){p1++;}while(*p2 != '\0'){*p1 = *p2;p1++;p2++;}*p1 = '\0';}#endif#if 0int my_strcmp(char* p1,char* p2){while(*p1 == *p2 && *p1 != '\0' && *p2 != '\0'){p1++;p2++;}return *p1-*p2;}#endif#if 0int main(){ //char s1[] = "hellloworld"; //char s2[100]; //my_strcpy(s2,s1); //puts(s2);//int ret = 0;//ret = my_strlen(s1);//printf("%d",ret);//char s1[6] = "hello";//char s2[6] = "world";//my_strcat(s1,s2);//puts(s1); //char s1[3] = "aaa"; //char s2[3] = "bbb"; //int i = 0; //i = my_strcmp(s1,s2); //printf("%d\n",i);return 0;}

2、”编写程序实现单词的倒置 “how are you” -> “you are how”

S1. 整体逆序
“uoy era woh”
S2. 逐个单词逆序
“you are how”

#includevoid reverse(char* begin,char* end){char t = 0; while(begin < end){t = *begin;*begin = *end;*end = t;begin++;end--;}}int main(){char s1[] = "how are you";reverse(s1,s1+10);reverse(s1,s1+2);reverse(s1+4,s1+6);reverse(s1+8,s1+10);puts(s1);return 0;}

4、编写程序实现将”12345″ 转化为12345 (数值)

#includeint char_int(char* p1){int i = 1;int j = 10000;int sum = 0;while(*p1 != '\0'){i = (*p1 - 48) * j;sum = sum + i;j = j/10;p1++;}return sum;}int main(){char s1[10] = "12345";printf("%d\n",char_int(s1));return 0;}

三、指针操作一维字符型数组(字符串)一般会用到的函数

gets
puts
strlen
strcpy /strncpy
strcat /strncat
strcmp /strncmp

注意:
1.const 能加都加
2.函数功能 尽可能写的全面

char *strncpy(char *dest, const char *src, size_t n)
{
正常拷贝
多了 一个n
n < strlen(src)
只拷贝前n个字符,最终dest中不会有’\0′
n == strlen(src)
正常拷贝
n > strlen(src)
if (n) 拷贝够了次数
剩余拷贝 统统补0

思路:

// 结束条件 *src == ‘\0’
// n次 拷贝完成没有
}
功能:拷贝指定长度的字符串

char *strncat(char *dest, const char *src, size_t n)
{
拼接的基础上 多 n控制条件

n < strlen(src) 拼n下就结束n == 0

n >= strlen(src) src拼完就结束 src==’\0′

*dest = ‘\0’ //” />

int Strncmp(const char *s1, const char *s2, size_t n)
{

}
功能:比较指定长度以内的字符串

//hello
//help //3

四、回调函数与函数指针

定义: 通过函数指针调用的函数 叫回调函数
技术上: 通过函数指针的实现
函数指针(指向基类型-为函数类型) 函数类型的指针

eg:

写一个程序 实现加,减,乘,除
以回调函数的形式,打印对应的结果

//用函数回调实现加减乘除int func0(int a,int b){return a+b;}int func1(int a,int b){return a - b;}int func2(int a,int b){return a*b;}int func3(int a,int b){return a / b;}//int (*pfunc)(int,int) 是函数指针,类型是 int (int,int) //可以理解为是: int (int,int) *pfunc//C规定书写格式是:int (*pfunc)(int,int)void processData(int a,int b,int (*pfunc)(int,int)){printf("%d",pfunc(a,b));}int main(){int a = 0;int b = 0;char c;scanf("%d%c%d",&a,&c,&b);//printf("%d %c %d",a,b,c);//检验scanf是否输入正确switch(c){case '+':processData(a,b,func0); // 回调 func0break;case '-':processData(a,b,func1);break;case '*':processData(a,b,func2);break;case '/':processData(a,b,func3);break;}return 0;}

五、万能指针

void * //万能指针 –可以接收任意类型的指针
//void类型
注意:
如果通过该类型的地址进行数据访问
一定要转换为 明确类型

int compar(const void *a, const void *b) //回调函数
{
*(const int *)a – *(const int *)b
}

int a[10] = {1,2,3,4};
int b[10] = {5,6,7,8};

六、数组指针

1、本质:指向数组的指针

int a[3][4]; //本质还是一维数组
int[4] a[3]; //理解角度
//a –数组名 –代表类型 int [3][4]
//a –代表的值 — 首元素的地址 — a[0]
//a[0] 的数据类型 int[4]
//&a[0]–对应的数据类型 int(*)[4] //数组类型 (一维整型数组类型)
//数组类型的指针 — 数组指针

2、格式:int (*p)[4] = a;

*p//三步运算完成后
*p 相当于 是 int[4]这种类型 //数组
*p 就相当于 int[4]这个数组的 数组名

*(*(p+i) + j)a[i][j]

p+1 //偏移到了 下一个 int[4],因为基类型是int[4],+1偏移一个基类型大小的空间
//类型为int(*)[4]
*(p+1) //偏移到下一个int
//*(p+1) 代表的类型int[4] 此时相当于是 int[4]的数组名
//*(*(p+1) + 1)

练习:
定义一个二维字符数组,找出数组最大值

#include{void maxarr(char (*p)[4],int row)//类型为 char[4]* 代表指针p 是变量名{//理解记忆 char[4]* p int i = 0;char max[4];strcpy(max,*p);//注意要对p进行*操作for(i = 0;i < row;i++){if(strcmp(max,*(p+i)) < 0){strcpy(max,*(p+i));}}puts(max);}int main(){char s1[3][6] = {"aaa","bbb","ccc"};maxarr(s1,3);return 0;}}

七、指针数组

指针数组:存放指针的数组char** pchar* --> 基类型,表示指向的对象的类型是char *类型* --> 指针标志p --> 变量名 **p *p--> 找到指针数组中所存储的地址元素**p --> 找到所存地址的所对应的变量练习://指针数组进行排序和查找#include#includevoid print(char** s1,int n){int i = 0;for(i = 0;i < n;i++){printf("%s ",*(s1+i));}}void line(char** s1,int n)//插入排序{int i = 0,j = 0;char* p = NULL;for(i = 0;i  0 && strcmp(*(s1+j-1),p) > 0){*(s1+j) = *(s1+j-1);j--;}*(s1+j) = p;}}int binary_search(char** begin,char** end,char* ch1){//二分查找char** mid = NULL;while(begin  0){end = mid - 1;}else if(strcmp(*mid,ch1) < 0){begin = mid + 1;}elsebreak;}if(begin char* s1[3]char ch1[] = "hello"; //字符串常量本身就代表自身的地址 line(s1,3);//print(s1,3);printf("%d",binary_search(s1,s1+2,ch1));return 0;}

八、函数指针数组

int func0(int a,int b){return a+b;}int func1(int a,int b){return a - b;}int func2(int a,int b){return a*b;}int func3(int a,int b){return a / b;}void processData(int a,int b,int (*pfunc)(int,int)){printf("%d",pfunc(a,b));}#endif#if 0//函数指针的数组int main(){int (*P0)(int,int) = func0; //用函数指针来存放函数入口地址 int (*P1)(int,int) = func1; int (*P2)(int,int) = func2; int (*P3)(int,int) = func3;int (*p[4])(int,int) = {func0,func1,func2,func3}; //定义一个函数指针的数组,来存放函数的地址,函数名就代表函数的入口地址//理解记忆 int (int,int) * p[4]// 函数类型int i = 0;int a = 10;int b = 3;for(i = 0;i func0(a,b)}return 0;}

九、类型大综合