网站开发合同下载,京东网站是哪个公司做的,电商网站运营团队建设方案模板,手机网站 开发者模式目录
一、指针概述
指针的定义
指针的大小
指针的解引用
野指针
指针未初始化
指针越界访问
指针运算
二级指针
指针与数组
二、字符指针
三、指针数组
四、数组指针
函数指针
函数指针数组
指向函数指针数组的指针
回调函数
指针与数组
一维数组
字符数组…目录
一、指针概述
指针的定义
指针的大小
指针的解引用
野指针
指针未初始化
指针越界访问
指针运算
二级指针
指针与数组
二、字符指针
三、指针数组
四、数组指针
函数指针
函数指针数组
指向函数指针数组的指针
回调函数
指针与数组
一维数组
字符数组
二维数组 一、指针概述
指针的定义
数据是存放在内存中的每一个内存空间都会有一个像房间号一样的编号比如某某五星级酒店3109号房间只有获得了这个编号才能找到这个房间这个编号就是地址。而指针就是用来存放这个地址的变量。总结来说指针是一个变量是用来存放内存地址的变量我们通常称谓指针变量。 指针的大小
是由计算机的物理性质决定的一般的计算机分为32位机和64位机32位机就是由32根地址线组成每一根地址线都会传递高电平(1)或者低电平(0)。那么32根地址线就会产生2^32次方个地址所以64位机的地址以此类推。8bite 1字节——32位机的地址就由4个字节存储62位机的地址就由8个字节的地址存储。
指针类型数据的存储方式有 char、short、int、long、long long、float、double、struct、void 因此对应反指针类型也就有char*、short*、int*、long*、long long*、float*、double*、struct*、void*
int main()
{//什么类型数据的地址就得放到对应类型的指针变量中char ch w;char* pc ch;int num 10;int* p num;return 0;
} 指针的解引用
指针的类型决定了对指针解引用的时候有多大的权限能取到多大的字节数。比如 char* 的指针解引用就只能访问一个字节而 int* 的指针的解引用就能访问四个字节。 野指针
指针指向的位置是不可知的随机的、不正确的、没有明确限制的
指针未初始化
int main()
{ //局部变量指针未初始化默认为随机值int *p;//没有指向对应的地址空间无法查找地址*p 20;return 0;
}
指针越界访问
int main()
{int arr[10] {0};int *p arr;for(int i0; i11; i){//当指针指向的范围超出数组arr的范围时p就是野指针*(p) i;}return 0;
}
指针指向的空间释放在堆里开辟内存的空间如果被回收就不能在使用因此指针就不能再指向那块空间的地址。 指针运算
指针的类型决定 - 整数指针变量所偏移的空间大小
int main()
{float arr[5];//指针-整数指针的关系运算for (float *p arr[0]; vp arr[5];){//后置先给数组赋值在偏移四个字节*p 0;}return 0;}
指针 - 指针
//模拟实现strlen
int my_strlen(char *s)
{char *p s;while(*p ! \0 )p;return p-s;//两个指针之间的空间个数
}
指针的关系运算允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
#define N_VALUES 5
int main()
{float values[N_VALUES];float *vp;//不能让数组第一个元素的地址与数组第一个元素之前的地址进行比较for(vp values[N_VALUES-1]; vp values[0];vp--){*vp 0;}return 0;
} 二级指针
指针变量也是一个变量每一个变量都会存放到内存中也就会有地址。二级指针就是用来存放一级指针变量地址的变量。因此存放一级指针变量地址的指针为二级指针存放二级指针变量地址的指针为三级指针以此类推。 指针与数组
可见数组名和数组首元素的地址是相同的。 因为数组是一块连续的空间所以可以用指针指向数组首元素的地址通过指针的加减来访问数组的每一个空间。 arr可以理解为一个变量即一个内存空间。 二、字符指针
将字符变量的地址放到字符指针中称为字符指针。一种比较特殊的字符指针指向的是常量池中字符串首字符的地址常量池当中的字符串是不能修改的。而将字符串放到数组当中进行存储的值是可以修改的。
char a ‘w’char* pa a;char* p abcdef;
备注const加在*前表示不能修改指针所指向空间的值const加在*后表示不能修改指针的指向
字符串数组和常量字符串的区别
int main()
{//用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块//str1str2字符串数组存放的是对应字符的asc码值//值虽然相同但是他们是两个不同数组开辟的地址也是不同的char str1[] hello bit.;char str2[] hello bit.;//字符指针str3str4存放的是字符串常量池的字符存放的都是字符h的地址const char* str3 hello bit.;const char* str4 hello bit.;if (str1 str2)printf(str1 and str2 are same\n);elseprintf(str1 and str2 are not same\n);if (str3 str4)printf(str3 and str4 are same\n);elseprintf(str3 and str4 are not same\n);return 0;
} 三、指针数组
整型数组 - 存放整型的数组 字符数组 - 存放字符的数组 指针数组 - 存放指针(地址)的数组 指针数组是存放指针的数组每种类型的数据都可以创建数组但是数组也可以是由指针组成。 存放字符指针的数组 参数pc是是一个二级指针存储的是abcdef字符串的地址每次挪1字节
存放数组首地址的指针数组 四、数组指针
字符指针——存放字符地址的指针——指向字符的指针 char* 整型指针——存放整型地址的指针——指向整型的指针 int* 浮点型的指针——存放浮点型地址的指针——指向浮点型的指针 float* double* 数组指针——存放数组地址的指针——指向数组的指针
数组名和数组名的区别 数组指针的使用使用一两种打印数组的方式 数组指针的使用使用二二维数组的使用
//正常的接受二维数组的参数
void print1(int arr[3][4], int r, int c)
{int i 0;for (i 0; i r; i){int j 0;for (j 0; j c; j){printf(%d , arr[i][j]);}printf(\n);}
}//用数组指针来接受二维数组表示的是数组的第几行
void print2(int(*p)[4], int r, int c)
{int i 0;for (i 0; i r; i){int j 0;for (j 0; j c; j){ //printf(%d , (*(p i))[j]);printf(%d , p[i][j]);}printf(\n);}
}int main()
{int arr[3][4] { {1,2,3,4}, {2,3,4,5} , {3,4,5,6} };print1(arr, 3, 4);printf(\n);//数组名arr表示首元素的地址//但是二维数组的首元素是二维数组的第一行//所以这里传递的arr其实相当于第一行的地址是一维数组的地址//可以数组指针来接收print2(arr, 3, 4);return 0;
} 函数指针
函数指针的定义与使用
类型名 (*指针变量名)(函数参数) 函数名或者直接写函数名
//函数指针
int Add(int x, int y)
{return x y;
}int main()
{//pf 是一个存放函数地址的指针变量 - 函数指针int (*pf)(int, int) Add;//可以理解为Add给pfint ret (*pf)(2,3);//函数名和函数名都是函数的地址int (*pf)(int, int) Add;//int ret Add(2, 3);int ret pf(2, 3);printf(%d\n, ret);return 0;
}
函数名也可以理解为一个指针变量指针变量就是一块内存内存中记录一个地址变量也是一块内存直接记录值。*a取a的值a取a的低地址a标识一块内存。 函数指针数组
函数指针数组的定义 数组是一个存放相同类型数据的存储空间前面有讲到指针数组那要把函数的地址存到一个数组中那这个数组就叫函数指针数组。 数据类型 ( * 数组名[数值] ) (函数参数); 如 int ( * parr1 [ 10 ])(); int * parr2 [ 10 ](); 函数指针数组的使用转移表
void menu()
{printf(*******************************\n);printf(****** 1. add 2. sub *****\n);printf(****** 3. mul 4. div *****\n);printf(****** 0. exit *****\n);printf(*******************************\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 (*pf[5])(int, int) { NULL, Add, Sub, Mul, Div };int main()
{int input 0;int x 0;int y 0;int ret 0;do{menu();printf(请选择:);scanf(%d, input);if (input 0){printf(退出计算器\n);break;}else if (input1 input4){printf(请输入两个操作数:);scanf(%d %d, x, y);ret pf[input](x, y);printf(%d\n, ret);}else{printf(选择错误\n);}} while (input);return 0;
} 指向函数指针数组的指针
指向函数指针数组的指针是一个 指针指向一个数组 数组的元素都是 函数指针
void test(const char* str)
{printf(%s\n, str);
}
int main()
{//函数指针pfunvoid (*pfun)(const char*) test;//函数指针的数组pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] test;//指向函数指针数组pfunArr的指针ppfunArrvoid (*(*ppfunArr)[5])(const char*) pfunArr;return 0;
} 回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针地址作为参数传递给另一个函数当这个指针被用来调用其所指向的函数时我们就说这是回调函数。回调函数不是由该函数的实现方直接调用而是在特定的事件或条件发生时由另外的一方调用的用于对该事件或条件进行响应。
void menu()
{printf(*******************************\n);printf(****** 1. add 2. sub *****\n);printf(****** 3. mul 4. div *****\n);printf(****** 0. exit *****\n);printf(*******************************\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;
}void calc(int (*pf)(int, int))
{int x 0;int y 0;int ret 0;printf(请输入两个操作数:);scanf(%d %d, x, y);ret pf(x, y);printf(%d\n, ret);
}int main()
{int input 0;do {menu();printf(请选择:);scanf(%d, input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf(退出计算器\n);break;default:printf(选择错误\n);break;}} while (input);return 0;
} 指针与数组
一维数组
int main()
{//一维数组int a[] { 1,2,3,4 };//4*416printf(%d\n, sizeof(a));//16printf(%d\n, sizeof(a 0));//a0 其实是数组第一个元素的地址是地址就是4/8字节printf(%d\n, sizeof(*a));//*a是数组首元素计算的是数组首元素的大小单位是字节4printf(%d\n, sizeof(a 1));//a1是第二个元素的地址是地址大小就是4/8printf(%d\n, sizeof(a[1]));//a[1]是第二个元素计算的是第二个元素的大小-4-单位是字节printf(%d\n, sizeof(a));//a是整个数组的地址整个数组的地址也是地址地址的大小就是4/8字节//a--- 类型int(*)[4]printf(%d\n, sizeof(*a));//a是数组的地址*a就是拿到了数组*a-- a,a就是数组名sizeof(*a)--sizeof(a)//计算的是整个数组的大小单位是字节-16printf(%d\n, sizeof(a 1));//a是整个数组的地址a1跳过整个数组指向数组后边的空间是一个地址大小是4/8字节printf(%d\n, sizeof(a[0]));//a[0]是首元素的地址计算的是首元素地址的大小4/8字节printf(%d\n, sizeof(a[0] 1));//a[0] 1是第二个元素的地址地址的大小就是4/8字节return 0;
} 字符数组
int main()
{//字符数组char arr[] { a,b,c,d,e,f };//char*//char [6]printf(%d\n, strlen(arr));//随机值printf(%d\n, strlen(arr 0));//随机值//printf(%d\n, strlen(*arr));//strlen(a)-strlen(97),非法访问-err//printf(%d\n, strlen(arr[1]));//b-98,和上面的代码类似是非法访问 - errprintf(%d\n, strlen(arr));//arr虽然是数组的地址但是也是从数组起始位置开始的计算的还是随机值//char(*)[6]printf(%d\n, strlen(arr 1));//arr是数组的地址arr1是跳过整个数组的地址求字符串长度也是随机值printf(%d\n, strlen(arr[0] 1));//arr[0] 1是第二个元素的地址是b的地址求字符串长度也是随机值printf(%d\n, sizeof(arr));//arr单独放在sizeof内部计算的是整个数组的大小单位是字节6printf(%d\n, sizeof(arr 0));//arr 0是数组首元素的地址4/8printf(%d\n, sizeof(*arr));//*arr是数组的首元素计算的是首元素的大小-1字节printf(%d\n, sizeof(arr[1]));//arr[1]是第二个元素大小1字节printf(%d\n, sizeof(arr));//取出的数组的地址数组的地址也是地址是地址大小就是4/8printf(%d\n, sizeof(arr 1));//arr1是跳过整个指向数组后边空间的地址4/8printf(%d\n, sizeof(arr[0] 1));//arr[0] 1是数组第二个元素的地址是地址4/8字节return 0;
} int main()
{char arr[] abcdef;//数组是7个元素//[a b c d e f \0]printf(%d\n, strlen(arr));//6,arr是数组首元素的地址strlen从首元素的地址开始统计\0之前出现的字符个数是6printf(%d\n, strlen(arr 0));//arr 0是数组首元素的地址同第一个结果是6printf(%d\n, strlen(*arr));//*arr是a,是97传给strlen是一个非法的地址造成非法访问printf(%d\n, strlen(arr[1]));//errprintf(%d\n, strlen(arr));//6printf(%d\n, strlen(arr 1));//arr 1是跳过数组后的地址统计字符串的长度是随机值printf(%d\n, strlen(arr[0] 1));//arr[0]1是b的地址从第二个字符往后统计字符串的长度大小是5printf(%d\n, sizeof(arr));//7 - 数组名单独放在sizeof内部计算的是数组的总大小单位是字节printf(%d\n, sizeof(arr 0));//arr0是首元素的地址大小是4/8printf(%d\n, sizeof(*arr));//*arr是数组首元素大小是1字节printf(%d\n, sizeof(arr[1]));//arr[1]是数组的第二个元素大小是1字节printf(%d\n, sizeof(arr));//arr是数组的地址数组的地址也是地址是4/8字节printf(%d\n, sizeof(arr 1));//arr 1是跳过整个数组的地址是4/8字节printf(%d\n, sizeof(arr[0] 1));//arr[0] 1是第二个元素的地址是4/8字节return 0;
} int main()
{const char* p abcdef;printf(%d\n, strlen(p));//6- 求字符串长度printf(%d\n, strlen(p 1));//p 1是b的地址求字符串长度就是5printf(%d\n, strlen(*p));//err*p是aprintf(%d\n, strlen(p[0]));//err - 同上一个printf(%d\n, strlen(p));//p拿到的是p这个指针变量的起始地址从这里开始求字符串长度完全是随机值printf(%d\n, strlen(p 1));//p1是跳过p变量的地址从这里开始求字符串长度也是随机值printf(%d\n, strlen(p[0] 1));//p[0] 1是b的地址从b的地址向后数字符串的长度是5printf(%d\n, sizeof(p));//p是指针变量大小就是4/8字节printf(%d\n, sizeof(p 1));//p 1是b的地址是地址就是4/8个字节printf(%d\n, sizeof(*p));//*p是asizeof(*p)计算的是字符的大小是1字节printf(%d\n, sizeof(p[0]));//p[0]--*(p0) -- *p 就同上一个1字节printf(%d\n, sizeof(p));//p是二级指针是指针大小就是4/8printf(%d\n, sizeof(p 1)); //p 1是跳过p变量后的地址4/8字节printf(%d\n, sizeof(p[0] 1));//p[0]就是‘a’,p[0]就是a的地址1就是b的地址是地址就是4/8return 0;
} 二维数组
int main()
{//二维数组int a[3][4] { 0 };//printf(%p\n, a[0][0]);//printf(%p\n, a[0]1);printf(%d\n, sizeof(a));//48 3*4*4printf(%d\n, sizeof(a[0][0]));//4printf(%d\n, sizeof(a[0]));//a[0]是第一行的数组名数组名单独放在sizeof内部计算的就是数组(第一行)的大小16个字节printf(%d\n, sizeof(a[0] 1));//a[0]作为第一行的数组名没有单独放在sizeof内部没有取地址表示的就是数组首元素的地址//那就是a[0][0]的地址a[0]1就是第一行第二个元素的地址是地址就是4/8个字节printf(%d\n, sizeof(*(a[0] 1)));//*(a[0] 1)是第一行第2个元素计算的是元素的大小-4个字节printf(%d\n, sizeof(a 1));//a是二维数组的数组名数组名表示首元素的地址就是第一行的地址a1就是第二行的地址//第二行的地址也是地址是地址就是4/8 //a - int (*)[4]//a1-- int(*)[4]printf(%d\n, sizeof(*(a 1)));//a1是第二行的地址*(a1)表示的就是第二行*(a1)--a[1] //16printf(%d\n, sizeof(a[0] 1));//a[0]是第一行的地址a[0]1是第二行的地址地址的大小就是4/8printf(%d\n, sizeof(*(a[0] 1)));//*(a[0] 1) 是对第二行的地址解引用得到的就是第二行计算的就是第二行的大小printf(%d\n, sizeof(*a));//a表示首元素的地址就是第一行的地址*a就是第一行计算的就是第一行的大小//*a -- *(a0)--a[0]printf(%d\n, sizeof(a[3]));//16字节 int[4]//如果数组存在第四行a[3]就是第四行的数组名数组名单独放在sizeof内部计算的是第四行的大小int a 10;printf(%d\n, sizeof(int));return 0;
}