基础4.1:指针入门

1. 指针是什么?

【指针】【重点】
1.1 指针的重要性
	表示一些复杂的数据结构
	快速的传递数据
	使函数返回一个以上的值
	能直接访问硬件
	能够方便的处理字符串
	是理解面向对象语言中引用的基础
 总结:指针是C语言的灵魂

1.2 指针的定义
	地址
		内存单元编号
		从0开始的非负整数
		范围:4G 【0----(4G-1)】
	指针
		指针就是地址,地址就是指针
		地址就是内存单元的编号
		指针变量是存放地址的变量
		指针和指针变量是两个不同的概念
		但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
		指针的本质就是一个操作受限的非负整数
  • 取地址
    我们可以在变量名前加上&,表示获取变量的地址。

完整代码见001_address.c
指针代表的是地址;
如果输入的是地址,则相当于&变量,得到对应的值;

#include <stdio.h>

int main(){
	int m=0;
	int n=0;
	printf("&m=%p\n",&m);
	printf("&n=%p\n",&n);

	int* p;
	scanf("%p",&p);
	scanf("%d",p);

	printf("m=%d\n",m);
	printf("n=%d\n",n);

}

在这里插入图片描述
变量地址通常使用16进制表示,使用%p或者%P打印地址。

试一试,多次执行上面的代码编译的可执行文件。

scanf()实参前面的&符号

1.3 指针的分类

	>1.3.1 基本类型指针【重点】

		For example 1:
			#include <stdio.h>
			int main(void)
			{
				int * P;  //p是变量的名字,int * 表示p变量存放的是int类型变量的地址,即int * 只能存放整型变量的地址
				int i = 3;
				p = &i; //OK,&是取地址符
			/*	p = i    error, 因为类型不一致,p只能存放int类型的变量的地址,不能存放int类型变量的值 */
			/*	p = 55;	error,原因同上 */
				return 0;
			}

For example 2:

			#include <stdio.h>
			int main(void)
			{
				int * P;  /*
							p是变量的名字,int * 表示p变量存放的是int类型变量的地址,即int * 只能存放整型变量的地址
							int * p;不表示定义了一个名字叫做*p的变量,而应该是p是变量名,p变量的数据类型是int *整型变量指针
							相当于这也是一个声明,声明了 int * 是它的数据类型,而后面的p才是它的变量名字
							所谓int *;类型--->实际就是存放int变量地址的类型
							int *限制了 后面的p只能存放整型变量的地址
						  */
				int i = 3;
				int j;
				p = &i;   /*
							1.p保存了i的地址,因此说p指向i
							2.p不是i,i也不是P,更准确的说:修改p的值不影响i的值,修改i的值也不影响p的值
							3.如果一个指针变量指向了某个普通变量,则:
								*指针变量  就完全等同于  普通变量
							例子:
								如果p是个指针变量,并且p存放了普通变量i的地址
								则p指向了普通变量i
								*p 就完全等同于 i
								或者说:在所有出现*p的地方都可以替换成i
										在所有出现i的地方都可以替换成*p 
								*p 就是以p的内容为地址的 变量
						  */
						 		 
				j = *p; //等价于 j=i;
				printf("i=%d,j=%d\n",i,j);
				return 0;
			}
		-------------输出结果为i=3,j=3----------------------------

1.3.2 指针常见错误解析-
For example 1:

			#include <stdio.h>
			int main(void)
			{
				int * p;
				int i=5;
 
				*P = i;   /*error,解析:
									因为p开始的时候并没有经过初始化,所以p里面肯定是一个垃圾值,而*P就代表这个垃圾值的地址
									且并不知道这个垃圾值是多少,而因为将i赋值给*P,就相当于强行将*p里面垃圾值的地址给
									替换成了i的地址。试想,如果有一个软件正在计算机内运行,且占用的内存恰好就为这个垃圾值的地址
									那么一旦这样进行替换,这个运行的软件就会崩溃。
						  */
				printf("%d\n",*P);
 
				return 0;
			}

-------这样写的话,整个程序就会出错而也没有输出结果----------

For example 2:

			#include <stdio.h>
			int main(void)
			{
				int i= 5;
				int * p;
				int * q;
 
				p = &i;
			/*	*q = p;   error, *q 和 p的类型不一致,程序会报错  因为*q代表一个变量,而p只代表一个地址 */
			/*	*q = *p;  error,  *q 是未知的地址 不能更改未知的地址,相当于上面的例题 */
			/*	p = q;    error, q是垃圾值,q赋给P, p也变成了垃圾值  */
				printf("%d\n",*q);    
									/*  q的空间是属于本程序的,所以本程序可以读写q的内容,但是如果q内部是垃圾值,
										则本程序不能读写*q的内容,因为*q所代表的内存单元的控制权限并没有分配给本程序。
										所以本程序运行到 printf行时就会立即出错。    
									*/
				return 0;
			}

2. 指针怎么用?

