C语言学习(九)多文件编程 存储类型 结构体

一、多文件编程

编写一个大型文件时,一般会按照功能生成多个.c文件,

(一)不写头文件的方方式进行多文件编程

extern(存储类型):定义的内容在其他文件中。
类似文件声明,只不过声明的函数在其他文件中。

eg
function1.c 文件

#include <stdio.h>

void function1()
{
	printf("This is fun1 file\n");
}

function2.c 文件

#include <stdio.h>

void function2(int len)
{
	for(int i=0;i<len;i++)
	{
		printf("This is fun2 file\n");
	}
}

test.c 文件

#include <stdio.h>

extern void function1(); //function()为定义在其他文件中的函数
extern int function2(int len);

int main()
{
	function1();
	function2(3);
	return 0;
}

编译方法
gcc test.c function1.c function2.c -o test

输出结果:
在这里插入图片描述

(二)通过头文件方式进行多文件编程

(1)方法

#include "head1"
  • 注:使用引号,是先在自己的路径下找,再去系统的库文件去寻找

编译方法
gcc test.c function1.c function2.c
或者
gcc *.c 意思是编译所有.c文件

(2)头文件守卫

防止重复导入头文件
head1.h文件

#ifndef __HEAD1_H__ 
#define __HEAD1_H__

/***code***/

#endif

(三) 使用多文件编程实现+ - * / 功能

eg
main.c文件

#include "func.h"                                                                                                                                                                 

int main(int argc, const char *argv[])
{
    int x,y;
    char op;

    printf("please input:");
    scanf("%d%c%d",&x,&op,&y);

    switch(op)
    {
    case '+':
        printf("%d%c%d=%d\n",x,op,y,add(x,y));
        break;
    case '-':
        printf("%d%c%d=%d\n",x,op,y,sub(x,y));
        break;
    case '*':
        printf("%d%c%d=%d\n",x,op,y,mul(x,y));
        break;
    case '/':
        printf("%d%c%d=%d\n",x,op,y,div(x,y));
        break;
    }

    return 0;
}

func.h文件

#ifndef __FUN_H__
#define __FUN_H__

#include <stdio.h>

extern int add(int x,int y); 
extern int mul(int x,int y); 
extern int div(int x,int y); 
extern int sub(int x,int y);                                                               

#endif

add.c 文件

int add(int x,int y)
 {
     return x+y;
 } 

sub.c 文件

int sub(int x,int y)
 {
     return x-y;
 } 

mul.c 文件

int mul(int x,int y)
 {
     return x*y;
 } 

div.c 文件

int div(int x,int y)
 {
     return x/y;
 } 

输出结果
在这里插入图片描述

二、存储类型

<存储类型> <数据类型> 标识符 = value;

(一)auto

主要用于修饰自动类型的变量,局部变量就是自动类型。
如果没有赋初值,就是随机值。(因为在栈区)
eg:auto int a = 10; //auto可以省略不写

(二)register

寄存器是硬件设备中的一小块空间,定义为rigister类型的变量可以使运算速度变快;但是其大小一般是4个字节,且个数有限,价格昂贵,所以尽量不要使用register。

由于register类型的变量是存储在寄存器上,因此无法取得它的地址。

(三)const

const修饰的变量是只读变量。不是常量。

1. const修饰局部变量和全局变量

const修饰的局部变量存储在内存中的栈区,可以通过指针修改(会报警告)
const修饰的全局变量存储在常量区的readonly区,不能通过指针修改

/***此处代码是为了理解***/
#include <stdio.h>

const int number2 = 20;

int main()
{
	const int number = 10;
	printf("number = %d",number);//可读

	number = 20; //对只读常量进行修改会报错
	
	//通过指针修改可以修改,但是会报警告
	int *p = &number;
	*p = 200;
	printf("number = %d\n",number);

	//const修饰的全局变量通过指针也不可修改
	int *p2 = &number2;
	*p2 = 200;
}  

2. const修饰指针

const修饰的指针,const在谁前面谁就不能修改

