第七章 指针

第一节 变量的地址

  • 计算机中的内存:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HCfwL7uI-1587286656736)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-1-1-计算机内存.png)]

    内存每一个字节都有一个对应的地址编号,方便计算机快速找到对应内存空间,也可以将地址形象的称为指针

int a = 6;

​ 假如:系统分配10002~10005地址空间给a,存储int类型的数值6,并且生成变量a和地址10002的对照表。在执行代码时,通过变量名找到对应的地址,然后通过数 据类 型int获取4个字节空间内的信息,读取对应的数值。

scanf("%d", &a);

&获取对应a变量的地址,将键盘输入的值存入到对应地址内存中。

  • 代码示例

    #include <stdio.h>
    int main() {
        int a = 6;
        printf("%d\n", &a);		//随机分配a的内存地址
        return 0;
    }
    #include <stdio.h>
    int main() {
        int a = 10, b = 12, c = 22;
        float d = 1.434f;
        printf("%d\t%d\t%d\t%d\n", &a, &b, &c, &d);		//随机分配abcd的内存地址
        printf("%d\n", sizeof(a));		//变量占据的空间大小
        return 0;
    }

第二节 指针变量

  • 存放地址的变量是指针变量,它用来指向另一个对象(如变量、数组、函数等)。

    • 如果将一个变量b用来存储另一个变量a的地址(指针),称变量b为指针变量

      int a = 10;
      int * b;

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vp2q0i81-1587286656740)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-2-1-指针变量.png)]

      变量a的地址为10001,地址10001的内容为int类型数据10;

      变量b的地址为10007,地址10007的内容为指针类型数据10001。

    • 定义指针变量的一般形式为:

      类型名 * 指针变量名;

      代码示例:通过指针变量访问整型变量。

      #include <stdio.h>
      int main() {
      	int a = 100, b = 10;
      	int* pointer_1, * pointer_2;
      	pointer_1 = &a;
      	pointer_2 = &b;
      	printf("a = %d, b = %d\n", a, b);
      	printf("* pointer_1 = %d, * pointer_2 = %d\n", * pointer_1, * pointer_2);
      	return 0;
      }
    • 定义指针变量时要注意:

      1. 指针变量前面的*表示该变量为指针型变量;

      2. 在定义指针变量时必须指定基类型(不同类型的数据在内存中所占的字节数和存放方式是不同德);指针或地址是包含有类型信息的,应使赋值号两侧的类型一致,以避免出现意外结果。如果指针是指向一个整型变量的,那么“使指针移动1个位置”意味着移动4个字节,“使指针增加1”意味着使地址值增加4个字节,必须指定指针变量所指向的变量的类型,即基类型。一个指针变量只能指向同一个类型的变量。一个变量的指针的含义包含两个方面,一是以存储单元编号表示的纯地址(如编号为2000的字节),一是它指向的存储单元的数据类型(如int、char、float等)。

      3. 指向整型数据的指针类型表示为int *,读作“指向int的指针”或简称“int指针”。

      4. 指针变量中只能存放地址(指针),不要将一个整数赋给一个指针变量,如:

        int * pointer_1 = 100; //pointer_1是指针变量,100是整数,不合法

        可以在定义指针变量时,同时对它初始化,如:

        int * pointer_1 = &a, * pointer_2 = &b;	//定义指针变量pointer_1和pointer_2,并分别指向a,b
    • 引用指针变量:

      1. 给指针变量赋值,如p = &a;,指针变量p的值是变量a的地址,p指向a。
    1. 引用指针变量指向的变量。如果已执行p = &a;,即指针变量p指向了整型变量a,则printf("%d", *p);的作用是以整数形式输出指针变量p所指向的变量的值,即变量a的值。如果有* p = 1;,表示将整数1赋给p当前所指向的变量,如果p指向变量a,则相当于把1赋给a,即a = 1
    2. 引用指针变量的值,如printf("%o", p);作用是以八进制数形式输出指针变量p的值,如果p指向了a,就是输出了a的地址,即&a。

    注意:

    & :取地址运算符,&a是变量a的地址。

    *:指针运算符(或称“直接访问”运算符),*p代表指针变量p指向的对象。

    代码示例:

    #include <stdio.h>
    int main() {
    	
    	int a = 3, b;
    	int* p;
    	p = &a;
    	printf("%d\n", p);
    	printf("%d\n", *p);
    	b = *p;
    	printf("%d\n", b);
    	*p = 1;
    	printf("%d\t%d\t%d\n", a, *p, b);
    	return 0;
    }
    • 指针变量作为函数参数:函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型,它的作用是将一个变量的地址传送到另一个函数中。

      代码示例:利用函数实现对输入的两个整数按大小输出

      #include <stdio.h>
      int main() {
      	void swap(int* pointer_1, int* pointer_2);
      	int a, b;
      	int* pointer_1, * pointer_2;
      	printf("请输入两个数字:\n");
      	scanf("%d%d", &a, &b);
      	pointer_1 = &a;
      	pointer_2 = &b;
      	if (a < b)
      		swap(pointer_1, pointer_2);
      	printf("max = %d\nmin = %d\n", a, b);
      	return 0;
      }
      
      void swap(int* pointer_1, int* pointer_2) {
      	int temp;
      	temp = *pointer_1;
      	*pointer_1 = *pointer_2;
      	*pointer_2 = temp;
      }

