我们专注攀枝花网站设计 攀枝花网站制作 攀枝花网站建设
成都网站建设公司服务热线:400-028-6601

网站建设知识

十年网站开发经验 + 多家企业客户 + 靠谱的建站团队

量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决

C语言详细知识点(下)-创新互联

⛄️上一篇⛄️C语言详细知识点(上)

创新互联服务项目包括东方网站建设、东方网站制作、东方网页制作以及东方网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,东方网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到东方省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

文章目录
    • 五、数组
      • 1、一维数组的定义及使用
      • 2、二维数组的定义及使用
      • 3、字符数组的定义及使用
    • 六、函数
      • 1、函数的定义
      • 2、函数的调用
      • 3、函数的声明
      • 4、函数的嵌套调用
      • 5、函数的递归调用
    • 七、指针
      • 1、什么是指针
      • 2、指针变量
      • 3、通过指针引用数组
      • 4、用数组名做函数参数
      • 5、通过指针引用字符串
    • 八、结构体
      • 1、结构体类型的定义
      • 2、使用结构体数组
      • 3、结构体指针

五、数组

(1)数组是一组有序数据的集合
(2)用一个数组名下标来唯一地确定数组中的元素
(3)数组中的每一个元素都属于同一个数据类型

1、一维数组的定义及使用

(1)数组声明
语法:
类型 数组名[常量表达式];
注意:
1)类型是任意合法的类型
2)数组名遵循标识符命名规则
3)定义数组时,需要制定数组中元素的个数,int a[10],不存在a[10]
4)常量表达式可以包括常量和符号常量不能包含变量

(2)数组引用
语法:
数组名[下标];
注意:
1)只能引用数组元素而不能一次整体调用整个数组全部元素的值
2)定义数组时用到的数组名[常量表达式]和引用数组元素时用的数组名[下标]形式相同,但含义不同

如:int a[10]; //前面有int,这是定义数组,指定数组中包含10个元素
t=a[6]; //这里表示引用数组中序号为6的元素

例1、对10个数组元素依次赋值为0,1,2,3,4,5,6,7,8,9,要求逆序输出

#includevoid main()
{int a[10],i;
    for(i=0;i<10;i++){a[i]=i;
    }
    for(i=9;i>=0;i--){printf("%d\t",a[i]);
    }
}

(3)数组初始化
1)在定义数组时对全部数组元素赋予初值
如:int a[10]={0,1,2,3,4,5,6,7,8,9};

2)可以只给数组中的一部分元素赋初值
如:int a[10]={0,1,2,3,4};

3)如果想使一个数组中全部元素值为0
如 :int a[10]={0,0,0,0,0,0,0,0,0,0}; 或 int a[10]={0};

4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组的长度
如:int a[5]={1,2,3,4,5}; 可以写成 int a[]={1,2,3,4,5};

注意:如果在定义数值型数组时,指定了数组的长度并对之初始化,凡未被初始化列表指定初始化的数组元素,系统会把它们初始化为0(如果是字符型数组,则初始化为'\0',如果是指针型数组,则初始化为NULL,及空指针)

(4)数组举例
例1、用数组解决斐波那契数列问题,求前20个斐波那契数列

#includevoid main()
{int a[20],i,t=0;
    a[0]=1;
    a[1]=1;
    for(i=2;i<20;i++){a[i]=a[i-1]+a[i-2];
    }
    for(i=0;i<20;i++){if(i%4==0)printf("\n");
    	printf("%d\t",a[i]);	
    }
}

例2、有10个地区面积,要求对它们按由小到大的顺序排列(冒泡排序)

#includevoid main()
{int a[10], i, j, t;
    for (i=0; i<10;i++){scanf("%d", &a[i]);
    }
	for(i=0;i<10-1;i++)//外层循环n-1次
	{for(j=0;j<10-i-1;j++)//内层循环比较次数-1
		{	if(a[j]>a[j+1])
			{		t=a[j];
				a[j]=a[j+1];
				a[j+1]=t;
			}
		}
	}
    for (i = 0; i< 10; i++){printf("%d\n", a[i]);
    }
}
2、二维数组的定义及使用

一维数组的数组
放批量同类型数据,数组元素在内存中连续存放(按行存放,第一行存放完再存放第二行…),数组名代表数组首地址,是一个地址常量

(1)数组声明
语法:
类型 数组名[常量表达式] [常量表达式];
注意:
1)类型是任意合法的类型
2)[]里一定是常量,代表数组元素的个数

  定义二维数组 float[3][4],C语言对二维数组采用这样的定义方式,使得二维数组可被看作一种特殊的一维数组:它的元素又是一个一维数组。把a看作一个一维数组,它有3个元素:a[0],a[1],a[2]

每个元素又是一个包含4个元素的一维数组
a[0]-----a[0][1] a[0][2] a[0][3] a[0][4]
a[1]-----a[1][1] a[1][2] a[1][3] a[1][4]
a[2]-----a[2][1] a[2][2] a[2][3] a[2][4]

可以把a[0],a[1],a[2]看作3个一维数组的名字,上面定义的二维数组可以理解为定义了3个一维数组相当于
float a[0][4] ,a[1][4],a[2][4],a[3][4]

