4.1-day11-C语言基础-结构体数组和函数
C语言基础-结构体和函数
程序中不同的函数可以写在不同的.c源文件中。不同的源文件之间使用扩展名为.h的头文件链接起来。
编写所有头文件时要使用条件编译进行控制,避免被多次编译
多文件程序编译有两种办法
1.使用gcc命令对所有.c源文件统一编译
2.使用gcc命令对每个单独的.c源文件进行编译,得到以.o作为扩展名的目标文件。最后使用gcc命令把所有目标文件合并成可执行文件。
cd
.c源文件中如果要使用其他源文件中声明的全局变量则需要使用如下方法声明一下
extern int result;
make工具可以用来进行项目管理,它可以根据记录在Makefile中的要求完成整个编译过程。
结构体可以用来把多个不同类型的变量合并成一个整体。结构体声明方法如下
struct 结构体名称 {
变量声明语句
};
其中struct是表示结构体的关键字。在结构体声明中所有的变量声明语句并不会产生新的变量。
结构体可以当成数据类型来使用,并可以声明结构体变量,使用方法如下
struct 结构体名称 结构体变量名称;
typedef关键字可以用来给数据类型起别名,它和#define完全不同。
结构体变量做参数的时候应该使用一个指针变量来替换,这样可以节省空间和时间。如果这个参数只是输入参数则指针声明前面加上关键字const。
变量的地址必须是变量大小的整数倍,这叫做数据对齐。double变量的地址只需要是4的整数被就可以了。数据对齐会导致结构体中不同变量之间有空隙,结构体变量的大小并不是其中所有子变量大小之和。
整个结构体变量的大小必须是其中最大变量大小的整数倍,double变量大小按4个计算。为了保证这一点,有时需要在结构体变量后面加入一些浪费的字节,这叫做补齐。
声明结构体的时候可以使用位域限制某个子变量所占的二进制位数,所有整数类型变量都可以使用位域。使用位域声明的子变量没有地址。
01add.c
/*多文件练习*/#include<stdio.h>#include"01add1.h"#include"01sub.h"externint result;int main(){add(3,7);printf("计算结果是%d\n", result);printf("计算结果是%d\n", sub(7,3));return0;}
01add1.c
#include"01add1.h"/*static*/int result;void add(int value,int value1){result = value + value1;}
01add1.h
#ifndef __ADD_H__#define __ADD_H__void add(int,int);#endif//__ADD_H__
01sub.c
#include"01sub.h"int sub(int value,int value1){return value - value1;}
01sub.h
Makefile:
a.out :01add.o 01add1.o01sub.ogcc 01add.o 01add1.o01sub.o01add.o :01add.cgcc -c 01add.c01add1.o:01add1.cgcc -c 01add1.c01sub.o :01sub.cgcc -c 01sub.cclean :rm *.o a.out
02struct.c
/*结构体练习*/#include<stdio.h>/*struct student {int age;char gender;float height;};typedef struct student stu;*/typedefstruct student {int age;char gender;float height;} stu;int main(){/*struct student {int age;char gender;float height;};*/struct student student;stu student1 ={24,'F',1.60f};printf("年龄是%d\n", student1.age);printf("性别是%c\n", student1.gender);printf("身高是%g\n", student1.height);/*struct {int age;char gender;float height;} student;*/return0;}
03rank.c
/*结构体练习*/#include<stdio.h>typedefstruct{int chinese;int english;int math;} rank;void read(rank *p_r){printf("请输入考试成绩:");scanf("%d %d %d",&(p_r->chinese),&(p_r->english),&(p_r->math));;};void print(const rank *p_r){printf("成绩分别是%d %d %d\n", p_r->chinese, p_r->english, p_r->math);}int main(){/*rank ranks[3] = {{20, 30, 40},{30, 40, 50},{40, 50, 60}};*/rank r;read(&r);print(&r);return0;}
04struct.c
/*结构体练习*/#include<stdio.h>typedefstruct{char band[20];int price;int quantity;} phone;void read(phone *p_phone){printf("请输入品牌信息:");fgets(p_phone->band,20, stdin);printf("请输入价格和数量信息:");scanf("%d %d",&(p_phone->price),&(p_phone->quantity));scanf("%*[^\n]");scanf("%*c");}void print(const phone *p_phone){printf("电话信息是%s, %d和%d\n", p_phone->band, p_phone->price, p_phone->quantity);}int main(){phone phones[3];int loop =0;for(loop =0; loop <=2; loop++){read(phones + loop);}for(loop =0; loop <=2; loop++){print(phones + loop);}return0;}
05struct.c
/*结构体练习*/#include<stdio.h>/*typedef struct {char ch;char ch1;int value;} stru1;*/typedefstruct{char ch;short value;char ch1;} stru1;int main(){printf("结构体大小是%d\n",sizeof(stru1));return0;}

