C语言-7、指针

指针
学前必学一句话:地址变量得地址,得谁地址指向谁,有"*"为内容值,不是读就是写(*放在赋值号的左边),放在赋值号的右边为读操作,无*为地址,地址的赋值即为改指向,

指针变量定义且初始化才可以用(详见指针经典练习题4)形参用完被释放,这里一定要看最后释放的时候是否有指针将系统本身的值进行修改。

一、指针变量的定义
C语言有两种变量“
普通变量:存储内容值
地址变量/指针变量:存储地址值

1、定义格式:
类型名 *指针变量名(*只是标志没有实际含义)
int a, b, *p1, *p2;    

2、地址变量的引用
"&"取地址符
&内容变量(取出这个变量得地址)--------得到得结果是地址值

"*"指针运算符,
*地址(取出这个地址得内容值)--------得到的结果是内容值
*的前面只要有类型,它就是标志,若前没有类型,它实际即使个内容值
int a = 10;
int *p = &a;--------完全正确的书写

在C语言中正确的做法是先让指针变量指向一个确定的存储单元后,在通过该指针变量引用它所指向的存储单元(先定义在初始化)
如:
int *p;
*p = 200;//危险

int *p;
*p = 200;
/*
上述代码从形式上是完全正确的,符合逻辑
但是上述的操作是危险的,首先定义了一个p的
指针变量,那么这个指针指向是随机的,如果该指针
指向的是OS的系统区,那么*p = 200就相当于在‘系统区
写个一个200的变量,这是很危险的,以破坏系统为代价即
让系统奔溃和死机完成一个写的操作

*/

p1 = p2;
将p2的地址给p1,最后的结果是p1和p2指向相同的地址
*p1 = *p2
含义:读出p2所指房子的内容值,写到p1所指的房子里面

二、指向数组的指针变量
1、指向数组元素的指针变量
由于数组元素与普通一样,所以定义指向数组元素的指针变量与定义指向普通变量的指针变量完全一样

2、指向一维数组的指针变量
注:
(1)在C语言中规定:数组名代表数组的首地址,而且是一个地址常量
int a[10];
int *p;
p = a;
p《-》&a[0]
(*p)++    ++(*p)     ++*p----------p所指向的内容发生变化,指针未发生变化
(*p)--    --(*p)     --*p----------p所指向的内容发生变化,指针未发生变化

int b[5] = {10, 30, 20, 15, 40};
int *q = b + 2;//q指针指向b[2]
//单目*和自增/自减都是二级运算符,从右向左计算
//q是地址而*q是内容,看++/--加到了谁的身上

*q++ 
//表达式的值为20.q指向了b[3]

*++q
//表达式的值为15.q指向了b[3] 

(*q)++
//表达式的值为20.q指向了b[2]

++(*q)
//表达式的值为21.q指向了b[2]

指针在数组中的使用

#include<iostream>
#include<cstdio>
using namespace std;
//指针在数组中使用 
int main(){
	int *p;
	int a[3];
	p = a;
	for(int i = 0; i < 3; i++){
		scanf("%d",p++);
	}
	printf("\n\n");
	for(p = &a[0]; p < a + 3;){
		printf("%d ", *p++);
	}
	
	return 0;
} 
//输入:1 2 3
//预期结果
1 2 3

(2)若两个指针变量指向同一个数组,则这两个指针变量可以进行大小比较
数组中下标小地址小,下标大地址大
地址做减法运算得到的是两个地址之间相差元素的个数

三、指向多维数组的指针变量
1、若a是一个二维数组,则有:
(1)a+i是行指针,即指向的是一整行,若对它加1则指向下一行
(2)*(a+i)和a[i]一样,都是一个列指针即指向的是一个元素
(3) []和*完全等价而&和[]可以相互抵消,二维是行,一位是列 
(4)二维------行 +[]->列    列+[]->元素,所以说二维数组想到得到元素得上两次[],[]和*完全等价可以相互替换
(5)*(a+i)+j和a[i]+j一样,都表示元素a[i][j]的地址 
(6)int *p;//列指针    
2、指向由m个元素组成的一维数组的指针变量
定义指向由m个元素组成的一维数组的指针变量的格式

类型 (*指针变量名) [m]//m是大于0的正整数,列数

#include<iostream>
#include<cstdio>