(2)数组元素的引用
语法
数组名[行标][列标]
通常用双层循环遍历二维数组,外层控制行,内层控制列

(3)数组初始化(定义的同时赋值)
1)分行给二维数组赋初值
如:int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}

2)可以将所有数据写在一个花括号内,按数组元素在内存的排列顺序对各元素赋初值
如:int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

3)可以对部分元素赋初值
如:int a[3][4]={{1},{2},{3}};

4)如果全部赋值,则数组第一维可以不写,但第二维必须写
如:int a[2][3]={1,2,3,4,5,6} 等价于 int a[][3]={1,2,3,4,5,6}

例3、将一个二维数组的行和列互换,存到另一个二维数组中
在这里插入图片描述

#includevoid main()
{int a[2][3]={{1,2,3},{4,5,6}};
	int b[3][2],i,j;
	for(i=0;i<2;i++){for(j=0;j<3;j++){	b[j][i]=a[i][j];
		}	
	}
}

例4、有一个3×4的矩阵,求出其中大值和行号列号

#includevoid main()
{int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
	int i,j,max=1,r,l;
	for(i=0;i<3;i++){for(j=0;j<4;j++){	if(a[i][j]>max){		 max=a[i][j];	
				 r=i;
				 l=j;
			}
		}	;
	}
	printf("大值%d行号%d列号%d",max,r,j);
}
3、字符数组的定义及使用

(1)数组声明
语法:
char 数组名[常量表达式];

(2)初始化
初始化列表,把各个字符以此赋值给数组中各元素,可以全部赋值,也可以部分赋值(其余元素都是默认值)全赋值数组大小可以省略
如:char a[6]={‘C’,‘h’,‘i’,‘n’,‘a’};
注意:
1️⃣如果在定义字符数组时不进行初始化,则数组中各个元素的值时不可预料的。
2️⃣如果花括号中提供的初值个数(即字符个数)大于数组长度,则出现语法错误
3️⃣如果初值个数小于数组长度,则只将这些字符给数组中前面那些元素,其余的元素自动定义为空字符(即'\0')

(3)数组元素引用
数组名[下标]

(4)字符串结束标志
1)c系统在用字符数组存储字符串常量时会自动加一个’\0’作为结束字符
如:char a[6]={“China”}; 共有5个字符,在数组中占6个字节,最后一个’\0’是系统自动加上的
2)第二种字符串初始化方法
如:char a[]={“China”} 可以写成char a[]=“China”;
3)字符数组并不要求最后一个字符为’\0’,甚至可以不包含’\0’
如:char a[6]={‘C’,‘h’,‘i’,‘n’,‘a’};

(5)字符数组的输入输出
1)逐个字符输入输出,格式符%c
2)整个字符串输入输出,格式符%s
3)输出的字符不包括结束符’\0’
4)如果一个字符数组包含一个以上的’\0’,则遇到第一个’\0’时输出结束
5)系统把空格符作为输入的字符串之间的分隔符,因此只将空格前的字符"How"送到 str 中,吧"How"作为一个字符串处理,故在其后加’\0’
如:char str[13]; scanf("%s",str); 输入 How are you 输出 How
6)scanf函数中输入项如果是字符数组名,不要再加地址符&,八进制输出数组首地址 如:printf("%o",str);

(6)字符串处理函数
1️⃣puts输出字符串函数
语法:puts(字符数组)
作用:将一个字符串(以’\0’结束的字符序列)输出到终端,用puts函数输出的字符串可以包含转义字符
如:char str[]={"China\nBeijing"}; puts(str);在用puts输出时将字符串结束标志'\0'转换成'\n',即输出玩字符串后换行

2️⃣gets输入字符串函数
语法:gets(字符数组)
作用:从终端输入一个字符串到字符串数组,并且得到一个函数值
如:char a[]={"Computer"};gets(a);

3️⃣strcat字符串连接函数
语法:stract(字符串数组1,字符串数组2)
作用:把两个字符数组中的字符串连接起来,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值------字符数组1的地址
如:

#includevoid main()
{char str1[20]={"China"},str2[]={"Beijing"};
    strcat(str1,str2);
    printf("%s",str1);
}
ChinaBeijing

注意:①字符数组1必须足够大,以便容纳连接后的新字符串
②连接时将字符串1后面的’\0’取消,只在新字符串最后保留’\0’

4️⃣strcpy和strncpy字符串复制函数
语法:strcpy(字符数组1,字符数组2)
作用:将字符串2复制到字符串1中去
如:

#includevoid main()
{char str1[]={"Bejing"},str2[]={"China"};
    strcpy(str1,str2);
    printf("%s",str1);
}
China

注意:①字符数组1必须定义足够大,以便容纳被定义的字符数组2,字符数组1的长度不应小于字符数组2的长度
②字符串2可以是数组名也可以是一个字符串常量
如:strcpy(str1,“China”);
③可以用strncpy将字符串2中前面n个字符复制到字符数组1中去,但复制的字符个数n不应多于原有的字符(不包括’\0’);
如:strncpy(str1,str2,2);

