引子:数据在内存中是如何存储的,又是如何读取的?内存编号就是内存的地址(内存中每个字节都有一个编号,即地址)

1.概念:

地址:内部存储器的编号,称为地址。如变量a的位置编号,变量b的位置都是指针。

指针变量:专门存放地址的变量称为指针变量。

地址、指针、指针变量都称为指针。

一、变量的地址(指针)和指向变量的地址变量(指针)

概念:

变量的指针: 就是变量的地址。

指针变量: 是用来存放地址的变量,普通变量是用来存放数据的,指针变量是存放地址的。

2.定义地址变量:

1) 格式:[ 存储类型 ]  数据类型   *指针变量名;

int i, j;

int * pointer1, *pointer2;

存储类型是这个地址变量的存储位置

数据类型指的是这个地址变量指向的目标变量类型,不代表本身的类型大小。

2) 指针变量的赋值:

方法一:

int  a = 5;  int *p = &a; //定义并初始化。

方法二:

int  a = 5; int *p;  p = &a;//  先定义后赋值。

PS:

定义时,int *p中*是为了说明该p是地址变量,用来存放地址;

定义地址变量时必须指定数据类型,不同类型指针不可相互赋值;

指针变量的数据类型不表示变量的类型,是表示该变量指向的目标的数据类型,访问内存时读取的内存空间大小。

3.指针变量引用

*和&符号

* 定义指针变量/取地址对应的变量的内容(间接访问);//i = 3直接,*p=3间接

& 取变量的地址。

*和&互为逆运算。自右向左

3) 引用

Ø 对指针变量赋值

p = &a;

Ø 引用地址变量指向的内容

printf( “a=%d\n”,  *p );

Ø 引用变量本身的内容(即存储的地址)

printf(“%x\n”,  p);

eg:

int i = 188; int *p = &i;

p  指针变量,内部存放的是目标的地址;

*p 目标,目标内存数据;

&p 指针变量的内存地址;

p = &i = &(*p)

i = *p = *(&i)

4.指针运算

指针运算就是地址运算,即指针变量中的地址作为运算量。

地址只能做算术、关系、赋值运算。

算术运算

px + n 代表指针向地址大的方向移动 n 个 数据。

移动后的地址量是: (px) + sizeof(px的类型) * n

px++  指针变量向地址大的方向移动一个数据。

px - py 表示两个相同类型指针间相差数据的个数,而不是一个地址量。

px - py的结果是  (px - py) /sizeof(数据类型)

px + py 的结果? 没有任何意义

指针加发运算加的数值是增加地址本身类型的N倍大小(这在数组中访问经常用到)

4) 关系运算

指针关系表示两个指针在内存位置的高低关系。

不同数据区域间的指针,关系运算没有意义。

指针和除0外的整数比较没有意义,和0比较可以判定指针是否为空。(标准写法为if (NULL == p) ).

5) 赋值运算

向指针变量传递一个地址值。这个值是地址常量或指针变量(同类型),不能是普通整数(0可以表示空值)。

6) const/void指针

const 表示的使变量常量化,即不可修改。

int  const a = 9;

a = 10; //报错,a为const修饰不可改变。

const 在遇到指针时会发生一些变化

const int a 与 int const a, const可以在int的左右位置。

int a = 9;

int b = 12;

const  int  *p = &a; // const 修饰的是*p ,  pa 指向变量a, int  const  *p     = &a;  //和上面效果相同, 都表示地址变量pa指向a,且*pa不可变

*p  = 10 ; // 通过p改变a的值,但*p是const类型,不可改变。

p = &b; //可以改变p的值,(即指向)。

*p =  11;// 同样不可以

int *const q = &a;  //const 修饰的是 q, 所以q是不能改变的,即不能改变q的指向

*q = 111;

q = &b; // 将q指向b,报错。

void 型指针

指针变量指向不确定数据类型的变量的时候,可以定义为void型指针,

因为void类型指针可以赋值给其他任意类型的指针,而其他类型不能相互赋值.

如:malloc函数

void * malloc(size_t size);

malloc 函数因为不知道分配空间的具体用途,所以返回void型地址。

7) 多级指针

指向地址变量的地址变量,称为多级指针(画图表示);

定义一个二级指针

int *p = &a;

int **q = &p;

8) 小结:指针自增与自减

ü p++(或 p+=1): 使p指向下一个元素

ü *p++:  ++与* 具有相同优先级且结合方向自右向左, 等价于*(p++), 先取*p的值,然后p再自加,指向下一个元素。

ü *(p++) 与 *(++p) 作用不同。 前者是先取*p的值,再使p自加。后者先使p自加,再取自加后指向的内容。