第三节 通过指针引用数组元素

  • 数组元素的指针

    • 变量有地址,同样的数组元素也有地址,也可以通过指针指向数组元素。
    • 数组名代表数组中第一个元素的地址。并不是代表整个数组。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5sobsPxZ-1587286656741)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-3-1-数组元素的地址.png)]

    代码示例:

    #include <stdio.h>
    int main() {
        int a[5] = {9,3,2,4,3};
        int * p;
        p = &a[0];
        printf("%d\n", p);		//打印出a[0]的地址
        printf("%d\n", a);		//与上述结果相同
        return 0;
    }
  • 数组元素的指针运算

    • 指针指向数组元素时,可以对指针进行加减运算:

      p+1: 指向同一个数组中的下一个元素地址,不是地址数值+1,根据定义的类型大小增加。(++)

      p -1: 指向同一个数组中的上一个元素地址,不是地址数值 -1 ,根据定义的类型大小减小。(–)

      #include <stdio.h>
      int main() {
          int a[] = {9,3,2,4,3};
          int * p = a;
          printf("%d\t%d\n", p+1, *(p+1));
          ++p;
          ++p;
          printf("%d\t%d\n", p, *p);
          return 0;
      }

      如果p = &a[0], p+3 是a[3]的地址,也等效于a+3;

      #include <stdio.h>
      int main() {
          int a[] = {9,3,2,4,3};
          int *p;
          p = &a[0];
          printf("%d\t%d\t%d\t%d\n", p+3, a+3, *(p+3), a[3]);
          return 0;
      }

      (p+3)获取p+3地址的数组元素。[ ]实际是变址运算符,a[i]等效为(a+i);

      如果p1和p2指向同一个数组中的元素,则p2-p1的结果为二者之间的元素个数。

      #include <stdio.h>
      int main() {
          int a[] = {9,3,2,4,3};
          int *p1, *p2;
          p1 = &a[1];
          p2 = &a[4];
          printf("p2-p1的结果为:%d\n", p2-p1);//(p1-p2)/4 = 3,p1和p2之间相差3个元素
          return 0;
      }
  • 指针引用数组元素

    • 通过指针的方法引用数组元素的方法:

      (1)下标法 例如:a[i]

      #include <stdio.h>
      int main() {
          int a[10], i;
          for (i = 0; i < 10; i++)
              scanf("%d", &a[i]);
          for (i = 0; i < 10; i++)
              printf("%d\t", a[i]);	//a[i] <==>*(a+i)
          return 0;
      }

      (2)指针法 例如:(a+i)或(p+i),其中a是数组名,p是指向数组元素的指针变量。

      #include <stdio.h>
      int main() {
          int a[10], i, *p = a;
          for (i = 0; i < 10; i++)
              scanf("%d", p + i);//p + i <==> a + i
          /*
          for (i = 0; i < 10; p++,i++)
              printf("%d\t", *p);
          */
          for (i = 0; i < 10; i++)
              printf("%d\t", *p++);//*p++同优先级,综合方向从右往左,*++p地址多加一个,从第下标为1的数值开始打印
          return 0;
      }

      如果p = &a[i];则:

      *(p++)表示先对p取地址内容,然后下一次p的地址减1

      *(++p)表示先对p自加,然后取地址内容

      #include <stdio.h>
      int main() {
          int a[5] = {9,3,2,4,3};
          int *p = &a[1];
          printf("%d\n", p);
          printf("%d\n", *(p--));//3
          printf("%d\n", p);
          printf("%d\n", *p);//9
          return 0;
      }
    • 注意:

      (1)a是数组首元素的地址,是一个常量,不能通过a++等方式改变a的值。

      (2)当指针p指向数组元素时,指针p可以使用p[i]带下标的方式。当p = a时,p[i] = a[i].当p = &a[2],p[i] = a[i+2]。

      #include <stdio.h>
      int main() {
          int a[5] = {9,3,2,4,3};
          int *p = &a[1];
          printf("%d\n", p[2]);//4
          printf("%d\n", *(p));//3
          return 0;
      }
  • 用数组名作函数参数

    • 函数如果传递数组名时,传递的是数组首地址,且如果形参数组中各元素的值发生变化,则实参数组元素的值也跟着改变。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uAscXENI-1587286656743)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-3-2-函数传递指针.png)]

    • 传递参数是变量时,主函数中的变量不会被改变。

      #include <stdio.h>
      int main() {
          int ff(int a);
          int a = 10;
          ff(a);
          printf("主函数中a的值:%d\n", a);
          return 0;
      }
      int ff(int a) {
          a = 3;
          printf("ff函数中a的值:%d\n", a);
      }
    • 函数传递数组名时,形参数组中元素的值如果发生变化,实参数组元素的值也跟着改变。

      #include <stdio.h>
      int main() {
          int ff(char a[]);
          char a[] = "peogeam"ff(a);			//传递的是数组首地址
          printf("%s\n", a);
          //printf("%d\n", a);
          return 0;
      }
      int ff(char a[]) {	//创建数组,但经过编译后,自动创建为一个指针变量a
          a[1] = 'r';		//[]变址运算符,a[1] = *(a+1)
          a[4] = 'r';
          //printf("%d\n", a);
      }

    形参经过编译后,自动编译为一个指针变量a,因此形参的定义[ ]中括号中可以是任意数值;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hjp6vtl-1587286656745)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-3-3-传递数组.png)]

    • 实参数组名,形参指针名

      #include <stdio.h>
      int main() {
          int ff(char *a);
          char a[] = "peogeam";
          ff(a);
          printf("%s\n", a);
          return 0;
      }
      int ff(char *a) {	
          a[1] = 'r';		//*(a+1) = 'r'
          a[4] = 'r';		//*(a+4) = 'r'
          //printf("%d\n", a);
      }
    • 实参指针名,形参数组名

      #include <stdio.h>
      int main() {
          int ff(char *a);
          char a[] = "peogeam";
          char *p = a;
          ff(a);	//ff(p);
          printf("%s\n", a);
          return 0;
      }
      int ff(char a[]) {	
          a[1] = 'r';		//*(a+1) = 'r'
          a[4] = 'r';		//*(a+4) = 'r'
      }
    • 形参和实参都用指针名

      #include <stdio.h>
      int main() {
          int ff(char *a);
          char a[] = "peogeam";
          char *p = a;
          ff(p);
          printf("%s\n", a);
          return 0;
      }
      int ff(char *a) {	
          *(a+1) = 'r'
          *(a+4) = 'r'
      }
    • 练习:将数组中n个元素按相反顺序存放。

      #include <stdio.h>
      int main() {
          int over(char *p);
          int ff(char *a);
          char a[] = "program";
          char *p;
          p = a;
          over(p);
          printf("颠倒后的内容是:%s\n", a);
          return 0;
      }
      
      int over(char *p) {
          char t;
          int i;
          for (i = 0; i < 3; i++){
              t = *(p+i);
              *(p+i) = *(p+6-i);
              *(p+6-i) = t;
          }  
      }
  • 通过指针引用多维数组

    • 现有一个二维数组:int a[3][4] = {{3,8,6,2},{5,6,3,9},{2,5,3,0}};a包含3行,分别用a[0], a[1], a[2]表示。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xjW1gpYR-1587286656748)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-3-4-数组内存地址示意图.png)]

    二维数组地址说明:

    (1)a代表二位数组首行的起始地址1244952

    (2)a+1代表序号为1的行的地址1244968

    ​ 计算方法:1244952+4*4 = 1244968

    (3)a[0],a[1],a[2]是一维数组名,也是各行的首地址。

    a[0] = &a[0][0] = *(a+0)

    a[1] = &a[1][0] = *(a+1)

    a[2] = &a[2][0] = *(a+2)

    (4)a[0]是一维数组名,则a[0]+1 表示 a[0][1]的地址

    a[1] + 2 表示a[1][2]的地址,即&a[1][2]

    a[1]+2 = *(a+1) +2 = &a[1][2]

    (5)a[0][2] = *(a[0]+2) = *(*(a+0) +2)

    a[1][2] = *(a[1]+2) = *(*(a+1) +2)

    注意:

    a+1 和a[1]的结果一样,但是性质不同。a+1指向第2行(属性为行),a[1]指向第2行首元素地址(属性为元素),因为C语言的地址信息中即包含了位置信息,也包含了数据的类型信息。

    例如 a+1再加1,是下一行,而a[1]+1是下一个元素。

    • 多维数组

      #include <stdio.h>
      int main()
      {	
      	int a[3][4]={{3,8,6,2},{5,6,3,9},{2,5,3,0}};
      	printf("a=%d\t  a+1=%d\n",a,a+1);
      	printf("a[0]=%d\t a[2]=%d\n",a[0],a[2]);//a[0],a[1],a[2]是一维数组名,也是各行的首地址
      	printf("&a[0][0]=%d\t *(a+0)=%d\n",&a[0][0],*(a+0));
      	printf("&a[1][0]=%d\t *(a+1)=%d\n",&a[1][0],*(a+1));
      	printf("a[1]+2=%d\t *(a+1)+2=%d\n",a[1]+2,*(a+1)+2);
      	printf("a[1][2]=%d\t *(a[1]+2)=%d\t *(*(a+1)+2)=%d\n",a[1][2],*(a[1]+2),*(*(a+1) +2));
      }
    • 输出多维数组

      #include <stdio.h>
      int main() {
          int a[3][4]={{3,8,6,2},{5,6,3,9},{2,5,3,0}};
          int *p;	//(*p)[4]
          for (p = a[0]; p < a[0] + 12; p++)
              printf("%d\t", *p);
          return 0;
      }

