C语言详细知识点(下)

news/2024/4/30 3:51:13/文章来源:https://blog.csdn.net/qq_52108058/article/details/127960529

⛄️上一篇⛄️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,要求逆序输出

#include <stdio.h>
void 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个斐波那契数列

#include <stdio.h>
void 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个地区面积,要求对它们按由小到大的顺序排列(冒泡排序)

#include <stdio.h>
void 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、将一个二维数组的行和列互换,存到另一个二维数组中
在这里插入图片描述

#include <stdio.h>
void 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的矩阵,求出其中最大值和行号列号

#include <stdio.h>
void 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的地址
如:

#include <stdio.h>
void 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中去
如:

#include <stdio.h>
void 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,则函数值为一个负整数
如:

#include <stdio.h>
void main()
{char str1[]={"Bejing"},str2[]={"China"};printf("%d",strcmp(str1,str2));
}
-1

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

#include <stdio.h>
void main()
{char str1[]={"Bejing"};printf("%d",strlen(str1));
}
6

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

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

#include <stdio.h>
void 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、输出以下结果,用函数调用实现
在这里插入图片描述

#include <stdio.h>
void 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、输入两个整数,求出其中较大者

#include <stdio.h>
void 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、输入两个实数求和

#include <stdio.h>
void 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个整数,找出其中最大的数。用函数的嵌套调用来处理

#include <stdio.h>
void 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!

#include <stdio.h>
void 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、通过指针访问整型变量

#include <stdio.h>
void 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、输入两个整数,按先大后小输出(指针方法)
不交换整型变量的值,交换两个指针的值
注意:此时改变的是地址,而不是指向的数据

#include <stdio.h>
void 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、输入两个整数,按先大后小输出。用函数处理,指针类型数据作为参数
注意:此时对指针指向的数据进行修改

#include <stdio.h>
void 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

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

#include <stdio.h>
void 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所指的元素之间差几个元素.两个地址相加没有意义

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

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

#include <stdio.h>
void 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个元素

#include<stdio.h>
void 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、用数组名做函数参数

语法:

#include<stdio.h>
void 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、形参为数组名

#include<stdio.h>
void 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 1010 9 8 7 5 6 4 3 2 1

2、形参为指针

#include<stdio.h>
void 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;//循环次数4p=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修改,形参和实参为指针

#include <stdio.h>
void 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;//循环次数4p=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个整数按照由大到小排序

#include <stdio.h>
void 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;i<n-1;i++)//外层循环0-8,9次{for(j=0;j<n-i-1;j++)//第一次内循环比较次数9每次-1{if(x[j]<x[j+1]){t=x[j];x[j]=x[j+1];x[j+1]=t;}}}
}

5、通过指针引用字符串

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

#include <stdio.h>
void main()
{char string[]="l love China";printf("%s\n",string);printf("%c\n",string[7]);
}
l love China
C

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

#include <stdio.h>
void 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
数组下标法:

#include <stdio.h>
void 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);
}

指针法:

#include <stdio.h>
void 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、把一个学生的信息(包括学号、姓名、性别、住址)放在一个结构体变量中,然后输出这个学生的信息

#include <stdio.h>
void 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、输入两个学生的学号、姓名和成绩,输出成绩较高的学生的学号、姓名和成绩

