C语言 指针

指针:

           指针的重要性:

                表示一些复杂的数据结构

                快速的传递数据(请看程序di-16中的确定一个一维数组需要几个参数_3

              (一个数据发送到函数,用函数对这个数据进行处理,这时候用指针速度是非常快的)

                使函数返回一个以上的值

                能直接访问硬件

                能够方便的处理字符串(专题)

                是理解面向对象语言中引用的基础(讲Java的时候会讲)

 

                总计:指针是C语言的灵魂

         

          指针的定义

                   地址

                         内存单元的编号

                         从零开始的非负整数

                         范围:(CPU可以对内存条直接进行处理,越小的东西越快,大的东西成本低)

 

控制线:数据传递的方向由控制线控制的

 

数据线:数据线进行数据的传输(内存数据给CPU处理,写入内存条)

 

地址线:地址线要确定哪个单元,具体要对内存条哪个单元进行操作,由地址线控制的

                        

 

现在只考虑地址线

                   2^n线(每个单元就是8位,一个单元一条线)

                   字节:2^32 * 8

                   4G  32位操作系统最高支持4G内存的原因

 

 

          指针      (导致内存泄漏和野指针问题)

                    指针就是地址,地址就是指针

                    指针变量就是存放内存单元编号的变量,或者说指针变量就是存放指针的变量

                   

                     指针变量是重复地址的变量(指针变量才可以存放数据)

                     指针和指针变量是两个不同的概念

                    指针的本质就是一个操作受限的非负 整数

                 (地址不能相加,房子的门牌号相加有什么意义,

                    乘也不能,乘是加法的一种,编号不能相除)

                    指针可以相减(你的门牌号是5,他的门牌号是8,相减为3,你们隔着3个)

                    只可以相除(受限,因为非负整数可以加减乘除,而指针只能除

 

          指针的分类

          1、基本类型指针

# include<stdio.h>

 

int main(void)

{

//地址一般用十六进制表示

 

int *p; 

//   声明;p:指针变量,指针是个修饰词,本质上还是一个变量

(而是一个特殊变量,能够存放其他变量地址的变量)(普通变量只能存放一个值)

// int *p; 表示定义了一个名字叫做 *p的变量

// int *p; 应该这样理解: p是变量名,p变量的数据类型是 int *类型

 //   所谓 int * 类型   实际就是存放int变量地址的类型

 

 

int i = 3;

 

p = &i;  // i 和 p  是不同的变量,一个变量修改不会影响另一个变量

 

 

 

 

//      要背

    /*

    1p保存了i的之地,因此p指向i;(指向)

2、p不是i,i也不是p,更准确的说, 修改 p的值不影响i的值,修改i的值也不会影响p 的值

3、如果一个指针变量指向了某个普通变量,则

         *指针变量  就完全等同于   普通变量

 

    例子:

    如果p 是个指针变量,并且 p 存放了普通变量 i 的地址

   则 p 指向了普通变量;

   *p  就完全等同于  i

   或者说:  在所有出现*p的地方都可以替换成 i

   在所有出现 i 的地方都可以替换成 *p

 

  * p 就是以p的内容地址的变量

 

*/

printf("%d\n", &i);

 

printf("%d\n", p);

 

 

printf("%d\n", *p); //*p 就是 i

/*

 

    1703720

    1703720

    3

    Press any key to continue

 

*/

 

 

return 0;

}

 

 

# include<stdio.h>

 

int main(void)

{

int i = 5;

int *p;

int *q;  

  // *q 是垃圾值, *q 所代表的内存单元的控制权限并没有分配给本程序,

没有初始化(q 没有指向)        

 

p = &i;

//*q = p;   //error 语法编译会出错

 

// *q 是垃圾值  ,*q 是整型,p是int *型

 

//*q = *p;  error , *q没有初始化

 

    p = q; //q是垃圾值,q 赋值给 p,p 也变成垃圾值

 

printf("%d\n",*q);       

// q 的空间是属于本程序的,所以本程序可以读写 q 的内容,

但是如果 q 内部是垃圾值,则本程序不能读写 *p 的内容

 

//因为 *q 所代表的内存单元的控制权限并没有分配给本程序 ,q 没有指向

 

return 0;

}

 

 只需要写一个free(p); 

因为p,q,r都指向同一个动态内存地址空间,而一个空间只需释放一次

 

但是一个程序写的很大的时候,

好多指针都指向同一个动态内存地址空间,如果你一次都不free,会导致内存泄漏

 

 

# include<stdio.h>

 

void huhuan_1(int , int);

void huhuan_2(int *, int *); // 形参不重要 ,声明的主要目的是告诉别人字符串的含义是什么,代表的是它的返回值和形参的类型

void huhuan_3(int *, int *);

 

void huhuan_3(int *p, int *q)

{

int t;   //如果要互换*p和*q的值,则t 必须定义成int ,不能定义成int *,否则语法出错

 

 

t = *p;  // p 是int * , *p 是 int ,是以 p的内容为地址的值,p 表示a 的地址,*p《=》a

*p = *q;

*q = t;

}

 

int main(void)

{

int a = 3;             

// a, b地址一开始就已经定死的了

 

// 没有一门语言可以把静态变量的空间换地址

 

int b = 5;             //但是实参a,b 还存在,还是没有互换

int t;                 //只是改变了形参a,b的值,主函数的a,b没有改变

 

 

 

// a 和b 的指针互换了, a ,b 的值和哪个指针指向它无关, *p , *q 的指针互换了

 

 

 

 

huhuan_3(&a, &b);     //huhuan_2(*p,*q);    error,main函数没有定义 p , q   

 

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

 

 

return 0;

}

 

void huhuan_1(int a, int b)   //形参 a,b互换了,但是释放了,没有了

{

int t;

 

t = a;

a = b;

b = t;

}            //执行完后没有了a,b

 

 

 

//不能完成互换功能

void huhuan_2(int *p, int *q)

//虽然*p,*q各自接收了 a,b的地址,只可惜,只改变了 p , q的值 ,没改变*p , *q 的值 , q ,p 和a ,b 是不同的值,改变 q ,p ,不会影响 a,b 的值  

//(地址还是那个地址,只是 q,p的内容互换了,就是 p 和 q 的指向地址互换了)

 

 

// q , p 是 int *类型  ,a ,b 是int 类型  ,void 没有返回值

 

 

//  a, b, p, q 是不同的变量,互不影响

 

 

{

int *t;  //想要互换 p, q的值,要用 int * 类型

 

t = p;   // t 是指针变量,地址变量,门牌号变量

p = q;

q = t;  

 

//互换 p , q 地址, a , b 没变

    // 只要是变量就有地址

 

}

 

 

附注:

       *的含义

                1、乘法

                2、定义指针变量

                      int *p;

                      //定义了一个名字叫p 的变量, int *表示 p 只能存放int 变量的地址

 

                3、指针运算符

                            该运算符放在已经定义好的指针变量的前面

                            如果p 是一个已经定义好的指针变量

                            *p 表示 p的内容为地址的变量(p指向 &I ,则 *pi的地址的变量 = *p = i

 

 

# include <stdio.h>

 

void swap_3(int * p, int * q) //形参名字是p和q,接收实参数据的是p和q,而不是*p和*q

{

int t;

t = *p; *p = *q; *q = t;

} // 形参和实参永远不可能是同一个变量,而且形参变量的空间在函数调用完后一定没有了,被释放了

  //终止之后,*p 和 *q 的值已经没有了,但是 a ,b 的值已经被改写了,swap_3内部执行过程中* p 和 * q指向的变量互换了

 

 

int main(void)

{

int a = 3;

int b = 5;

 

swap_3(&a, &b);   // 如果要把a 发送给一个变量p,一定要先为这个变量p 分配空间,分配一个空闲的空间,所以p和a的地址空间一定不是同一个地方

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

 

return 0;

}

 

void swap_1(int i, int j)

{

int t;

 

t = i; i = j; j = t;

}

 

 

//使用发送地址

void swap_2(int * p, int * q) //形参名字是p和q,接收实参数据的是p和q,而不是*p和*q

{

int * t;

t = p; p = q; q = t;

}

 

 

如何通过被调函数改主调函数普通变量的值

          1、实参必须为该普通变量的地址

          2、形参必须为指针变量

          3、在被调函数中通过

                   * 形参名 = ……

               的方式就可以修改主调函数相关变量的值

 

 

 

          2、指针和数组(关系)(链表)

                指针和一维数组  (数组是连续的,下标,下标和指针联系在一起的)

                               一维 数组名a

                                       一维数组名是个指针常量

                                       a:它存放的是一维数组第一个元素的地址

 

例子:

int a[5];   //a 是数组名 ,5是数组元素的个数,元素就是变量  a[0] -- a[4]

//int a[3][4]; // 3行4列 ,a[0][0] 是第一个元素 a[i][j] 第i+1 行 j+1列

int b[5];

 

//        a = b;  // error , a是个常量 ,常量变了就不是常量了

 

printf("%#X\n", &a[0]);   //   十六进制输出   %#X  ,&a[0]  a[0]第一个元素的地址

printf("%#X\n ", a);      //  数组名a 是第一个元素的地址

 

/*

 

  输出结果:

  --------------------------------------------

      0X19FF1C

      0X19FF1C

      Press any key to continue

  --------------------------------------------

  结论:

  a   等同于   &a[0]

 

*/

 

                     

 

                              下标和指针的关系

                                       如果p 是个指针变量,则

                                       p     永远等价于  &p[0]

                                       p[i]  永远等价于 *(p+i)

 

# include<stdio.h>

 

void f(int *pArr, int len)

{

pArr[3] = 88;         //p[i]  永远等价于 *(p+i)  ,p[i] 是变量

}

 

int main(void)

{

int a[6] = {1, 2, 3, 4, 5, 6};

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

 

f(a, 6);  // f(a,10000);  下标越界

printf("%d\n",a[3]); //a[3] == *(a+3)

 

// 为什么a[3] 是第四个元素?  a[3] 等价于 *(a+3),

a+3 是第四个元素的地址 ,*(a+3)代表的是第四个元素,

所以 a[3] 是第四个元素

 

return 0;

}

 

 

 确定一个一维数组需要几个几个参数

 

【如果一个函数要处理一个一维数组,则需要接收该函数数组的哪些信息】

需要两个参数:

数组第一个元素的地址 a  (指针类型 int *)   a a[0]的地址  a == &a[0]

a = &a[2]; //error ,a 是个指针,存放a[0]地址,是个常量,不可能改变

数组的长度  len  (int 类型)

//此程序体现了指针如何快速的传输数据(通过发送变量地址解决)

# include<stdio.h>

 

void f(int *pArr, int len ) 

// 对 pArr[i]的操作 就相当于 在主函数中 对 b[i] 操作 ,一模一样的

 

// 就可以通过f 函数修改 主函数 int b[6] 六个变量的值,修改主函数所有变量!

//而且通过发送它的地址和长度,在 f 函数操作,

感觉就像在main 函数对 b 数组进行操作

// b数组发送给pArr, pArr数组就是b数组的一份拷贝(副本)

{

int i;

for(i=0; i<len; ++i)

{

printf("%d  ", pArr[i]); 

//  *(pArr + i)等价于 pArr[i]  也等价于 b[i]  也等价于 *(b + i)  下标转换成指针

}

printf("\n");

}

 

int main(void)

{

int b[6] = {-1, -2, -3, 4, 5, -6};

 

f(b, 6);

 

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值