using namespace std;
//列指针的方式 
int main(){
	 int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
	 int *p;//列指针而不是行指针 
	 //a[0]就相当于&a[0][0] 
	 for(p = a[0]; p < a[0]+9; p++){
	 	printf("%d", *p);
	 }
	
	return 0;
}
//预期结果
123456789
int a[7][7];//a是行指针,行地址
int (*p)[7];
p = a;

char b[10][80];
char (*p)[80];//行指针变量
r = b + 5; //r指向b的第5行

#include<iostream>
#include<cstdio>

using namespace std;
//行指针的使用 
int main(){
	int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};//按列画圈 
	int (*p)[4];//行指针 画二维表 
	p = a;
	
	for(int i = 0; i < 3; i++){
		for(int j = 0; j < 4; j++){
			printf("%d",p[i][j]);
		printf("\n");
		}
		
	}
	
	return 0;
} 
#include<iostream>
#include<cstdio>

using namespace std;

int main(){
	int a[][3] = {{1, 2, 3}, {4, 5, 0}};
	int (*pa)[3];
	pa = a;
	
	for(int i = 0; i < 3; i++){
		if(i < 2){
			pa[1][i] = pa[1][i] - 1;
		}else{
			pa[1][i] = 1;
		}
	
	}
	printf("%d\n",a[0][1] + a[1][1] + a[1][2]);

	return 0;
} 
//预期结果
7

四、指向字符串的指针变量
字符串常量:C语言对字符串常量是按首地址处理字符串常量
"abc"
①首地址
②地址常量
③第一个字符的地址   
 如:

//合法
char str[] = "China";//数组长度为6
//合法
char str[] = {"China"};

//合法
char *p = "China";

//非法
char *p = {"China"};

//非法
str = "Chinese";

//合法
p = "Chinese"

//非法
char *p;
*p = "China";//内容值不等于地址值

五、指向函数的指针变量
函数名与数组名一样,是起始地址,而且是一个地址常量
①首地址
②地址常量
③函数地址 
def:类型(*指针变量名)(参数列表);//专指函数的指针表示是两对小括号
  类型(*指针变量名)()
(1)在定义指向函数的指针变量时,要注意有两个小括号必须有,不需要定义形参
(2)单独的函数名代表该函数的首地址(函数的入口地址)
(3)函数的指针变量只能指向函数的 入口处(函数的首地址)不能指向函数中的某条指令。(指向函数的指针变量加1是没有意义的)
(4)给指向函数的指针变量赋值的时候,只需要写函数名即可,不必写参数

#include<iostream>
#include<cstdio>

using namespace std;
//函数指针的使用 
int min(int a,int b){
	return a > b ? b : a;
}

int max(int a,int b){
	return a > b ? a : b;
}

int main(){
	int x = 6, y = 10;
	int (*p)(int a,int b);//函数指针格式后面的括号加参数,但可以不加,只不过DEVC++不加的话会报错
	p = max;
	printf("%d",max(x,y));
	printf("%d",p(x,y));
	p = min;
	printf("%d",min(x,y));
	printf("%d",p(x,y));
	return 0; 
}
//预期结果
101066

六、返回指针函数
//返回指针函数的定义
类型名 *函数名(参数列表){
    return 地址;
}

//返回指针函数的定义
类型名 *函数名(参数列表){


    return 地址;

}
#include<iostream>
#include<cstdio>

using namespace std;
//返回指针的函数的使用 

int *fun(int *x, int *y){
	
	if(*x < *y){
		return x;
	}else{
		return y;
	}
} 
int main(){
	
	int a = 7, b = 8;
	int *p;
	int *q;
	int *r;
	p = &a;
	q = &b;
	r = fun(p, q);
	printf("%d,%d,%d",*p, *q, *r);//7,8,7
	return 0;
} 
//预期结果
7,8,7

七、指针数组和指向指针的指针变量
1、若一个数组的所有元素均为指针类型(地址),则称为指针数组
格式:
类型名 *数组名[常量表达式]
int *s[10]

/*1、若一个数组的所有元素均为指针类型(地址),则称为指针数组
格式:
类型名 *数组名[常量表达式]
int *s[10]//地址数组,是指向,换句话说就是啊s[0]向外指出一个X
2、见到字符数组无论一维还是二维,第一步填'\0',第二填单引号''
 */
