达内C语言学习Day(11)
回顾:
1. 字符串
“abc\0”
占位符:%s
指针形式的字符串
char *p = "abc"; // 只能读不能修改
数组形式的字符串
char a[] = "abc"; // 可以读修改
char a[] = {'a', 'b', 'c','\0'};
strlen / strcmp / strcat / strcpy / sprintf
2. 指针数组
元素是地址
数据类型 *数组名[元素个数] = {地址列表};
玩法跟数组一模一样
3.字符指针数组
char *p = "abc";
4. 预处理指令
头文件包含#include
宏定义:#define 常量宏 宏函数
#和##
编译器定义好的宏
用户通过-D选项定义的宏
条件编译:#if …
5. 大型程序的开发流程
5.1 明确:头文件编写规范
vim test.h
#ifndef __TEST_H // 头文件卫士:防止变量或者函数重复定义声明
#define __TEST_H头文件要包含的内容
#endif
5.2 实际开发产品的代码组成部分:三部分
5.2.1 明确:
实际的产品代码不可能一个源文件搞定,代码维护极其繁琐
实际的产品代码根据按照功能将一个源文件分拆为多个源文件来实现,提高开发效率和可维护性
5.2.2 实际产品代码分拆三部分
- 头文件(.h):负责各种声明(不会分配内存),例如:变量声明和函数的声明
变量声明的语法:extern 数据类型 全局变量名;
函数的声明语法:extern 返回值数据类型/void 函数名(形参表/void);
例如:根据动物类型,描述鸡,狗,鸭对应的头文件分别是:dog.h, chick.h, duck.h
- 源文件(.c):负责各种定义(会分配内存),例如:变量的定义和函数的定义
例如:根据动物的类型描述狗,鸡,鸭对应的源文件分别是:dog.c, chick.c, duck.c
主文件(.c):负责实现main函数,程序运行的第一个文件就是主文件,起到了统领的作用,统领各种源文件,也就是在main函数中各种调用访问源文件的变量和函数
建议:一个源文件对应一个头文件,并且源文件包含自己对应的头文件
例如:dog.c 包含dog.h等
建议:如果头文件中有相同的内容摘出来房贷一个公共的头文件
vim common.h // 公共头文件 两只眼 共有特性vim dog.h #include "common.h" // 包含公共头文件
案例1:实现加,减,清0,置1
分析:
算数运算:加,减:cal.h, cal.c
位运算:清0,置1,bit.h,bit.c
主文件调用:main.c
编译命令:
gcc -o main main.c cal.c bit.c // 一步到位法
./main gcc -E -o main.i main.c // 分步法
gcc -E -o cal.i cal.c
gcc -E -o bit.i bit.c
gcc -c -o main.o main.i
gcc -c -o cal.o cal.i
gcc -c -o bit.o bit.i
gcc -o main main.o cal.o bit.o
第九课:结构体(核心中的核心,实际开发必用)
1. 明确
目前C语言分配内存的方法:定义变量和定义数组
定义变量分配内存缺陷:不能大量分配内存(目前最多8字节,double和long long类型)
可以采用数组来一次性分配大量内存
定义数组分配内存缺陷:虽然可以一次性大量分配内存,但是数据类型都一样
有些场合可能需要不同种类的数据类型
例如:描述学生信息:年龄,学号,分数,姓名
信息 | 数据类型 |
---|---|
年龄 | unsigned char |
学号 | int |
分数 | float |
姓名 | 字符串:char */ char a[] |
问:如何解决?
答:采用结构体解决数组的缺陷问题
2. 结构体特点
既可以大量一次性分配内存(借助数组)还能保证数据类型可以不一致
结构体对应的关键字:struct
结构体也是一种数据类型,它是程序员自行定义的一种数据类型,类比成int类型
结构体分配的内存也是连续的,一个成员挨着一个成员
3. 结构体数据类型声明和定义的方法:
方法1:
直接声明和定义结构体数据类型变量(很少)
- 语法:
struct {结构体成员变量(就是一堆变量); // 又称结构体字段
} 结构体变量名;
- 例如:描述学生信息
// 定义一个学生信息的结构体变量student1
struct {int age; // 年龄itn id; // 姓名float score; // 分数cahr name[30]; //姓名
} student1;
// 再定义一个student2
struct {int age; // 年龄itn id; // 姓名float score; // 分数cahr name[30]; //姓名
} student2;
- 缺陷:每次定义结构体变量,结构体成员都要重新写一遍
方法2:
先声明一个结构体数据类型(先造一个结构体数据类型,类似造int类型),
然后拿着声明好的结构体数据类型去定义结构体变量(开发很常用)
声明结构体数据类型的语法(造结构体数据类型):
struct 结构体名 {结构体成员 }; 注意:此时不会分配内存,对于大型程序,此代码应该写在头文件中
然后拿着声明好的结构体数据类型去定义结构体变量语法:
struct 结构体名 结构体变量名; 注意:会分配内存,应该放在大型程序中源文件中 类比:把struct 结构体名 类比成int类型
例如:
//先声明结构体数据类型描述学生信息 struct student {int age; // 年龄itn id; // 姓名float score; // 分数cahr name[30]; //姓名 }; //定义两个学生的信息结构体变量 struct student student1; struct student student2; 或者 struct student student1, student2;
缺陷:每次定义结构体变量,都要写struct 结构体名,优点长
方法3:
先用typedef关键字给声明的结构体数据类型取别名
然后拿着别名去定义结构体变量(实际开发最常用)
务必掌握typedef关键字
功能:给数据类型取别名
语法:type 原来的数据名 别名
例如:typedef int s32; // 给int类型取别名叫s32使用:s32 a = 250; // 等价与int a = 250;
用typedef对声明的结构体数据类型取别名
注意:别名建议后面加_t,对于大型程序此代码写在头文件中
两种写法:
写法1:
typedef struct 结构体名 {结构体成员: } 别名_t; 例如: typedef struct student { // 这里的结构体名可以省略int age; // 年龄itn id; // 姓名float score; // 分数cahr name[30]; //姓名 } stu_t;
写法2:
struct student { // 这里的结构体名不可以省略int age; // 年龄itn id; // 姓名float score; // 分数cahr name[30]; //姓名 }; 然后取别名: typedef struct student stu_t;
不管使用哪种取别名的方法,利用别名定义结构体变量都一样
别名定义结构体变量语法:别名 结构体变量名:
例如:定义两个学生信息的结构体变量:
stu_t student1; stu_t student2; 或者 stu_t student1, student2;
4. 结构体变量的初始化方式:两种方式
4.1 传统初始化方式:
语法:struct 结构体名/别名 结构体变量名 = {初始化的值};
例如:
struct student student1 = {18, 6666, 80, "小明"}; 或者 stu_t student1 = {18, 6666, 80, "小明"}; 结果: age成员 = 18; id成员 = 6666: score成员 = 80; name成员 = "小明"
缺陷:定义结构体变量和初始化时需要按照顺序初始化,并且还要初始化全部成员
有些场合可能不需要按照顺序初始化,也不需要初始化全部成员,可能就初始化某些成员
4.2 标记初始化方式:
语法:
struct 结构体名/别名 结构体变量 = {.成员1 = 初始化值,.成员2 = 初始化值,.... };
例如:
struct student student1 = {.name = "小明", age = 18}; 或者 stu_t student1 = {.name = "小明", age = 18};
优点
不用按照顺序,也不用全部初始化
5. 结构体变量成员的访问:两种形式
1.形式1:
通过"."运算符来访问结构体变量的成员
语法:
结构体变量名.成员名; // 将来就可以对结构体的这个成员进行访问了
例如:
stu_t student1 = {.name = "小明", .age == 18};
printf("%s, %d\n", student1.name, student1.age); // 打印
student1.age = 20; // 修改成员的值
strcpy(student1.name, "小花"); 修改成员的值
2.形式2:
通过"->"来访问结构体变量的成员
结构体变量的首地址等于结构体变量第一个成员的首地址
语法:
结构体变量的指针->成员名;
例如:
stu_t student1 = {.name = "小明", .age == 18};
stu_t *p = &student1; // 定义结构体指针变量p指向student1结构体变量,p保存student1变量的首地址
printf("%s, %d\n", p->name, p->age); // 打印
p->age = 20; // 修改成员的值
strcpy(p->name, "小花"); 修改成员的值
参考代码:struct.c,struct2.c,struct3.c
3.结构体之间可以直接赋值操作
stu_t student1 = {.name = "小明, .age = 18};
stu_t *p = &student1;
stu_t student2 = student1;
stu_t student3 = *p;
参考代码:struct3.c
/*结构体演示*/
#include <stdio.h>
/*声明结构体数据类型然后取别名*/
typedef struct student
{char name[30];int age;int id;
} stu_t; // 取别名
//或者: typedef struct student stu_t;int main(void)
{/*定义结构体变量描述学生信息*/stu_t student1 = {.name = "刘备",.id = 1,.age = 18};// 用指针方式定义stu_t *p = &student1;printf("%s, %d, %d, %s, %d, %d\n", student1.name, student1.id, student1.age, p->name,p->id,p->age);stu_t student4 = {.name = "关羽",.id = 10,.age = 1000};stu_t *p4 = &student4;printf("%s, %d, %d, %s, %d, %d\n", student4.name, student4.id, student4.age, p4->name,p4->id,p4->age);// 修改student1.age++;student1.id = 2;printf("%s, %d,%d, %s, %d, %d\n", student1.name, student1.id, student1.age, p->name,p->id,p->age);p->age++;p->id++;printf("%s, %d,%d, %s, %d, %d\n", student1.name, student1.id, student1.age, p->name,p->id,p->age);// 结构体变量可以直接赋值操作stu_t student2 = student1; // 将student1的值直接赋值给student2stu_t student3 = *p4; // 通过p去除student1的值给student3printf("%s, %d,%d, %s, %d, %d\n", student2.name, student2.id, student2.age, student3.name,student3.id,student3.age);return 0;
}
6. 结构体嵌套
结构体成员还是一个结构体
例如:
typedef struct B {int a;int b;
} B_t;
typedef struct C {int c;int d;B_t e; // 说明结构体C包含结构体B
} C_t;
参考代码:struct4.c, struct5.c,struct6.c
/*struct6.c 结构嵌套 多层嵌套*/
#include <stdio.h>
#include <string.h>
/*声明描述学生生日的结构体*/
typedef struct birthday
{int year;int month;int day;
} birthday_t;/*声明描述学生信息的结构体*/
typedef struct student
{char name[30];int age;birthday_t *pbirth; // 生日日期
} stu_t;int main(void)
{// 定义初始化描述生日的结构体变量 小明的birthday_t birthday_stu1 = {.year = 2002,.month = 2,.day = 1};// 定义初始化描述生日的结构体变量 小军的birthday_t birthday_stu2 = {.year = 2020,.month = 6,.day = 10};// 定一初始化描述学生小明信息的结构体变量stu_t student1 = {.name = "小明",.age = 18,.pbirth = &birthday_stu1 // 让pbirth指向stu_birth};// 定一初始化描述学生小军信息的结构体变量stu_t student2 = {.name = "小军",.age = 100,.pbirth = &birthday_stu2 // 让pbirth指向stu_birth};stu_t *p = &student1; // p指向student1printf("%s, %d, %d.%d.%d\n", p->name, p->age, p->pbirth->year, p->pbirth->month,p->pbirth->day);// 修改strcpy(p->name, "明明");p->age++;p->pbirth->year++;p->pbirth->month++;p->pbirth->day++;printf("%s, %d, %d.%d.%d\n", p->name, p->age, p->pbirth->year, p->pbirth->month,p->pbirth->day);stu_t *p2 = &student2; //p1指向student2printf("%s, %d, %d.%d.%d\n", p2->name, p2->age, p2->pbirth->year, p2->pbirth->month,p2->pbirth->day);return 0;
}
7.函数形参是结构体变量
函数的形参是结构体,两种形式
直接传递结构体变量本身,形参是实参的一份拷贝,结构体有多大就需要拷贝多大
函数通过形参是不能修改结构体实参,只是对形参做了改变
直接传递结构体变量的地址函数通过形参可以直接修改结构体实参,代码执行效率高,如果是指针只需拷贝4字节
公式,规矩:如果函数要访问结构体,将来要传递结构体指针,不要传递结构体变量如果函数对结构体成员不进行修改,形参用const修饰
void show(const stu_t *pst)
{printf("%s\n", pst->name);//不让修改:strcpy(pst->name, "王八蛋");
}
void grow(stu_t *pst)
{pst->age++;
}
参考代码:struct9.c
/*结构体函数,形参是结构体变量*/
/*声明描述学生信息的结构体*/
typedef struct student
{int id;char name[30];int age;
} stu_t;
#include <stdio.h>
/*定义打印函数*/
void print(stu_t st1) // stu st1 = student1
{ // stu_t st = student1printf("%d, %s, %d\n", st1.id, st1.name, st1.age);
}
/*定义修改函数*/
void grow(stu_t st1)
{st1.age++; //函数通过形参是不能修改结构体实参
}int main(void)
{/*定义初始化学生信息*/stu_t student1 ={.id = 1,.name = "小明",.age = 18};print(student1);grow(student1);print(student1);return 0;
}
函数形式的结构体:struct10.c
/*结构体函数 有指针*/
#include <stdio.h>
#include <string.h>
/*声明学生信息结构体*/
typedef struct student
{int id;char name[30];int age;
} stu_t;/*定义print函数*/
void print(stu_t *p)
{printf("%d, %s, %d\n",p->id, p->name, p->age);
}
/*定义修改函数 加const*/
void grow(const stu_t *p)
{// p->age = 20; // 加了const就不能修改了,会报错// p->id++; // 不能修改
}
/*定义修改函数,不加const*/
void grow1(stu_t *p)
{strcpy(p->name, "小军");
}int main(void)
{/*定义结构体信息*/stu_t studetn1 ={.id = 1,.name = "小明",.age = 18};print(&studetn1);grow1(&studetn1);print(&studetn1);return 0;
}
8. 联合体
8.1.特点:a)它和结构体使用语法一模一样,只是将关键字struct换成unionb)联合体中所有成员是共用一块内存,优点节省内存c)联合体占用的内存按成员中占内存最大的来算例如:union A {char a;short b;int c;};sizeof(union A) = 4;d)初始化问题union A a = {8}; //默认给第一个成员a,a = 8union A a = {.c = 8} //强制给c赋值8.2.经典笔试题(作业)现象:1.X86架构的CPU为小端模式:数据的低位在内存的低地址,数据的高位在内存的高地址处例如:int a = 0x12345678;内存条低地址 高地址0-------1-----2-------3------4--------------------------------->0x78 0x56 0x34 0x122.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=1;if (c.b==1)printf("小端\nn");elseprintf("大端\n");return 1;}
参考代码:ubion.c
/*union*/
#include <stdio.h>/*声明联合体数据类型*/
typedef union A {int val;float fval;
}A_t;int main(void)
{//定义初始化联合体变量A_t a = {250}; //val = 250;A_t b = {.fval = 250.250f}; //fval = 250.250printf("a.val = %d\n", a.val);printf("b.fval = %g\n", b.fval);printf("b.val = %d\n", b.val);printf("val地址是%p\n", &a.val);printf("fval地址是%p\n", &a.fval);printf("sizeof(a)=%d\n", sizeof(a));return 0;
}
达内C语言学习Day(11)相关推荐
- 达内C语言学习(day03)
文章目录 每日英语 回顾: 1.linux系统的命令 2. vim编辑器 3.C语言的编程规范 4.gcc编译器 5.C语言的变量 5.1 概念 5.2 定义变量语法 5.3 标识符命名规则 5.4 ...
- Go语言学习之11 日志收集系统kafka库实战
本节主要内容: 1. 日志收集系统设计 2. 日志客户端开发 1. 项目背景 a. 每个系统都有日志,当系统出现问题时,需要通过日志解决问题 b. 当系统机器比较少时,登陆到服务器上查 ...
- Go语言学习-- No.11结构体--结构体内嵌
结构体内嵌 结构体内嵌 结构体内嵌的初始化 结构体内嵌 Go语言的结构体内嵌是一种组合特性,使用结构体内嵌可构建一种面向对象编程思想中的继承关系. 结构体实例化后,可直接访问内嵌结构体的所有成员变量和 ...
- C语言学习笔记06-占位符格式、C基本类型及逃逸字符一些细节(附介绍BCD码)
主要整理有关占位符格式与逃逸字符的一些细节 朋友们,看栗子--"BCD解码" (文末附BCD码介绍) 一个BCD数的十六进制是0x12(对应二进制表示:0001 0010),它表达 ...
- C语言学习之求1-1/2+1/3-1/4+···+1/99-1/100
C语言学习之求1-1/2+1/3-1/4+···+1/99-1/100 #include <stdio.h> int main(){int a=1;//定义+,-的变量double x,y ...
- Python|线程和进程|阻塞|非阻塞|同步|异步|生成器和协程|资源竞争|进程间通信|aiohttp库|daemon属性值详解|语言基础50课:学习(11)
文章目录 系列目录 原项目地址 第34课:Python中的并发编程-1 线程和进程 多线程编程 使用 Thread 类创建线程对象 继承 Thread 类自定义线程 使用线程池 守护线程 资源竞争 G ...
- const 指针_C语言学习日记(11)——const与指针
对于一个普通指针typet *p.p有三个最基本的能力,第一就是可以通过p = &obj来指向一个type类型对象,并随意切换指向对象.第二就是通过value = *p来读取它指向的对象的值. ...
- C语言学习11:strlen()函数详解
目录 1.strlen()函数功能 2.strlen()函数的使用 2.1 数组 3.注意事项 1.strlen()函数功能 strlen()函数 用于 计算 指定字符串的 长度,但 不包括 结束字符 ...
- c语言错误的等式,C语言学习中几个常见典型错误分析.docx
C语言学习中几个常见典型错误分析 打开文本图片集 摘要:C语言是一门优秀,应用广泛的结构化程序设计语言,是中职计算机.机电和电子技术等专业一门理论和实践相结合的课程,教学实践中,学生常觉得c语言难学, ...
最新文章
- 魔改Attention大集合
- Python面对对象编程——对象、类详解及实例
- 机器学习实战读书笔记--朴素贝叶斯
- Java 多维数组遍历
- 【机器学习】特征工程七种常用方法
- flask-session 在redis中存储session
- 什么是CAN总线中继器?
- 为什么需要MiniDao? 新持久层 3.5.1 版本发布,让hibernate跟mybatis一样灵活
- Matlab 【应用】【1】用Matlab找一组模拟波形的极值(含极大值、极小值、最大值)并在图中画出来
- appium+python自动化98-非select弹出选择框定位解决
- oracle12 group by 拼接字符串
- 吴江智慧城市产业化“航母”起航
- 【计算机网络实验】DHCP报文捕获和分析
- 常用的免费CMS建站系统推荐
- 华为服务器安装操作系统
- linux如何安装压缩软件,linux之安装软件,压缩解压文件
- python 培训教程
- 宇宙飞机(space plane)
- vue 悬浮图标_vue实现移动端悬浮窗效果
- 单链表的头插法与尾插法详解
热门文章
- oracle中loder,Oracle Sql Loader的学习使用
- kafka中kafkaConsumer的poll拉取方法
- 磁带库连接服务器协议,如何确定磁带库设备和OS设备文件的对应关系
- U盘便携式hexo博客搭建极速纯净低bug主题推荐部署到codingSEO优化搜索
- python实现小说阅读器
- bundled camera paths for videos stabilization 论文翻译 卷帘快门(Rolling Shutter)讲解
- jmeter serveragent连接超时ERROR.java.net.ConnectException.Connection.timed.out.connect解决方法
- 【模型训练】YOLOv7自行车检测
- mysql的在线安装
- 易语言与服务器即时通讯,求易语言简单即时通讯