2.1 定义指针

定义指针与定义变量的方式一样,只不过要在类型和变量名中间加上一个*星号。

类型* 指针变量;

指针变量只能使用同类型变量的地址赋值。

方式1;定义+初始化;

  int n = 10;
    int* p;
    p = &n;
    printf("&n=%p\n",p);
    printf("n=%d\n",n);

方式2:可以直接初始化。

 int n = 10;
    int* p = &n;
    printf("&n=%p\n",p);
    printf("n=%d\n",n);

变量必须赋值后才能使用,指针也是必须赋值后才能使用。

2.2 解引用

指针的强大之处可以直接操作储存地址里面的数据。这种操作称为解引用。使用时需要在指针前加上*星号。

注意:这里的*与声明指针的含义不一样,与乘号也不一样。

完整代码见002_jieyinyong.c

#include <stdio.h>

int main(){
int n = 10;
int* p = &n;
printf("n=%d\n",n);
printf("*p=%d\n",*p);
n = 100;
printf("n=%d\n",n);
printf("*p=%d\n",*p);
*p = 1000;
printf("n=%d\n",n);
printf("*p=%d\n",*p);

}

在这里对*p操作就是对n操作;对n操作就是对*p操作。

在这里插入图片描述
在这里插入图片描述

访问变量两种方式:一是通过变量名直接访问,而是指针解引用访问。


					* 号的三种含义:
						1.乘法
						2.定义指针变量
							int * p; //定义了一个名字叫p的变量,int *表示p只能存放int类型变量的地址
						3.指针运算符
							该运算符放在已经定义好的指针变量的前面
							如果p是一个已经定义好的指针变量
							则 *p表示 以p的内容为地址的变量

one:

			#include <stdio.h>
			int main(void)
			{
				int * p;   //等价于int *p; 也等价于int* p;
				int i = 5;
				char ch = 'A';    //如果要表示单个字符,c语言要求用单引号括起来,如果要表示字符串,用双引号括起来
				p = &i;   //*p 以p的内容为地址的变量
				*p = 99printf("i=%d,*p=%d\n",i,*p);
				//p = ch;  //error
				//p = 5;   //error
				//p = &ch; //error
				return 0;
			}
		----输出结果为i=99,*P=99(因为p=&i,所以当把99赋值给*P的时候,相当于也把99赋值给了i,因为i的地址是不变的)---------

		two:检测实参和形参是否是同一个变量
			#include <stdio.h>
			void f(int x)
			{
				x = 99;     //局部变量只能在定义的函数内使用
			}
			int main(void)
			{
				int i = 6;    //局部变量
				printf("i = &d\n",i);
				f(i)                   //实参和形参一定不是一个变量,跟实参和形参的名字没有关系,不会因为都是i就是一个变量
				printf("i = %d\n",i)
				return 0;
			}
		----输出的结果为i = 6, i = 6(证明f(i)的括号里面的i跟上面的x = 99;没有任何关系)-----------
			#include <stdio.h>
			void swap(int i, int j)
			{
				int t;
				t = i; i = j; j = t;
			}
			int main(void)
			{
				int a = 3;
				int b = 5;
 
				swap(a,b);   //调用swap函数
				printf("a = %d,b = %d\n", a, b);
 
				return 0;
			}
		同样的即便是使用带返回值的函数,也是一样的结果,a=3,b=5
			#include <stdio.h>
			int swap(int i,int j)
			{
			        int t;
			        t = i;
			        i = j;
			        j = t;
			        return 0;
			}
			int main()
			{
			        int a = 3;
			        int b = 5;
			        swap(a,b);
			        printf("a = %d,b = %d \n",a,b);
			        return 0;
			}
		-----------------------------------------------
		借助指针的地址换换来实现值的互换:
		---------------------------------
			#include <stdio.h>
			void swap(int *i,int *j)
			{
			        int t;
			        t = *i;
			        *i = *j;
			        *j = t;
			}
			int main()
			{
			        int a = 3;
			        int b = 5;
			        swap(&a,&b);
			        printf("a = %d,b = %d \n",a,b);\
			        return 0;
			}
		--------------------这样结果就会成功互换a=5,b=3---------------------------

调用函数的执行过程:
			1.当程序在执行调用swap函数的时候,会立马给形参i和j分配内存空间以便用来存储由实参传递过来的a和b的值
			2.在swap(int i, int j)接收到实参传递的过来的值以后,将3传递给i,将5传递给j,然后执行{}内的调换运算,
			  当执行完成之后,i和j的值已经互换了,即i=5和j=3,这时候会立刻释放之前分配给形参i和j的内存空间,然后
			  程序会继续向下执行到printf("a = %d,b = %d\n", a, b);
			3.这个时候因为printf输出的是a和b的值,可是因为a和b的值并没有因为调用swap函数而发生变化,所以依然维持原值输出。
			  即i = 3, b =5
			4.swap函数的内部只是执行了形参内的变量对调,并没有影响到实参。
			5.所以要实现上例总的值互换就需要借助指针的地址互换来实现。
                 ---------------------------------------------     
  • 试一试