5️⃣strcmp字符串比较函数
语法:strcmp(字符串1,字符串2)
作用:比较字符串1和字符串2
如:strcmp(“China”,“Beijing”);
说明:字符串比较规则是:将两个字符串自左至右逐个字符相比(按照ASCII码值大小比较,小写字母比大写字母大),直到出现不同字符遇到'\0'为止

比较结果由函数值带回:
①如果字符串1与字符串2相同,则函数值为0
②如果字符串1>字符串2,则函数值为一个正整数
③如果字符串1<字符串2,则函数值为一个负整数
如:

#includevoid main()
{char str1[]={"Bejing"},str2[]={"China"};
    printf("%d",strcmp(str1,str2));
}
-1

6️⃣strlen测字符串长度函数
语法:strlen(字符串数组)
作用:测字符串长度,函数的值为字符串中的实际值
如:

#includevoid main()
{char str1[]={"Bejing"};
    printf("%d",strlen(str1));
}
6

7️⃣strlwr转换为小写函数
语法:strlwr(字符串)
8️⃣strupr转换为大写函数
语法strupr(字符串)

字符数组举例
例1、输入一行字符,统计其中有多少个单词,单词之间用空格分隔开

#includevoid main()
{char a[20];
    int i,world,sum=1;
    gets(a);
    for(i=0;a[i]!='\0';i++){if(a[i]==' '){world=0;	
    	}else if(world==0){sum++;
    		world=1;
    	}	
    }
    printf("单词个数%d",sum);
}
l am a boy
单词个数4
六、函数

函数就是功能。每一个函数用来实现一个特定的功能。函数的名字反映其代表的功能,完成一定功能的模块(算法),实现代码的复用

例1、输出以下结果,用函数调用实现
在这里插入图片描述

#includevoid main()
{void star();
	void word();
	star();
	word();
	star();
}
void star(){printf("***********************\n");	
}
void word(){printf("How do you do!\n");	
}
***********************
How do you do!
***********************

函数分类:
(1)用户使用角度①库函数②用户自己定义的函数
(2)函数的形式角度①无参函数②有参函数

1、函数的定义

定义函数包括以下内容:

  1. 指定函数的名字
  2. 指定函数类型
  3. 指定函数的参数的名字和类型,以便在调用函数时向它们传递数据
  4. 指定函数应当完成什么操作,也就是函数是做什么的,即函数的功能

定义函数的方法:
分为函数首部(函数头),函数体两部分
语法:

返回值类型(函数类型) 函数名(形参表列)--------函数首部
{  
	声明部分(变量定义或函数声明)          --------函数体
	执行部分(语句)
}

注意:

  • 返回值类型(函数的输出)的确定,首先判断函数是否需要返回一个值,如果需要再看返回值的类型,如果没有返回值,则明确声明为void,一定注意,返回值只能有一个,如果多于一个,只能通过别的方式带回,此时也将函数返回值类型声明为void
  • 函数名:一个合法的标识符,尽量见名知意,多个单词连用,通常除了第一个单词外,其余单词首字母大写
  • 形参表列:形参1类型 形参1名,形参2类型 形参2名….,一个函数可以有多个形参,形参之间用逗号隔开,形参名是任意合法的标识符。是函数完成其功能时,所需要的必要的已知条件,相当于函数的输入,如果函数不需要已知条件,则空着,此时是无参函数。
  • 函数体,是函数功能的具体实现(算法的具体实现),此时形参相当于已经有值(在函数调用时,由实参传递过来的值),不需要重新赋值,如果函数需要返回一个值,用return 语句带回(return语句将后面表达式的值带回到函数调用处,并结束函数的调用,return后面可以不加表达式,此时仅仅结束函数的调用,return语句可以有多个,但只有一个会执行)

例1、求两个数中的大值

int max(x,y){return x>y?x:y;
}

例2、求两个数的大公约数
例3、求两个数的最小公倍数

2、函数的调用

1、函数调用的形式
语法:
函数名(实参表列)
注意:

  • 实参表列,实参可以是常量,变量或表达式。要求实参的个数和形参一致,类型和形参赋值兼容(形参1=实参1),实参必须有确定的值
  • 如果要使用函数的返回值,可以定义一个和函数返回值类型一致的变量,接收返回值

(1)函数调用语句
把函数调用单独作为一个语句,不要求函数带回值,只要求函数完成一定的操作
(2)函数表达式
函数调用出现在另一个表达式中,如:c=max(a,b),max(a,b)是一次函数的调用,他是赋值表达式中的一部分。这时要求函数带回一个确定的值以参加表达式的运算
(3)函数参数
函数调用作为另一个函数调用时的实参,如:m=max(a,max(b,c));

2、函数调用时的数据传递

(1)形式参数和实际参数
1)定义函数时函数名后面括号中的变量名称为形式参数
2)在主调函数中调用一个函数时,函数名后面括号中的参数为实际参数。实际参数可以是常量、变量或表达式

(2)实参和形参间的数据传递
调用过程中,系统会把实参的值传递给被调用函数的形参

例1、输入两个整数,求出其中较大者