第四节 通过指针引用字符串

  • 字符串的引用方式:
    • 用字符数组存放一个字符串,可以通过数组名和下标引用字符串中一个字符,也可以通过数组名和格式声明“%s”输出该字符串。
    • 用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XNYWdE42-1587286656749)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-4-1-指针指向字符串常量.png)]

​ 代码示例:字符数组存储字符串

#include <stdio.h>
int main()
{
	char gq[] ="Happy birthday motherlan";
	printf("%s\n",gq);
}

​ 代码示例:字符指针指向字符串常量

#include <stdio.h>
int main()
{	
	char *gq = "Happy birthday to our motherlan";   //定义了一个字符指针变量,指向字符串常量第1个元素的地址
	printf("%s\n",gq);		//%s通过gq找到字符串首地址,不断使gq加1,使之指向下一个字符,再输出字符,直到\0
	printf("%d\n",gq);		//打印指针变量的地址
	gq = "Happy birthday";	//在内存中重新开辟内存空间存储字符串常量
	printf("%s\n",gq);
	printf("%d\n",gq);

​ 注意:

  1. %s是通过字符指针找到字符串常量的首地址,使地址不断加1,直到遇到\0为止,输出地址对应内容。

  2. 对数值型数组是不能通过%s一次性输出全部元素,只能元素逐个输出。

    练习:将数组x中的字符逐个复制到数组y中

#include <stdio.h>
int main() {
	char x[] = "C program";
	char y[15];
	int i;
	for (i = 0; *(x + i) != '\0'; i++) //x[i] = *(x + i)
		* (y + i) = * (x + i);		//y[i] = x[i]
	*(y + i) = '\0';	//y[i] = '\0';
	printf("%s\n", y);
	return 0;
}
  • 字符指针作函数参数

    把一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数,也可以用字符指针变量作参数。

    • 代码示例:用函数调用实现字符串的复制。

      #include <stdio.h>
      int main() {
      	int cp(char* old_str, char* new_str);
      	char str1[] = "I am a boy";
      	char str2[20];
      	cp(str1, str2);
      	printf("赋值后的字符串为:%s\n", str2);
      	return 0;
      }
      
      int cp(char * old_str, char * new_str) {
      	int i;
      	for (i = 0; old_str[i] != '\0'; i++)
      		new_str[i] = old_str[i];
      	new_str[i] = '\0';
      }
  • 使用字符指针变量和字符数组的比较

    用字符数组和字符指针变量都能实现字符串的存储和运算,但二者之间有区别:

    1. 字符数组包含多个元素,每个元素都有自己的地址,而字符指针变量中存放的是地址。

    2. 可以对字符指针变量赋一个地址,但不能对字符数组名赋值,它是一个常量。

    3. 字符数组除了在初始化时可以一次性对所有元素赋值,非初始化部分不能一次性赋值,但字符指针变量可以实现。

    4. 编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元。可能指向内存中空白的(未用的)用户存储区中,也有可能指向已存放指令或数据的有用内存段,会破坏程序或有用数据,甚至破坏系统,造成严重后果。

    5. 字符数组的元素值可以被改变,但字符指针指向的字符串常量中的元素不能被改变

      #include <stdio.h>
      int main()
      {
      //	char a[] = "dioo";
      //	char *p = "dioo";
      
      //	a = "dada";
      //	p = "dada";
      
      //	char *p;
      //	scanf("%s",p);
      
      
      	char a[] = "dioo";
      	char *p = "dada";	//不可被修改
      	a[1] = 'Y';
      	p[1] = 'Z';
      	printf("%s\n%s\n",a,p);
      }
    6. 使用字符指针变量指向的字符串或字符数组都可以实现printf函数的可变格式输出。

      #include <stdio.h>
      int main()
      {
      	int a = 3, b = 100;
      	char * p = "a=%d\t b = %d\n";	
      	printf(p,a,b);
      	return 0;
      }

第五节 指向函数的指针

  • 什么是函数的指针?

    • 程序代码中的函数在编译时,会分配一段存储空间存储函数可执行代码,函数名代表函数的起始地址。调用该函数时,会从函数名处找到函数的起始地址,并执行对应函数。

    • 函数名就是函数的指针,代表函数的起始地址。

    • 定义一个指针变量用于指向函数,存放函数的起始地址,则此指针为函数指针变量

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BtT4an5c-1587286656751)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-5-1-函数指针变量.png)]

    • 代码示例:打印菱形

      #include <stdio.h>
      int main()
      {
      	void print_star();
      	void (*p)();
      	p = print_star;
      	printf("打印菱形:\n");
      	(*p)();
      	printf("第二次打印菱形:\n");
      	(*p)();
      	printf("最后一次打印菱形:\n");
      	(*p)();
      	return 0;
      }
      
      void print_star() {
      	char a[5][5] = { {' ', ' ', '*'},
      	{' ', '*', ' ', '*'},
      	{'*', ' ', ' ', ' ', '*'},
      	{' ', '*', ' ', '*'},
      	{' ', ' ', '*'} };
      	int i, j;
      	for (i = 0; i < 5; i++) {
      		for (j = 0; j < 5; j++)
      			printf("%c", a[i][j]);
      		printf("\n");
      	}
      }
  • 定义函数指针变量

    • 定义指向函数的指针变量的一般形式为:

      类型名 (* 指针变量名)(函数参数表列);
    • 注意:

      1. 定义函数指针变量之前需要确定好类型名,类型名代表可以指向的函数类型。例如:int ( *p )(int ,int)代表指针p只能指向int类型的函数,且对应的函数有两个整型形参。*
      2. * 指针变量名 需要用括号括起来。
      3. 给函数指针变量赋值时,只需给出函数名, 不用写入参数。
      4. (*指针名)是调用函数,使用(*指针名)替代函数名即可
      5. 函数指针变量没有加减运算,是无意义的运算。
    • 函数指针变量对比函数名的两者使用的优点:

      ​ 函数名调用函数,只能调用指定的一个函数,而函数指针变量可以通过改变指针变量的地址灵活调用不同的函数。

    • 代码示例:通过指针求二者中的较大者

      #include <stdio.h>
      int main()
      {
      	int max(int x, int y);
      	int a, b;
      	int (*p)(int, int);
      	printf("请输入要比较的两个数字:\n");
      	scanf("%d%d", &a, &b);
      	p = max;
      	printf("max = %d\n", (*p)(a, b));
      	return 0;
      }
      
      int max(int x, int y) {
      	return x > y ? x : y;
      }
  • 用指向函数的指针作函数参数

    指向函数的指针变量的一个重要用途是把函数的入口地址作为参数传递到其他函数。

    在实际使用中,常常将函数指针作为另一个函数的参数,传递到其他函数中。例如函数指针变量p指向函数a,函数b中的一个参数为变量p:

    int b( int ( *p ));
    • 代码示例:有两个整数a和b,由用户输入并选择执行操作。

      #include <stdio.h>
      int main()
      {
      	int max(int x, int y);
      	int min(int x, int y);
      	int add(int x, int y);
      	int a, b, n;
      	printf("请输入两个数字:\n");
      	scanf("%d%d", &a, &b);
      	printf("请选择方案1,2,3:");
      	scanf("%d", &n);
      	if (n == 1)
      		fun(a, b, max);
      	else if (n == 2)
      		fun(a, b, min);
      	else if (n == 3)
      		fun(a, b, add);
      	return 0;
      }
      
      int max(int x, int y) {
      	return x > y ? x : y;
      }
      
      
      int min(int x, int y) {
      	return x < y ? x : y;
      }
      
      int add(int x, int y) {
      	int z;
      	z = x + y;
      	return z;
      }
      
      int fun(int x, int y, int (*p)(int, int)) {
      	int result;
      	result = (*p) (x, y);
      	printf("%d\n", result);
      }

