精通到熟悉C系列1——函数&数组&指针&结构体

这篇博客详细讲解了C语言中的函数,包括值传递和指针传递,以及return的作用。接着介绍了数组,涵盖一维数组、多维数组和变长数组的初始化、赋值、打印和存储。此外,还探讨了指针的概念、运算以及与函数、数组的结合使用。最后,文章阐述了结构体的概念、声明、定义及其在存储、引用、指针和数组中的应用。
摘要由CSDN通过智能技术生成

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,
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值