#includevoid main()
{int a,b;
	int max(x,y);
	scanf("%d%d",&a,&b);
	printf("较大的数是:%d",max(a,b));
}

int max(int x,int y){return x>y?x:y;
}
5 8
较大的数是:8

注意:
①在发生函数调用时,首先程序执行的流程转到被调用函数,会给被调用函数的局部变量(定义在函数内部的变量,包括形参)分配内存空间,会将实参的值对应地传给形参
②在函数调用结束时(return语句遇到函数体结束的右括号),程序执行的流程转到主调函数中函数调用处回收被调用函数的局部变量(定义在函数内部的变量,包括形参)所占内存空间,如果需要return返回1个值,则把这个值带回函数调用处
因此,除了main函数外,其他函数中的局部变量只在被调用期间占有内存空间,不同函数的局部变量可以重名,仅仅是重名,一定占用不同的内存空间

(3)函数调用的过程
1)在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在函数发生调用时,函数的形参才被临时分配存储单元
2)将实参的值传给对应的形参
3)利用形参的值进行运算
4)通过return语句将函数值带回到主调函数。注意返回值类型与函数类型一致,如果函数不需要返回值,则不需要return语句。这时函数的类型应定义为void类型
5)调用结束,形参单元被释放实参单元仍保留并维持原值,没有改变。如果在执行一个被调用的参数时,形参的值发生改变,不会改变主调函数的实参的值。因为形参和实参是两个不同的存储单元

(4)函数的返回值
1)函数的返回值是通过函数中的return语句获得的
2)函数值的类型 如:int max(float x,float y)
3)在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。如果函数值得类型和return语句中表达式的值不一致,则以函数类型为准。对数值数据,可以自动进行类型转换。即函数类型决定返回值得值。如:int max(float x,float y) 返回int值
4)对于不带回值得函数,应当用定义函数为void类型

3、函数的声明

(1)首先被调用的函数必须是已经定义的而函数
(2)如果使用库函数,应该在本文件开头使用#include指令将调用有关库函数时所需用到的信息包含到本文件中来
(3)如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面,应该在主调函数中对被调用的函数作声明。声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。

例2、输入两个实数求和

#includevoid main()
{float a,b;
	float add(float x,float y);
	scanf("%f%f",&a,&b);
	printf("%f",add(a,b));
}

float add(float x,float y){return x+y;
}
1.2 1.5
2.700000

函数声明(函数原型声明):当被调用函数写在主调函数之后,此时可以在主调函数中,对被调用函数进行声明,方便编译系统检查函数调用表达式的正确性(检查项包括函数的类型、函数名字、参数的类型、参数个数和参数的顺序)。如果被调用函数写在主调函数之前,此时函数声明可以省略。

语法:
(1)函数返回值类型 函数名(形参1的类型 形参1,形参2类型 形参2);
如:float add(float x,float y);
(2)函数返回值类型 函数名(形参1的类型,形参2的类型…);
如:float add(float,float);

4、函数的嵌套调用

C语言的函数定义是相互平行、独立的,也就是说,在定义函数时,一个函数内不能再定义另一个函数,即不能嵌套定义,但可以嵌套调用函数,即在调用函数的过程中,又调用另一个函数。

例3、输入4个整数,找出其中大的数。用函数的嵌套调用来处理

#includevoid main()
{int a,b,c,d;
    int max4(int a,int b,int c,int d);
    scanf("%d%d%d%d",&a,&b,&c,&d);
    printf("%d",max4(a,b,c,d));
}

int max4(int a,int b,int c,int d)
{int max;
	int max2(int x,int y);
	max=max2(a,b);
	max=max2(max,c);
	max=max2(max,d);
	return max;
}

int max2(int x,int y)
{return x>y?x:y;
}
5、函数的递归调用

在调用一个函数的过程中又出现直接或间接的调用该函数本身,称为函数的递归调用

例4、用递归的方式求n!

#includevoid main()
{int a;
    int fun(int x);
    scanf("%d",&a);
    printf("%d",fun(a));
}
int fun(int x)
{int t;
	if(x==1||x==0) 
		t=1;
	else
	{t=x*fun(x-1);
	}
	return t;
}
七、指针

指针存在的意义:通过指针来改变变量(变量的本质是某一块内存空间的别名)的值,即改变内存空间的值。

1、什么是指针

1、什么是地址
  在程序中定义了一个变量,在对程序进行编译时,系统会给这个变量分配内存单元。根据变量的类型,分配一定长度的空间(如:Visual C++ 为整型分配4个字节)。内存中每一个字节有一个编号,这就是地址
  C语言中的地址包括内存编号数据类型

2、什么是指针
  由于通过地址能找到所需变量单元,可以说,地址指向该变量单元。因此,将地址形象化地称为指针(通过它能找到以它为地址的内存单元)。

3、直接访问
通过变量名找到相应的地址,从字节中按照不同的存储方式读出变量的值

4、间接访问
将变量的地址存放在另一变量中,然后通过该变量来找到变量的地址,从而访问变量值
如:i_pointer=&i //将i的地址存放在i_pointer中

一种想法:

int a,b;
b=10;
a=&b;//把b的地址存放在整型变量中,虽然可以存储,但是不能通过存储的地址访问数据