第六节 返回指针的函数

  • 函数除了可以返回整型、字符、浮点数等以外,还可以返回指针。

    • 定义返回指针值的函数的原型的一般类型为:

      类型名 * 函数名(参数表列);

      返回指针的方法:通过return函数返回指针变量名。

    • 代码示例:

      #include<stdio.h>
      
      char *max(char *a, char *b)
      {	
      	int i,flag;
      	for(i = 0;i<4;i++)
      	{
      		if(a[i]<b[i])
      		{	flag = 0;
      			break;
      		}
      		else if(a[i]> b[i])
      		{
      			flag  = 1;
      			break;
      		}
      	}
      	if(flag == 0) return b;
      	else return a;
      }
      
      void main()
      {	
      	char a[] = "bbyd";
      	char b[] = "zbda"; 
      	char *p;
      	p = max(a,b);
      	printf("%s\n",p);
      }

第七节 指针数组和多重指针

  • 一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。

    • 定义一维指针数组的一般形式为:

      类型名 * 数组名[数组长度];

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YqtaOvB-1587286656753)(E:\workspace\TyporaProjects\计算机组成原理\images\第二章\7-7-1-指针数组.png)]

    • 使用场景:当有多个字符串时,可以使用指针数组处更加灵活。

    • 代码示例:

      #include<stdio.h>
      
      s[0]= "C program"
      s[1] = "control"
      s[2] = "logic"
      
      void main()
      {	
      	int i;
      	char *s[]={"C program","control","logic"};//char s[][9] = {"C program","control","logic"}不灵活;
      	for(i = 0;i<3;i++)
      		printf("%s\n",s[i]);
      }
  • 指向指针数据的指针变量(多重指针)

    • 一个指针变量指向另一个指针称为多重指针

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YJdqc1ZH-1587286656754)(E:\workspace\TyporaProjects\C笔记\网易-C程序设计第四版\images\第七章\7-7-2-多重指针.png)]

    • 定义多重指针的方法:

      指针类型 **指针名;//char **p;

      理论上可以出现3重地址,甚至更多重地址,但是通常间接访问变量一般不会超过二级地址。级数越多越难于理解和使用。

    • 代码示例:

      #include <stdio.h>
      int main()
      {
      	int a = 3;
      	int *p = &a;
      	int **y = &p;
      	printf("%d\n",a);		//3
      	printf("%d\n",*p);		//3
      	printf("%d\n",**y);		//3
          return 0;
      }
      #include <stdio.h>
      int main()
      {	
      	char *s[]={"C program","control","logic"};
      	char **p;
      	p = s+1;
      	printf("%s\n",*p);
      	printf("%d\n",*p);
      	printf("%c\n",**p);
          return 0;
      }
  • 指针数组作main函数的形参

    • 指针数组的一个重要应用是作为main函数的形参,一般写成int main()int main(void),括号中是空的或有“void”,表示main函数没有参数,调用main函数时不必给出实参,这是一般程序常采用的形式。实际上,在某些情况下,main函数可以有参数,如:

      int main(int argc, char *argv[]);

      其中argc和argv就是main函数的形参。argc接收参数个数,argv是字符指针数组。

    • 如果用带参数的main函数,其第一个形参必须是int型,用来接收形参个数,第二个形参必须是字符指针数组,用来接收从操作系统命令行传来的字符串中首字符的地址(由操作系统传递实参给main函数。实参是和执行文件的命令一起给出的)。

    • main函数是操作系统调用的,实参只能由操作系统给出。在操作命令状态下,实参是和整型文件的命令一起给出的,例如在DOS,UNIX或Linux等系统的操作命令状态下,在命令行中包括了命令名和需要传给main函数的参数,命令行的一般形式为:命令名 参数1 参数2 ··· 参数n。其中命令名为可执行文件名(包含main函数的.exe文件),各个参数之间用空格分隔。

