广州新塘网站seo优化,深圳网站营销seo多少费用,做网站赚钱的点在哪里,dede视频网站目录
一、指针初阶
1.指针是什么#xff1f;
1.1指针的定义
1.2指针变量的定义
2.指针和指针类型
2.1指针类型不同的意义
2.2指针的解引用
3.野指针
3.1野指针成因
3.1.1指针未初始化
3.1.2指针越界访问
3.1.3指针指向的空间释放
3.2如何规避野指针
4.指针的运算…目录
一、指针初阶
1.指针是什么
1.1指针的定义
1.2指针变量的定义
2.指针和指针类型
2.1指针类型不同的意义
2.2指针的解引用
3.野指针
3.1野指针成因
3.1.1指针未初始化
3.1.2指针越界访问
3.1.3指针指向的空间释放
3.2如何规避野指针
4.指针的运算
4.1指针-整数
4.2指针-指针
4.3指针的关系运算
5.指针和数组
6.二级指针
7.指针数组
二、指针进阶
1.字符指针
1.1一般使用
1.2字符串使用
2.数组指针
2.1数组指针的定义
2.2数组名与数组名
2.3数组指针的使用
3.数组参数、指针参数
3.1一维数组传参
3.2二维数组传参
3.3一级指针传参
3.4二级指针传参
4.函数指针
5.回调函数 一、指针初阶
1.指针是什么
1.1指针的定义
指针是内存中一个最小单元的编号也就是地址以字节为单位每一个字节的存储单位具有一个地址。
1.2指针变量的定义
指针变量是用来存放内存地址的变量可以通过取地址符取出某一变量字符型、整型、数组类型等的起始地址存放在指针变量中。经常将指针变量称为指针。
注意区分指针变量的大小即地址的大小取决于计算机是32位还是64位对于32位的计算机有32根地址线在寻址时产生32个0或1即0000 0000 0000 0000 0000 0000 0000 0000~1111 1111 1111 1111 1111 1111 1111 1111此时有2的32次方个地址4GB空闲编址该指针变量占32/84个字节。对于64位的计算机有64根地址线在寻址时产生64个0或1即0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000~1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 此时有2的64次方个地址16GB空闲编址该指针变量占64/88个字节。指针变量存放着其他变量的地址而存放着其他变量地址的指针变量也作为其中一种类型的变量存放在内存中。
2.指针和指针类型
char *pc NULL;
int *pi NULL;
short *ps NULL;
long *pl NULL;
float *pf NULL;
double *pd NULL;
int num 10;
int *pj num;
指针定义的方式是 type * 指针变量名 其中类型是表明该指针变量中存放的是哪种类型变量的地址(如int * pj存放的是int类型的num)如果要将变量存入标有与变量类型不同的指针变量时要进行强制类型转化
int n 10;
char *pc (char*)n;
int *pi n; 易得n的类型是 int *要将n存入char *类型的指针变量中就要先将其强制类型转化为char *型
2.1指针类型不同的意义
printf(%p\n, n);//000000065911F984
printf(%p\n, pc);//000000065911F984
printf(%p\n, pc1);//000000065911F985
printf(%p\n, pi);//000000065911F984
printf(%p\n, pi1);//000000065911F984 不同类型的指针在-1或其它整数时跳过的字节数与指针变量定义的类型有关。在n的地址经过强制类型转化后存入char * pc而指针变量pc与指针变量pi中存的都是n的起始第一个字节地址不同之处在于当指针pc1时跳过了1个字节指针pi1时跳过了4个字节原因是char * pc的类型char长度为1个字节int * pi的类型int为四个字节。
2.2指针的解引用
对指针变量进行解引用*操作的时候指针的类型决定解引用的权限比如char *的指针解引用只能访问第一个字节而int *类型的指针可以访问四个字节。
int n 256;
char* pc (char*)n;
int* pi n;
printf(%d\n, *pi);//打印结果256
printf(%d, *pc); //打印结果0 上述打印结果的原因解释256以二进制的形式表示为
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000
在解引用指针变量pi类型为int *类型时可以访问四个字节后32bit位0000 0000 0000 0000 0000 0001 0000 0000256;在解引用指针变量pc类型为char *类型时只能访问1个字节后8bit位0000 00000
3.野指针
野指针就是指针指向位置是不可知的随机的、不正确的、没有明确限制的
3.1野指针成因
3.1.1指针未初始化
int *p;//局部变量指针未初始化默认为随机值程序运行过程中报错*p 20;
return 0;
3.1.2指针越界访问
int arr[10] {0};int *p arr;int i 0;for(i0; i11; i){//当指针指向的范围超出数组arr的范围时p就是野指针*(p) i;}
3.1.3指针指向的空间释放
3.2如何规避野指针
int* p NULL;
int* q NULL;
int n 10;
p n;
printf(%d, *p);//10
*q 10;//报错
创建指针变量时初始化无指向值时使其指向为空之后该指针变量可以重新指向其他变量但是由于指针变量置空在没指向某一变量时不能进行赋值编译器会报错因此在对指针变量进行直接赋值操作时可以先检查它是否为空。
4.指针的运算
4.1指针-整数
float values[5];float* vp;//指针-整数指针的关系运算for (vp values[0]; vp values[5];){*vp 0;}
4.2指针-指针
int my_strlen(char *s)//计算字符串长度
{char *p s;while(*p ! \0 )p;return p-s;
}
两个指针相减实际上是两个指针值(地址)相减之差再除以该指针所指变量的的大小字节数)。上图代码就是通过将p自增至指向s字符串的末位再减去s原指向的首位得到中间元素的个数。
4.3指针的关系运算
for(vp values[MAX]; vp values[0];)//代码1
{*--vp 0;
}
for(vp values[MAX-1]; vp values[0];vp--)//代码二
{*vp 0;
}//循环在最后一次判断时vp递减到数组首位元素在内存中的前一位
代码一不可行代码二可行标准规定
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较。
5.指针和数组
int arr[10] { 1,2,3,4,5,6,7,8,9,0 };
printf(%p\n, arr);//000000C1FA4FF6C8
printf(%p\n, arr[0]);//000000C1FA4FF6C8 可见数组名表示的是数组首元素的地址。但sizeofarrsizeof(arr[0])此时arr表示整个数组arr[0]表示数组首元素那么将数组的地址给到指针变量时就可以表示为
int arr[1,2,3,4,5];
int* p arr;
int* q arr[0];
指针p、q均解引用后表示 数组的首元素在表示arr[i]时又可以有多种表达
arr[i]; *(pi;* (arri) pi指得是数组第i个元素的地址。2.1有提到再对指针变量进行-操作时是以其所指向元素大小为单位i即跳过i个该元素大小的字节后所指向的字节它的地址。
6.二级指针
指针变量中存放着地址那么指针变量也可以存放其他指针变量的地址该指针变量称为二级指针变量二级指针。表示为 type** 变量名type *表示其所指向的变量类型。
int a 10;
int *pa a;
int **ppa pa;
printf(%p\n, ppa);//pa的地址000000EA244FF598
printf(%p\n, *ppa);//pa中存放的a的地址000000EA244FF574
printf(%d\n, **ppa);//先对pa的地址解引用得到a的地址 在对a的地址解引用得到a的值
7.指针数组
指针数组表示的是数组中存放的每个元素的类型是指针。即有整型数组、字符数组、指针数组。
int arr[5]; char arr[5]; int* arr3[5];//表示每个元素的类型是 * int
二、指针进阶
1.字符指针
在指针的类型中我们知道有一种指针类型为字符指针char *
1.1一般使用 char ch w;char *pc ch;*pc w;
1.2字符串使用
const char* pstr hello world.;
printf(%s\n, pstr);//hello world.
printf(%c\n, *pstr);//h
const char* pstr hello world.;该行代码并不是将hello world.字符串放入了指针变量pstr中而是将该字符串的首字节元素地址放入 了pstr中。那么通过指针变量创建的字符串又和字符数组初始化创建的字符串有什么区别呢看下面的例子
char str1[] hello world.;
char str2[] hello world.;
const char* strp1 hello world.;
const char* strp2 hello world.;
if (str1 str2)printf(str1 and str2 are same\n);
elseprintf(str1 and str2 are not same\n); //打印输出if (strp1 strp2)printf(strp1 and strp2 are same\n); //打印输出
elseprintf(strp1 and strp2 are not same\n);
字符串的比较要用strcmp()但在此代码中我们直接用来比较是为了判断它们所创建的hello world.在内存中是不是指的同一串由打印输出的结果来看在创建数组str1[ ]和str2[ ]时在内存中创建了两个hello world.而在创建指针变量strp1时hello world.被创建放置在内存中再由strp1指向其又在创建strp2时让其直接指向已经被创建的hello world.
2.数组指针
2.1数组指针的定义
指针数组时存放指针的数组数组指针是指向数组的指针
int *p1[10];//指针数组
int (*p2)[10];//数组指针 [ ] 的优先级高于 *。当没有括号时int* p1[10]p1先和[ ]结合表示p1[ ]数组int *此时表示数组类型表示存放指针类型变量当有括号时int (*p2) [10]p2先和*结合说明p2是一个指针变量int [10]此时为指针的类型表示指向数组。
2.2数组名与数组名
arr与arr的含义相同吗
int arr[10] { 0 };
printf(%p\n, arr[0]);//000000046BF9FA18
printf(%p\n, arr);//000000046BF9FA18
printf(%p\n, arr);//000000046BF9FA18
printf(arr1 %p\n, arr1);//000000046BF9FA1C
printf(arr1 %p\n, arr1);//000000046BF9FA40在此段代码中arrarr打印输出都是数组首元素的地址 但打印arr1跳过了4个字节arr1跳过了40个字节因为取地址arr取的是arr数组的地址若存放进指针变量则变量类型为int(*[10]的数组指针再进行取地址1操作时跳过的字节大小是整个数组。
2.3数组指针的使用
数组指针可以用来在二维数组传参时做形参。
void print_arr2(int (*arr)[5], int row, int col)//相当于int arr[3][5]
{int i 0;for(i0; irow; i){for(j0; jcol; j){printf(%d , arr[i][j]);}printf(\n);}
}
3.数组参数、指针参数
3.1一维数组传参
void test(int arr[]){}//正确
void test(int arr[10]){}//正确
void test(int* arr){}//正确
void test2(int* arr[20]){}//正确
void test2(int** arr){}//正确
int main()
{int arr[10] { 0 };int* arr2[20] { 0 };test(arr);test2(arr2);
}
3.2二维数组传参
void test(int arr[3][5]){}//正确
void test(int arr[][]){}//错误
void test(int arr[][5]){}//正确
//总结二维数组传参函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组可以不知道有多少行但是必须知道一行多少元素。
void test(int* arr){}//正确
void test(int* arr[5]){}//正确
void test(int(*arr)[5]){}//正确
void test(int** arr){}//正确
int main()
{int arr[3][5] { 0 };test(arr);
}
3.3一级指针传参
void test1(int *p)
{}//test1函数能接收int类型变量的地址
void test2(char* p)
{}//test2函数能接收char类型变量的地址
3.4二级指针传参
void test(char **p){}//能接收存放字符型变量地址的指针变量的地址
int main()
{
char c b;
char*pc c;
char**ppc pc;
char* arr[10];
test(pc);
test(ppc);
test(arr);//指针数组arr表示数组首元素的地址数组首元素是存放char类型变量的指针
return 0;
}
4.函数指针
void test(){printf(hehe\n);}
int main()
{
printf(%p\n, test);//00007FF618AB1406
printf(%p\n, test);//00007FF618AB1406
}//函数也具有地址 函数具有地址并且它的地址可以用指针保存该指针类型为函数指针。格式为
void (*pfunction) (int ,char ); 函数返回值类型*函数指针变量名函数参数类型【没有参数时可为空】
//代码一
void (*signal(int , void(*)(int)))(int);
//代码二
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);代码一和代码二相同。这是一个函数的声明signal函数有两个参数其中一个是整型另一个参数是返回值为空、有一个整型参数的函数指针类型而signal函数的返回值是void *int类型即signal函数的返回值是一个无返回值有一个整型参数的函数指针类型。
5.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针地址作为参数传递给另一个 函数当这个指针被用来调用其所指向的函数时我们就说这是回调函数。回调函数不是由该函数 的实现方直接调用而是在特定的事件或条件发生时由另外的一方调用的用于对该事件或条件进 行响应。
典型回调函数qsort函数的使用
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)//void表示指针是无指向类型
{return (*( int *)p1 - *(int *) p2);//需要先将无类型的指针强制转换为要比较的类型
}
int main()
{int arr[] { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i 0;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;
}
qsort函数C语言编译器函数库自带的排序函数。qsort 的函数原型是
void qsort(void*base,size_t num,size_t width,int(__cdecl*compare)(const void*,const void*)); 在传参时,需要传入数组的地址,数组的元素个数,数组单个元素的长度,以及排序判断函数。qsort最后一个参数就是函数指针类型在使用qsort函数进行排序时就要调用比较函数。