将数值3送到变量i中,有两种表达方法
(1)将3直接送到变量 i 所标识的单元中,如:i=3
(2)将3送到变量i_pointer所指向的单元,如:*i_pointer=3,其中 *i_pointer表示i_pointer指向的对象

注意:区分“指针”和“指针变量” 。如:可以说变量 i 的指针是2000,而不能说 i 的指针变量是2000。指针是一个地址指针变量是存放地址的变量

2、指针变量

例1、通过指针访问整型变量

#includevoid main()
{int a=100,b=10;
    int *x,*y;
    x=&a;
    y=&b;
    printf("%d %d\n",a,b);
    printf("%d %d",*x,*y);
}

1、定义指针变量
语法:类型名 * 指针变量名
注意:
①指针变量前面的 “*” 表示该变量为指针型变量
②定义指针变量必须指定基类型。不同数据类型的数据在内存中所占的字节数存放方式是不同的

int a;
float *b;
b=&a;//这样写不对

③一个变量的指针的含义包括两个方面,一是以内存单元编号表示的纯地址,一是它指向的存储单元的数据类型
④指向整型数据的指针类型表示为“ int * ”
⑤指针变量中只能存放地址,不能将一个整数赋给一个指针变量
如:* a=100; //不合法

2、引用指针变量
(1)给指针变量赋值,如 :p=&a; //指针变量p的值时变量a的地址
(2)引用指针变量指向的变量,如:pintf(“%d”,*p);//输出变量a的值
如果有 *p=1;//将1赋给p当前所指向的变量,即a=1
(3)引用指针变量的值,如:printf(“%o”,p);//以八进制输出p的地址

例2、输入两个整数,按先大后小输出(指针方法)
不交换整型变量的值,交换两个指针的值
注意:此时改变的是地址,而不是指向的数据

#includevoid main()
{int a,b,*x,*y,*t;
    scanf("%d%d",&a,&b);
    x=&a;
    y=&b;
    if(*x<*y)
    {t=x;x=y;y=t;//x=&b;y=&a;
    }
    printf("%d %d\n",a,b);
    printf("max=%dmin=%d",*x,*y);
}
5 9
5 9
max=9min=5

3、指针变量作为参数函数
函数的参数不仅可以是整型、浮点型、字符型还可以是指针数据。它的作用是将一个变量的地址传送到另一个函数中。

例3、输入两个整数,按先大后小输出。用函数处理,指针类型数据作为参数
注意:此时对指针指向的数据进行修改

#includevoid main()
{ void swap(int * x,int * y);
 	 int a,b;
 	 scanf("%d%d",&a,&b);
 	 swap(&a,&b);
 	 printf("max=%dmin=%d",a,b);  
}
void swap(int * x,int * y)
{int t;
	if(*x<*y)
	{t=*x;
		*x=*y;
		*y=t;
	}
}
5 9
max=9min=5

不能企图通过改变指针形参的值而使指针实参的值改变,如下:

#includevoid main()
{ void swap(int * x,int * y);
 	 int a,b;
 	 scanf("%d%d",&a,&b);
 	 swap(&a,&b);
 	 printf("max=%dmin=%d",a,b);  
}
void swap(int * x,int * y)
{int *t;
	if(*x<*y)
	{t=x;
		x=y;
		y=t;
	}
}
5 9
max=5min=9
3、通过指针引用数组

1、数组元素的指针
一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素。所谓数组元素的指针就是数组元素的地址

引用数组可以用下标法,如:a[3],可以用指针法,即通过指向数组元素的指针找到所需的元素。

指针法特点:使目标程序质量高(占内存少,运行速度快)
数组名代表数组中首元素地址,如:p=&a[0];等价于p=a;

2、引用数组元素时指针的运算
在指针已指向一个数组元素时,可以对指针进行以下运算:
(1)加(减)一个整数
(2)自加(减)运算

1️⃣如果指针变量p已指向数组中的一个元素,这p+1指向同一数组中的下一个元素,p - 1指向同一数组中的上一个元素
2️⃣如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址。实际地址为p+1乘数据类型
3️⃣ *(p+i)和 *(a+i)是p+i和a+i指向的数组元素,及a[i]
4️⃣指针变量p1和p2指向同一数组中的元素,执行p1-p2,结果是(p1-p2)/数组元素的长度。如:(2020-2012)/4=2,这个结果是由意义的,表示p2所指的元素与p1所指的元素之间差几个元素.两个地址相加没有意义

5️⃣数组名是指针型常量,因此不能自增或加减(常量的性质),但是指针型常量是可以进行加减运算的(注意区别自增减与相加减,自增减是把加减后对值储存到自身去的)

3、通过指针引用数组元素
(1)下标法,如:a[i]
(2)指针法,如:*(a+i)

例4、输出数组中全部值
(1)下标法(2)通过数组名计算数组元素地址,找出元素值(3)指针法

#includevoid main()
{int a[]={1,2,3,4,5,6,7,8,9,10};
	int i,*p;
	for(i=0;i<10;i++)
	{printf("%d",a[i]);//1
	} 
		printf("\n");
	for(i=0;i<10;i++)
	{printf("%d",*(a+i));//2
	}  
		printf("\n");
	for(p=a;p<(a+10);p++)
	{printf("%d",*p);//3
	}
}

