C语言负浮点数乘法报错,C语言复习

本文详细介绍了计算机中二进制、八进制、十进制、十六进制之间的转换方法,以及如何进行八进制转十进制、二进制转十进制等操作。此外,还讲解了内存中的数据存储、变量类型、数组、指针、结构体、文件操作等基础知识,涵盖C语言中的主要数据类型和操作。
摘要由CSDN通过智能技术生成

dc550e8cff09532f2d65c27e339089a2.png

计算机中的数据

常用的二进制、八进制、十进制、十六进制,它们之间区别在于运算时是逢几进一位。

如:八进制转十进制:通常可以先转二进制再转其它进制,这样的方法比较简单,八进制-> 二进制 -> 十进制

进制转换

二进制转十进制

方法为:把二进制数按权展开、相加即得十进制数。

128 64 32 16 8 4 2 1

例子1:

101011

把结果相加:32 + 8 + 2 + 1 = 43

例子2:

10010110

128 + 16 + 4 + 2 = 150

八进制转十进制

方法:取一分三法,即将一位八进制数分解成三位二进制数,用三位二进制按权相加去凑这位八进制数,小数点位置照旧。

a027ab432d44d0d5c04da314fb56d874.png

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为止。

dbb3dfb71b43671eccccf7db0f6c7c21.png

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

十进制转八进制

c7f48807902a138b655dc3d05c418c2b.png

转二进制,3位为一组,缺少补零

011 100 010

最后得出结果是:342

十进制转十六进制

feea4f1ec8254ee04cba2b411c7fb015.png

原码、反码、补码

正数:三码合一

负数:

原码:最高位不变,后面数据和其绝对值相同

反码:将原码除最高位,其余按位取反

补码:将反码+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 个关键字

编译过程

cc0f8253ea3ddf6c523d883711a6b731.png

1.gcc -E hello.c -o a.c 会把#include 里的文件都包含进来拷进来,宏之类的

267de6350aafae30f7830c0c2c3e8a1c.png

2.gcc -S a.c -o a.s 把高级语言变成低级语言,称为编译过程.

ec228ff9728c435c8d0542c5da25fbfa.png

3.gcc -c a.s -o a.o 把低级语言转成机器语言,二进制

636a23bec55cd169f8d35dae82f1e9c8.png

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]);

数组内存结构

数组的内存结构是一款连续的内存空间. 每个元素都紧紧相邻.

如果数组越界, 那么就会修改数组所占内存之外的其它内存, 就可能会导致程序运行崩溃.

0c7c7bf7afceae3f27093953ca125556.png

输入和输出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

29f6095ea005d50984c606cf91426352.png

'\0'是 ASCII 码表中的第 0 个字符,英文称为 NUL,中文称为“空字符”。该字符既不能显示,也没有控制功能,输出该字符不会有任何效果,它在C语言中唯一的作用就是作为字符串结束标志。

知识关联

在Web文件上传漏洞中,当在上传的时候通过%00进行截断或者在BurpSuite下进行对文件进行十六进制修改为00则可以达到绕过上传限制。

创建空数组,内存是烫烫烫烫

问题:创建空数组,为什么在内存中会显示中文?

其实这个问题的往往是由于要访问的地址的内容字符未被初始化,或者我们访问了非法内存。

未初始化的变量会被系统赋初值为0xCC,超过了ASCII码0-127这个范围,因此这个"字符串"被系统当成了宽字符组成的字符串,即两个字节数据组成一个字符,而0xCCCC表示的宽字符正好是乱码中的那个"烫"字。"屯"字也是一样的原因。

烫是debug中未初始化的栈变量

屯是debug中未初始化的堆变量

e5836c23bf200c912aee515a72a9ab77.png

数组相关函数

在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

a33209ccfdba0237736f41b65431538c.png

17b6b9c3c0188dd34e665dfcb0eed612.png

遍历二维数组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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值