指针

@

取地址运算符&

&是一元运算符,后接一个变量名,给出这个变量的地址.如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的值.

编写程序时,变量有两个属性:变量的名称和变量的值.

在程序编译后,变量有两个属性:变量的地址和变量的值.

变量的名称可以认为是给程序员看的名称,地址则是计算机眼中的名称.

普通变量(非指针)的值是基本量,通过&获得的变量的地址是派生量.指针的值(是一个地址)是基本量,通过*获得的该值即地址指向的内存位置的值是派生量.

posted @   有空  阅读(24)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示