存在着数据对齐;
char ch;char ch1;int value;
在内存中是4个字节,4个字节为一组进行存储; char char (2) 4字节 + int 4字节 = 8字节 ;
数据对齐:把变量从小到大依次排列,这样结构体空间的浪费最小。
char ch;short value;char ch1;
在内存中char ch (1) + short (2) ,补上一个浪费的字节凑出来是4个字节; + char (2个字节) ;一共是6个字节;
补齐:是占地最大short(2)的整数倍是6。
06struct.c
- /*
结构体位域练习*/#include<stdio.h>typedefstruct{int gender:1;int age:6;} stru1;int main(){stru1 s;//&(s.age);printf("大小是%d\n",sizeof(stru1));return0;}
int 一般为4个字节;
什么是文件包含?
文件包含指令的功能是把指定的文件插入该指令行位置取代该指令行,从而把指定的文件和当前的源程序文件连成一个源文件。在程序设计中, 文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其他文件的开头用包含指令包含该文件即可使用。这样,可避免在每个文件开头都去书写那些公用部分,从而节省时间,并减少出错。
我们通常以头文件来编写这些被包含的文件,也就是以".h"作为这些文件的扩展名,当然不是非要这样命名才可以。也可以使用“.c”为扩展名,或者干脆不写扩展名,这样做都是允许的,只是使用".h"更普遍,因为它能够表示该文件的性质。
例如在前面我们已多次用此指令包含过库函数的头文件。例如:
#include<stdio.h>#include<math.h>或者#include"myfile.c"
myfile.c的文件:
#include<stdio.h>#include"common.h"/*包含头文件*/int main(){int radius;float circule;printf("请输入圆的半径:");scanf(PD,&radius);/*调用common头文件的宏定义*/circule =2*PI*radius;/*调用common头文件的宏定义*/printf(PF, circule);/*调用common头文件的宏定义*/printf(NEWLINE);/*调用common头文件的宏定义*/return0;}
common.h的文件:
#define PI 3.14#define NEWLINE "\n"#define PD "%d"#define PF "%f"
在编译这两个文件时,编译器并不是分开编译生成两个目标文件的,而是通过文件包含#include指令,把文件common.h先包含到文件myfile.h中。当然在myfile.c文件中还包含了标准输入输出头文件stdio.h,这些 文件经过预处理指令合并在一起,得到一个新的源程序,然后对这个 文件进行 编译,即可得到一个新的.obj文件。
条件编译:
在C的高级编程中,会遇到在基础学习 中没有遇到过 的“条件编译”。何渭“条件编译”,简单地说,就是“程序的内容指定编译的 条件 ”。我们在写程序的时候,一般的情况是将源程序的所有行都参加编译,但是我们希望部分行在满足条件的情况下再进行编译,从而引出下面的几种条件编译。
一般的条件编译形式如下:
1.#ifdef 形式
------------------------------------------------
#ifdef 标识符
程序段 1
#else
程序段2
#endif
------------------------------------------------
作用是标识符已经被定义,则对程序段1进行 编译,否则编译程序段2。但也可以不写#else, 如下:
------------------------------------------------
#ifdef 标识符
程序段
#endif
------------------------------------------------
其中的“标识符”是用 # define指令定义的,程序段可以是语句,也可以是命令行。
2.#ifndef 形式
------------------------------------------------
#ifndef 标识符
程序段1
#else
程序段2
#endif
-----------------------------------------------
这种形式的 条件编译与第1种形式相同,内容 相反。只是ifdef替换成了 ifndef,即如果标识符没有被定义过,就会编译程序段1, 否则就编译程序段2。也可以不写#else,如下:
-----------------------------------------------------
#ifndef 标识符
程序段
#endif
---------------------------------------------------
(1)调试中使用条件编译:
我们可以在程序中输出参数的值,判断什么位置出现了问题,但是如果程序调试结束再一一删除输出值的语句,工作量会比较大。因此对于大型程序,就可以在调试中使用宏。
如果代码前面有指令:
#define DEBUG
程序中就会输出变量x的值,使代码调试方便了很多。调试结束后,只需 删除宏定义#define DEBUG即可,操作也很简便。在代码很多,需要显示复杂调试结果的情况下,效果就会更为显著。例如:
------------------------------------------------------------
#if DEBUG
printf("调试状态下,当前x的值是%d\n", x);
#endif
----------------------------------------------------------------
(2)文件嵌套包含和条件编译:
由于嵌套包含文件的原因,一个头文件可能会被多次包含在一个源文件中,而使用条件指示符就可以防止这种头文件的重复处理。例如:
----------------------------------------------------------------
#ifndef COMMONFILE_H
#define COMMONFILE_H
#endif
---------------------------------------------------------------
条件指示符#ifndef 检查COMMONFILE-H在前面是否已经被定义,这里的COMMONFILE_H是一个预编译器常量,习惯上预编译器常量往往被写成大写字母。如COMMONFILE_H在前面没有被定义,则条件指示符的值为真,于是从#ifndef到#endif之间的所有语句都被包含进来进行处理。
相反,如果#ifndef 指示符的值为假,则它与#endif 指示符之间的行将被忽略。为了保证头文件只被处理一次,可把如下的 #define指示符 #define COMMONFILE_H放在#ifndef 后面,这样在头文件的 内容第1次被处理时, COMMONFILE_H将被定义, 从而可防止在程序文本文件中,以后#ifndef指示符的值为真。
#ifndef 指示符常被用来判断一个预处理器常量是否已被定义,以便有条件地包含程序写代码。#ifndef除了用于防止重复包含,还可以用于针对不同环境的条件编译。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">



浙公网安备 33010602011771号