int n = 10;
printf("*(&n) = %d\n",*(&n));  //10

3. 指针与函数

3.1 值传递

值传递:地址并未发生改变,所以未发生调用;
试分析下面代码执行结果。

完整代码见00301——value_pass.c

   int func(int n){
        n = 100;
    }
    
    int main(){
       int n = 10;
       func(n);
       printf("n = %d\n",n);
    }
10

3.2 指针/地址传递

指针传递:地址发生改变,所以发生调用;
试分析下面代码执行结果。
完整代码见00302——address_pass.c

#include <stdio.h>
int func(int* n){
    *n = 100;
}

int main(){
   int n = 10;
   func(&n);
   printf("n = %d\n",n);
}

100

函数内部改变函数外部定义的局部变量必须满足两个条件:

  1. 指针参数
  2. 解引用

3.3 如何通过被调函数修改主调函数的的值:

			1.实参是普通变量的地址	
			2.形参必须为指针变量
			3.在被调函数中通过
					*形参名 = ......
					的方式就可以修改主调函数相关变量的值

三种表达:

变量类型实参形参
int n&nint*
int* ppint*
int* p&pint**

4. 实践

  1. 实现函数swap()交换两个变量的值。

方案1:值传递
完整代码见00401——value_swap.c

#include <stdio.h>
void Swap(int m, int n){
	int t = m;
	m = n;
	n = t;

}
int main(){
	int n = 10;
	int m = 100;
	Swap(n,m);
	printf("n:%d  n:%d", n,m);
	cout << "n:" << n << "\tm:" << m << endl;
}

交换不了,n =10; m=100;

方案2:指针传递

#include <stdio.h>
			void huhuan_2(int * p, int * q)
			{
				int * t;  //如果要互换p和q的值,则t必须是 int * 不能是 int 否则会出错
 
				t = p;
				p = q;
				q = t;
				return;
				        /*最终更换的是a和b的地址编号,但实际的值并没有改变 
				          形参的改变不会影响实参*/
			}
			int main(void)
			{
				int a = 10;
				int b = 100;
				huhuan_2(&a,&b);     //huhuan(*p,*q); 是错误的,huhuan(a,b); 也是错误的
				printf("a=%d,b=%d\n",a,b);
				return 0;
			}
		------------输出的结果还是a=10,b=100依然互换失败--------------------------------

方案3:指针传递
完整代码见00402——address_swap.c

#include <stdio.h>


void Swap(int *m, int *n){
	int t = *m;  //t表示变量,而非指针;
	*m = *n;
	*n = t;

}
int main(){
	int n = 10;
	int m = 100;
	Swap(&n,&m);
	printf("n:%d  n:%d", n,m);
}

可以交换,n =100; m=10;

  1. 实现函数divmod()输入参数ab,同时获取ab的商和余数。

多个返回值:采用指针返回;
完整代码见005——divmod.c

#include <stdio.h>
int Divide(int n , int m, int* mod){
	int div = n/m;
	*mod = n%m;
	return div;
}  
 int main(){
 int m = 101;
 int n = 10;
 int mod;
int div =  Divide(m,n,&mod);
 cout << "div:" << div << "\tmod:" << mod << endl;
 
 }

div:10 mod: 1;

指针在函数中有这两种应用,一种是即作为输入又作为输出;另一种只作为输出。

5. 练习

一个函数最多一个返回值

  1. 实现函数triangle()输入参数abc,返回能否构成三角形,如果能够构成三角形从参数获得周长和面积。
#include<iostream>
#include<cmath>
using namespace std;

bool isTriangle(int a, int b, int c) {
    // 判断三条边长度是否能构成三角形
    if (a + b > c && a + c > b && b + c > a)
        return true;
    else
        return false;
}

double getArea(int a, int b, int c) {
    if (isTriangle(a, b, c)) {
        double p = (a + b + c) / 2.0; // 半周长
        return sqrt(p * (p - a) * (p - b) * (p - c)); // 海伦公式
    }
    else
        return -1; // 表示不能构成三角形
}

int main() {
    int a = 3, b = 4, c = 5;
    if (isTriangle(a, b, c)) {
        cout << "Perimeter: " << a + b + c << endl;
        cout << "Area: " << getArea(a, b, c) << endl;
    } else {
        cout << "These sides cannot form a triangle." << endl;
    }
    return 0;
}
  1. 实现函数circle()输入参数r,从参数获得周长和面积。
#include<iostream>
#include<cmath>
using namespace std;

double getPerimeter(double r) {
    return 2 * M_PI * r;
}

double getArea(double r) {
    return M_PI * r * r;
}

int main() {
    double r = 5;
    cout << "Perimeter: " << getPerimeter(r) << endl;
    cout << "Area: " << getArea(r) << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值