ü ++(*p): 表示将p指向的元素的值加1.

二、指针与数组

指针与一维数组

数组的指针是指数组在内存中的起始地址,即第一个数组元素的地址.

一维数组的数组名代表一维数组的指针(起始地址)

[ ] 又叫做变址运算符

a[i] <=> *(a+i) 在计算机内部实现的时候,数组下标都会转化为地址。

若地址变量px的地址值等于数组指针x(指针变量px指向数组的首地址),则:

x[i]、*(px+i)、 *(x+i) 和px[i]具有相同功能的功能:访问数组第i+1个数组元素。

数组元素访问过程中,数组地址与指针变量具有相同的访问效果

不同: 地址变量是变量。

数组地址(数组名)是常量,不能自加或自减

地址变量与数组的赋值

1.  int  *p = &a[0];

2.  int *p;

p = &a[0];

3.  int *p = a;

小结:

1. p+i和a + i就是a[i]的地址, 指向a数组的第i个元素。

2. *(p+i) 或*(a+i) 是取a[i]元素的值。

3. 指向数组的地址变量也可以带下标 p[i]和*(p+i)和*(a+i) 等效。

9) 指针与数组常见操作

数组

指针表示

含义

array

&array[0]

数组名是第一个元素的地址

*array

array[0]

数组的第一个元素

array + i

&array[i]

数组第i个元素的地址

*(array + i )

*(&array[i]) == array[i]

数组第i个元素

*array + m

array[0] + m

数组第一个元素加m

*array++

error

error

经典例子:

一维字符 指针数组ps[5] 里面存放着字符串首地址

char  *ps[5] = {“beijing city”,  “New York”, “London”, “Paris city”, “Moscow city”};

定义一个指针变量,并指向数组首地址;

char **pps = ps; 那么ps指向 指针数组的首地址

5.指针与二维数组

定义一个二维数组a,有3行4列

int a[3][4] = { {1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23} };

a 是数组名, a数组包含3行,即3个元素,分别是a[0]、a[1]、a[2]。每个元素同样是一个一维数组,包含4个元素,a[0][0], a[0][1], a[0][2], a[0][3].

a[0] 是一维数组名,代表的是第一行的第一个元素的首地址,a[0] = &a[0][0].

a[1] 是一维数组名, 代表的是第二行的第一个元素的首地址, a[1] = &a[1][0].

a[0][0] 是一个元素

&a[0][0] ==> a[0] 取一维数组的首地址,即一维数组名a[0], (a[0] 为第一行首地址)

&a[0] ==> a 取a[0], a[1], a[2[ 三个元素中的首地址,即数组名a.

&a == &a 取数组a的地址,即取数组的位置。

a 指向? (指向第0行首地址) *(a+0) == a[0];

a+1 指向? (指向第一行的首地址) *(a+1) == a[1];

*(a+0) + 1 是a[0][1]的地址, *(*(a+0) +1) ==  a[0][1]

*(a+1) + 1 是a[1][1]的地址, *(*(a+1) + 1) == a[1][1] == *(a[1] + 1)

PS:

对数组名取值就得到数组元素;

对数组元素取址就得到当前数组地址;

a[0][0] =(&a[0][0]) => a[0], 对第0行首元素取地址得到地址;

a[0]  =(&a[0]) => a, 对3个元素的第0个元素取地址得到数组名。

a  =(&a) =>  &a, 对数组名取地址,得到数组地址。

思考:

&a +1 指向?  (指向下一个数组,增加一整个数组);

a+1  指向?   (指向下一行首地址, *(a+1) == a[1] );

*(a+1) + 1 指向? (指向a[1][1], *(*(a+1) + 1) == a[1][1] );

注意点:

a + 1 表示的是第一行首地址,  a表示是行数组的地址变化。

a[1]  表示的是第一行0列的地址 &a[1][0], a[1] = * (a + 1); a表示的是当前行的首地址变化

二维数组的行地址、列地址

行地址:二维数组名是个特殊的地址,参与运算时以行为单位移动,因此被称为是行地址。如int a[2][3],  a代表的是第一行的首地址,a + 1代表第二行的首地址。

列数组:int *p = a;

printf(“%d”, *(p+i) );p+i 相当于移动了i列, 因此指针p为列指针。

综上:

a+1与a[0] + 1表示的地址值是相同的, 但含义是不同的, a+1 是序号为第一行的首地址, a[1] 或 *(a+1)或 *a[1] + 1指向1行0列元素。

二维数组名是指向行的,在行指针前面加上*, 就可以转换为指向列的指针。

eg:

a和a+1是指向行的地址;

*a和*(a+1)是指向列的指针。

反之,在列指针前面加上&,就可以转换为行指针。

eg:

a[0] 指向0行0列的列元素指针, &a[0] 与*(a + 0)

int  main()

{

int   a []={5,8,7,6,2,7,3};

int y,*p=&a[1];

y=(*--p)++;

printf(“%d  ”,y);  // 5

printf(“%d”,a[0]);  //6

}

10) 指向元素的指针变量和指向数组的指针变量(数组指针)比较

ü 指向数组元素的指针变量定义和普通指针变量相同

ü 指向由m个元素组成的一维数组的指针变量:

对数组指针赋值:a为二维数组名:

p = a; 或 p = &a[0];

int (*p)[4],4表示一维数组长度,且*p两端的括号不可省略, 方括号的优先级高, int *p [4], p会先和[ ] 组成数组 p[4], 然后再和*组成 指针数组(地址数组);

p指向的是行的起始地址, *p 相当于p[0]。

6.字符串与字符指针

C中没有字符串数据类型。通常借助字符数组来存储字符串。

字符指针可以存储字符串的起始地址,即指针会指向字符串第一个字符。

字符串指针初始化,可直接把内存中字符串的首地址赋予指针变量。

eg:

char *s = “Welcom!”; //相当于const char *s = “welcome!”。字符串常量是存放在常量区的,不可修改。

7.指针数组

指针数组即这个数组元素里存放的是地址(相同存储类型和数据类型的地址集合)

说明形式:

<存储类型>  <数据类型>  *<指针变量数组名> [<长度>]

int *p[2];

char  *c[9];

11) 指针数组名含义:

表示这个数组的存储首地址,即指针数组名为数组的地址。(和常规的数组相同,不同的是这个数组存储地址)。

指针数组中的元素就是地址,要取得元素对应的值,直接对数组元素取值即可,或对数组名**(p+i)取值。

指针数组元素就是地址,数组名代表数组的起始地址, 数组名是地址的地址,即二级指针。

指针数组与数组指针的比较:

数组指针:

int (*p)[n] , p是一个指针,指向整个一维数组,这个一维数组长度为n, p+1时,p增加整个数据的长度。

数组指针也称为指向一维数组的指针, 或叫行指针。

指针数组:

int *p[n],  p是一个数组,这个数组元素是地址,p+1,指向下一个数组元素。

p = a属于赋值错误,因为p是常量。只能对元素赋值a[0] = a, *p = a。

三、函数

函数定义、声明和调用

定义:

形式:

<数据类型>  <函数名称> (<形式参数说明>)

{

语句集;

return (<表达式>)

}

无参函数。 有参函数,空函数

参数类型:变量,指针,数组名,数组指针,函数指针。。。。

声明:函数在声明中可以省略形参,但这样编译器就不能检查实参和形参是否匹配, 所以不建议省略。

8.函数传参与返回值

Ø 函数未调用时,形参没有分配空间。调用时,系统为形参分配空间。调用结束后,所占内存单元被释放。

Ø 实参可以是常量、变量或表达式。max(a, a+b);

Ø 函数定义时要指定数据类型。

Ø 实参与形参的类型相同或兼容。

Ø C语言中,实参向形参传递参数是单向的,只能由实参传递给形参,反之不可以,在内存中,实参和形参占用的是不同的内存单元。

值传递与址传递

值传递

(分析值传递过程,值传递过程相当于隐含动作int a = x, int  b = y)

址传递

典型strcpy实现

char * strcpy(char *dest, const char *source)

{

asssert(dest != NULL && source != NULL);

char * r = dest;

while ((*dest++ = *source++) != ‘\0’)

;

return dest;

}

12) 返回值

ü 函数的返回值由return语句返回,如:return  z; return (z); return (表达式)三种形式都可以,括号可以省略,保持简洁。

ü 函数值在类型不指定时,系统按整型处理。

ü 函数定义类型和返回类型保持一致。

ü void 定义的函数表示空类型;

9.函数调用

调用形式

Ø 函数语句:printf(“”);

Ø 函数表达式: a = read(buff,  fd, n);

Ø 函数参数: m = max(a, max(a, b));

Ø 函数调用函数的几点说明:

被调用函数必须是存在的库函数或自定义函数;如果使用库函数,要在文件开头进行#include <> 进行头文件包含,对函数进行声明。

调用自定义函数时,被调用函数应该在调用函数前面,在主函数中进行被调用函数的声明。

声明可以函数声明可以在主函数内部或在主函数前面,也可以在主函数前面进行函数定义。函数类型省略的情况下,系统默认是int型.(思考:两个函数相互调用怎么解决先后问题?)