第一种第二种效率相同,第三种比前两种快,提高了执行效率

注意:数组名a代表数组首元素的地址,它是一个指针型常量,它的值在程序运行期间是固定不变的,所有a++是错误的

例5、通过指针变量输出整型数组a的10个元素

#includevoid main()
{int a[10],i,*p=a;
    for(i=0;i<10;i++)
    	scanf("%d",p++);
    	p=a;//此时p已经指向最后一位,必须重新赋值
    for(i=0;i<10;i++,p++)
    	printf("%d\n",*p);
}

注意:①指针变量 p 可以指向数组以后的存储单元,虽然编译不会出错,但是运行结果无法预期,所有要避免
②指向数组元素的指针变量可以带下标,如p[ i ] 转换为 *(p+i)
*p++;。由于++和 * 同优先级,结合方向为自右向左,因此它等价于 *(p++)++后置的时候,本身含义就是先运算后增加1(运算指的是p++作为一个整体与前面的 * 进行运算;增加1指的是p+1)。先引用 *p 的运算,然后再使p加1
④ *(p++)和 *(++p)是不同的
⑤ (*p)++ 与 ++(*p)
(*p)++,使用()强制将 * 与p结合,只能先计算 * p,然后对 *p整体的值++
++(*p),先 * p取值,再前置++,[该值+1后]作为整个表达式的值。

4、用数组名做函数参数

语法:

#includevoid main()
{void fun(int arr[],int n);//函数声明
	int array[10];//数组定义
	//省略
	fun(array,10);//用数组名作为函数参数
}
void fun(int arr[],int n)//定义函数
{//省略
}

当用数组名作为参数时,如果形参数组中各个元素发生变化,实参数组元素的值随之发生变化
(1)数组元素作为实参。定义一个函数 swap(int x,int y);调用swap(a[0],a[1]);当x,y改变,数组值不变
(2)数组名作为实参。因为数组名代表该数组首地址,而形参是用来接收从实参传递过来的数组首地址,所以形参应该是一个指针变量(只有指针变量才能够存放地址)。定义函数fun(int arr[],int n)相当于fun(int * arr,int n)
注意:实参数组名代表一个固定地址,或者说是指针常量,但形参数组名并不是一个固定地址,而是按指针变量处理。

例6、将数组a中n个整数按相反顺序存放
实参用数组名a,形参可用数组名,也可用指针变量
1、形参为数组名

#includevoid main()
{void swap(int arr[],int n);
	int a[10],i;
	for(i=0;i<10;i++)
	scanf("%d",&a[i]);
	swap(a,10);
	for(i=0;i<10;i++)
	printf(" %d",a[i]);
}
void swap(int arr[],int n)
{int i,t;
	for(i=0;i<=(n-1)/2;i++)
	{t=arr[i];
		arr[i]=arr[n-1-i];
		arr[n-1-i]=t;	
	}
}
1 2 3 4 5 6 7 8 9 10
 10 9 8 7 5 6 4 3 2 1

2、形参为指针

#includevoid main()
{void swap(int * arr,int n);
	int a[10],i;
	for(i=0;i<10;i++)
	scanf("%d",&a[i]);
	swap(a,10);
	for(i=0;i<10;i++)
	printf(" %d",a[i]);
}
void swap(int *arr,int n)
{int *i,*j,*p,t,m;
	m=(n-1)/2;//循环次数4
	p=arr+m;//a[4]
	i=arr;//起始值,数组首地址 a[0]
	j=(arr+n-1);//最后值,数组个数-1 a[9]
	for(;i<=p;i++,j--)
	{t=*i;
		*i=*j;
		*j=t;
	}	
}

归纳分析
(1)形参和实参都用数组名
如:

void main()
{int a[10];
    fun(a,10);
}
void fun(int x[],int n)
{}

在函数调用期间,形参与实参共用一段内存单元
(2)实参用数组名,形参用指针

void main()
{int a[10];
    fun(a,10);
}
void fun(int *x,int n)
{}

通过x值得改变可以指向数组a的任一元素
(3)实参和形参都用指针变量

void main()
{int a[10],*p;
    p=a;
    fun(a,10);
}
void fun(int *x,int n)
{}

通过x值得改变可以指向数组a的任一元素
(4)实参为指针变量,形参为数组名

void main()
{int a[10],*p;
    p=a;
    fun(a,10);
}
void fun(int x[],int n)
{}

对例6修改,形参和实参为指针

#includevoid main()
{void swap(int * arr,int n);
	void swap(int *arr,int n);
	int a[10],i,*p=a;
	for(i=0;i<10;i++,p++)//通过指针变量自增访问数组元素
	scanf("%d",p);//p为地址,所以不用加&符
	swap(a,10);
	p=a;//注意要重新赋值
	for(i=0;i<10;i++,p++)
	printf("%d ",*p);
	
}
void swap(int *arr,int n)
{int *i,*j,*p,t,m;
	m=(n-1)/2;//循环次数4
	p=arr+m;//a[4]
	i=arr;//起始值,数组首地址 a[0]
	j=(arr+n-1);//最后值,数组个数-1 a[9]
	for(;i<=p;i++,j--)
	{t=*i;
		*i=*j;
		*j=t;
	}	
}

