计算机中的数据
常用的二进制、八进制、十进制、十六进制,它们之间区别在于运算时是逢几进一位。
如:八进制转十进制:通常可以先转二进制再转其它进制,这样的方法比较简单,八进制-> 二进制 -> 十进制
进制转换
二进制转十进制
方法为:把二进制数按权展开、相加即得十进制数。
128 64 32 16 8 4 2 1
例子1:
101011
把结果相加:32 + 8 + 2 + 1 = 43
例子2:
10010110
128 + 16 + 4 + 2 = 150
八进制转十进制
方法:取一分三法,即将一位八进制数分解成三位二进制数,用三位二进制按权相加去凑这位八进制数,小数点位置照旧。
128 64 32 16 8 4 2 1
八进制:226
取一分三法:
2 = 010
2 = 010
6 = 110
再从二进制转十进制:
10010110
结果相加:128 + 16 + 4 + 2 =150
150就是八进制转十进制。
十六进制转十进制
方法为:十六进制数通过除2取余法,得到二进制数,对每个十六进制为4个二进制,不足时在最左边补零。
十六进制:0xC4
128 64 32 16 8 4 2 1
十六进制:1 2 3 4 5 6 7 8 9 A B C D E F
C = 12 -> 1100
4 -> 0100
128 + 64 + 4 = 196
十进制转二进制
方法为:十进制数除2取余法,即十进制数除2,余数为权位上的数,得到的商值继续除2,依此步骤继续向下运算直到商为0为止。
150转二进制
1
2
3
4
5
6
7
8150 / 2 = 75 -> 0
75 / 2 = 37 -> 1
37 / 2 = 18 -> 1
18 / 2 = 9 -> 0
9 / 2 = 4 -> 1
4 / 2 = 2 -> 0
2 / 2 = 1 -> 0
1 / 2 = 0.5 -> 1
150二进制:1001 0110
十进制转八进制
转二进制,3位为一组,缺少补零
011 100 010
最后得出结果是:342
十进制转十六进制
原码、反码、补码
正数:三码合一
负数:
原码:最高位不变,后面数据和其绝对值相同
反码:将原码除最高位,其余按位取反
补码:将反码+1
问题
计算机有哪五大主要部件?
计算机由输入设备,输出设备,运算器,存储器和控制器五大部件组成。
什么是RAM?什么是ROM?我们平常所说PC机的内存是指哪个?
ROM 只读存储器
RAM:随机存取存储器,高速存取,任意位置读写时间相等(即与地址无关),如计算机内存。通常用来加载操作系统,各种正在运行的软件、输入和输出数据、中间结果及与外存交换信息等,我们常说的内存主要是指RAM
谈一谈你对计算机指令和数据的理解。
计算机由输入设备,输出设备,运算器,存储器和控制器五大部件组成。指令是计算机能够识别的规定好含义的一系列数字,CPU能够读取指令,并按照设计好的电路,执行指令含义。支配计算机中的信息传递以及主机与输入输出设备之间的信息传递,是构成计算机软件的基本元素。
谈一谈编程语言的分类
三大类:机器语言、汇编语言、高级语言
机器语言为纯粹的机器代码,由010101……的二进制代码组成,可以由计算机直接执行,运行效率最高,通用性不强,对于不同的硬件需要不同的程序。
汇编语言可分为8086汇编,arm汇编等,运行效率低于机器语言(但也很快了),用简单的助记符代替二进制代码,通用性不强,对于不同的硬件需要不同的程序。
高级语言有Java、C、C++、python、php等等,用人类可识别的自然语言(主要英语)进行编程,运行效率更低,但编程难度和程序可读性大幅提高。可移植性好,实用性较好,适合大规模开发,是现在大多数程序员选择的语言。
字长与字节分别表示什么含义?
字长:CPU一次可处理的数据的最大长度,其位数取决于具体的计算机。
字节:衡量数据量以及存储容量的基本单位。1字节等于8位二进制信息。
8位有符号整数能表示的数范围? 16位有符号整数呢?
8位
最大:0111 1111 = 127
最小:1000 0000 = -128
16位
最大:0111 1111 1111 1111 = 32767
最小:1000 0000 0000 0000 = -32768
C语言基础1
2
3
4
5
6
7#include "pch.h"
#include
#include
int main( ){
printf("Hello 15PB\n");
system("pause");
}
stdio 是 standard input output 的缩写,stdio.h 被称为"标准输入输出文件",包含的函数大都和输入输出有关,puts() 就是其中之一。
stdlib 是 standard library 的缩写,stdlib.h 被称为"标准库文件",包含的函数比较杂乱,多是一些通用工具型函数,system() 就是其中之一。
#include 开头是预处理指令,inlucde 是其中一条预处理指令,功能是包含头文件(导入头文件), .h 的文 件被称为头文件, .c 的文件被称为源文件.
main 每个C程序必须有一个main函数,是整个程序入口点,程序执行的第一行代码所在地。
int 是一个关键字 , 目前而言(2017年5月18日) C语言中有 32 + 5 + 7 = 44 个关键字
编译过程
1.gcc -E hello.c -o a.c 会把#include 里的文件都包含进来拷进来,宏之类的
2.gcc -S a.c -o a.s 把高级语言变成低级语言,称为编译过程.
3.gcc -c a.s -o a.o 把低级语言转成机器语言,二进制
4.gcc a.o -o a.exe 最后把链接转成可执行文件。
变量
1.字符型: char,wchar_t 常用的是char
2.整型: short , int , long , long long ,常用的是int , short是短整型,long是长整型
浮点型: float , double 常用的是 double
数据是放在内存中的,在内存中存取数据要明确三件事情:数据存储在哪里、数据的长度以及数据的处理方式。
连续定义的多个变量以逗号分隔,并且要拥有相同的数据类型;
变量可以初始化,也可以不初始化。
类型转换
隐式转换:当在运算符号左右两边类型不一致时,编译器就会将其中一个类型转换为目标类型。
强制类型转换
int n2 = (int)'B';//将字符类型’B’强制转换为int类型.
变量和常量都是带有类型( 类型实际指的就是变量占用内存空间的字节数)
不同类型变量之间的赋值, 会产生隐式转换, 产生转换时, 高精度的数据赋值给低精度会造成精度丢失.
输出和输入
printf()
常用的字符串格式符:
1
2
3
4
5
6
7
8
9十进制整型 : %d
八进制 : %o
十六进制: %x/%X
字符 : %c
字符串: %s
双精度浮点数: %lf
输出内存地址: %p
%hd用来输出 short int 类型
%ld用来输出 long int 类型
scanf_s()
等待控制台的输入, 将控制台输入的内容按照指定格式保存到变量中.
1
2
3
4
5
6
7
8
9
10
11
12
13
14int main(){
// 接收不同类型的数据,然后输出
char ch = 0;
int nNum = 0;
double dNum = 0;
printf("请输入一个字符:"); // 接收一个字符
scanf_s("%c", &ch, 1);
printf("输入的字符是:%c\n", ch);
printf("请输入一个数字:");
// 接收一个整数
scanf_s("%d", &nNum);
printf("输入的数字是:%d\n", nNum);
vs提供了一个安全版本的输入函数:scanf_s, 它能保证所接受的数据不会造成溢出.
char数据类型的其他输入输出函数;
1
2name = getchar()
putchar(c); 相当于printf
_getch
功能 和getchar一样.
区别: _getch不用接收回车,而且不会有回显.
表达式和运行符
单目
负号 -
取地址 &
双目:
加法 +
大于 >
三目:
? : ;
问题
谈一谈main函数的作用
main函数是C语言约定的入口,main函数为程序的入口、起点,程序运行时会先去找main函数。
注意--并从main函数开始执行.如果出现多个main函数,则编译系统就无法判断从哪一个main函数运行,也就会编译错误!
对变量的认识:
变量就是一块用来存储数据的内存空间。
定义变量就是开辟一段内存空间,用来存放数据,空间的大小取决于变量的类型,并通过变量名字来操作该内存空间中保存的数据值。
编写一个程序要求输入一个ASC码值,然后输出ASC码相应的字符。
通过隐式转换输出。
1
2
3
4
5
6
7
8
9
10
11
12
13#include "pch.h"
#include
#include
#include
int main(void)
{
int ch = 0;
printf("Input:");
scanf_s("%d", &ch);
printf("output:%c", ch);
}
数组
数组就将多个相同类型元素集合在一起的容器.
定义一个数组,并给出这个数组的最大元素个数,数组一经定义,其大小就不能发生改变.
定义完数组之后,在使用时,只能使用数组的单个数组元素. 不能直接引用整个数组, 也不能直接给这个数组重新初始化.
定义方式
语法: 数据类型 数组名[最大元素个数];
1
2int arrTem[10];
//数组中每个元素的数据类型必须相同
初始化
初始化数组时, 必须使用 {} 将初始值列表括起来
数组中的每个元素都有一个序号,这个序号从0开始
数组只能在定义的时候初始化
1
2
3
4int arrTem[10] = {0,1,2,3,4,5,6,7,8,9};
//没有给出初始值的数组元素,会被用0来初始化.
int arrTem2[5] = { 1 };
访问方式1
2int arrTem[10] = {1,2,3,4,5,6,7,8,10};
printf("%d\n", arrTem[4]);
数组内存结构
数组的内存结构是一款连续的内存空间. 每个元素都紧紧相邻.
如果数组越界, 那么就会修改数组所占内存之外的其它内存, 就可能会导致程序运行崩溃.
输入和输出1
2
3
4
5printf("%s\n", strBuff);
scanf_s("%s", &strBuff, sizeof(strBuff));
int arrTem[10] = {1,2,3,4,5,6,7,8,10};
printf("%d %d %d %d\n", arrTem[1],arrTem[2],arrTem[3],arrTem[4]);
C语言中, 没有直接对整型数组进行全部元素都输出的格式化控制符.
字符数组
字符数组, 就是数组元素为char类型的数组.
每个字符串常量, 都会在其末尾携带一个特殊的字符:'\0', 这个字符被称为字符串结束符.
字符串会以\0为结束,而十六进制中则是00
'\0'是 ASCII 码表中的第 0 个字符,英文称为 NUL,中文称为“空字符”。该字符既不能显示,也没有控制功能,输出该字符不会有任何效果,它在C语言中唯一的作用就是作为字符串结束标志。
知识关联
在Web文件上传漏洞中,当在上传的时候通过%00进行截断或者在BurpSuite下进行对文件进行十六进制修改为00则可以达到绕过上传限制。
创建空数组,内存是烫烫烫烫
问题:创建空数组,为什么在内存中会显示中文?
其实这个问题的往往是由于要访问的地址的内容字符未被初始化,或者我们访问了非法内存。
未初始化的变量会被系统赋初值为0xCC,超过了ASCII码0-127这个范围,因此这个"字符串"被系统当成了宽字符组成的字符串,即两个字节数据组成一个字符,而0xCCCC表示的宽字符正好是乱码中的那个"烫"字。"屯"字也是一样的原因。
烫是debug中未初始化的栈变量
屯是debug中未初始化的堆变量
数组相关函数
在C语言, 数组不能直接进行赋值
字符数组之间拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//字符串拷贝函数的原型: void strcpy(char dest[] ,char src[]);
//安全版: void strcpy_s(char dest[], int destSize ,char src[]);
//将字符数组src的内容全部拷贝到dest字符中.
#include
int main()
{
char strBuff[32] = { "Hello" };
char strBuff2[6] = { 0 }; //使用了strcpy_s安全函数,所以不能小于这个字节
strcpy_s(strBuff2, sizeof(strBuff2), strBuff);
printf("szBuff=%s strBuff2=%s\n", strBuff, strBuff2);
}
//通过strcpy_s函数将strBuff数组中的内容拷贝到strBuff2中,因为C语言数组不能直接进行赋值。
字符数组的拼接
1
2
3
4
5
6
7
8
9
10
11
12
13// 要求 : 将World追加到数组szBuff的末尾. 让数组保存: Hello World
// 函数原型: void strcat(char dest[] , char src[]);
// 安全版: void strcat_s(char dest[], int destSize ,char src[]);
#include
int main()
{
char strBuff[32] = { "Hello" };
strcat_s(strBuff, sizeof(strBuff), "World");
printf("szBuff=%s\n", strBuff);
}
字符数组比较
1
2
3
4
5
6
7
8int main()
{
char password[100] = { 0 };
printf("Input password:");
scanf_s("%s", &password, sizeof(password));
int Npass = strcmp(password, "123456");
printf("%d", Npass);
}
字符数组查找
1
2
3
4
5
6
7
8
9// 要求: LKSD是否在字符数组szBuff里面出现.
// 原型: char* strstr(char str[] , char subStr[]);
// 功能 : 函数会在字符数组str中查找子串subStr, 如果字符数组str中包含了这个子串subStr, 就返回它的地址, 如果没有, 就返回NULL.
int main()
{
char password[100] = { "password" };
strstr(password, "ss");
}
二维数组
一维数组的作用就是将多个基本类型保存在一起的语法.
二维数组的作用就是将多个一维数组保存在一起的语法
在定义二维数组时, 一维数组的最大个数必须给出, 不能缺省.
定义的语法: 数据类型 数组名[最大行数][最大列数];
1
2int narr[3][4];
//表示narr数组中有3个数组,每个数组的大小是4个int;
初始化
int nArr[2][3] = { 1,2,3,4,5,6 };
部分初始化
char nArr[2][10] = { "aa","123456" };
二维数组在概念上是二维的,但在内存中是连续存放的;
部分初始化, 初始化后的内存为: aa\0\0\0\0\0\0\0
遍历二维数组1
2
3
4
5
6
7
8
9
10
11int main()
{
int nArr[3][2] = { 1,2,3,4,5,6 };
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 2; ++j) {
printf("%d", nArr[i][j]);
}
printf("\n");
}
}
预处理
用尖括号 #include <>
一般用于包含标准的库头文件,编译器会去系统配置的库环境变量和者用户配置的路径去搜索,而不会在项目的当前目录去查找
用双引号 #include ""
一般用于包含用户自己编写的头文件,编译器会先在项目的当前目录查找,找不到后才会去系统配置的库环境变量和用户配置的路径去搜索
编译预处理主要用于在程序正式编译前进行一些预加工操作,编译预处理共分:宏定义、文件包含、条件编译
无参宏
宏只是进行简单的替换
宏定义的作用就是在程序中某段代码的一个别名,也可以理解为常量。
所有宏命令行都以符号:#define开头,并且结尾不用分号:
1
2
3#define PI 3.14
#undef PI
宏一般用大写字母,以便与变量名区别,在编译预处理时宏名与字符串进行替换时,不作语法检查,只是简单的字符替换,只有在编译时才对已经展开宏名的源程序进行语法检查。
有参宏
#define 宏的名字(参数列表) 宏的内容
有参宏可以简单编写代码
1
2
3
4#define MACRO(n) n * n
int nNum = 3;
printf("%d\n", MACRO(nNum + 2));
可以把以上代码翻译为:
1.MACRO(nNum + 2 * nNum + 2) //先进行替换
2.n * n -> nNum + 2 * nNum + 2 //把n都规换成nNum,宏只是简单的替换并没有做什么运算。
3 + 2 * 3 + 2 //先算乘除后算加减,最终得出结果是11
1
2
3#define AREA(a,b) a+b
int s = AREA(3, 4)*AREA(3, 4);
1
2
31. AREA(a,b) -> a = 3; b = 4;
2. AREA(3,4) * AREA(3+4) -> 3 + 4 * 3 + 4
3. 最终结果:19
例3:
1
2
3
4#define CUBE(x) (x*x*x)
int a = 0, b = 2;
a = CUBE(b+1)
2 + 1 * 2 + 1 * 2 + 1 = 7
函数
函数原型: 返回值类型 + 函数名 + 参数列表组成: int strlen(char* str);
在函数原型的参数列表中, 参数是有数据类型和变量名组成, 它们叫做 形参.
在定义函数时,参数有数据类型和变量名,称为形参 int fun2(int strl);
在调用函数时, () 给出的数据是函数形参的初始值, 这些数据称为 实参
在调用函数时输入的参数就是实参 fun2(strl);
void是C语言中的一个关键字,表示“空类型”或“无类型”,绝大部分情况下也就意味着没有 return 语句。
1
2
3
4
5
6
7
8void func(int n, char ch);
int main()
{
int narrt[10] = { 1,2,3,4,5,6,7 };
int nNum = 10;
char ch = "Hello World";
func(nNum, ch, narrt);
return 作用是:结束函数、将一个表达式的结果作为函数调用表达式的最终结果.
作用域 : 一般就是一个从声明位置开始, 到大括号的结束
生存周期 : 进入作用域就被分配内存, 离开作用域就被回收内存
静态变量
定义的语法是: 在变量的定义前加 static 关键字, 这个变量就成为静态变量:
对于不同作用域下的变量, 这个变量被修饰成静态变量之后会产生不同的影响.
静态变量只执行一次
如果给全局变量加上 static 关键字,它的作用域就变成了当前文件,在其它文件中就无效了。
1
2
3
4
5
6
7
8
9
10
11
12
13void fun(){
int n1 = 0;
static int n2 = 0;
++n1;
++n2;
printf("n1:%d n2:%d\n", n1, n2);
}
int main()
{
for (int i = 0; i < 10; ++i) {
fun();
}
1
2
3
4
5全局变量被保存在静态数据区,未初始化的静态数据区是 0
静态变量只会被初始化一次,第二次执行到时会自动跳过
静态变量被保存在静态数据区,未初始化的静态数据区是 0
局部变量是保存在栈空间中,未初始化的栈空间是 0xCC(烫)
动态分配的数据被保存在堆空间中,未初始化的堆空间是 0xCD(屯)
指针
怎么获取一个变量的内存首地址: 通过取地址运算符 &
得到一个变量的地址,怎么通过这个地址修改这个变量的值? 通过指针
指针的作用:
用于保存一个内存地址
修改所保存的内存地址上的数据
语法: 数据类型 * 变量名;
*标识符可以有几种写法,前、中、后都可以。
1
2
3
4
5int nNum = 0;
int* pNum = &nNum;
int * pNum = &nNum;
int *pNum = &nNum;
定义一个指针类型的变量之后, 没有给变量初始化, 这个变量就是野指针变量.
1
2
3
4int * pNum = NULL;
void * pNum2 = NULL;
char * pStr;
double * pDum;
保存地址的时候, 指针是什么类型, 一般就只能保存什么类型地址. 如果类型不一样, 就会报错.
练习题
1
2
3
4
5
6
7
8
9int nNumber1 = 0;
int nNumber2 = 20;
int* p = NULL;
p = &nNumber1; // p 保存的 nNumber1 的地址
*p = 100;// 找到 p 保存的地址,并且将100存入 100
p = &nNumber2;// p 保存的 nNumber2 的地址
*p = *p * 4;// nNumber2 = nNumber2 * 4; 80
printf("%d %d", nNumber1, nNumber2);
指针可使用的运算符
赋值运算符: =
算术运算符: +,-,++,–,+=,-=
1
2
3
4
5int main(){
int nArr[10]={1,2,3,4,5,6,7,8,9,10};
int* p = nArr;
p + 2; // 数据类型:int* , 得到的结果:数组中值为3的元素的地址
}
不能对指针变量进行乘法、除法、取余等其他运算,除了会发生语法错误,也没有实际的含义。
解引用运算符
解引用运算符: *,[]
1
2
3
4
5
6int main(){
int nArr[10]={1,2,3,4,5,6,7,8,9,10};
int* p = nArr;
*p = 0; // 修改数组中值为1的元素
*(p + 2)=0; // 数据类型:int* , 修改数组中值为3的元素 p[0] = 0 ; // 和 *(p+0)=0等价
p[2] = 0 ; // 和 *(p+2)=0等价 }
对一个指针使用 sizeof 运算符 , 得到永远是4字节(32位程序),如果是64位则是8字节。
数组指针
指针变量的4个字节保存的只是一个首地址.
数组名可以认为是一个指针,它指向数组的第 0 个元素。
第 0 个元素的地址称为数组的首地址。
1
2
3
4
5int main(){
int arr[] = { 1,2,3,4,5,6,7 };
printf("%d", *(arr+2));
}
*(arr+i)
arr是数组名,指向数组的第0个元素,表示数组首地址,
*(arr+i)表示取第i个元素的数据,它等价于arr[i]
1
2
3int *p[5]; //一级指针数组
int **p[5]; //二级指针数组
int (*p2[5])[10]; //数组指针数组
如果一个指针指向了数组,我们就称它为数组指针(Array Pointer)。
*p++应该理解为 *(p++)
1
2
3
4
5
6int main(){
int arr[] = { 1,2,3,4,5,6,7 };
int *p = arr;
printf("%d", *p++);
}
*p++、*++p、(*p)++ 分别是什么意思呢?
1
2
3
4
5*p++ 等价于 *(p++),表示先取得第 n 个元素的值,再将 p 指向下一个元素,上面已经进行了详细讲解。
*++p 等价于 *(++p),会先进行 ++p 运算,使得 p 的值增加,指向下一个元素,整体上相当于 *(p+1),所以会获得第 n+1 个数组元素的值。
(*p)++ 就非常简单了,会先取得第 n 个元素的值,再对该元素的值加 1。假设 p 指向第 0 个元素,并且第 0 个元素的值为 99,执行完该语句后,第 0 个元素的值就会变为 100。
二维数组指针:
1
2
3int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
*(*(p+1)+1)表示第 1 行第 1 个元素的值。
等价关系:
1
2
3a+i == p+i
a[i] == p[i] == *(a+i) == *(p+i)
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)
指针数组和二维数组指针在定义时非常相似,只是括号的位置不同
1
2int *(p1[5]); //指针数组,可以去掉括号直接写作 int *p1[5];
int (*p2)[5]; //二维数组指针,不能去掉括号
结构体
结构体也是一种数据类型,它由程序员自己定义,可以包含多个其他类型的数据。
结构体可以包含多个基本类型的数据,也可以包含其他的结构体,我们将它称为复杂数据类型或构造数据类型。
不变的数据用常量来处理,会变化的数据用变量来处理,
同类型多个数据用数组来处理。
具有多个不同类型的数据的集合用结构体
1
2
3
4struct {
类型1 成员名1;
类型n 成员名2;
}结构体变量名 = {初始化元素1,初始化元素2};
给新的数据类型起名字
类型名的前面加上struct关键字. 在C++中不需要.
1
2
3
4
5
6
7
8
9
10struct MYSTRUCT {
int n;
double d;
};
int main()
{
struct MYSTRUCT obj;
printf("%d\n",sizeof(obj));
如果只需要 stu1、stu2 两个变量,后面不需要再使用结构体名定义其他变量,那么在定义时也可以不给出结构体名,如下所示:
1
2
3
4
5
6
7struct{ //没有写 stu
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;
因为没有结构体名,后面就没法用该结构体定义新的变量。
成员的获取和赋值
结构体变量名.成员名;
除了可以对成员进行逐一赋值,也可以在定义时整体赋值
1
2
3
4
5
6
7struct{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1, stu2 = { "Tom", 12, 18, 'A', 136.5 };
需要注意的是,结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。
定义结构体数组和定义结构体变量的方式类似:
1
2
3
4
5
6
7struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
}class[5];
获取结构体成员
(*pointer).memberName
pointer->memberName
两种写法是等效的
1
2
3
4
5
6
7
8
9
10
11
12struct {
char *name;
int num;
int age;
char group;
float score;
}stu1 = { "Tom",12,18,'A',136.5 }, *pstu = &stu1;
printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n",
(*pstu).name,(*pstu).num,(*pstu).age,(*pstu).group,(*pstu).score);
printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n",
pstu->name, pstu->num, pstu->age, pstu->group, pstu->score);
联合体
联合体的所有字段共用一块内存.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24typedef union ip {
struct {
unsigned char sb1;
unsigned char sb2;
unsigned char sb3;
unsigned char sb4;
}S_un_b;
struct {
unsigned short s_w1;
unsigned short s_w2;
}S_un_w;
unsigned long S_addr;
}IP,*PIP;
int main()
{
IP ipObj;
ipObj.S_addr = 0x12345678; printf("%d对应的IP地址是:%d.%d.%d.%d\n",
ipObj.S_un_b.sb1,
ipObj.S_un_b.sb2,
ipObj.S_un_b.sb3,
ipObj.S_un_b.sb4);
typedef:给类型起一个别名
起别名的目的不是为了提高程序运行效率,而是为了编码方便。
1
2
3
4
5typedef struct stu{
char name[20];
int age;
char sex;
} STU;
STU 是 struct stu 的别名,可以用 STU 定义结构体变量:
STU body1,body2;
它等价于:
struct stu body1, body2;
初始化1
2
3
4
5
6
7
8
9
10typedef struct _person {
int Age;
char Name[32];
double eight;
}PERSON;
int main()
{
PERSON obj = { 18,"xiaoliu",1.7 };
printf("%d %s %f", obj.Age, obj.Name, obj.eight);
没有初始化值的字段默认初始化为0.
使用结构体指针时, 只能使用 -> 来访问字段
1
2
3
4
5
6
7
8
9
10
11
12typedef struct mystruct1 {
int Num;
char szBuff[32];
}MYSTRUCT1;
int main()
{
MYSTRUCT1 obj = { 0 };
MYSTRUCT1* pObj = &obj;
pObj->Num;
输入输出1
2
3
4
5
6
7
8
9
10
11
12
13typedef struct mystruct1 {
int Num;
char szBuff[32];
}MYSTRUCT1;
int main()
{
MYSTRUCT1 obj = { 100,"Hello World" };
printf("%d %s\n", obj.Num, obj.szBuff);
scanf_s("%d%s", &obj.Num, obj.szBuff, sizeof(obj.szBuff));
接收10个人的信息, 每个人的信息包括有: 年龄,性别,身高,姓名. typedef struct person{
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30typedef struct person {
int Age;
char sex;
double Height;
char szName[32];
}PERSON;
int main()
{
PERSON obj[10] = {
{18,'M',1.7,"Hello"},
};
for (int i = 0; i < 10; i++) {
printf("请输入姓名:");
scanf_s("%s", obj[i].szName, sizeof(obj[i].szName));
printf("请输入年龄:");
scanf_s("%d", &obj[i].Height);
printf("请输入身高:");
scanf_s("%lf", &obj[i].Height); printf("请输入性别(m/f):");
scanf_s("%c", &obj[i].sex, 1);
}
for (int i = 0; i < 10; ++i) {
printf("我的名字叫%s,性别:%s,今年%d岁,身高%lf\n",
obj[i].szName,
obj[i].sex == 'm' ? "男" : "女",
obj[i].Age,
obj[i].Height);
}
文件操作
打开文件的函数 : FILE* fopen(char* path, char* model) ;
S中的中打开文件函数: size_t fopen_s(FILE** file, har* path, char* model);
1
2
3
4
5
6"r" - 以只读的方式打开文件, 且文件必须存在
"w" - 以只写的方式打开文件, 无论文件存在与否,都创建新文件
"a" - 以只写的方式打开文件, 追加模式, 如果文件存在,则保留文件内容,并在写入时,自动写入到文件末 尾. 如果文件不存在, 就创建一个新的.
"b" - 以二进制方式打开文件, 对文件中的内容不做任何转换.
"r+" : 以可读可写方式打开文件, 文件必须存在
"w+": 以可读可写方式打开文件,无论文件存在与否,都创建新的 "a+":以可读可写方式打开文件,追加模式
创建文件:
1
2
3
4
5
6
7
8
9
10
11
12
13FILE* fd = NULL;
fopen_s(&fd, "C:\\Users\\V\\source\\repos\\stude\\Day9\\test.txt",
"w");
if (fd == NULL) {
printf("File No!\n");
}
else {
printf("File Yes!\n");
}
fclose(fd);
以文本方式读取文件内容:
1
2
3while (feof(fd) == 0) {
printf("%c", (char)fgetc(fd));
}
格式化读取
fscanf_s(fd, "%d", &nNum);
逐行写入
1fputs("hello 15PB\n", fd);