C语言程序设计 学习笔记 指针

翁恺 C语言程序设计

  • 1.取地址运算
    scanf("%d",&i);里的&:
    获得变量的地址,它的操作数必须是变量
    查看地址:printf("%x",&i);
    地址的大小是否取决于编译器(32位与64位不同)
    多个变量的地址
#include<stdio.h>
int main(){
	int i = 1;
	int p;
	printf("i: 0x%x\n",&i);//增加0x是表示16进制
	printf("p: 0x%x\n",&p);
	return 0;
}

输出结果:

i: 0x62fe4c
p: 0x62fe48
注:不同电脑运行后的地址结果是不尽相同的,但是地址分配的位置差都是相同(紧挨着),具体见推论

推论:
4c和48相隔4个字节,每一个int变量在32位编译器中占据4个字节,因此表示在内存里头这两个是紧挨在一起的。
在定义时,我们先定义的i,后定义的p,在地址上4c>48,内存上的位置分配即i在p上方,原因是c语言内存模型均保存在堆栈(stack)中,堆栈实行自顶向下分配,因此如果再在p后面定义一个q,那么q的地址可以推断出是0x62fe44


&结果的sizeof

	#include<stdio.h>
	int main(){
	int i = 1;
	int p;
	printf("%lu\n",sizeof(int));
	printf("%lu\n",sizeof(&i));
	return 0;
}

64位下,输出的是4(字节)和8(字节),32位下,输出的均为4(字节)


数组的地址

#include<stdio.h>
	int main(){
	int a[10];
	
	printf("%x\n",&a);\\输出数组a的地址
	printf("%x\n",a);\\直接输出数组a
	printf("%x\n",&a[0]);
	printf("%x\n",&a[1]); 
	return 0;
}

输出结果:

62fe20
62fe20
62fe20
62fe24

可见,
1.取数组的地址和直接取数组本身所求的地址的结果是相同的。
2.数组本身的地址和数组初始位a[0]的地址相同。
3.a[0]至a[1]的地址大小开始递增,相邻数组单位间差距均为4,以此类推。原因同“多个变量的地址”。

  • 2.指针
    背景:
    如果能通过将取得的变量的地址传递给一个函数,能否通过这个地址在那个函数内访问这个变量?

指针——就是保存地址的变量

int i;
int* p = &i; \\将i的地址交给p
int* p,q;
int *p,q;

“*”表示的是p是一个指针,表示指向的是一个int类型的变量。3、4行均表示p是一个指针,q只是一个普通的int类型变量,如果想要p,q都是指针,那应该定义为

int *p,*q;

指针变量:
变量的值是内存的地址
普通变量的值是实际的值
指针变量的值是具有实际值的变量的地址

作为参数的指针:

void f(int *p);

在被调用的时候得到了某个变量的地址:

int i = 0;
f(&i);

在f函数里面可以通过这个指针p访问外面的i

示例代码:

#include<stdio.h>

void f(int *p);

int main(){
	int i = 6;
	printf("&i = %x\n",&i);
	f(&i);
	//printf("i = ",i);
	return 0;
}
void f(int *p){
	printf(" p = %x\n",p);
	printf("*p = %d",*p);
	//*p = 7;
}
}

输出结果:

&i = 62fe4c
p = 62fe4c
*p = 6

对于int *p = i;这件事,p代表的是变量i本身的值,p代表的是变量的地址。
若在f中,最后加上一句
p = 7;那么变量i的值也会随之改变,代码见上方注释

指针的运算符&,*
互相反作用:

*&yptr -> *(&yptr) -> *(yptr的地址) ->得到这个地址上的变量 -> yptr

&*yptr -> &(*yptr) -> &(y) ->得到y的地址,也就是yptr ->yptr

对指针求地址→原来的指针
对地址求指针→原来的地址

  • 3.指针的应用
    在函数中可以直接交换两个变量的值,减少重复动作
void swap(int *a,int *b){
	int t = *a;
	*a = *b;
	*b = t;
}

应用场景:
函数返回多个值,某些值就必须用指针返回
传入的参数实质上是需要保存带回的结果的变量

例题:同时求最大值最小值

#include<stdio.h>

void minmax(int a[],int len,int *max,int *min);

int main(void){
	int a[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,34,22};
	int min,max;
	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
	printf("min = %d,max = %d\n",min,max);
	return 0;
}
void minmax(int a[],int len,int *min,int *max){//这里的min和max作为形参和上面的min,max实参名字一样没有关系
	int i;
	*min = *max = a[0];
	for( i = 1 ; i<len ; i++){
		if(a[i] < *min){
			*min = a[i];
		}
		if(a[i] > *max){
			*max = a[i];
		}
	}
}

应用场景2:
函数返回运算的状态,结果通过指针返回
常用的套路是让函数返回特殊的不属于有效范围内的值表示出错:-1或0
但是当任何数值都是有效的可能结果时,就得分开返回

例:两个整数做除法

#include<stdio.h>
int divide(int a,int b,int *result);

int main(void){
	int a = 5;
	int b = 2;
	int c;
	if(divide(a,b,&c)){
		printf("%d / %d = %d\n",a,b,c);
	}
	return 0;
}
int divide(int a,int b,int *result){
	int ret = 1;
	if( b == 0)//除数是0,返回0,除数不是0,指针保存值,返回1,进入printf
		ret = 0;
	else{
		*result = a/b;
	}
	return ret;
}

但如果b=0时,就不会输出任何结果
后续语言(c++,java)采用了异常机制解决这个问题

指针最常见的错误
定义了指针变量,还没有指向任何变量,就开始使用指针

  • 4.指针与数组
    还是用上面的minmax代码,增加了printf查看main()、minmax()中数组的size以及地址