指针、数组、函数阶段小结相关推荐

  1. C/C++ 一段代码区分数组指针|指针数组|函数指针|函数指针数组

    1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<windows.h> 4 /* 举列子说明什么是函数指针 * ...

  2. 指针 数组指针 指针数组 函数指针等说明。

    指针的概念 原文地址:http://www.cnblogs.com/ggjucheng/archive/2011/12/13/2286391.html 指针是一个特殊的变量,它里面存储的数值被解释成为 ...

  3. 面试准备每日五题:C++(六)——CC++、staticconstextern、sizeof strlen、指针引用、数组指针指针数组函数指针

    文章目录 一.C和C++的区别是什么? 二.关键字static.const.extern作用 三.sizeof和strlen的区别 四.指针和引用的区别 五 .指针数组.数组指针.函数指针 一.C和C ...

  4. php常用操作数组函数,PHP常见数组函数用法小结

    本文实例讲述了PHP常见数组函数用法.分享给大家供大家参考,具体如下: 1.array array_merge(array $array1 [, array  $array2 [, $array]]) ...

  5. php数组实例,php常用数组函数实例小结

    本文实例总结了php常用数组函数.分享给大家供大家参考,具体如下: 1. array array_merge(array $array1 [, array $array2 [, $array]]) 函 ...

  6. C语言基础10——指针进阶。字符指针、指针数组、数组指针、函数指针、函数指针数组、回调函数、数组名详解、杨氏矩阵、字符串旋转

    目录 字符指针 指针数组 数组指针 数组传参.指针参数 函数指针 函数指针数组 指向函数指针数组的指针 回调函数 练习 数组名的意义 指针笔试题 字符指针 字符指针的另一种使用方式 #include ...

  7. 函数指针和函数指针数组及其应用

    1. 函数指针 先来看一个简单的例子: int a=3; void *p=&a; 这是一个基础的不能再基础的例子.相信学过指针的都能看得懂.P是一个指针,指向a.a 是一个整形变量.函数指针和 ...

  8. 指针数组 数组指针 函数指针 指针函数

    目录 数组指针 指针数组 函数指针 指针函数 数组指针 数组指针的本质: 它是一个指针,指向的是一个数组 数组指针定义: int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一 ...

  9. C语言程序设计 | 指针的进阶(一):字符指针、数组指针、指针数组、函数指针

    指针的进阶(一)目录: 字符指针 数组指针和指针数组 函数指针 字符指针 在开始讲解这一章节之前,我们需要了解指针前面声明的类型的意义 类型 * 指针名 对于指针来说,我们在给指针进行声明时,我们声明 ...

最新文章

  1. dictionary changed size during iteration
  2. 键盘 Input子系统
  3. 停止linux下正在执行的ping命令
  4. centos 安装PHP7并且与其他版本共存并且为PHP7安装redis扩展
  5. [leetcode]19.删除链表的倒数第N个节点
  6. OpenMap教程3 –使用MapHandler构建基本的地图应用程序–第2部分
  7. SerializeUtil 序列化 java
  8. 传智php入学测试题,传智播客PHP 0912 基础班 入学测试题
  9. 最短路径VS最小生成树
  10. Unity 官方教程 学习
  11. 高德地图拾取经纬度 + 搜索 + 标记
  12. 注册99美元苹果开发帐号
  13. 美国这100年来一共发生了多少次金融危机
  14. 元素JavaScript知识点梳理与经典百例
  15. 在运行java程序时出现 Error: could not open c:\program Files\Java\jre6\lib\amd64\jvm.cfg'
  16. jar包运行报错jar中没有主清单属性、springGateway访问接口报错302,跳转login接口
  17. STM32F103C8T6的TIM1的CH1、CH2、CH3三路互补PWM实现四路PWM两两输出
  18. SAP权限管理的基本概念
  19. 吴晓华当选河北省衡水市市长
  20. 【js】onchange事件不被触发

热门文章

  1. 关于ssh的一些问题
  2. SharePoint【调试,诊错系列】-- 开发环境中不同调试对象的Attach方式
  3. mysql 命令行导入sql脚本
  4. c51语言的设计步骤,第3章节单片机c51语言程序的设计基本.ppt
  5. C语言入门题-求阶乘序列前N项和
  6. 底层实现红黑树_stl map底层之红黑树插入步骤详解与代码实现 | 学步园
  7. linux怎么取消文件隐藏命令,Linux基础命令:显示隐藏的文件
  8. mysql数据库复制基本原理_MySQL的复制原理以及流程
  9. Debian下Cannot set LC_CTYPE to default locale: No such file or directory解决方法
  10. html+css+javascript之间的关系与作用