/***此处代码是为了理解***/
#include <stdio.h>
int main()
{
	int number1 = 10;
	int number2 = 20;
	//*p1不能修改 p1可修改
	const int *p1 = &number1;
	*p1 = 200; //报错
	p = &number2; //可以执行
	
	//p2不能修改 *p2可以修改
	int * const p2 = &number1;
	*p2 = 200; //可执行
	p2 = &number2; //报错

	//*p3和p3都不能修改
	const int * const p3 = &number1;
	*p3 = 200; //报错
	p3 = &number2; //报错
}  

(四)static

static 修饰的未初始化的变量在.bss段;
static 修饰的初始化的变量在.data段;

1. static修饰的局部变量

可以延长局部变量的生命周期,到程序结束的时候才会被释放。
作用域不会发生改变。

#include <stdio.h>

void add1()
{
	static int num = 0;
	printf("num = %d\n",num++);
}
int main()
{
	for(int i=0;i<5;i++)
		add1();
	return 0;
}

输出结果:
图片

2. static修饰的全局变量

static修饰的全局变量的生命周期是整个程序。
作用域:只能在本文件中使用。

3.static修饰函数

作用域:static修饰的函数只能在本文件中进行使用,不能跨文件使用。

(五)extern

外部声明,extern修饰的变量以及函数定义在其他文件中。
只能声明,不能定义。

(六)volatile

每次读值都要从内存中读取,不可以从cache中读取。

应用:
多线程操作时在全局变量前添加volatile
硬件操作时添加volatile
在中断处理函数中访问非自动类型的变量需要加volatile。(ARM接口技术)

三、结构体

结构体是一个构造类型,可以由不同类型的元素的成员组成。

结构体是一个类型,而非变量。
一般定义在全局,如果定义在函数中,只能在函数内部使用

(一)结构体类型定义

struct <结构体名称>
{
	<变量类型1> <变量名1>;
	<变量类型2> <变量名2>;
	...
	<变量类型n> <变量名n>;
}; //分号不能省略

eg: 定义一个结构体

#include <stdio.h>

struct Student{
	char name[128];
	int age;
	float sore;
};
  • 注:
    1. struct 是结构体的关键字,必须书写。
    1. 结构体名称可以省略,一般定义在结构体内部时使用这种方式,定义变量的方式略有不同。
    1. 结构体内部的成员是通过花括号来包括的
    1. 结构体类型的最后必须包括一个’;’
    1. 结构体内部的成员必须是确定大小的,所以结构体内部是不能写函数的。
    1. 结构体内部成员的数据类型可以相等,也可以不等
    1. 注意C语言中不能在定义结构体时在结构体内部进行赋值,即下面的定义是错误的
#include <stdio.h>
/***这种定义方法是错误的***/
struct Student{
	char name[128] = “lily”;
	int age = 10;
	float sore = 100;
};

(二)结构体变量的定义

结构体变量定义:
struct <结构体名称> 标识符;

结构体指针变量:
struct <结构体名称> *标识符;

(三)结构体变量赋值

1. 定义结构体变量的同时进行赋值

struct Student s2 ={"xiao,18,99.9"};
//访问
printf("name:%s\n",s2.name);

2. 定义结构体类型的同时定义变量并进行赋值

struct Student{
	char name[128];
	int age;
	float sore;
}s1={"lily",17,99}; 

3. 在定义结构体变量时对指定成员进行赋值

struct Student{
	char name[128];
	int age;
	float sore;
}s1={.name="Lily"};
  • 注:不能写成下面的形式
/***错误的写法***/
struct Student{
	char name[128];
	int age;
	float sore;
}s1={s1.name="Lily"};//这种是错误的写法

4. 在定义完结构体变量后,通过’.'进行赋值

struct Student{
	char name[128];
	int age;
	float sore;
};

int main()
{
	struct Student s1;
	
	strcpy(s1.name,"Lily");
	s1.age = 18;
	s1.sore = 99;
} 

(四)结构体成员访问

结构体变量:
通过 <结构体变量>.<成员> 来访问

结构体指针:
*p . <成员>
p -> <成员>