第八节 动态内存分配与指向它的指针变量

  • 全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区是一个称为栈(stack)的区域。除此以外,C语言还允许建立内存动态分配区域,以存放临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时随时开辟,不需要时随时释放。这些数据是临时存放在一个特别的自由存储区,称为堆(heap)区。

  • 对内存的动态分配是通过系统提供的库函数来实现的,主要有malloc,calloc,free,realloc这4个函数。

    1. 用malloc函数开辟动态存储区,其函数原型为void * malloc(unsigned int size);。其作用是在内存的动态存储区中分配一个长度为size的连续空间。

      malloc(100);	//开辟100字节的临时分配域,函数值为其第1个字节的地址

      注意指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址。如果此函数未能成功执行(例如内存空间不足),则返回空指针(NULL)。

    2. 用calloc函数开辟动态存储区,其函数原型为void * calloc(unsigned n, unsigned size);。其作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。

      用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。这就是动态数组。函数返回指向所分配域的第一个字节的指针;如果分配不成功,返回NULL,如:

      p = calloc(50, 4);	//开辟50×4个字节的临时分配域,把首地址赋给指针变量p
    3. 用realloc函数重新分配动态存储区,其函数原型为void * realloc(void * p, unsigned int size);。如果已经通过malloc或calloc函数获得了动态空间,想改变其大小,可以用realloc函数重新分配

      用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果重分配不成功,返回NULL,如:

      realloc(p, 50);		//将p所指向的已分配的动态空间改为50字节
    4. 用free函数释放动态存储区,其函数原型为void free(void *p);。其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值,如:

      free(p);	//释放指针变量p所指向的已分配的动态空间

      free函数无返回值。

  • void指针类型

    • C99允许使用基类为void的指针类型。可以定义一个基类型为void的指针变量(即void * 型变量),它不指向任何类型的数据。请注意:不要把“指向void类型”理解为能指向“任何的类型”的数据,而应理解为“指向空类型”或“不指向确定的类型”的数据。在将它的值赋给另一指针变量时由系统对它进行类型转换,使之适合于被赋值的变量的类型。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值