#include<stdio.h>

void minmax(int a[],int len,int *max,int *min);

int main(void){
	int a[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,34,22};
	int min,max;
	printf("main sizeof(a) = %lu\n",sizeof(a));
	printf("main a = %p\n",a);
	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
	printf("min = %d,max = %d\n",min,max);
	return 0;
}
void minmax(int a[],int len,int *min,int *max){
	int i;
	printf("minmax sizeof(a) = %lu\n",sizeof(a));
	printf("minmax a = %p\n",a);
	*min = *max = a[0];
	for( i = 1 ; i<len ; i++){
		if(a[i] < *min){
			*min = a[i];
		}
		if(a[i] > *max){
			*max = a[i];
		}
	}
}

输出结果为:

main sizeof(a) = 80
main a = 000000000062FDF0
minmax sizeof(a) = 8
minmax a = 000000000062FDF0
min = 1,max = 34

可见,main的sizeof和minmax中的sizeof不同,minmax的sizeof是8,不禁令人联想到在64位中(我电脑编译器是64位)的int变量大小就是8

然后main和minmax中的a的地址是完 全 一 致,这就说明minmax()函数中的int a[]其实就是表示是一个指针

如果在minmax()中添加

a[0] = 1000;

main函数中添加

printf("a[0] = %d\n",a[0]);

那么输出的结果会是1000
更能证明这是一个指针

因此,如果把

void minmax(int a[],int len,int *min,int *max)

修改成

void minmax(int *a,int len,int *min,int *max)

编译也是通过的
因此之前在函数中的sizeof=8也就是sizeof(int*)
PS:在函数中的操作可以用数组的运算符,依然保持a[]运算是可行的
总结:以下四种函数原型是等价的:

int sum(int *ar,int n);
int sum(int *,int);
int sum(int ar[],int n);
int sum(int [],int);

因此,在函数中,数组变量是特殊的指针
数组变量本身表达地址,所以
*int a[10]; int *p = a; //无需用&取地址
但是数组的单元表达的是变量,需要用&取地址
调用函数中,a == &a[0]
[]运算符可以对数组做,也可以对指针做:
函数使用中,p[0] <= => a[0](p是指针)
上述代码中,在main函数中添加

int *p = &min;
printf("*p = %d\n",*p);
printf("p[0] = %d\n",p[0]);

输出结果会是

*p = 2
p[0] = 2

此处p[0]代表等于2的含义是:
在内存中有一min,min分配的地址里面存有一个数2
还有一个指针p,p指向的是min的地址
*p就是指针指向的变量的值
p[0]是指将p所指的地方当作一个数组,取其数组第一项,可以将min在内存中理解为

int min[1]

(当然定义是不能这么用)对于一个int min[1]来说,有效的下标就是p[0],也就是上述提到的

[]运算符可以对数组做,也可以对指针做: 函数使用中,p[0] <= => a[0](p是指针)

运算符可以对指针做,也可以对数组做:
同样的,在main函数中写

printf("*a = %d\n",*a);

输出结果会是

*a = 1000;

就是数组中的a[0],就可以解释为什么当初我们求&a和&a[0]的地址是相同的原因了

数组变量是const的指针,所以不能被赋值

int b[] = a;//错误
int *q = a;//正确

实质上,对于int b[],其本身就是—>int * const b;这个b是常数,不能被改变,所以被创建了之后,它不能代表任何其它数组。(const相当于是define)
结论:一个数组是常量指针,所以不能被赋值,两个数组之间不能直接赋值。

  • 5.指针与const

1.指针是const
表示一旦得到了某个变量的地址,不能再指向其它变量

int * const q = &i;//q是const
*q = 26;//ojbk
q++;//ERROR

q的值(i的地址)不能被改变
改变*q的值是可以的,因为地址不变

2.所指是const
表示不能通过这个指针取修改那个变量(并不能使得那个变量成为const)

const int *p = &i;
*p = 26;//ERROR(*p)是const
i = 26;//OJBK
p = &j;//OJBK

不能通过p去修改i,此时固定的是p的值;
但是i可以改变,改变后*p也随之改变;
p也可以成为另一个变量的指针,因为p并没有被const所束缚

思考题:这些是什么意思

int i;
const int* p1 = &i;
int const* p2 = &i;
int *const p3 = &i;

普通定义整型变量i
定义常量指针*p1,p1的值不变
定义常量指针
p2,*p2的值不变
定义常量指针p3,地址不变
(判断哪个被const了的标志是const在星号的前面还是后面)

转换
总是可以把一个非const的值转换成const的

void f(const int* x);
int a = 15;
f(&a);//ojbk
const int b = a;

f(&b);//ok
b = a + 1;//Error

void f()的意思就是表示:给函数一个指针,保证不会动指针所指的值

☆应用:
当要传递的参数的类型比地址大的时候(当以后用struct(结构体)的时候),这是常用的手段:既能用比较少的字节数传递给参数,又能避免函数对外面的变量的修改。

const数组
const int a[]={1,2,3,4,5,6};
数组变量已经是const的指针了,这里的const标明数组的每个单元都是const int
所以必须通过初始化进行赋值,否则无法赋值

保护数组的值
因为把数组穿入函数时传递的是地址,所以那个函数内部可以修改数组的值
为了保护数组不被函数破坏,可以设置参数为const

int sum(const int a[],int length);
int sum(const int *a,int length);

2018年9月26日22:09:25 明天继续更新 背单词去……

猜你喜欢

转载自blog.csdn.net/a656418zz/article/details/82825676
今日推荐