eg :使用指针访问结构体的值,实现两个虚数相加。

#include <stdio.h>

struct vir 
{
    int x;
    int y;
}s1={12,3},s2={65,32},s3; 
//此时s1,s2,s3均为全局变量
//他们的成员也都是全局变量,存储在常量区的.bss段和.data段

int main(int argc, const char *argv[])
{
    
    struct vir *p1 = &s1;
    struct vir *p2 = &s2;
    struct vir *p3 = &s3;

    p3->x=(p1->x)+(p2->x);
    p3->y=(p1->y)+(p2->y);
    printf("s1+s2=%d+%d*i\n",p3->x,p3->y);

    p3->x=(p1->x)-(p2->x);
    p3->y=(p1->y)-(p2->y);
    printf("s1-s2=%d+%d*i\n",p3->x,p3->y);                                                                                                                     
    
    return 0;
}

(五)结构体内部指针的使用

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
struct Place
{
	int *arr;
	int size;
	int capacity;
};
int main()
{
	struct Place t;

	t.arr=(int *)malloc(4*50);//申请50个int型数据的空间大小
	t.size = 0;//相当于下标
	t.capacity = 50;//可以容纳的数据的个数
}

(六)结构体指针传参

eg:在堆区申请一段地址连续的空间
create函数,申请一块空间
write函数,向申请的空间内写值

#include <stdio.h>
#include <stdlib.h>

//定义结构体类型
typedef struct Place{
    int* arr;
    int size;
    int capacity;
}Place_t;

//创建空间
int create(Place_t *s,int size)
{
	if(!p)
	{
		printf("struct is empty!\n");
		return -1;
	}
    s->arr = (int *)malloc(4*size);
    if(!(s->arr))
    {
        printf("create fail!\n");
        return -1;
    }
    s->size=0;
    s->capacity=size;

    printf("create %d please success!\n",size);

    return 0;
}
//向空间中写值
int write(Place_t *s,int num)
{
	if(!p)
	{
		printf("struct is empty!\n");
		return -1;
	}
    if((s->size)<=(s->capacity))
    {   
    	*((s->arr)+(s->size)) = num;
    	(s->size)++;
    }else
    {   
        printf("capacity is full!\n");
        return -1;                                                                                                                                                                                                   
    }   
    return 0;
}

int main(int argc, const char *argv[])
{
    Place_t a;
    int size;
    int num;

    printf("请输入需要多少空间:");
    scanf("%d",&size);  
    create(&a,size);

    for(int i=0;i<size;i++)
    {   
        printf("please input %d number:",i);
        scanf("%d",&num);
        if(write(&a,num))
        {
            printf("write fail!\n");
        }
    }

    for(int i=0;i<size;i++)
    {
        printf("%d\n",*(a.arr+i));
    }
    
    //释放内存
    free(a.arr);
    a.arr=NULL;
    
    return 0;
}  

相关推荐

  1. C语言-----自定义类型-----结构

    2024-05-12 10:54:05       38 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-12 10:54:05       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-12 10:54:05       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-12 10:54:05       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-12 10:54:05       20 阅读

热门阅读

  1. MongoDB聚合运算符:$toString

    2024-05-12 10:54:05       10 阅读
  2. Flutter备用依赖

    2024-05-12 10:54:05       11 阅读
  3. 什么是渐进式框架

    2024-05-12 10:54:05       8 阅读
  4. matlab人脸识别

    2024-05-12 10:54:05       8 阅读
  5. 基于STM32的衣柜防潮系统设计的毕业论文

    2024-05-12 10:54:05       8 阅读
  6. Android中C++如何读写json文件

    2024-05-12 10:54:05       12 阅读
  7. 跟我学C++中级篇——内联补遗

    2024-05-12 10:54:05       14 阅读
  8. oracle 递归查询(结构树)

    2024-05-12 10:54:05       12 阅读
  9. Dijkstra算法

    2024-05-12 10:54:05       8 阅读
  10. 目标检测 yolov8 pth ==> onnx

    2024-05-12 10:54:05       8 阅读