本文主要是介绍Linux-Level1-day11: 大型程序编译makefile;复合类型之结构体(核心);联合体union,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
回顾:
1.指针数组
每个元素都是一个地址
语法:数据类型 *数组名[元素个数] = {地址列表}; int *a[2]={&c,&d};
2.字符指针数组
每个元素都是一个字符串的首地址
3.预处理指令(替换作用)
1.#include
-
#define :它可以提高代码可移植性
常量宏与 宏函数:代码执行效率高,函数相比效率低
编译器预定义的宏:FILE,_FUNCTION,LINE,DATE,TIME__ 用于将来软件调试或者打印日志
-D选项指定一个自己的宏,注意字符串需要用\"转义
3.条件预处理指令:#if/#ifdef/#ifndef/#else/#elif/endif-----条件判断编译器是否编译
4.大型程序文件分类:三大类
头文件:变量声明,函数声明,自定义数据类型声明
源文件:变量定义,函数定义。源文件包含自己对应的头文件
主文件:包含main函数,调用其他源文件的变量和函数,所以前提还需包含源文件对应的头文件
公共头文件:头文件中如果有相同的内容,摘出来单独放到一个公共的头文件。其他头文件包含公共的头文件即可
注意:static定义的变量与函数只能在本文件使用
5.大型程序的编译靠:Makefile
5.1.问:如果项目产品代码有1万源文件.c,编译极其的繁琐,郁闷
gcc -o main main.c a.c b.c ....
这么简化程序的编译呢?
答:必须只能利用Makefile来实现
5.2.Makefile功能:能够制定编译规则,将来让gcc编译器根据这个规则来编译程序,
Makefile本质就是一个文本文件,此文件给make命令使用,
将来make命令会根据Makefile里面的编译规则让gcc编译程序
5.3.Makefile语法格式:
目标文件:依赖1 依赖2 依赖3 ....依赖N
(TAB键)编译命令1
(TAB键)编译命令2
...
(TAB键)编译命令N
(TAB键) 还可以是其他命令:ls/cp/cd等
注意:Makefile注释用#
例如:目标是把helloworld.c编译生成helloworld
#指定规则:一步到位法 helloworld:helloworld.cgcc -o helloworld helloworld.c或者#指定规则1:分步法helloworld:helloworld.ogcc -o helloworld helloworld.o #指定规则2:helloworld.o:helloworld.cgcc -c -o helloworld.o helloword.c
案例:利用Makefile编译helloworld.c文件
mkdir -p /home/tarena/stdc/day11/Makefile1/
cd /home/tarena/stdc/day11/Makefile1/
vim helloworld.c
vim Makefile
make //编译程序命令
./helloworld
编辑
编辑
make //编译提示helloworld是最新的
vim helloworld.c //修改源文件
ls -lh //查看helloworld.c和helloworld的时间戳
make //又重新编译
说明make命令可以帮你检查这个文件是不是最新编译过的。
案例:将昨天的多文件代码拷贝并且利用Makefile编译
mkdir -p /home/tarena/stdc/day11/Makefile2/
cd /home/tarena/stdc/day11/Makefile2/
vim Makfile
make
./main
编辑
编辑
5.4.Makefile工作原理了解
当执行make命令时,make命令首先在当前目录下找Makefile,一旦找到Makfile
文件,打开此文件并且找到所有的编译规则,通过这些编译规则确定了最终的目标是
helloworld和源文件helloworld.c,然后make命令首先在当前目录下找是否存在
目标文件helloworld,如果helloworld存在,然后检查helloworld和helloworld.c
的时间戳哪个更新,如果helloworld的时间戳比helloworld.c新,说明源文件没有
改过,无需编译,提示文件最新,如果helloworld的时间戳比helloworld.c要旧
说明helloworld.c修改过,根据编译规则的命令重新编译
如果一开始没有找到helloworld,程序整个重新编译
5.5.Makefile小技巧---灵魂
%.o:%.c(TAB键)gcc -c -o $ @ $<说明:%.o:目标文件.o%.c:源文件.c$@:目标文件$<:源文件
作用是将当前目录下所有的.c文件单独编译生成对应的.o目标文件
编辑
6.复合类型之结构体(核心)
6.1)明确:目前C程序分配内存的方法两种:定义变量和定义数组
定义变量的缺陷:不能大量定义,所以诞生数组
定义数组的缺陷:数据类型是相同的,所以诞生结构体
问:什么场合需要定义大量变量和变量的数据类型不相同呢?
答:比如让计算机记录或者描述一个学生的信息.学生的信息如下:
int age; //年龄
char *name ;//名字
int id; //学号
float score; //学分
显然变量的数据类型不一致,数组无法做到!采用结构体!
6.2)结构体特点:能够包含大量的变量并且对变量的数据类型无要求
对应的关键字:struct
结构体也是一种数据类型,它是程序员自行定义的一种数据类型
类比成一个int类型
结构体分配的内存是连续的,一个成员挨着一个成员
6.3)结构体声明定义的使用方法:
a)方法1:直接定义结构体变量(很少用)
1.语法:struct { 结构体成员; //又称结构体字段}结构体变量名; 2.例如:描述学生信息//定义一个学生信息的结构体变量student1 struct {int age; //描述学生的年龄int id; //描述学生的学号float score; //描述学生的学分char name[30]; //描述学生的姓名}student1; //再定义一个学生信息的结构体变量student2struct {int age; //描述学生的年龄int id; //描述学生的学号float score; //描述学生的学分char name[30]; //描述学生的姓名}student2;
缺陷:每次定义一个结构体变量,结构体成员都要重新写一遍,很烦躁!
b)方法2:先声明结构体数据类型, 然后用这种结构体数据类型定义结构体变量(常用,掌握)
1.声明结构体数据类型的语法:
struct 结构体名 {
结构体成员;
};
注意:不会分配内存.大型程序,结构体声明放到头文件来写
2.用结构体数据类型定义结构体变量的语法:
struct 结构体名 结构体变量名;
注意:会分配内存. 大型程序,结构体定义放到源文件中来写.
3.例如:
//1.声明描述学生信息的结构体数据类型struct student {int age; //描述学生的年龄int id; //描述学生的学号float score; //描述学生的学分char name[30]; //描述学生的姓名}; //2.定义两个学生信息的结构体变量struct student student1;struct student studnet2;
4.缺陷:每次定义结构体变量,struct 结构体名每次都要书写,很烦躁!
c)方法3:先用typedef关键字给一个声明的结构体数据类型取别名(外号).
然后用别名定义结构体变量(实际开发最常用)
1)务必掌握typedef关键字(否则不是一个合格的程序员)
功能:给数据类型取别名(外号)
语法:typedef 原数据类型 别名;
例如:对于基本数据类型取别名(实际开发代码)
typedef char s8; //s=signed:有符号,8:8位 typedef unsigned char u8; //u=unsiged typedef short s16; typedef unsigned short u16; typedef int s32; typedef unsigned int u32; typedef long long s64; typedef unsigned long long u64; typedef float f32; typedef double f64; 使用: int a 写成 s32 a; unsigned char b 写成 u8 b
2.用typedef对声明的结构体取别名
注意:规定:别名后面加_t,对于大型程序写头文件
形式1: 语法:typedef struct {结构体成员;}别名_t; 例如:typedef struct {int age; //描述学生的年龄int id; //描述学生的学号float score; //描述学生的学分char name[30]; //描述学生的姓名}stu_t;
形式2:语法:typedef struct 结构体名{结构体成员;}别名_t; 例如:typedef struct studnet{int age; //描述学生的年龄int id; //描述学生的学号float score; //描述学生的学分char name[30]; //描述学生的姓名}stu_t;
形式3: struct student{int age; //描述学生的年龄int id; //描述学生的学号float score; //描述学生的学分char name[30]; //描述学生的姓名int weight; //学生的体重};//取别名typedef struct student stu_t;
3.不管使用哪种typedef对结构体数据类型取别名,定义结构体变量都一样
定义结构体变量语法:别名 结构体变量名;
例如:定义两个学生信息的结构体变量
stu_t student1;
stu_t student2;
或者:
stu_t student1, student2;
6.4)结构体变量的初始化方式,两种方式:
a)传统初始化方式:
1.语法:struct 结构体名/别名 结构体变量名 = {初始化的值};
2.例如:
struct student student1 = {18, 666, 100, "游哥", 128};
或者
stu_t student1 = {18, 666, 100, "游哥"};
3.缺陷:定义初始化的时候需要按照顺序全部初始化
因为有些场合可以不用按照顺序,关键是可以不用全部初始化
b)标记初始化方式:
1.语法:struct 结构体名/别名 结构体变量名 = {
.某个成员名 = 初始化值,
.某个成员名 = 初始化值,
...
};
2.例如:
struct student student1 = {.name = "游哥",.weight = 128,.age = 18,};或者stu_t student1 = {.name = "游哥",.weight = 128,.age = 18,};
3.特点:不用按照顺序,不用全部成员初始化
6.5)结构体变量成员的访问:两种形式
a)通过"."运算符来访问结构体变量的成员
语法:结构体变量名.成员名; //将来可以访问这个成员的内存区域
例如:stu_t student1 = {.name = "游哥",.weight = 128,.age = 18,}; //读查看printf("%s %d %d\n",student1.name, student1.weight, student1.age); //写修改strcpy(student1.name, "葛鹏"); student1.weight = 821;student1.age = 17;
b)通过"->"运算符来访问结构体指针变量的成员
语法:结构体指针变量名->成员名; //将来可以访问这个成员的内存区域
例如:
stu_t student1 = {.name = "游哥",.weight = 128,.age = 18,}; stu_t *p = &student1; //定义一个结构体指针变量p指向student1结构体变量 printf("%s %d %d\n",p->name, p->weight, p->agestrcpy(p->name, "葛鹏"); p->weight = 821;p->age = 17;
/*struct1.c先声明后定义结构体玩法*/ #include <stdio.h> #include <string.h> struct student {char name[30]; //姓名int id; //学号int age; //年龄float score; //学分 }; int main(void) {//定义并且初始化结构体变量:描述关羽同学的学生信息struct student student1 = {"关羽", 666, 18, 65.5};//不取别名定义变量方法printf("%s, %d, %d, %g\n", student1.name, student1.id,student1.age, student1.score);student1.age++;student1.score = 80;printf("%s, %d, %d, %g\n",student1.name, student1.id,student1.age, student1.score); //通过指针变量访问struct student *p = &student1; printf("%s, %d, %d, %g\n",p->name, p->id, p->age, p->score);p->age++;p->score = 95.5;strcpy(p->name, "张飞");printf("%s, %d, %d, %g\n",p->name, p->id, p->age, p->score); return 0; }
/*struct2.c 采用别名方式*/ #include <stdio.h> #include <string.h> /*对声明的结构体取别名*/ typedef struct student {char name[30]; //姓名int id; //学号int age; //年龄float score; //分数 }stu_t; int main(void) {stu_t student1;//取别名定义变量方式stu_t student1 = {.score = 65.5,.id = 555,.name = "关羽"};printf("%s, %d, %d, %g\n", student1.name, student1.id, student1.age, student1.score);strcpy(student1.name, "刘备");student1.score = 85.5;printf("%s, %d, %d, %g\n", student1.name, student1.id, student1.age, student1.score);stu_t *p = &student1; printf("%s, %d, %d, %g\n",p->name, p->id, p->age, p->score);strcpy(p->name, "张飞");p->score = 15.5;printf("%s, %d, %d, %g\n",p->name, p->id, p->age, p->score);return 0; }
/*struct3.c采用别名方式2*/ #include <stdio.h> #include <string.h> /*声明结构体*/ struct student {char name[30]; //姓名int id; //学号int age; //年龄float score; //分数 }; /*取别名*/ typedef struct student stu_t; int main(void) {//定义初始化结构体变量:描述学生信息stu_t student1 = {.score = 65.5,.id = 555,.name = "关羽"};printf("%s, %d, %d, %g\n", student1.name, student1.id, student1.age, student1.score);//修改strcpy(student1.name, "刘备");student1.score = 85.5;printf("%s, %d, %d, %g\n", student1.name, student1.id, student1.age, student1.score);stu_t *p = &student1; //p指向student1printf("%s, %d, %d, %g\n",p->name, p->id, p->age, p->score);strcpy(p->name, "张飞");p->score = 15.5;printf("%s, %d, %d, %g\n",p->name, p->id, p->age, p->score);return 0; }
6.6)结构体变量之间可以直接赋值
例如:
stu_t student1 = {18, 666, 100, "游哥", 128}; stu_t student2 = student1;或者 stu_t student1 = {18, 666, 100, "游哥", 128}; stu_t *p = &student1; //p指向student1 stu_t student2 = *p;
6.7)结构体嵌套:结构体成员还是一个结构体
例如:
//声明描述学生出生日期的结构体 typedef struct birthday {int year; //年int month; //月int date; //日 }birthday_t; //声明描述学生信息的结构体 typedef struct student {char name[30]; //姓名int age; //年龄//struct birthday birth; //学生的出生日期birthday_t birth; //学生的出生日期 };
/*struct5.c结构体嵌套*/ #include <stdio.h> //声明描述学生出生日期的结构体 typedef struct birthday {int year;//年int month; //月int date; //日 }birthday_t; //声明描述学生信息的结构体 typedef struct student {char name[30]; //名字int age;//年龄birthday_t birth; //出生日期 }stu_t; int main(void) {//定义初始化结构体变量//stu_t student1 = {"关羽", 18, {2001, 2, 10}}; //传统初始化stu_t student1 = { //标记初始化.name = "关羽",.age = 18,.birth = {.year = 2001,.month = 2,.date = 10}}; stu_t *p = &student1; //p指向student1printf("%s, %d, %d:%d:%d\n", student1.name, student1.age,student1.birth.year, student1.birth.month,student1.birth.date);p->birth.year = 2000;p->birth.month = 3;printf("%s, %d, %d:%d:%d\n", p->name, p->age,p->birth.year, p->birth.month,p->birth.date);return 0; }
/*struct6.c结构体嵌套*/ #include <stdio.h> //声明描述学生出生日期的结构体 typedef struct birthday {int year;//年int month; //月int date; //日 }birthday_t; //声明描述学生信息的结构体 typedef struct student {char name[30]; //名字int age;//年龄birthday_t *pbirth; //出生日期 }stu_t; int main(void) {//定义初始化描述出生日期的结构体变量birthday_t stu_birth = {2001, 2, 10}; //定义初始化结构体变量描述学生的信息//stu_t student1 = {"关羽", 18, &stu_birth}; //传统初始化stu_t student1 = { //标记初始化.name = "关羽",.age = 18,.pbirth = &stu_birth //让pbirth指向stu_birth};stu_t *p = &student1; //p指向student1printf("%s, %d, %d:%d:%d\n", student1.name, student1.age,student1.pbirth->year, student1.pbirth->month,student1.pbirth->date);p->pbirth->year = 2000;p->pbirth->month = 3;printf("%s, %d, %d:%d:%d\n", p->name, p->age,p->pbirth->year, p->pbirth->month,p->pbirth->date);return 0; }
6.8)函数的形参是结构体:两种形式
a)直接传递结构体变量本身,形参是实参的一份拷贝,结构体有多大就需要拷贝多大
函数通过形参是不能修改结构体实参,只是对形参做了改变
b)直接传递结构体变量的地址
函数通过形参可以直接修改结构体实参,代码执行效率高,如果是指针只需拷贝4字节
c)公式,规矩:如果函数要访问结构体,将来要传递结构体指针,不要传递结构体变量
如果函数对结构体成员不进行修改,形参用const修饰
void show(const stu_t *pst){printf("%s\n", pst->name);//不让修改:strcpy(pst->name, "王八蛋");} void grow(stu_t *pst){pst->age++;}
/*struct7.c函数的形参是结构体变量*/ #include <stdio.h> //声明描述学生信息的结构体 typedef struct student {char name[30];int age; }stu_t; //定义打印函数 void show(stu_t st) {printf("%s %d\n", st.name, st.age); } /*定义grow函数*/ void grow(stu_t st) {st.age++; } int main(void) {//定义初始化结构体变量stu_t student1 = {"关羽", 18};show(student1); grow(student1); //岁数没有加1show(student1);return 0; }
/*struct8.c函数的形参是结构体变量*/ #include <stdio.h> //声明描述学生信息的结构体 typedef struct student {char name[30];int age; }stu_t; //定义打印函数 void show(const stu_t *pst) {printf("%s %d\n", pst->name, pst->age);//strcpy(pstr->name, "刘备"); //不允许改名 } /*定义grow函数*/ void grow(stu_t *pst) {pst->age++; } int main(void) {//定义初始化结构体变量stu_t student1 = {"关羽", 18};show(&student1); //打印grow(&student1); //岁数加1show(&student1);//打印return 0; }
6.9)结构体内存对齐问题(笔试题必考)
a)gcc对结构体成员编译时,默认按4字节对齐
例如:
struct A {
char buf[2];
int val;
};
结果:sizeof(struct A) = 8
内存分布图:结构体对齐.png
结构体内存对齐最小单位2,4,8....
b)终极演示代码: /*结构体内存对齐*/ #include <stdio.h> //声明结构体数据类型A struct A {char buf[2]; //4int val; //4 }; //声明结构体数据类型B struct B {char c; //4short s[2]; //4int i; //4 }; #pragma pack(1) //让gcc强制从这个地方开始后面代码按照1字节对齐方式编译 //声明结构体类型Cstruct C {char c; //1short s[2]; //4int i; //4}; #pragma pack() //让gcc到这里在恢复成默认4字节对齐 //声明结构体类型D struct D {int i; //4char c; //4 }; //声明结构体类型E struct E {double d; //8char c; //4 }; int main(void) {printf("sizeof(struct A) = %d\n", sizeof(struct A)); //8printf("sizeof(struct B) = %d\n", sizeof(struct B)); //12printf("sizeof(struct C) = %d\n", sizeof(struct C)); //9printf("sizeof(struct D) = %d\n", sizeof(struct D)); //8printf("sizeof(struct E) = %d\n", sizeof(struct E)); //12return 0; }
7.联合体:很少用
7.1.特点:
a)它和结构体使用语法一模一样,只是将关键字struct换成union
b)联合体中所有成员是共用一块内存,优点节省内存
c)联合体占用的内存按成员中占内存最大的来算
例如:
union A {char a;short b;int c;}; sizeof(union A) = 4;
d)初始化问题
union A a = {8}; //默认给第一个成员a,a = 8 那么c,b就不要访问了,因为数据随机
union A a = {.c = 8} //指定给c赋值 那么a,b就不要访问了,因为数据随机
编辑
编辑
7.2.经典笔试题(作业)
现象:
1.X86架构的CPU为小端模式:数据的低位在内存的低地址,数据的高位在内存的高地址处
例如:
int a = 0x12345678;
内存条
低地址 高地址
0-------1-----2-------3------4--------------------------------->
0x78 0x56 0x34 0x12
2.POWERPC架构的CPU为大端模式:
数据的低位在内存的高地址,数据的高位在内存的低地址处
例如:
int a = 0x12345678;
内存条
低地址 高地址
0-------1-----2-------3------4--------------------------------->
0x12 0x34 0x56 0x78
要求:编写一个程序求当前处理器是X86架构还是POWERPC架构
思路:采用union或者指针
提示:
union A {
char a;
int b;
};
参考代码:
#include <stdio.h> typedef union w {int a; //4 字节char b; //1 字节 } c_t; int main(void) {//定义联合体变量c_t c.a=0x12345678;if (c.b==78)printf("小端\nn");elseprintf("大端\n");return 1; }
指针验证是什么架构
编辑
编辑
X86架构
这篇关于Linux-Level1-day11: 大型程序编译makefile;复合类型之结构体(核心);联合体union的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!