#include<iostream>
#include<cstdio>
using namespace std;
//指针数组的使用 
int main(){
	char ch[3][4] = {"123", "456", "78"};
	char *p[3];
	
	for(int i = 0; i < 3; i++){
		p[i] = ch[i];
	}
	for(int i = 0; i < 3; i++){
		printf("%s",p[i]);
	}

	return 0;
}
//预期结果
12345678

2、指向指针的指针变量
用来存放指针变量地址的指针变量称为指向指针的指针变量
定义格式:类型名 **指针变量名

int a = 3;
int *p = &a;
int **k = &p;
//*k得到变量p(变量a的地址) **k得到变量a的值(a的数据是3)   

八、空指针
指针变量可以是空值,即指针变量不指向任何变量,不指向任何有用的存储单元。

int *p = NULL;//是空指针,那么指针所指的房子是不存在的
//此时p的值为空指针,即不指向任何有用的存储单元,尽管NULL的值为0,但我们不能认为
//p指向可地址为0的存储单元

int *p;
*p = 10;//危险


#include<stdio.h>
int main(){
    int a[] = {1, 2, 3}
    int *p = a + 1;
    int *q = NULL;
    *q = *(p + 1);
    printf("%d %d\n",*p, *q);

}
//预期结果
Error
//int *q = NULL;//是空指针,那么指针所指的房子是不存在的,不存在那么就没值

Note:
(1)内容/普通变量存内容,地址/ 指针变量存地址
(2)C语言中,常量都被认做内容值,只能存在普通变量里面;
(3)指针变量专门用来存放地址,禁止将一个整型值直接赋给一个指针变量。
(4)地址变量得地址,得谁地址指向谁,有"*"为内容值,不是读就是写,放在赋值号的右边为读操作。
          做指针问题得时候一定要画图,一个内容变量就对应一个房子,具体问题具体分析
(5)使用内存的两种方法:内容变量,指针变量用*读
(6)*的前面只要有类型,它就是标志,若前没有类型,它实际即使个内容值
(7)所以的指针变量在内存中分配的字节数相同。sizeof();---------2字节

int *p1
float *p2;
double *p3;
sizeof(p1) = sizeof(p2) = sizeof(p3) = 2字节
/*
但是p1所指的房子内容容量是2字节大的
p2所指的房子内容容量是4字节大的
p3所指的房子内容容量是8字节大的
*/

指针中的经典练习题

#include<iostream>
#include<cstdio>

using namespace std;

void fun(int *x, int *y){
	printf("%d %d",*x, *y);
	*x = 3;
	*y = 4;
} 
int main(){
	
	int x = 1;
	int y = 2;
	fun(&y, &x);
	printf("%d %d ",x, y);

	return 0;
} 
//预期结果
2 1 4 3
#include<iostream>
#include<cstdio>

using namespace std;
void swap(int *p1, int *p2){
	int temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}//结束时形参都将被释放只要出现void就没有带返回值回来

int main(){
	
	int a;
	int b;
	int *p1 = &a, *p2 = &b;
	scanf("%d,%d",p1, p2);
	swap(p1,p2);
	printf("%d,%d",*p1, *p2);
	
	return 0;
} 
//输入:3,4
//预期结果
4,3
//指针的伟大:通过形参影响实参
#include<iostream>
#include<cstdio>

using namespace std;
void swap(int *p1, int *p2){
	int *temp;
	temp = p1;
	p1 = p2;
	p2 = temp;
}//结束时形参都将被释放

int main(){
	
	int a;
	int b;
	int *p1 = &a, *p2 = &b;
	scanf("%d,%d",p1, p2);
	swap(p1,p2);
	printf("%d,%d",*p1, *p2);
	
	return 0;
} 
//输入:3,4
//预期结果
3,4
#include<iostream>
#include<cstdio>

using namespace std;
void swap(int *p1, int *p2){
	int *temp;
	*temp = *p1;
	*p1 = *p2;
	*p2 = *temp;
}

int main(){

	int a;
	int b;
	int *p1 = &a, *p2 = &b;
	scanf("%d,%d",p1, p2);
	swap(p1,p2);
	printf("%d,%d",*p1, *p2);
	 
	return 0;
} 
//输入:3,4
//预期结果:
报错,出错了,系统死机,得不到运行结果
原因:指针变量定义了却没有初始化

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值