4、函数
4.1、传参
值传递
// 在被调用的函数中,只能使用实参的值,而不能去修改实参的值,例如:
void fun1(int x,int y)
{
printf("fun1:%d,%d\n",x,y); //只能使用实参的值
x = 20; //不能改变实参的值
y = 30;
}
指针传递
// 在被调用函数中,既可以使用实参的值,也可以改变实参的值,例如:
void fun2(int* x,int* y)
{
printf("fun2:%d,%d\n",*x,*y); //可以使用实参的值,
*x = 100; //可以改变实参的值
*y = 200;
}
int sum(int *ar,int n)
{
//代码
}
int sum(int ar[],int n)
{
//代码
}
错误思路:
只要函数传参是数值就是值传递,那么改变形参,实参不会变化。
只要函数传参是地址就是址传递,改变形参,对应的实参就会改变。 //因为在函数中,指针的指向可能改变,不在指向实参。
正确思路:
先分析代码,发现改变形参能够改变对应的实参,这个函数就属于址传递。
其他传参方式可以见数组&指针&函数的结合的章节
4.2、return作用
(1)结束当前函数;
(2)return语句只能传一个数据到函数外部
return sum,sub,mul,div; //只会返回div。
如果一个函数的返回值类型确定是int,一般return一个整形数据,也可以不写return语句;
如果一个函数的返回值类型确定是void,一般return语句可不写,也可以有return语句,但是不能return一个数值,只能return。
5、数组
5.1、一维数组
字符数组
char str[] = {'h','e','l','l','o','\0'};
一般简写为
char str[6] = {"hello"};
char str[ ] = {"hello"};
char str[6] = "hello";
char str[ ] = "hello";
5.1.1、初始化!!!
完全初始化
int a[5] = {1,2,3,4,5};
int a[ ] = {1,2,3,4,5}; //元素个数可以省略
//字符数组----比较特殊:保存字符串
char str[6] = {'h','e','l','l','o','\0'};
|
字符串结束标志
char str[ ] = {'h','e','l','l','o','\0'};
字符数组初始化一般会简写,如下:
char str[6] = {"hello"};
char str[ ] = {"hello"};
char str[6] = "hello"; //常用
char str[ ] = "hello"; //常用
部分初始化
int a[10] = {1,2,3};
//字符数组:
char str[10] = {'h','e','l','l','o','\0'};
char str[10] = {"hello"};
char str[10] = "hello"; //常用
指定元素初始化
int a[10] = {[3]=10,11,12,[7]=100,[3]=20};
5.1.2、赋值!!!
除了初始化之外,数组不能整体赋值。(意思是先定义再赋值时,只能一个一个赋值)
int a[5];
int i;
a = {1,2,3,4,5}; //错误
a[5] = {1,2,3,4,5}; //错误
//a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; a[4]= 5;
for(i = 0; i < 5) //循环语句给数组赋值
a[i] = i+1;
//字符数组---通过字符串函数赋值:strcpy()
char str[10];
strcpy(str,"hello");
5.1.3、打印!!!
除了字符数组之外,不能整体打印,只能一个一个元素分别打印。
for(i = 0; i < 5; i++)
printf("%d\n",a[i]);
//字符数组-可以直接打印:%s
printf("%s\n",str);
5.1.4、存储
数组的存储空间是连续的,数组名为数组空间的名称。
数组的第一个元素分配的内存地址最小,从小到大依次分配空间。
5.2、多维数组
5.2.1、初始化
完全初始化
int a[2][3] = {{1,2,3},{4,5,6}};
部分初始化
int a[3][4] = {{1,2}};
指定元素初始化
int a[3][4] = {[2] = {[1]=10,[3]=20}};
5.2.2、赋值
一般通过循环赋值
5.2.3、打印
一般通过循环语句打印
5.2.4、存储
数组的存储空间是连续的,数组名为数组空间的名称。
数组的第一个元素分配的内存地址最小,从小到大依次分配空间。
5.3、变长数组
概念:定义数组时,如果数组的元素个数通过变量指定,则这样的数组称为变长数组。
n = 5;
int a[];
注意:
变长数组不能初始化。!!!
变长数组不能在函数外定义,也就是不能定义成全局的数组。
一次申请,数组大小就不变了。
int main()
{
int n;
int a[];
scanf("%d",&n);
printf("%d\n",sizeof(a));
n =100;
printf("%d\n",sizeof(a));
n = 10;
printf("%d\n",sizeof(a));
return 0;
}
输出结果:
5
20
20
20
5.4、数组的运算
5.5、数组的特殊点
缓冲区清空函数
bzero()
使用需包含#include <strings.h>
memset()
使用需包含#include <string.h>
子数组
注意:
字符数组—通过strcpy赋值,%s打印。
数组之间的比较用strcmp()函数。
其他类型数组–通过循环语句赋值和打印。
6、指针
6.1、概念和定义
用于保存内存地址的变量,称为指针变量,简称指针。
int *p; //一般和指针名挨着
或
int* p;
注意:
int* p,q;和int *p,q;//都是定义指针p,int q
int *p,*q;//定义了两个指针
6.2、指针的用法
*p ------- 指针指向的数据
p ------- 指针的空间
&p ------- 指针的空间地址
6.3、指针的运算
=,&,*,+,-,>,>=,<,<=,==,!=
注意:
同类型指针可以相减,相减的结果为整数。
指针运算以基类型大小为单位。
基类型不同的指针不能进行运算。
6.4、const关键字用来定义指针!!!
用法一
const int * p1 = &a; 或 int const * p1 = &a;
*p1 = 100; //错误,在定义指针时,加了const,所以*p1为只读不能赋值
a = 11; //正确,不影响a
用法二
int * const p2 = &a;
*p2 = 100;
p2 = &b; //p不能赋值,因为在定义时加const,所以,p为只读
用法三
const int * const p = &a; //这时p和*p都不能赋值
6.5、数组&指针&函数的结合!!!
6.5.1、数组与指针的结合使用
假设定义了
int a[5];
数组名a-------表示数组在内存中的空间的名称,sizeof(a);也表示数组的第一个元素的地址,而且是地址常量(指针),a = &a[0]。
针对一维数组
int a[10]={1,2,3,4,5,6,7,8,9,10};
int *p;
p = a;
四种写法:
a[5] *(a+5) *(p+5) p[5]
//*(p+5)->*(a+5)->*(&a[0]+5)->*(&a[5])->a[5]
//注意:
a+1 &a[1]
a++ 错误写法
p++ &a[1] //p本身不变
++p &a[1] //p指向了a[1]
针对二维数组
int a[2][4];
在数值上相等:a &a[0] &a[0][0] a[0]
在关系上相等:
a == &a[0]
a[0] == &a[0][0]
a[i] == &a[i][0]
int a[2][4];
int (*p)[4]; //数组指针
p = a;
四种写法:
a[1][2] *(*(a+1)+2) *(*(p+1)+2) p[1][2]
数组指针&指针数组
int (*p)[4]; //数组指针 本质上是一个指针,保存int[4]类型的地址
int *p[4]; //指针数组 本质上是一个数组,数组下的4个成员都是int *指针
6.5.2、函数与指针的结合使用
函数指针&指针函数
int *fun(int n){} //指针函数
int (*pfun)(int n) //函数指针。保存函数的地址,函数名字就是函数的地址
6.5.3、数组与函数的结合使用
要把数组传到函数中进行操作,数组名做实参,形参有三种写法:
//数组作为形参,本质上还是指针
假设有int a[10],则
int *p
int a[10]
int a[]
假设有int a[2][4];则
int (*p)[4]
int p[2][4]
int p[][4]
以下声明等价(函数原型可以省略参数名)
int sum(int *ar,int n);
int sum(int *,int);
int sum(int ar[],int n);
int sum(int [],int)
以下定义等价(*函数定义中不能省略参数名。只有在函数原型或函数定义头中,才可以用int ar[]代替int ar)
int sum(int *ar,int n)
{
//代码
}
int sum(int ar[],int n)
{
//代码
}
实例:
int sum(int *ar)
{
int i;
int total = 0;
for(i=0;i<10;i++)
total += ar[i]; //ar[i]与*(ar+i)相同
return total;
}
int sum(int *ar,int n) //常用方式
{
int i;
int total = 0;
for(i=0;i<n;i++)
total += ar[i]; //ar[i]与*(ar+i)相同
return total;
}
6.5.4、函数指针数组
void (*p[10])(int,int); //一次性定义10个函数指针
7、结构体
7.1、概念
不同数据类型的集合。
7.2、声明结构体类型
struct 结构体类型名
{
成员类型 结构体成员名;
成员类型 结构体成员名;
...
};
例:
struct People
{
char name[20];
int height;
int weight;
float score;
char num[5];
};
7.3、定义结构变量(三种写法)
写法一
struct People
{
char name[20];
int height;
int weight;
float score;
char num[5]; //001
};
struct People xiaoming;
写法二
struct People
{
char name[20];
int height;
int weight;
float score;
char num[5]; //001
}xiaoqiang;
写法三
struct
{
char name[20];
int height;
int weight;
float score;
char num[5]; //001
}xiaoqiang;
7.4、结构体成员赋值
初始化:可以整体赋值
struct People xiaoming={
"xiaoming",
"001",
173,
120,
99.9
};
struct People xiaoming={
.name="xiaoming",
.height=173,
.weight=120,
.score=99.9,
.num="001"
};
struct People xiaoming={
name:"xiaoming",
height:173,
weight:120,
score:99.9,
num:"001"
};
先定义再赋值时,不可以整体赋值,只能一一赋值。
可以把一个结构体直接赋给另一个结构体。
7.5、结构体成员的引用
结构体变量名.结构体成员名
结构体指针名->结构体成员名
(*结构体指针名).结构体成员名
注意:
.和->是结构体、共用体的符号。优先级比()低,比单目高。
7.6、结构体指针
typedef struct person{
chaer name[20];
int age;
}person_t,*pperson_t;
person_t <=> struct person
pperson_t <=> struct person *
struct People *p =(struct People *)malloc(sizeof(struct People));
结构体指针也是指针,只有四个字节的空间,用于存放结构体变量的地址。在操作结构体指针之前,要先指向一块明确的空间。
7.7、结构体数组
struct Seq{
int a[10];
int length;
};
7.8、结构体中包含数组(顺序表)
struct ABC
{
int a[10];
int length;
};
struct ABC *p=(struct ABC *)malloc(sizeof(struct ABC));
p->a[i]
7.9、结构体中包含指针(链表)
struct QQ
{
int *p;
int n;
};
int main(void)
{
struct QQ value;
value.p=(int *)malloc(40);//还需要给结构体的指针成员p申请指向的空间。
*(value.p)=10;
value.n=100;
return 0;
}
//链表
struct Link
{
int data;
struct Link *next;
};
int main(void)
{
return 0;
}
7.10、结构体求大小
struct A
{
int a;
char b;
int c;
char d;
}; //16
struct A
{
int a;
char b;
char d;
int c;
}; //12
7.11、位域
struct 结构体类型名
{
int a:10;
int b:10;
int c:12;
};
如上,指定对应的位数。
#include <stdio.h>
struct QQ
{
int a:10;
int b:10;
int c:12;
};
int main(void)
{
struct QQ q;
printf("%d\n",sizeof(q));
q.a=2;
q.b=2;
q.c=1;
int *p=(int *)&q;
printf("%x\n",*p);
return 0;
}
输出结果
4
100802
7.12、联合体
概念
联合体是同一内存空间存放不同类型数据的一种复合类型。不同类型数据称为联合体成员。
需要说明联合体类型中具体包含哪些类型,所以联合体类型也是自定义类型。
定义
union <联合体标签>{
成员类型 成员名;
成员类型 成员名;
... ...
成员类型 成员名;
};
例如:
union A{
int x;
float y;
};
使用
联合体变量不能初始化
在同一时刻只有一个成员是可以用的。
7.13、枚举类型——本质为整型
定义
enum <枚举类型标签>{
//要限定的取值,这个值称为枚举常量,本质为整型常量
枚举常量, //默认第一个为0 ,下面的值依次加1
枚举常量, // 1
枚举常量, // 2 ....
枚举常量,
枚举常量,
枚举常量,
枚举常量,
};
例如:
enum week{
mon, //默认第一个为0 ,下面的值依次加1
tus, // 1
wed, // 2 ....
thi,
fri,
sat,
sun,
};
在定义枚举类型时,也可是指定枚举常量的值,如下:
enum week{
mon = 10, //指定mon的值为10,后续的值依次加1
tus, // 11
wed, // 12 ....
thi,
fri = 11, //指定fri值为11,则后续的值在fri基础上依次加1
sat,
sun,
};