例7、用指针方法对10个整数按照由大到小排序

#includevoid main()
{void sort(int x[],int n);
	int a[10],i,*p=a;
	for(i=0;i<10;i++)
	scanf("%d",p++);
	sort(a,10);
	p=a;
	for(i=0;i<10;i++)
	printf("%d ",*p++);
	
}
void sort(int x[],int n)
{int i,j,t;
	for(i=0;ifor(j=0;j	if(x[j]		t=x[j];
				x[j]=x[j+1];
				x[j+1]=t;
			}
		}
	}
}
5、通过指针引用字符串

1、字符串的引用方式
(1)用字符数组存放一个字符串,可以通过数组名和下标引用字符串中的一个字符,也可以通过数组名和格式声明%s输出该字符串
如:

#includevoid main()
{char string[]="l love China";
    printf("%s\n",string);
    printf("%c\n",string[7]);
}
l love China
C

(2)用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量
如:

#includevoid main()
{char * string="l love China";//定义字符指针变量string并初始化
    printf("%s\n",string);
}
l love China

对字符串指针变量string初始化,实际上是把字符串第一个元素的地址(即存放字符串的字符串数组的首地址)赋给指针变量string,使string指向字符串的第一个字符
注意:
①在C语言中只有字符变量,没有字符串变量
②在定义char * string="l love China";时不是吧“l love China”这些字符串存放到string中(指针变量只能存放地址),只是把“l love China”的第一个字符的地址赋给指针变量string
③可以对指针变量进行再赋值,string="l am a student";此后string就指向“l am a student”的第一个字符,不能再引用“l love China”了
printf("%s",string);系统会输出string所指向的字符串第一个字符,然后自动使string+1指向下一个字符,再输出字符,往复直到遇到'0'为止

例8、将字符串a,复制为字符串b,然后输出字符串b
数组下标法:

#includevoid main()
{char a[]="l love China",b[20];
	int i;
	for(i=0;*(a+i)!='\0';i++)
	{*(b+i)=*(a+i);
	}
	*(b+i)='\0';
	printf("%s\n",a);
	printf("%s\n",b);
}

指针法:

#includevoid main()
{char a[]="l love China",b[20];
	int i;
	char *p1,*p2;
	p1=a;p2=b;
	for(i=0;*(p1+i)!='\0';i++)
	{*(p2+i)=*(p1+i);
	}
	*(p2+i)='\0';
	printf("%s\n",a);
	printf("%s\n",b);
}
八、结构体 1、结构体类型的定义

1、自己建立结构体类型
用户自己建立由不同类型数组组成的组合型的数据结构,它成为结构体
语法:struct 结构体名 {成员表列};
(1)struct是声明结构体类型时必须使用的关键字,不能省略
(2)结构体名是由用户指定的,以区别于其它结构体类型
(3)花括号内是该结构体所包括的子项,成为结构体的成员
成员可以属于另一个结构体类型
如:

struct Date
 	{int year;
 		int month;
 		int day;
 	};
 	struct Student
 	{int num;
 		char name[20];
 		char sex;
 		int age;
 		struct Date birthday;//成员birthday属于struct Date类型
 		char addr[30];
 	};

2、定义结构体类型变量
前面只是建立了一个结构体类型,它相当于一个模型,并没有定义变量,其中并无数据,系统对之也不分配存储单元。

  1. 先声明结构体类型,再定义该类型的变量
    如:struct Student student1,student2;
    在定义了结构体变量后,系统会为之分配存储单元。根据结构类型中包含的成员情况 Student (4+20+1+4+12+30=71)

  2. 在声明类型的同时定义变量
    语法:struct 结构体名 {成员列表}变量名列表;
    如:

    struct Student
     	{int num;
     		char name[20];
     		char sex;
     		int age;
     		float score;
     		char addr[30];
     	} student1,student2;
  3. 不指定类型名而直接定义结构体类型变量
    语法:struct {成员列表}变量名列表;

注意:
①结构体类型与结构体变量名是不同概念,在编译时,对类型不分配空间,只对变量分配空间
②结构体类型中的成员名可以与程序中的变量名相同,但二者不代表同一对象
③对结构体类型中的成员,可以单独使用,它的作用与地位相当于成员变量

3、结构体变量的初始化和引用
在定义结构体变量时,可以对它初始化,即赋予初始值,然后引用这个变量
例1、把一个学生的信息(包括学号、姓名、性别、住址)放在一个结构体变量中,然后输出这个学生的信息

#includevoid main()
{struct Student
 	{int num;
 		char name[20];
 		char sex;
 		char addr[30];
 	} student1={111,"liu wei",'M',"wei hai"};
 	printf("%d %s %c %s",student1.num,student1.name,student1.sex,student1.addr);
}
111 liu wei M wei hai

