栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【C语言基础】10 指针初步

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【C语言基础】10 指针初步

一、取地址运算 1、取地址运算符& (1) 对一般变量取地址
  • 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)类似这种均为非法,&右侧均为表达式。
(2) 对数组取地址
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函数结束后,交换实效;
采取交换两变量的地址可避免函数结束后失效。

(1)值交换的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);

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/862162.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号