- scanf("%d", &i);里的&
- 获取变量地址,操作数必须为变量。只能对变量取地址,不能对表达式取地址。
int i = 0;
printf("%X", &i); //取i的地址,以整型输出。 不推荐!
- 求地址长度
int i = 0;
printf("&lun", sizeof(&i)); //输出地址占用字节数
输出结果为8,占用8个字节。(取决于编译器,32位为4个字节,64位为8个字节)
- 地址的大小是否与以int型输出相同,取决于编译器位数。(指针变量始终相同)
int i = 0;
printf("%p", &i); //取i的地址,直接输出
- printf函数输出地址时,转换说明用%p;
- &不能对没有地址的东西取地址,例如变量。&(a+b)、&(a++)、&(++a)类似这种均为非法,&右侧均为表达式。
int a[10];
printf("%pn", &a);
printf("%pn", a);
printf("%pn", &a[0]);
printf("%pn", &a[1]);
输出结果:
说明:&a,a,a[0]的地址均相同,一个整型数据占用4bit。
二、指针变量:储存地址的变量
指针使用的常见错误:
- 在使用指针之前,指针没有指向任何对象。
1、指针变量的声明
- 地址的取值范围不同于整数的范围,因而不能用整型存储变量地址,而是专用的指针变量。
- 指针变量p存储i的地址时,称为p指向i。
- 指针就是地址,地址就是指针,指针变量就是存储地址的变量。
声明格式:int *p;
- int为指针指向对象的类型;
- p为指向int型对象的指针变量,p本身不是int类型;
- 用对象代替变量,因为指针可以指向不属于变量的内存区域。
- 注意:此声明中,p是指针,*p不是指针。
int i; //定义整型变量i int* p = &i; //定义指针变量p,初始化为指向整型变量i的地址 int* p, q; //定义整型的指针变量p,一般整型变量q int *p, q; //同上,*的位置与空格无关
- j = *&i;等价于j = i;
2、间接寻址运算符*
- *为单目运算符,用于访问存储在对象中的内容。
- 可作右值或左值。例如int i = *p;、*p = i+1;
- *p表示p当前指向的对象。
- 只要p指向i,*p就是i,i就是*p,若更改*p的值,则i的值也将被改变。(但此并不严谨,例外情况见本文第五章)
- *p是左值,只能被赋值。但给*p赋值尤其危险,可能篡改存储在该地址上的有效数据。
- 间接寻址运算符不得用于未初始化的变量,例如:
int *p; //*p未初始化
printf("%dn", *p); //错误用法,会引起未定义的行为
注意: 指针变量声明中的*和间接寻址运算符*是不同的,不可混淆。前者是指针的声明格式的一部分,后者是运算符。
3、指针 赋值
- 可以用赋值运算符进行指针的复制,前提是两指针类型相同。
- 任意数量的指针都可指向同一个对象。
int i, j, *p, *q; p = &i; //把i的地址赋给指针p q = p; //p,q类型相同,可将p的内容赋值给q
注意:p = q和*p = *q不要搞混,前者为指针赋值,后者为指针的对象赋值。
4、指针 作为函数参数指针作为函数参数,也即是 将地址作为函数参数。
- 以void f( int i, int *p );为例:
- 参数表说明第二个参数传入的是地址;
- 函数的形式参数是p,调用时传入的实际参数为&i;
- 调用格式:
- f( i,&x );
- 在函数内可通过该指针访问外部的变量x。
- 函数体内*p的每次出现都是对x的间接引用,既可读取、也可修改x的值。
- 以上函数原型也可以是void f ( int, int * );(形式参数可省略,但*不可省略)
5、综合样例
void f(int *p);
int main() {
int i = 12;
printf(" i = %dn", i); //查看i的值
printf("&i = %pn", &i); //查看i的地址
f(&i); //&i提取变量i的地址,即指针
printf(" i = %dn", i); //更改*p之后,再次查看i的值
return 0;
}
void f(int *p) //函数参数为指针,指向对象为int型
{
printf(" p = %pn", p); //查看指向i的指针p的值,即i的地址
printf("*p = %dn", *p); //查看指针p所指向的对象的值,int型数据需格式化为%d输出
*p = 26; //等价于 i = 26; p存储i的地址,*p访问i。
}
输出结果:
6、总结
- &i——(取)变量i的地址
- *i——(访问)i作为地址的变量
三、指针的用途举例 1、交换两个变量的值
由于函数的特性,采用直接交换两变量的值的swap函数,在swap函数结束后,交换实效;
采取交换两变量的地址可避免函数结束后失效。
void swap1(int a, int b);
int main(){
int a = 3, b = 6;
swap1(a,b) //实参为变量的值
printf("a = %dnb = %dn",a,b); //查看交换后的结果
return 0;
}
swap1(int a, int b) //值交换
{
int t = a;
a = b;
b = t
}
执行结果:
可见,函数结束后,交换实效。交换只在swap函数体内部有效。
(2)地址交换的swap函数
void swap2(int *pa, int *pb);
int main(){
int a = 3, b = 6;
swap2(&a,&b) //实参为变量地址
printf("a = %dnb = %dn",a,b); //查看交换后的结果
return 0;
}
swap1(int *pa, int *pb) //地址交换
{
int t = *pa;
*pa = *pb;
*pb = t
}
执行结果:
与上一个不同,swap函数结束后,交换仍有效。
2、函数返回多个值
- 若要函数返回多个值,由于函数只能返回一个值,某些值就可以通过指针返回。
- 传入的参数实际上是需要保存带回的结果的变量。
举例: 查看数组内的最值
//查看数组内的最值
void min_max(int a[], int len, int *min, int *max);
int main() {
int a[] = {1, 5, 3, 12, 65, 10, 99, 40, 12, 47};
int min, max;
min_max(a, sizeof(a)/sizeof(a[0]), &min, &max);
printf("min = %dnmax = %dn", min, max);
return 0;
}
//此函数中的min和max是与主函数中的min和max为同名但无关的不同变量
void min_max(int a[], int len, int *min, int *max)
{
int i;
*min = *max = a[0];
for(i=0; i
if(a[i]<*min) *min = a[i];
if(a[i]>*max) *max = a[i];
}
}
3、指针作为返回值
(1)返回两整数中较大数的指针。
int *max(int *a, int *b)
{
if(*a > *b) return a;
else return b;
}
调用此函数:
int *p, i, j; p = *max(&i, &j); //将较大数的地址存在p中
说明:以上的函数原型中,*不是函数名的一部分,指针返回类型的格式的一部分,表明函数的返回类型是指向整型变量的指针。
(2)返回数组内最大元素的指针int *find_max(int a[], int len) //此处的*不是函数名的一部分,而是返回类型为指针的函数声明格式的一部分
{
int i,m=0;
for(i=0;i
if( a[i]>a[m] ) m = i;
}
return &a[m];
}
调用时:
printf( "max = %dn", *find(a, sizeof(a)/sizeof(a[0]) ) ); //通过地址访问最大值,并显示输出
注意: 永远不要返回指向自动局部变量的指针 例如:
int *f(void)
{
int i;
...
return &i; //错误!一旦f返回,i就不存在了,指向i的指针将是无效的。
}
指向全局变量的指针是有效的,例如:
int *find_middle( int a[], int n )
{
return &a[n/2];
}
2、函数 通过指针 返回运算状态
- 常用手段是让函数返回特殊的不属于有效范围内的值来表示出错(例如0或-1),但当任何数值均为有效的可能结果时,需分开返回。
举例:
int devide(int a, int b, int *result);
int main() {
int a=5, b=2, c;
if( devide(a,b,&c) ) printf("%d / %d = %dn", a, b, c);
return 0;
}
//如果除法成功,返回1,否则返回0
int devide(int a, int b, int *result)
{
int ret = 1; //状态码,默认1为成功
if(b == 0) ret = 0;
else *result = a/b;
return ret;
}
五、指针与const 1、指针是const
此时,指针一旦得到某变量的地址,该指针就不能再指向其他变量。
int * const p = &i; //p是const,《p只能指向i》 *p = 12; //合法,修改的是i的值 P++; //非法,p内的数据(地址)被改变2、指针指向的对象是const
保护指针的对象,指针指向的对象不能通过该指针被修改。但并不是使该对象成为const,只是不能通过该指针修改其内容。
const int *p = &i; //*p是const,但i不是const。 《不能通过p修改i》 *p = 12; //非法,指针对象被修改 i = 12; //合法,并未通过指针修改i p = &j; //合法3、区分
int i; const int *p = &i; // 保护p的对象,不能通过p修改i。 int const *p = &i; // 保护p的对象,不能通过p修改i,同上 int * const p = &i; // 保护p,p只能指向i const int * const p = &i; //同时保护p和p的对象
依据const的位置判断哪个被const了。const在*前面表示对象不能被修改,在*后面表示指针不能被修改。
4、应用:用const保护参数调用函数时,将指针(地址)作为参数传入时,通常会假设函数将会修改变量。
例如,看到f(&x);,大概是希望函数 f 改变 x 的值。但也可能仅是检查而非修改。
使用const表明函数不会改变该指针所指向的对象,达到保护参数的目的。
void f(const int *p)
{
int j;
*p = 0; //非法
p = &j; //合法
}
5、const数组
- const int a[] = {1,2, 3, 4, 5, 6, };
- 此处的const表明此数组的每个单元都是const int,数组内元素不能改变,只能通过初始化来赋值。
保护数组值
数组传入函数时,为了保护数组元素不被改变,可设置const。例如:int sum(const int a[], int length);



