网站群系统,单页面网站推广方法,阿里云服务器官网登录入口,上海工商网企业查询指针用法简单介绍
指针#xff0c;是内存单元的编号。
内存条分好多好多小单元#xff0c;一个小单元有 8 位#xff0c;可以存放 8 个 0 或 1#xff1b;也就是说#xff0c;内存的编号不是以位算的#xff0c;而是以字节算的#xff0c;不是一个 0 或 1 是一个编号是内存单元的编号。
内存条分好多好多小单元一个小单元有 8 位可以存放 8 个 0 或 1也就是说内存的编号不是以位算的而是以字节算的不是一个 0 或 1 是一个编号而是 8 个 0 或 1 合在一起是一个编号。这个编号就是地址。
内存条就分为好多小格子一个格子一个编号地址相当于房子一个房子一个地址。
[所谓的指针就是地址。]
*p表示的是以p的内容为地址的变量 #include stdio.h int main() { int* p;//p是变量的名字int*表示变量p存放的是int类型变量的地址 //int* 是p的数据类型p是变量的名字。所谓的int*类型就是可以存放int类型变量的地址的变量的类型 int i 3; /* i是整型int类型的变量可以把变量i的地址放到变量p里 i表示i的地址是取址符表示获取一个变量的地址 */ p i;//把int类型的变量放到int*类型中 /* 类似的double* p只能存放double类型的变量的地址 而不能存放int类型的变量的地址 */ /* 关于对pi的理解 p存放了i的地址所以根据p可以知道i的位置进而找到i。 所以说p指向i。 p不是ii也不是p修改i的值不会改变p的值修改p的值也不会影响i的值 举个例子[小明]指针变量p手里拿了一张写了[小红家庭地址]变量的[纸条]指针变量p存储的i地址i 小红的家庭发生任意改变比如小红家新买了一辆汽车、丢了一袋垃圾小明手中的纸条都不会发生改变 变量i的值发生任意改变1或者-1都不会影响指针变量p里存放的值 小明在手里原本写着小红家庭地址的纸条上进行任意修改小红的家的位置也不会发生任何变化 原本存放变量i的地址的指针变量p的值发生任意变化也不会影响变量i的值 */ /* int* p;int i; p和i都是变量p是能存放其他类型的变量的地址的指针变量i是只能存放其相应类型的值的普通的整型变量。 指针和地址是一个概念指针变量也可以叫做地址变量 指针变量p存放了变量i的地址---指针变量p指向了变量i */ printf(%d, *p i);//1--说明*p和i的值是一样的 /* 如果一个指针指向了某个变量则 *指针变量---等价于---普通变量 如 如果p是指针变量p存放了普通变量i的地址则p指向i *p---等价于---i *p和i可以彼此之间相互替换使用 */ /* *p表示的是以p的内容为地址的变量 */ return 0; } 指针和指针变量的区别:
指针就是地址地址就是指针。
地址[指针]就是内存单元的编号。
指针变量是存放地址[指针]的变量
指针[地址]和指针变量是两个不同的概念
但是有时候在叙述的时候会把指针变量简称为指针但是二者不是一回事儿
指针的重要性
·指针可以表示一些复杂的数据结构比如用链表表示的一些数据结构
·快速的传递数据
·使函数返回一个以上的值
·能直接访问硬件
·能够方便的处理字符串
·是面向对象语言中引用的基础
指针是C语言的灵魂 指针的定义
指针就是地址地址就是指针。那么指针和地址到底是什么?
地址
·内存单元的编号就像房门牌号一样
·地址是从0开始的非负整数
·[地址的范围取决于系统的位数。在32位系统中一个地址的范围是从0到2^32-1即从0到4294967295。而在64位系统中一个地址的范围是从0到2^64-1即从0到18446744073709551615。这些范围都是基于系统的内存大小和寻址能力来确定的。]
指针
·指针就是地址地址就是指针
·指针变量就是存放内存单元编号的变量或者说指针变量就是存放地址/指针的变量
·指针[地址]和指针变量是两个不同的概念。但是有时候在叙述的时候会把指针变量简称为指针但是二者不是一回事儿
·指针的本质就是一个操作受限的非负整数
[“非负整数”指地址是“从0开始的非负整数”。
“操作受限”指针之间的运算只可以进行减法不能进行加乘除运算]
指针可以进行那些运算
为什么指针只可以进行减法不能进行加乘除运算 两个指针相减的结果是一个整数值表示两个指针之间相隔的元素个数或者内存单元的数目。 在数组中如果两个指针指向同一个数组的元素那么它们相减的结果就是它们之间相隔的元素个数。这个结果是一个整数表示两个指针之间有多少个元素。例如如果一个指针指向数组的第i个元素另一个指针指向数组的第j个元素那么它们的差值就是j-i表示它们之间相隔的元素个数。 同样地在内存中如果两个指针指向不同的内存单元那么它们相减的结果就是它们之间相隔的内存单元数目。这个结果是一个整数表示两个指针之间有多少个内存单元。例如如果一个指针指向内存地址A另一个指针指向内存地址B那么它们的差值就是B-A表示它们之间相隔的内存单元数目。 需要注意的是两个指针相减的结果是一个整数值而不是地址量。这个整数值表示两个指针之间相隔的数据个数而不是它们的内存地址差值。因此在计算两个指针相减的结果时应该将它们的地址值进行整数相减而不是将它们的内存地址直接相减。 总之两个指针相减的结果是一个整数值表示两个指针之间相隔的元素个数或者内存单元的数目。这个结果是根据指针所指向的数据类型和内存布局来确定的。通过计算指针之间的差值程序员可以方便地进行数组遍历、内存管理和其他相关操作。 然而两个指针[地址]相加就像一个门牌号加上另一个门牌号一样没有实际意义。一个门牌号在减去另一个门牌号得到的整数就可以表示两个房间之间相差了多少个房间。比如房间1006和房间1001之间差了5个房间1006-10015。同样的两个指针[地址]门牌号相乘或相除也没有实际意义。
但是指针还可以与整数进行相加减。 指针与整数的加减表示指针在内存空间中向下或向上移动整数个单位。这个单位的大小取决于指针所指变量的类型。例如如果是指向short型变量的指针那么每次移动2个字节如果是指向double型变量的指针那么每次移动8个字节。当指针变量增加1时其增加的值等于指向的类型占用的内存字节数。 指针与整数的加减运算在数组中位置的移动和内存地址的偏移中有实际应用。例如通过将指针变量增加1可以使其指向下一个数组元素从而实现数组的遍历。同样地通过指针与整数的加减运算可以方便地访问和操作内存中的不同位置实现动态内存分配、函数参数传递和数据结构实现等功能。 需要注意的是指针与整数的加减运算是有单位的以它所指向数据类型的字节数为单位进行加减。在C语言中指针加法是以所指向数据类型的大小为单位的例如int类型的指针加1就意味着偏移4个字节假设int类型占用4个字节。 总之指针与整数的加减运算表示指针在内存空间中的移动。这种运算在数组索引、指针移动和内存访问等操作中具有重要意义使程序员能够灵活地操作内存和数据结构实现高效的数据处理和内存管理。
就行门牌号1003加上一个整数2就变成了门牌号1005的房间的地址减去一个整数3就变成了门牌号1000的房间的地址。
同样的指针和整数之间不能进行乘除运算没有实际意义。 【如果两个指针变量指向的是同一块连续空间中的不同存储单元这两个指针变量才可以相减】
基本类型指针
[知识点前面已经说了这里看几个代码示例]
示例一
注意下面的代码的错误 #include stdio.hint main(){ int* p; int i5; *pi; printf(%d,*p); return 0;}
*p指针变量只是进行了声明给变量起名字和确定类型而没有进行定义给变量分配空间没有给变量分配空间就无法进行初始化赋值。如果没有进行定义那么这个变量的空间就是不确定的、随机的。
如果没有给指针变量分配内存空间那么这个指针变量所指向的内存位置是不确定的、随机的。这意味着当你试图通过这个指针进行读写操作时你可能会不小心覆盖了其他重要的数据或者访问了不属于你的内存空间称为“越界”。这会导致程序出现未定义的行为可能是程序崩溃、数据损坏或其他不可预测的结果。 示例二 #include stdio.h int main() { int i 5; int* p; int* q; p i; //*qp; /* 错误*q是int类型的变量p是int*类型的变量类型不一样不能进行赋值 */ //*q*p; /* 错误*q没有进行定义就给他赋值 */ //pq; /* 错误q没定义q变量存储的是int*类型的数据但是是不确定的、随机的、垃圾值 把q存储的垃圾值赋值给pp存储的东西也变成了垃圾值 */ return 0; }
用指针交换两个变量的值 #include stdio.hvoid jiaohuan(int a,int b){ int ta; ab; bt;}int main(){ int a3; int b5; jiaohuan(a,b); printf(%d,%d,a,b);//3,5 return 0;}
在这个代码中经过jiaohuan(int a,int b)函数后a、b的值没有发生变化。因为虽然函数jiaohuan(int a,int b)中的参数变量的名字是a、b但是这是形参变量形参变量不会影响函数外的变量。即使jiaohuan(int a,int b)中的参数变量的变量名换成别的c、d等等都是一样的 上述代码没有实现想要的功能接下来用代码实现 #include stdio.hvoid jiaohuan(int* p,int* q){ int * tp; pq; qt;}int main(){ int a3; int b5; jiaohuan(a,b); printf(%d,%d,a,b);//3,5 return 0;}
对于函数jiaohuan()传参数的时候传的是a,b不能是a和b。因为函数的形参列表里面的参数是int*类型的。所以这个函数传递的参数也只能是指针地址而不能是一个值。 之前不太明白。我想如果传参数的时候传的是a,b为什么函数的形参列表里面的参数变量的类型不能是*p、*q类型的[原理*p是表达式而不是变量。对于int* p,只能是int*类型的p变量而不是int类型的*p变量] 但是上面这个代码也不能实现交换两个变量的值的功能。
因为函数jiaohuan()交换的不是a、b的值而是p、q的值。p、q是形参不会改变实际的实参a、b的值。
开始的时候把a的地址传递给了p把b的地址传递给了q。函数结束后p存储的是b的地址q存储的是a的地址。函数使得p和q存储的地址进行了交换不会影响a和b。
没有任何一个语言可以改变静态变量的位置。变量就相当于一个房子可以改变房子里的东西但是无法把房子连根拔起换到别的地方。
形参的变化不会影响到实参 #include stdio.hvoid jiaohuan(int* p,int* q){ int t*p; *p*q; *qt;}int main(){ int a3; int b5; jiaohuan(a,b); printf(%d,%d,a,b);//5,3 return 0;}
这个代码就可以实现交换两个变量的值的功能了。
在函数中p存储的是a的地址*p表示的是int类型的以p的内容a的地址为地址的“变量”。所以p指向a同理q指向b。
*p等价于a*q等价于b
所以在变量中变量盒子里的东西值进行了交换而不是盒子的地址进行了交换。
*的三种含义
乘法定义指针 int * p;定义了一个名字叫p的变量int*表示p只能存放int类型的变量的地址指针运算符 该运算符放在已经定义好的指针变量的前面如果p是一个已经定义好的指针变量则*p表示以p的内容为地址的变量 int* p; *p a;
这两行代码中两个*的含义是不一样的
如何通过被调函数修改主函数中普通变量的值 #include stdio.hvoid f(int* p,int* q){ *p1; *q2;}int main(){ int a3; int b5; f(a,b); printf(%d,%d,a,b);//1,2 return 0;}
实参必须为该普通变量的地址形参必须是指针变量在被调函数中通过[*形参名…]的方式可以修改主函数相关变量的值
指针和数组
一维数组名
一维数组名是个指针常量存放的是一维数组的第一个元素的地址 第六行错误因为一维数组名是常量不能把一个值赋值给一个常量 #include stdio.hint main(){ int a[5]; printf(%#x\n,a[0]); printf(%#x\n,a); return 0;} 地址在内存中以十六进制形式存储
一维数组名是个指针常量存放的是一维数组的第一个元素的地址
那么以上代码输出的十六进制应该是一样的 如果一个函数要处理一个一维数组则需要接收该数组的哪些信息
确定一个一维数组需要几个参数 两个参数数组名、数组长度 #include stdio.hvoid f(int *pArr,int len){ for(int i0;ilen;i){ printf(%d ,*(pArri)); }}int main(){ int a[5]{1,2,3,4,5}; f(a,5); return 0;} 看下面的例子 #include stdio.hvoid f(int *pArr,int len){ pArr[3]999;}int main(){ int a[5]{1,2,3,4,5}; f(a,5); printf(%d,a[3]);//999 return 0;}
a存放的是一维数组第一个元素的地址
*a等价于a[0]
*(a1)等价于a[1]
*(a2)等价于a[2]
……
上面的代码中函数f()改变了主函数中数组的值将a传进了函数*pArr和*a是一样的相当于两个变量都指向了相同的位置。 一个指针变量占几个字节
假设p指向char类型变量1个字节
假设q指向int类型变量4个字节
假设r指向double类型变量8个字节
p、q、r本身所占字节数是否一样 #include stdio.hint main(){ char cha; int w1; double d1.2; char* pch; int* qw; double* rd; printf(%d\n,sizeof(p));//8 printf(%d\n,sizeof(q));//8 printf(%d\n,sizeof(r));//8 return 0;} 这段代码中指针的大小是固定的不管它是指向哪种类型的变量。在大多数现代的64位系统上一个指针的大小是8字节64位。所以不论是指向字符、整数还是双精度浮点数的指针它们的大小都是8字节。32位系统是4字节
传统数组静态内存分配的缺点
数组长度必须事先指定且只能是常整数不能是变量传统形式定义的数组该数组的内存程序员无法手动释放
数组一旦定义系统为该数组分配的空间就会一直存在除非该数组所在的函数终止。
在一个函数运行期间系统为该函数中数组所分配的空间会一直存在直到该函数运行完毕数组的空间才会被系统释放。
数组的长度不能在函数运行的过程中动态的扩充或缩小数组的长度一旦定义就不能再改变了A函数定义的数组在A函数运行期间可以被其他函数使用但A函数运行完毕之后A函数中的数组将无法被其他函数使用
传统方式定义的数组不能跨函数使用 使用动态内存分配的动态数组很好的解决了传统数组静态数组的这四个缺陷
malloc函数 #include stdio.h #include malloc.h//使用malloc函数需要这个头文件 int main() { int i 5;//分配了4个字节 静态分配 int* p (int*)malloc(4); /* * 对于这句代码有以下理解 1、malloc函数只有一个形参这个形参是整型 2、malloc4是请求系统分配4个字节的内存 这个函数会返回系统所分配的内存的起始地址。 malloc4会返回系统分配的这4个字节的第一个字节的地址 3、(int*)malloc(4) 强制类型转换将 malloc 返回的 void* 类型转换为 int* 类型。 malloc函数会返回一个 void* 类型的指针[地址]这个指针指向分配的内存的起始地址。 由于 void* 是一个泛型指针类型它不包含任何关于所指向数据类型的信息因此在使用前需要进行类型转换。 在 (int*)malloc(4) 中将 malloc 返回的 void* 指针转换为 int* 类型。 这意味着我们告诉编译器我们希望这块内存能够存储整数类型的变量。 4、int* p 定义了一个名为 p 的指针变量所以p用于存放int类型的数据的地址[指针] 5、最后p指向了4个字节但本质上p里面只保存了这4个字节中的第一个字节的地址。 这4个字节在用的时候是作为一个整体来用的 6、int* p (int*)malloc(4); 一共分配了12个字节其中系统为指针变量p分配了8个字节64位系统 还分配了4个字节的空间这4个字节被p指向本质上p指向的是这4个字节的第一个字节的地址 其中p本身的8个字节是静态分配的p指向的4个字节是动态分配的 7、p本身的8个字节p具有拥有权而他指向的4个字节只具有使用权 8、*p不是变量是表达式。但是*p可以作为变量来使用。*p所指向的内存是通过动态分配的 如果int a*p; 则可以使用变量a代替表达式*p来使用 */ free(p); //表示把p所指向的内存给释放掉而不会把p本身所占用的内存释放所谓释放内存就是系统收回这段内存的使用权而不会清空这块内存中的数据。被释放后整个程序都失去了对这段内存的使用权而不仅仅是某个变量 return 0; } #include stdio.h #include malloc.h//使用malloc函数需要这个头文件 void f(int* q) { *q 200; //free(q); } int main() { int* p (int*)malloc(sizeof(int)); *p 10; printf(%d\n, *p);//10 f(p); printf(%d\n, *p);//200 return 0; }
这段代码
第一次输出*p的值10
第二次输出*p的值200
经过f()函数将p中存储的地址复制给了q所以在函数f()中*q和*p是一样的
如果执行了free(q),将起不到修改*p的值的作用还把p指向的空间给释放掉了最后输出的时候不会输出200而是一个不确定的随机的垃圾值
动态一维数组的构造 #include stdio.h #include malloc.h//使用malloc函数需要这个头文件 int main() { int a[5];//int占4个字节则本数组总共包含20个字节每4个字节被当做一个int变量来使用 //把系统所分配的20个字节的前4个字节当做a[0]来使用第5~8个字节当做a[1]来使用…… //只有所在的函数终止的时候这20个字节才会被释放 int len; printf(请输入要创建的数组长度\n); scanf(%d, len); int* p (int*)malloc(sizeof(int) * len); //在使用上和静态数组的使用是一样的相当于int p[len]只是静态定义的数组不能用len这个变量 /* 这句代码分配了20个字节的内存p指向了因为是强转成了int*类型 所以p表示前4个字节p1表示第5~8个字节p2表示第9~12个字节…… *p等价于p[0],*(p1)等价于p[1]*(p2)等价于p[2]…… 类似的对于静态定义的数组也可以有这样的写法 */ return 0; }
动态内存和静态内存的比较
静态内存和动态内存的区别主要表现在以下几个方面 1. 分配时间静态内存在程序编译开始阶段分配动态内存在程序运行时分配。
2. 分配位置静态内存分配在栈区或全局/静态存储区动态内存分配在堆区。
3. 分配方式静态内存由编译器自动分配动态内存由程序员手动分配。
4. 分配大小静态内存大小明确分配时就确定动态内存无法确定大小需要指针来指示位置。
5. 管理方式静态内存由编译器在程序开始运行时分配并在程序结束时释放动态内存由程序员在运行时分配和释放。
6. 运行速度静态内存分配速度快但占用空间大动态内存分配速度慢但节省空间。
7. 安全性静态内存由编译器管理安全性较高动态内存管理需要程序员手动管理容易出错安全性较低。 综上所述静态内存和动态内存的区别主要表现在分配时间、分配位置、分配方式、分配大小、管理方式、运行速度和安全性等方面。在实际编程中根据需要选择使用哪种类型的内存以提高程序的效率和安全性。
多级指针 无论是几级指针变量都是之占8个字节