指针编号即为地址即为指针vps1.vip ,存放指针的变量成为:指针变量

int main(){ int a = 10; int* pa = &a; //拿到的是a的四个字节中的第一个字节的地址 *pa = 20; return 0;}各类型指针不同类型的指针大小相同​

int main(){ printf("%d\n", sizeof(char*));//4 printf("%d\n", sizeof(short*));//4 printf("%d\n", sizeof(int*));//4 printf("%d\n", sizeof(double*));//4 return 0;}​指针类型的意义:1.指针类型决定了:指针引用的权限有多大(int*指针可以访问操作四个字节vps1.vip ,char*指针可以访问操作一个字节)2.指针类型决定了,指针走一步,能走多远(步长)

野指针概念:野指针就是指针指向的位置是不可知的(随机的vps1.vip ,不确定的)

1.指针未初始化

int main(){ int* p;//p是一个局部的指针变量vps1.vip ,局部变量不初始化的话,默认时随机值 int* p = NULL;//不知道该初始化什么地址时先初始化为NULL,和int a=0;类似 *p = 20;//非法访问内存了 return 0;}​2.指针访问越界c语言本身不会检查数组是否越界

int main(){ int arr[10] = { 0 }; int *p = arr; int i = 0; for (i - 0; i <= 11; i++) { //当指针指向的范围超过数组arr的范围时vps1.vip ,p就是野指针 *(p++) = i; } return 0;}3.指针指向的内存空间释放​

int* test(){ int a = 10; return &a;//返回时内存就被释放vps1.vip 了}int main(){ int* p = test(); *p = 20; return 0;}​如何规避野指针:1.指针初始化2.小心指针越界3.指针指向空间释放及时置NULL4.指针使用之前检查有效性

//-----指针使用之前检查有效性int main(){ int* p = NULL; /**p = 10;*///会报错vps1.vip ,指针置为NULL时的内存用户不能对其进行操作 //修改 if (p != NULL) { *p = 10; } return 0;}指针的运算指针+-整数​​

int main(){#define N_VALUES 5 float values[N_VALUES]; float *vp; //指针的关系运算 for (vp = &values[0]; vp < &values[N_VALUES];) { *vp++ = 0;//指针+-整数 } return 0;}​​

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

int main(){#define N_VALUES 5 float values[N_VALUES]; float *vp; //指针的关系运算 for (vp = &values[0]; vp < &values[N_VALUES];) { *vp++ = 0;//指针+-整数 } //************************ for (vp = &values[0]; vp > &values[0];) { *--vp = 0; }​​​

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

for (vp = &values[N_VALUES-1]; vp >= &values[0];vp--) { *vp = 0; }​​

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

第二种写法应该尽量避免,不符合标准规定标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的内存位置的指针比较,但是不允许指向第一个元素之前的那个内存位置的指针进行比较vps1.vip

指针-指针​

int main(){ int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; printf("%d\n", &arr[9] - &arr[0]); return 0;}​​

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

​​指针相减的前提:两个指针指向同一块空间

补充:指针+指针无意义vps1.vip ,类比日期+日期无意义指针+/-数字有意义,类比日期+/-天数有意义日期-日期有意义,类比日期-日期有意义

指针和数组数组名是数组元素的地址

int main(){ int arr[10] = { 0 }; int* p = arr; int i = 0; for (i = 0; i < 10; i++) { printf("%p <==> %p\n", &arr[i], p + i);//arr是首元素的地址vps1.vip ,所以p+i就是这个数组下标为i的元素的地址 } return 0;}​

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

​​​打印

int main(){ int arr[10] = { 0 }; int* p = arr; int i = 0; for (i = 0; i < 10; i++) { *(p + i) = i; } for (i = 0; i < 10; i++) { printf("%d", *(p + i)); }//0 1 2 3 4 5 6 7 8 9 return 0;}拓展​

int main(){ int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* p = arr; //[]是一个操作符 2和arr是两个操作数 a+b=b+a加法交换律 //arr[2]<==>*(arr+2)<==>*(p+2)<==>*(2+p)<==>*(2+arr)==2[arr] //2[arr]<==>*(2+arr) printf("%d\n", 2[arr]); printf("%d\n", arr[2]); //本质arr[2]-->*(arr+2)-->*(2+arr)-->2[arr] return 0;}二级指针一级指针就是指向变量的指针vps1.vip ,二级指针就是指向以及指针的指针,存放一级指针的地址​

int main(){ int a = 10; int* pa = &a;//pa是指针变量vps1.vip ,一级指针//*表明是指针,int表明目标的类型 //ppa就是一个二级指针变量 int** ppa = &pa;//pa也是个变量,&pa取出pa在内存中起始地址//*表示是指针,int*表明目标的类型 return 0;}​

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

指针数组存放指针的数组​​​​​

int main(){ int arr[10];//整型数组 - 存放整形的数组就是整型数组 char ch[5];//字符数组 - 存放的是字符 //指针数组 - 存放指针的数组 int* parr[5];//整型指针的数组 char* pch[5];//字符型指针的数组 return 0;}结构体结构体是什么?类比于数组之间的不同,数组是类型相同的元素的集合,结构体是类型不同的元素的集合vps1.vip 。 主要用于描述复杂对象,比如描述一个学生 。​

struct Stu //是个类型{ //成员变量 char name[20]; int age; char id[20];}s1,s2;//这里创建的s1vps1.vip ,s2也是结构体变量,是全局变量int main() { struct Stu s;//由struct Stu类型创建的s对象//s是局部变量 return 0;}​

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

结构体成员的类型很多vps1.vip ,可以是标量、数组、指针,甚至是其他结构体,当成员为其他结构体时的初始化和成员访问​​​

struct B{ char c; short s; double d;};struct Stu //是个类型{ //成员变量 struct B sb; char name[20]; int age; char id[20];}s1, s2;//这里创建的s1vps1.vip ,s2也是结构体变量,是全局变量int main(){ struct Stu s;//由struct Stu类型创建的s对象//s是局部变量 struct Stu s = { { 'w', 20, 3.14 }, "张三", 30, "202005034" };//初始化 //用.和->操作符访问成员 printf("%c\n", s.sb.c);//打印W struct Stu* ps = &s; printf("%c\n", (*ps).sb.c); printf("%c\n", ps->sb.c);//ps是指针可以直接用->访问成员,sb不是指针所以要用.访问成员 return 0;}传值调用和传址调用的对比写一个打印函数打印s的内容​

struct B{ char c; short s; double d;};struct Stu //是个类型{ //成员变量 struct B sb; char name[20]; int age; char id[20];}s1, s2;//这里创建的s1vps1.vip ,s2也是结构体变量,是全局变量void print1(struct Stu t){ printf("%c %d %lf %s %d %s\n", t.sb.c, t.sb.s, t.sb.d, t.name, t.age, t.id);}void print2(struct Stu* ps){ printf("%c %d %lf %s %d %s\n", ps->sb.c, ps->sb.s, ps->sb.d, ps->name, ps->age, ps->id);}int main(){ struct Stu s = { { 'w', 20, 3.14 }, "张三", 30, "202005034" };//初始化 //写一个打印函数打印s的内容 print1(s); //传值调用 传参是会开辟空间,造成浪费性能下降 print2(&s); //传址调用 仅仅传输地址,效率高 首选 return 0;}采用传址调用比传值调用好,节省资源开销,因为传参类似于压栈操作,具体原理如图​

<strong></p>
<p>vps1.vip
</strong>我的C语言学习日记10-指针

结论:结构体传参的时候vps1.vip ,要传结构体的地址​​