【指针】【重点】
指针的重要性
表示一些复杂的数据结构
快速的传递数据
使函数返回一个以上的值
能直接访问硬件
能够方便的处理字符串
是理解面向对象语言中引用的基础
总结:指针是C语言的灵魂
指针的定义
地址
内存单元编号
从0开始的非负整数
范围:4G 【0----(4G-1)】
指针
指针就是地址,地址就是指针
地址就是内存单元的编号
指针变量是存放地址的变量
指针和指针变量是两个不同的概念
但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
指针的本质就是一个操作受限的非负整数
指针的分类
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----------------------------
---------------指针常见错误解析----------
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;
}
------------------------------------------------------------------------
经典指针程序_两个数字互换举例:
#include <stdio.h>
int main(void)
{
int a = 3;
int b = 5;
int t;
t = a;
a = b;
b = t;
printf("a=%d,b=%d\n",a,b);
return 0;
}
-------输出的结果为a=5,b=3 互换成功-----------------------------
#include <stdio.h>
void huhuan_1(int a, int b)
{
int t;
t = a;
a = b;
b = t; //没有返回值
return;
}
int main(void)
{
int a = 3;
int b = 5;
huhuan_1(a,b); //这里最终互换的是上面形参里面的a和b,而和实际的主函数里面的a和b的值没有关系
//所以最终互换失败,a依然是3,b也依然是5
printf("a=%d,b=%d\n",a,b);
return 0;
}
--------输出的结果还是a=3,b=5互换失败-------------------------------
#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 = 3;
int b = 5;
huhuan_2(&a,&b); //huhuan(*p,*q); 是错误的,huhuan(a,b); 也是错误的
printf("a=%d,b=%d\n",a,b);
return 0;
}
------------输出的结果还是a=3,b=5依然互换失败--------------------------------
#include <stdio.h>
void huhuan_3(int * p, int * q)
{
int t; //如果要互换*p和*q的值,则t必须定义成int不能定义成int*因为*p代表的是一个以p的内容为地址的变量
t = *p; //p是int *那么*p是int 是最终变量的值
*p = *q;
*q = t;
return;
}
int main(void)
{
int a = 3;
int b = 5;
huhuan_3(&a,&b);
printf("a=%d,b=%d\n",a,b);
return 0;
}
---------------------输出结果是a=5,b=3互换成功------------------------------------
如何通过被调函数修改主调函数的的值:
1.实参是普通变量的地址
2.形参必须为指针变量
3.在被调函数中通过
*形参名 = ......
的方式就可以修改主调函数相关变量的值
-------------------------------------------------------------------------
* 号的三种含义:
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 = 99;
printf("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.所以要实现上例总的值互换就需要借助指针的地址互换来实现。
------------------------------------------------------------------------