(1)是对结构体变量的初始化,而不是对结构体类型的初始化
C99标准允许对某一成员初始化,如:struct Student studnet1={.num=1}.num隐含代表结构体变量student1中的成员变量student1.num
(2)可以引用结构体变量的值,如:结构体变量名.成员名
(3)如果成员本身又属于一个结构体类型,则要用若干个成员变量符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。如:student1.birthday.year;
(4)对结构体变量的成员可以像普通变量一样进行各种运算
(5)同类型的结构体变量可以相互赋值,如student1=student2;
(6)可以引用结构体变量成员的地址,也可以引用结构体变量的地址,如:scanf("%d",&student1.num); printf("%o",&student1);结构体变量的地址主要用作函数的参数,传递结构体变量的地址

例2、输入两个学生的学号、姓名和成绩,输出成绩较高的学生的学号、姓名和成绩

#includevoid main()
{struct Student
 	{int num;
 		char name[20];
 		float score;
 	} student1,student2;
	printf("学生1\n");
 	scanf("%d%s%f",&student1.num,student1.name,&student1.score);
	printf("学生2\n");
 	scanf("%d%s%f",&student2.num,student2.name,&student2.score);
 	if(student1.score>student2.score)
 		printf("%d %s %.2f",student1.num,student1.name,student1.score);
 	else if(student1.scoreprintf("%d %s %.2f\n",student1.num,student1.name,student1.score);
 	 	printf("%d %s %.2f\n",student2.num,student2.name,student2.score);
 	 }
}
学生1
100 liuwei 90
学生2
200 liuwe 100
200 liuwe 100.00
2、使用结构体数组

1、定义结构体数组
语法:
(1)struct 结构体名{成员列表} 数组名[数组长度];
(2)结构体类型 数组名[数组长度]={初值列表}

例3、有3个候选人,每个选民只能投票选一人,要求编写一个统计选票的程序,先后输入被选人的名字,输出各个人的得票情况

#include#includestruct Person
{char name[20];
	int count;
} leader[3]={"li",0,"liu",0,"wang",0};

void main()
{char a[30];
	int i,j;
	for(i=1;i<=5;i++)
	{scanf("%s",a);
		for(j=0;j<3;j++)
		{	if(strcmp(a,leader[j].name)==0) leader[j].count++;
		}
	}
	for(i=0;i<3;i++)
	{printf("%s  %d\n",leader[i].name,leader[i].count);
	}
}
li
liu
li
wang
li
li  3
liu  1
wang  1
3、结构体指针

结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针。如果把一个结构体变量的地址存放在一个指针变量中,那么这个指针变量就指向该结构体变量

1、指向结构体变量的指针
指向结构体对象的指针变量既可以指向结构体变量,也可指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同。
如:struct Student *p;//p可以指向struct Student 类型的变量或数组元素

例4、通过指向结构体变量的结构体指针变量输出结构体变量中的成员

#includevoid main()
{struct Student
	{int num;
		char name[20];
		char sex;
		char addr[30];
	};
	struct Student stu1={1010,"liu wei",'M',"wei hai"};
	struct Student *pt;
	pt=&stu1;
 	printf("%d %s %c %s\n",stu1.num,stu1.name,stu1.sex,stu1.addr);
 	printf("%d %s %c %s\n",(*pt).num,(*pt).name,(*pt).sex,(*pt).addr);
}
1010 liu wei M wei hai
1010 liu wei M wei hai

注意:* p两侧的括号不可省略,因为成员运算符“.”优先于“*”运算符,*p.num就等价于 * (p.num)了
如果p指向一个结构体变量stu,一下3种用法等价
(1)stu.成员名,如:stu.num;
(2)(*p).成员名,如:(*p).num;
(3)p->成员名,如:p->num;

2、指向结构体数组的指针
例5、有3个学生的信息,放在结构体数组中,要求输出全部学生的信息

#include#includevoid main()
{struct Student
	{int num;
		char name[20];
		char sex;
		char addr[30];
	};
	struct Student stu1[3]={1010,"liu wei",'M',"wei hai",
		1011,"li na",'M',"beijing",
		1012,"jiayinghan",'M',"tai an"};
	struct Student *pt;
	pt=stu1;
	for(;ptnum,pt->name,pt->sex,pt->addr);
}
1010 liu wei M wei hai
1011 li na M beijing
1012 jiayinghan M tai an

注意:
(1)如果p的初值为stu,即指向stu的序号为0的元素,p+1后,p就指向下一元素,如:

(++p)->num;//先使p+1(即stu1[1]),然后得到p指向的num值(即1011)
(p++)->num;//先得到p指向的num值(即1010),然后使p+1(即stu1[1])

(2)程序定义的p是一个指向struct Student类型的指针变量,不能存储其他类型的地址,如:p=stu1[0].name;//stu1[0].name是stu[0]元素的成员变量name的首字符的地址可以强转后赋值,如:p=(struct Student*)stu1[0],name;

3、用结构体变量和结构体变量的指针作函数参数
(1)用结构体变量的成员作参数
(2)用结构体变量作参数
(3)用结构体变量的指针作参数

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


新闻标题:C语言详细知识点(下)-创新互联
文章出自:http://shouzuofang.com/article/dcipgp.html

其他资讯