#include <stdio.h>
void 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.score<student2.score)printf("%d %s %.2f",student2.num,student2.name,student2.score);else{printf("%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 <stdio.h>
#include <string.h>
struct 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、通过指向结构体变量的结构体指针变量输出结构体变量中的成员

#include <stdio.h>
void 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 <stdio.h>
#include <stdio.h>
void 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(;pt<stu1+3;pt++)printf("%d %s %c %s\n",pt->num,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)用结构体变量的指针作参数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_228021.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

链表之反转链表

文章目录链表之反转链表题目描述解题思路代码实现链表之反转链表 力扣链接 题目描述 定义一个函数&#xff0c;输入一个链表的头节点&#xff0c;反转该链表并输出反转后链表的头节点。 示例&#xff1a; ​ 输入: 1->2->3->4->5->NULL ​ 输出: 5->4-&…

基于vue项目的代码优化

前言 项目上线后其整体性能的优良是用户也是研发人员所关注的。项目优化非常重要&#xff0c;一丝一毫的提升都是对用户的负责。因此我们在开发中就应该注重细节&#xff0c;优化工作从日常开发做起。本篇文章就分享一些在日常开发中代码层面的优化手段。 开发常用优化手段 …

达摩院快速动作识别TPS ECCV论文深入解读

一、论文&代码 论文&#xff1a;https://www.ecva.net/papers/eccv_2022/papers_ECCV/papers/136630615.pdf 模型&代码&#xff1a;ModelScope 魔搭社区 二、背景 高效的时空建模(Spatiotemporal modeling)是视频理解和动作识别的核心问题。相较于图像的Transforme…

开源共建 | TIS整合数据同步工具ChunJun,携手完善开源生态

TIS整合ChunJun实操 B站视频&#xff1a; https://www.bilibili.com/video/BV1QM411z7w5/?spm_id_from333.999.0.0 一、ChunJun 概述 ChunJun是一款易用、稳定、高效的批流统一的数据集成框架&#xff0c;可基于实时计算引擎Flink实现多种异构数据源之间的数据同步与计算&…

flink学习

Flink学习之路&#xff08;一&#xff09;Flink简介 - 走看看 Flink(一)-基本概念 - 知乎 Flink架构&#xff1a; Flink整个系统包含三个部分&#xff1a; 1、Client&#xff1a; 给用户提供向Flink系统提交用户任务&#xff08;流式作业&#xff09;的能力。用户提交一个F…

全球无人驾驶大洗牌,百度Apollo Day宣告Robotaxi进入2.0时代

作者 | 德新 编辑 | 王博1. 全球无人驾驶大洗牌&#xff0c;Robotaxi越发向头部聚集 全球无人驾驶落地正呈现两幅面孔。随着资本热潮褪去&#xff0c;一部分公司在资金和研发上已经难以为继&#xff0c;Robotaxi落地的资源和希望&#xff0c;正无限向头部公司聚集。 10月&#…

OVS DPDK VXLAN隧道处理

在学习OVS VXLAN实现之前&#xff0c;我们先回顾一下传统VTEP设备是如何处理VXLAN报文的。如下图所示&#xff1a; vxlan报文进入交换机端口后&#xff0c;根据报文头部信息进行vxlan隧道终结。隧道终结后&#xff0c;根据underlay信息进行overlay映射&#xff0c;得到overlay的…

鲲鹏devkit性能分析工具介绍(四)

鲲鹏devkit性能分析工具介绍&#xff08;四&#xff09; 前面我们已经介绍了鲲鹏devkit性能分析工具的全景分析、热点函数分析、进程/线程分析、微架构分析、和访存分析&#xff0c;由此可见进行性能调优绝对不能够仅仅去进行一方面的考察而是需要全方面的数据分析进行一定的舍…

8、多进程之间的通信

多进程之间的常用通信方法有两种&#xff0c;及Queue和Pipe 一、Queue Queue([maxsize])&#xff1a;创建共享的进程队列。maxsize是队列中允许的最大项数。如果省略此参数&#xff0c;则无大小限制。底层队列使用管道和锁定实现。另外&#xff0c;还需要运行支持线程以便队列中…

[附源码]计算机毕业设计springboot基于Web的软考题库平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[论文阅读] Curriculum Semi-supervised Segmentation

[论文地址] [代码] [MICCAI 19] Abstract 本研究调查了半监督CNN分割的课程式策略&#xff0c;它设计了一个回归网络来学习图像级信息&#xff0c;如目标区域的大小。这些回归被用来有效地规范分割网络&#xff0c;约束未标记图像的softmax预测&#xff0c;使其与推断的标签分…

16-JavaSE基础巩固项目:拼图小游戏

阶段项目-拼图小游戏 一、项目介绍 1、目的 锻炼逻辑思维能力&#xff0c;让我们知道前面学习的知识点在实际开发中的应用场景。 1、为了学习一个新知识&#xff1a;GUI GUI全称&#xff1a;Graphical User Interface&#xff08;又称图形用户接口&#xff09;是指采用图形化…

【Android进阶之旅】内存泄漏的危害有哪些?(案例分析)

随着计算机应用需求的日益增加&#xff0c;应用程序的设计与开发也相应的日趋复杂&#xff1b; 开发人员在程序实现的过程中处理的变量也大量增加&#xff0c;如何有效进行内存分配和释放&#xff0c;防止内存泄漏的问题变得越来越突出 例如&#xff1a; 服务器应用软件&#x…

Redis 内存淘汰和过期删除策略

提起使用Redis的优点&#xff0c;大家可以列举出许多&#xff0c;比如&#xff1a;数据存储在内存&#xff0c;读写速度快&#xff0c;性能优异。比如数据持久化&#xff0c;便于数据备份及恢复等等。 分布式服务系统平台发展至今&#xff0c;Redis活跃在平台的各个领域&#…

RabbitMQ事务消息

通过对信道的设置实现 channel.txSelect()&#xff1b;通知服务器开启事务模式&#xff1b;服务端会返回Tx.Select-Ok channel.basicPublish&#xff1b;发送消息&#xff0c;可以是多条&#xff0c;可以是消费消息提交ackchannel.txCommit() &#xff1b;提交事务&#xff1b;…

mmdetection3d SUN RGB-D数据集预处理

SUN RGB-D是普林斯顿大学发布的一种关于室内场景理解的数据集&#xff0c;共包含了10335个样本&#xff0c;其中训练样本和验证测试样本数量分别为5285和5050。每个样本包含了彩色图像&#xff08;RGB&#xff09;和深度&#xff08;D&#xff09;信息&#xff0c;并且分别进行…

基于BDD的接口自动化框架开箱即用

1、背景说明 项目思想&#xff1a;BDD 行为驱动开发的思想褒贬不一&#xff0c;这里不多说。遵循的宗旨能解决业务痛点的思想就是好思想。 接口测试工具在实际的业务测试场景中往往会遇到一些使用上的局限性&#xff0c;自定义扩展要求技术较高&#xff0c;如果二次开发工具…

小程序瀑布流实现

什么是瀑布流布局 瀑布流布局&#xff0c;一般等宽&#xff0c;不等高的列表排列 原理是找出高度之和最小的那一列&#xff0c;在高度最小列继续添加元素 可以通过 absolute 定位实现&#xff0c;动态计算每一项的 top 和 left 封装瀑布流方法 function getAllRect(context…

[附源码]Python计算机毕业设计Django的疫苗接种管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

c#、wpf开发中页面在win10下被缩放125%引起页面错乱的解决办法。

正常情况下,我们开发的页面页面应该是100%缩放的,这样程序在win7和win10下保持一致,但是win10里面会根据显示器的情况自动调整“缩放与布局”,这使得桌面程序有时候会发生页面错乱,怎么调整就是个问题。 如图:在“缩放与布局”100%显示如下: 而在 “缩放与布局”125%显…