目录 |
正确而灵活地运用指针,可以有效地表示复杂的数据结构;能动态分配内存;方便地使用字符串;有效而方便地使用数组;在调用函数时能获得1个以上的结果;能直接处理内存单元地址等。 | ||||||||||||||||||||
10.2变量的指针和指向变量的指针变量10.2.1定义指针变量如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。 在程序中一般是通过变量名来对内存单元进行存取操作的。其实程序经过编译以后已经将变量名转换为变量的地址,对变量值得存取都是通过地址进行的。 | ||||||||||||||||||||
10.2.2指针变量的引用*&a 按自右而左方向结合,先进行&a运算,得到a的地址,再进行*运算,即&a所指向的变量,也就是变量a。 (*p)++ 括号是必须的,因为++和*为同一优先级别,而结合方向为自右而左。 10.2.3指针变量作为函数参数eg10.3 | ||||||||||||||||||||
10.3数组与指针10.3.2通过指针引用数组元素eg10.5 eg10.6
引用数组元素可以用下标法(eg. a[3]),也可以用指针法,即通过指向数组元素的指针找到所需的元素。使用指针法能使目标程序质量高(占内存少,运行速度快)。
(1) int a[10]; int *p;
p = &a[0]; <=> p = a;
(2) int a[10]; int *p = &a[0]; <=> int *p = a;
(3)指向数组的指针变量也可以带下标,eg. p[i] <=> *(p+i) p[i] <=> *(p+i) <=> a[i] <=> *(a+i)
引用一个数组元素,可以用: (1)下标法,eg. a[i] (2)指针法,eg. *(p+i) 或 *(a+i)。其中,a是数组名,p是指向数组元素的指针变量,其初值p=a。
指针变量的运算 p指向数组a的首元素(即p=a) (1)*p++ 由于++和*同优先级,结合方向为自右向左,因此 *p++ <=> *p(++) (2)*p++ <=> *p(++) 先得到p所指向的变量的值(即*p),然后再使p+1=>p *(++p) 先使p+1,再取*p 若p初值为a(即&a[0]),则*p(++)为a[0] *(++p)为a[1] (3)++(*p) 表示p所指向的元素值加1 若p初值为a(即&a[0]),则++(*p)相当于++a[0]。若a[0]=3,则在执行++(*p)后,a[0]的值为4. 元素值a[0]加1,而不是指针p的值加1.
10.3.3用数组名作函数的参数eg10.7 eg10.8
fun(int arr[], int n) <=> fun(int *arr, int n) 实参数组名代表该数组首元素的地址,而形参是用来接收从实参传递过来的数组首元素地址的。因此,形参应该是一个指针变量(只有指针变量才能存放地址)。实际上,C编译都是将形参数组名作为指针变量来处理的。 在该函数被调用时,系统会建立一个指针变量arr,用来存放从主调函数传递过来的实参数组首元素的地址。
有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
10.3.4多维数组与指针eg10.11 eg10.12 eg10.13 eg10.14
int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
(1)a是一个数组名。 (2)a数组包含3行,即3个元素:a[0] a[1] a[2]。而每一个元素又是一个一维数组,包含4个元素。eg. a[0]所代表的一维数组又包含4个元素:a[0][0] a[0][1] a[0][2] a[0][3],可以认为二维数组是“数组的数组”,即二维数组a是由3个一维数组所组成的。 (3)a代表二维数组首元素的地址,现在的首元素不是一个简单的整型元素,而是由4个整型元素所组成的一维数组,因此a代表的是首行(即第0行)的首地址。a+1代表第1行的首地址。 (4)a[0] a[1] a[2] 既然是一维数组名,而c语言又规定数组名代表数组首元素地址,因此a[0]代表一维数组a[0]中第0列元素的地址,即&a[0][0]。a[1]的值是&a[1][0],a[2]的值是&a[2][0]。 (5)a[0]+0、a[0]+1、a[0]+2、a[0]+3分别是a[0][0]、a[0][1]、a[0][2]、a[0][3]元素的地址(即&a[0][0]、&a[0][1]、&a[0][2]、&a[0][3])。 (6)a[i] <=> *(a+i) a[0]+1 <=> *(a+0)+1 <=> &a[0][1] a[1]+2 <=> *(a+1)+2 <=> &a[1][2] (7) a[0]+1 <=> *(a+0)+1 <=> &a[0][1] *(a[0]+1) <=> *(*(a+0)+1) <=> *(&a[0][1]) <=> a[0][1] *(a[i]+j) <=> *(*(a+i)+j) <=> *(&a[i][j]) <=> a[i][j]
(8)二维数组名(eg. a)是指向行的,因此a+1中的“1”代表一行中全部元素所占的字节数。 一维数组名(eg. a[0] a[1])是指向列元素的,a[0]+1中的1代表一个元素所占的字节数。 在指向行的指针前面加一个*,就转换为指向列的指针。 在指向列的指针前面加一个&,就成为指向行的指针。 (9)在二维数组中,&a[i]、a+i、a[i]、*(a+i)、&a[i][0]的值相等,即它们代表同一地址。 &a[i] <=> a+i 指向行 a[i] <=> *(a+i) 指向列
输出某个指定的数值元素 如果要输出某个指定的数值元素,则应事先计算该元素在数组中的相对位置(即相对于数组起始位置的相对 位移量)。计算a[i][j]在数组中的相对位置的计算公式为:i*m+j ,其中m为二维数组的列数。 eg.对上述3x4的二维数组,它的元素a[2][3]对a[0][0]的相对位移量为2*4+3=11个元素。 如果开始时使指针变量p指向a[0][0],可以用*(p+2*4+3)表示a[2][3]的值。(p+11)是a[2][3]的地址。 a[i][j]的地址为&a[0][0]+4*(i*m+j) (一个整数占4个字节)
多维数组名做函数参数
| ||||||||||||||||||||
10.4字符串与指针10.4.1字符串的表示形式eg10.15 eg10.16 eg10.17 eg10.18
10.4.2字符指针作函数参数eg10.19
10.4.3对使用字符指针变量和字符数组的讨论虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但他妈二者之间是有区别的,主要有以下几点: (1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。 (2)赋值方式。 对字符数组只能对各个元素赋值,不能用以下方法对字符数组赋值: char str[14]; str = "I love China!"; //错误 而对字符指针变量,可以采用下面方法赋值: char *a; a = "I love China!"; //注意:赋给a的不是字符,而是字符串第一个元素的地址 (3)对字符指针变量赋初值 char *a = "I love China!"; <=> char *a; a = "I love China!"; 而对数组的初始化: char str[14] = "I love China!"; 不等价于 char str[14]; str = "I love China!"; 即数组可以在定义时整体赋初值,但不能在赋值语句中整体赋值。 (4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。 而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址,也就是说,该指针变量可以指向一个字符型数组,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。
想输入一个字符串时,
(5)指针变量的值是可以改变的。 char *a = "I love China!"; a = a+7; 数组名虽然代表地址,但它是常量,它的值是不能改变的。 char str[14] = "I love China!"; str = str+7; //错误
(6)用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。
| ||||||||||||||||||||
10.5指向函数的指针10.5.1用函数指针变量调用函数可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。 一个函数在编译时被分配一个入口地址,这个函数的入口地址就称为函数的指针。 可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
eg10.22
函数的调用
10.5.2用指向函数的指针作函数参数这样可以增加函数使用的灵活性 eg10.23 10.13
| ||||||||||||||||||||
10.6返回指针值的函数一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。 eg10.24 eg10.25 | ||||||||||||||||||||
10.7指针数组和指向指针的指针10.7.1指针数组的概念https://blog.csdn.net/weixin_42072280/article/details/83210655 指针数组与数组指针 一个数组,若琪元素均为指针类型数据,称为指针数组。也就是说,指针数组中的每一个元素都相当于一个指针变量。 为什么要用指针数组呢? 因为它比较适合于用来指向若干个字符串,使字符串处理更加方便灵活。 eg.图书馆有若干本书,想把书名放在一个数组中,然后要对这些书名进行排序和查询。按一般方法,字符串本身就是一个字符数组。因此要设计一个二维的字符数组才能存放多个字符串。但在定义二维数组时,需要指定列数,也就是说二维数组中每一行中包含的元素个数(即列数)相等。而实际上各字符串(书名)长度一般是不相等的。如按最长的字符串来定义列数,则会浪费许多内存单元。 可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串。如果想对字符串排序,不必改动字符串的位置,只需改动指针数组中各元素的指向。这样,各字符串的长度可以不同,而且移动指针变量的值(地址)y要比移动字符串所花的时间少得多。
char *name[] = {"Follow me", "BASIC", "The Great Wall", "FORTRAN", "Computer Design"}; 这些字符串是不等长的,并不是按同一长度定义的。 eg10.26 比较字符串 类似于10.12 10.12
10.7.2指向指针的指针char **p; *的结合方向为自右向左,char **p; <=> char *(*p);
eg10.27 eg10.28 10.7.3指针数组作main()函数的形参https://blog.csdn.net/weixin_42072280/article/details/83514441
| ||||||||||||||||||||
10.8有关指针的数据类型和指针运算的小结10.8.1有关指针的数据类型小结
10.8.2指针运算小结说明: (1)指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:p = NULL; 其中NULL是整数0,它使p的存储单元中所有二进制位为0,也就是使p指向地址为0的单元。系统保证使该单元不做他用(不存放有效数据),即有效数据的指针不指向0单元。 (注)p的值为NULL和p未赋值是两个不同的概念。 (2)两个指针变量可以相减 如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数。 (3)两个指针变量比较 如果两个指针指向同一个数组的元素,则可以进行比较,指向前面的元素的指针变量“小于”指向后面元素的指针变量。 |