指针
@
取地址运算符&
&是一元运算符,后接一个变量名,给出这个变量的地址.如a是一个变量,则&a是a的地址.
printf()用转换说明%p打印地址.
示例:
#include<stdio.h> void fun1(void); void fun2(int a , int b); int main(void) { int a = 1 , b = 2; printf("In main, a = %d , &a = %p.\n" , a , &a); printf("In main, b = %d , &b = %p.\n" , b , &b); fun1(); fun2(a , b); return 0; } void fun1(void) { int a = 1 , b = 2; printf("In fun1, a = %d , &a = %p.\n" , a , &a); printf("In fun1, b = %d , &b = %p.\n" , b , &b); } void fun2(int a , int b) { printf("In fun2, a = %d , &a = %p.\n" , a , &a); printf("In fun2, b = %d , &b = %p.\n" , b , &b); }
结果:
In main, a = 1 , &a = 0061FF1C. In main, b = 2 , &b = 0061FF18. In fun1, a = 1 , &a = 0061FEEC. In fun1, b = 2 , &b = 0061FEE8. In fun2, a = 1 , &a = 0061FF00. In fun2, b = 2 , &b = 0061FF04.
指针(pointer)
指针类型的变量,简称指针变量,或简称指针.
指针的值是一个地址,就像int类型的变量的值为整数,char类型的变量的值为字符.
假设已经定义了一个指针pointer_a,那么语句pointer_a=&a指将a的地址赋值给pointer_a,即pointer_a的值是a的地址,此时称pointer_a指向a.指针pointer_a是变量,&a是常量.可以将pointer_a指向别处,如又使用语句pointer_a=&b指将b的地址赋值给pointer_a,即pointer_a的值是b的地址,此时称pointer_a指向b.
指针是一个变量,所以本身也占用内存空间,指针本身也有地址.
可以用一个指针给另一个指针赋值:
int * a , * b; int c = 1; a = &c; b = a;
赋给指针的地址应和指针做到类型兼容,如不能将double类型的地址赋给指向int类型的指针,C99和C11强制不允许这样做。
对于指针变量,用&可以得到指针本身的地址。
递增指针是改变指针的值,即指针指向的位置,指针本身的递增不变,指针本身的递增是常量,声明了指针那么它在内存中的位置就固定了。
前缀后缀的递增和递减都可以用于指针。
使用关系运算符可以比较指向相同类型的指针的值。
用printf()打印两个指针的差值用转换说明%td,如果编译器不支持,用%d或%ld代替。指向不同数组的指针相减的结果是不确定的。
一个指针减一个指针得到一个整数,一个指针减一个整数得到一个指针。
不可解引用未初始化的指针。如:
int * p; *p = 1;
这段代码的意思是把1存放到指针p指向的内存位置,但指针p未初始化,不知道p指向何处,存进去可能擦掉有用的数据。
声明一个指针时只创建了这个指针本身的内存空间,没有创建这个指针指向的内存空间。使用指针之前必须指明指针指向的内存位置,即用一个地址初始化这个指针,例如用一个已有变量的地址初始化这个指针,或用函数malloc()分配内存给这个指针。
调用指针形参的函数就是用已有的变量的地址初始化已定义(即已定义的指针形参变量)但尚未初始化的指针。
间接运算符(indirection operator)*
间接运算符(indirection operator) * 也称为解引用运算符(dereferencing operator).
*后接一个指针或一个地址,表示这个指针的值即指针指向的内存位置的值.
假设指针p的值是0061FF04,那么*p是地址0061FF04上的值.
假设已有指针p=&a,则*p=a.
尤其注意:
*(&a)=a; // 这里*后面直接跟一个地址
示例:
a = 1; p = &a; b = *p; // 此时b = 1
声明指针
声明指针时必须指明指针指向的变量的类型,因为两种类型可能占用相同的存储空间但存储的值或存储方式不同.
如:
int * i; char * ch1 , * ch2;
i是指向int类型的指针,ch1和ch2是指向char类型的指针.
(int *)是衍生的类型,是指向int类型的指针类型,i则是这种类型的某个变量的变量名.
同理(char *)是衍生的类型,是指向char类型的指针类型,ch1,ch2则是这种类型的某两个变量的变量名.
同时*i
是int类型,* ch1
和* ch2
是char类型.
示例:
// 交换两个变量的值 #include<stdio.h> void interchange(int * u , int * v); int main(void) { int a , b; a = 1; b = 2; printf("Originally a = %d, b = %d.\n" , a , b); interchange(&a , &b); printf("After change, a = %d, b = %d.\n" , a , b); return 0; } void interchange(int * u , int * v) { int temp; temp = *u; *u = *v; // 交换的是值 *v = temp; }
结果:
Originally a = 1, b = 2. After change, a = 2, b = 1.
// 交换两个变量的值的方案二 #include<stdio.h> void interchange(int * u , int * v); int main(void) { int a , b; a = 1; b = 2; printf("Originally a = %d, b = %d.\n" , a , b); interchange(&a , &b); printf("After change, a = %d, b = %d.\n" , a , b); return 0; } void interchange(int * u , int * v) { int * temp; temp = u; u = v; v = temp; }
结果:
Originally a = 1, b = 2. After change, a = 1, b = 2.
交换地址不好使.声明变量a之后a只能指向内存中一个固定的位置,这个位置的值可以变,a不能再指向别的位置,即&a是常量,a是变量.只有指针可以指向不同的内存位置.
函数声明:
void interchange(int * u , int * v);
也可以写成:
void interchange(int * , int * );
调用函数可以传递关于变量的两个信息,一是变量的值,二是变量的地址.传参要求形参和实参类型匹配.传值时要求形参是和实参同类型的变量,传地址时要求形参是指向实参类型的指针.如果函数调用是要计算或处理值,就传值,如果函数调用是要改变主调函数的变量的值,就传地址.
scanf()的使用,如:
scanf("%d" , &a);
就是传地址,调用函数scanf(),在被调函数scanf()中读取一个数,改变主调函数的变量a的值.
编写程序时,变量有两个属性:变量的名称和变量的值.
在程序编译后,变量有两个属性:变量的地址和变量的值.
变量的名称可以认为是给程序员看的名称,地址则是计算机眼中的名称.
普通变量(非指针)的值是基本量,通过&获得的变量的地址是派生量.指针的值(是一个地址)是基本量,通过*获得的该值即地址指向的内存位置的值是派生量.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术