前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言万字基础笔记总结(三)

C语言万字基础笔记总结(三)

原创
作者头像
远方的星
修改2021-08-02 14:17:43
8730
修改2021-08-02 14:17:43
举报

  • C语言学习笔记,记录所学,便于复习。 由于篇幅过大,考虑到观感,准备分多篇记录。
  • 学习视频链接:《带你学C带你飞》
  • IDE:Dev-C++ 5.11
  • 为了养成良好的编程习惯,暂时放弃Clion
  • 前排提醒:建议收藏

1、函数初识
  • 例:打印一段话
# include <stdio.h>
void hello();  //函数声明 

void hello()  //函数定义 
{
	printf("你好哇,nice to meet you"); 
 } 
int main(){
	hello();  //函数调用 
	
	return 0;
}

输出结果为:

你好哇,nice to meet you

①、函数声明

有时候,函数声明也可以省略(子函数放在主函数前面的时候)

但在初学阶段,为了养成良好的习惯,建议写上函数声明。

②、函数定义

  • 基本框架如下:
类型名  函数名(参数列表)  //参数列表可以空,但是()不能省
{
    函数体
}
2、函数的参数和输出值

- 例:编写一个函数sum,由用户输入**参数n**,计算1+2+...+(n-1)+n的**结果并返回**。

# include <stdio.h>

int sum(int n);  //函数声明

int sum(int n)    //函数定义
{
	int result = 0;
	for(;n>0;n--)   //经典的用一个循环进行求和
	{
		result += n;
	}
	
	return result;
 } 
  
int main(){
	int n,result;
	printf("请输入您想输入的数字:");
	scanf("%d", &n);
	
	result = sum(n);   //函数调用
	printf("求和结果为:%d", result); 
	
	return 0;
}

输出为:

请输入您想输入的数字:10
求和结果为:55

值得一提的是:==函数遇到return 就会立即返回值。==

比如说,利用if条件语句的时候,有两个return,是有两个返回值吗?不是的,只有一个!

  • 例:编写一个函数max,接受两个**整型参数**,并**返回**它们中较大的值。
# include <stdio.h>
int max(int x, int y);  //函数声明

int max(int x, int y)  //函数定义
{
	if (x > y)   //条件语句比较大小
	{
		return x;
	}
	else
	{
		return y;
	}
}
  
int main(){
	int a, b, c;
	
	printf("请输入你想比较的两个数:");
	scanf("%d %d", &a, &b);
	
	c = max(a, b);
	printf("这两个数中较大的那个是:%d", c);
	
	return 0;
}

==子函数(max函数)和主函数(main函数)中的参数名是不冲突的,可以一样,也可以不一样。==

3、指针函数

函数也可以返回指针类型的数据

  • 例:根据输入的字母返回不同的值
# include <stdio.h>
char *getword(char);

char *getword(char c)
{
	if (c = 'A'){  //这里的if语句也可以写成switch语句
		return "Apple";
	}
	else if (c = 'D'){
		return "Dog";
	}
	else{
		return "None";
	}
}
  
int main(){
	char input;
	
	printf("请输入一个字母:");
	scanf("%c", &input);
	
	printf("输出结果为:%s\n", getword(input));
	
	return 0;
}

值得注意的是:==不要返回局部变量的指针==

  • 例:将上述例子加以修改
# include <stdio.h>

char *getword(char);

char *getword(char c)
{
	char str1[] = "Apple";
	char str2[] = "Dog";
	if (c = 'A'){
		return str1;  //返回局部变量
	}
	else if (c = 'D'){
		return str2;
	}
	else{
		return "None";
	}
}
  
int main(){
	char input;
	
	printf("请输入一个字母:");
	scanf("%c", &input);
	
	printf("输出结果为:%s\n", getword(input));
	
	return 0;
}

编译会报warring:

[Warning] function returns address of local variable [-Wreturn-local-addr]

运行结果如下:

请输入一个字母:A
输出结果为:P

因此,返回局部变量的指针是不可取的

4、函数指针

像数组指针一样,它是一个指针。

# include <stdio.h>

int sum(int);

int sum(int num){
	return num + num;
} 
  
int main(){
	int num;
	int (*fs)(int);  //函数指针
	
	printf("请输入一个数字:");
	scanf("%d", &num);
	
	fs = sum;  //函数名就是函数的地址
	
	printf("%d + %d = %d\n", num, num, (*fs)(num)); 
	
	return 0;
}

二、全局变量和局部变量

1、局部变量

在函数里边定义的变量

2、全局变量

在函数外边定义的变量

Q:哪些情况下,会使用全局变量呢?

A:当一个程序中,多个函数都需要使用一个变量,那么就会用到全局变量

==全局变量的一些特点:==

  • ①、如果不对全局变量进行初始化,那么它会初始化为0。
  • ②、如果在函数的内部存在一个与全局变量同名的局部变量,那么在这个函数中,全局变量不起作用。
  • 例:
# include <stdio.h>

void func();

int a, b = 110;   // 定义全局变量a,b  其中a=0,b=110

void func()
{
	int b;   //定义局部变量b 
	
	a = 119;
	b = 120;   //对a,b进行赋值
	
	printf("在func中的值,a = %d, b = %d\n", a, b) ;
 } 
  
int main(){
	printf("在main中的值,a = %d, b = %d\n", a, b);
	func();
	printf("func函数处理后,在main中的值,a = %d, b = %d\n", a, b);
	return 0;
}

运行结果:

在main中的值,a = 0, b = 110
在func中的值,a = 119, b = 120
func函数处理后,在main中的值,a = 119, b = 110

简单分析一下:

首先,定义了全局变量a=0,b=110,所以在第一次打印的时候,结果是不变的;第二次打印,是作用在func函数中,此时定义了一个局部变量b,并对a=119,b=120进行分开赋值,打印结果应该是赋值后的值;第三次打印,是在main函数中,由于a是全局变量,func对其影响是有的,所以a=119继续保持,而b在func中变成了局部变量,赋值仅仅作用在func函数中,到了main函数,又变回110。

==全局变量不建议大量使用,原因:==

  • ①、使用全局比那辆会使程序占用更多的内存
  • ②、污染命名空间,降低程序可读性
  • ③、提高了程序的耦合性,当代码体比较长了之后,时间再久一点,自己会忘记全局变量受到哪些函数的影响了。

三、作用域

  • 当变量被定义在程序的不同位置时,它的作用范围时不一样的,这个作用范围就是作用域。
  • C语言编译器可以确认的4种不同的作用域 ①、代码块作用域 ②、文件作用域 ③、原型作用域 ④、函数作用域

四、链接属性

  • external(外部的)

多个文件中声明的同名标识符表示同一个实体

  • internal(内部的)

单个文件中声明的同名标志符表示同一个实体

  • none(无)

声明的同名标识符被当作独立不同的实体

  • 注意:

只有具备文件作用域的标识符才能拥有external或internal的链接属性,其他作用域的标识符都是none属性。

默认情况下,具备文件作用域的标识符拥有external属性。也就是说该标识符允许跨文件访问。对于external属性的标识符,无论在不同文件中声明多少次,表示的都是同一个实体。

使用static关键字可以使得原先拥有external属性的标识符变为internal属性。==有两点需要注意==:

①、使用static关键字修改链接属性,支队具有文件作用域的标识符生效(对于拥有其他作用域的标识符时另一种功能)

②、链接属性只能修改一次,也就是说一旦将标识符的链接属性变为internal,就无法变为external。

五、生存期

C语言的变量拥有两种生存期:

  • 静态存储期

具有文件作用域的变量属于静态存储期,函数也属于静态存储期。属于静态存储期的变量在程序执行期间将一致占据存储空间,知道程序关闭才释放。

  • 自动存储期

具有代码块作用域的变量一般情况下属于自动存储期。属于自动存储期的变量在代码块结束时将自动释放存储空间。

六、存储类型

C语言提供了五种不同的存储类型。

  • 自动变量(auto)

在代码块中声明的变量默认的存储类型就是自动变量,使用关键字auto来描述。

  • 寄存器变量(register)

将一个变量声明为寄存器变量,那么该变量就有可能被存放于寄存器中。

寄存器变量和自动变量在很多方面是一样的,它们都拥有代码块作用域,自动存储期和空连接属性。

将变量声明为寄存器变量,那么就没办法通过取址运算符获得该变量的地址。

  • 静态局部变量(static)

使用static来声明局部变量,那么就可以将局部变量指定为静态局部变量。

static的局部变量具有静态存储期,所以它的生存期与全局变量一样,直到程序结束才释放。

  • 外部变量(extern)

extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。

也就是说,extern关键字是用于告诉编译器这个变量或函数在别的地方已经定义过了,不要急着报错

  • typedef

==后续补充==

七、更灵活的内存管理方式

  • malloc

申请动态内存空间

  • free

释放动态内存空间

  • calloc

申请并初始化一系列内存空间

  • realloc

重新分配内存空间

1、mallloc

函数原型:

void *malloc(size_t size);

malloc函数向系统申请分配size个字节的内存空间,并==返回一个指向这块空间的指针==。

如果函数调用成功,返回一个指向申请的内存空间的指针,由于==返回类型是void 指针==(void *),所以它可以被转换成任何类型的数据;如果函数调用失败,返回值是NULL。另外,如果size参数设置为0,返回值也可能是NULL,但并不意味着函数调用失败。

  • 例:
# include<stdio.h>
# include<stdlib.h>

int main()
{
	int *ptr;
	
	ptr = (void *)malloc(sizeof(int));  
	// ptr = (int *)malloc(sizeof(int));
	//因为viod类型,可以转换为任意类型,写成int 可以提高可读性 
	if (ptr == NULL)
	{
		printf("分配内存失败!\n");
		exit(1);
	}
	
	printf("请输入一个整数:");
	scanf("%d", ptr);
	
	printf("你输入的整数是:%d", *ptr);
	
	return 0;
}

输出:

请输入一个整数:6
你输入的整数是:6
2、free

函数原型:

void free(void *ptr);

==free函数释放ptr参数指向的内存空间==。该内存空间必须是由malloc、calloc或realloc函数申请的。否则,该函数将导致未定义行为。如果ptr参数是NULL,则不执行任何操作。

注意:该函数不会修改ptr参数的值,调用它后仍然指向原来的地方。(非法空间)

  • 例:
# include<stdio.h>
# include<stdlib.h>

int main()
{
	int *ptr;
	
	ptr = (void *)malloc(sizeof(int));  
	// ptr = (int *)malloc(sizeof(int));
	//因为viod类型,可以转换为任意类型,写成int 可以提高可读性 
	if (ptr == NULL)
	{
		printf("分配内存失败!\n");
		exit(1);
	}
	
	printf("请输入一个整数:\n");
	scanf("%d", ptr);
	
	printf("free前,整数是:%d\n", *ptr);
	free(ptr);  //比较释放前后的区别
	
	printf("free后,整数是:%d\n", *ptr);
	
	return 0;
}

输出:

请输入一个整数:
6
free前,整数是:6
free后,整数是:13435216

释放后就出现了“错误数值”。

==内存泄漏==

  • 隐式内存泄漏

(用完内存块没有及时使用free函数释放)

  • 丢失内存块地址
3、calloc

函数原型:

void *calloc(size_t nmemb, size_t size);

calloc函数在内存中动态地申请nmemb个长度为size的连续内存空间(申请的总空间尺寸为nmemb*size),这些内存空间全部被初始化为0。

  • calloc函数与malloc函数的区别:

==calloc==函数在申请完内存后,自动初始化该内存空间为零。

==malloc==函数不进行初始化操作,里边的数据是随机的。

  • 例:以下两种写法是等价的
//calloc()分配内存空间并初始化
int *ptr = (int*)calloc(8, sizeof(int));

//malloc() 分配内存空间并用meset()初始化
int *ptr = (int *)malloc(8 * sizeof(int));
memset(ptr, 0, 8 * sizeof(int));
4、realloc

函数原型:

void *realloc(void *ptr, size_t size);

以下几点是需要注意的:

  • realloc函数修改ptr指向的内存空间大小为size字节
  • 如果新分配的内存空间比原来的大,则就内存块的数据不会发生变化;如果新的内存空间大小小于旧的内存空间,可能会导致数据丢失。
  • 该函数将移动内存空间的数据并返回新的指针
  • 如果ptr参数为NULL,那么调用该函数就相当于调用malloc
  • 如果调用size参数为0,并且ptr参数不为NULL,那么调用该函数就相当于调用free(ptr)
  • 除非ptr参数为NULL,否则ptr的值必须由先前调用malloc、calloc或者realloc函数返回。
  • 例:
# include <stdio.h>
# include <stdlib.h>

int main()
{
	int i,num;
	int count = 0;
	int *ptr = NULL; //  注意,这里必须初始化为NULL
	
	do 
	{
		printf("请输入一个整数(输入-1代表结束):");
		scanf("%d", &num);
		count++;
		
		ptr = (int *)realloc(ptr, count * sizeof(int));
		if (ptr == NULL)
		{
			exit(1);
		 } 
		ptr[count-1] = num; 
	 } while(num != -1);   //直到num=-1,循环停止 
	 
	 printf("输入的整数分别是:");
	 for (i = 0;i < count;i++)
	 {
	 	printf("%d  ", ptr[i]);
	 }
	 putchar('\n');  //这里是单引号 
	 
	 return 0;
 } 

输出:

请输入一个整数(输入-1代表结束):9
请输入一个整数(输入-1代表结束):0
请输入一个整数(输入-1代表结束):7
请输入一个整数(输入-1代表结束):-1
输入的整数分别是:9  0  7  -1

八、C语言的内存布局规律

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  • 代码段

代码段通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

  • 数据段

通常用来存放已经初始化的去那句变量和局部静态变量。

  • BSS段

通常是指用来存放程序中未初始化的全局变量的一块内存区域。这个区段中的数据在程序运行前将被自动初始化为数字0。

1、堆

堆是用于==存放进程运行中被动态分配的内存段==,它的大小并不固定,可动态扩展或缩小。当进程调用malloc等函数分配内存时,新分配的内存就动态添加到堆上;当利用free等函数释放内存时,被释放的内存从堆中被删除。

2、栈

栈是函数执行的内存区域,通常和堆共享一片区域。

3、堆和栈的区别
  • 申请方式:

堆由程序员手动申请

栈由系统自动分配

  • 释放方式:

堆由程序员手动释放

栈由系统自动释放

  • 生存周期:

堆的生存周期由动态申请到程序猿主动释放为止,不同函数之间均可自由访问

栈的生存周期由函数调用开始到函数返回时结束,函数之间的局部变量不能互相访问

  • 发展方向:

堆是从低地址向高地址发展

栈是由高地址向低地址发展

九、宏定义

1、不带参数的宏定义
  1. 为了和普通的变量进行区分,宏的名字全部由大写字母组成。
  2. 宏定义只是简单地进行替换,并且由于预处理是在编译之前进行,而编译工作的任务之一就是语法检查,所以编译器不会堆宏定义进行语法检查。
  3. 宏定义不是说明或语句,在末尾不必加分号。
  4. 宏定义的作用域是从定义的位置开始到整个程序结束。 例:计算一个圆的面积
# include <stdio.h>

#define PI 3.14
  
int main(){
	int r = 4;
	float s;
	
	s = r * r * PI;
	printf("圆的面积是:%.2f", s);
	
	return 0;
}

输出结果为:

圆的面积是:50.24
  1. 可以用#undef 来终止宏定义的作用域。 例:将上例加上#undef
# include <stdio.h>

#define PI 3.14
  
int main(){
	int r = 4;
	float s;
	
#undef PI           
	s = r * r * PI;
	printf("圆的面积是:%.2f", s);
	
	return 0;
}

编译结果:

[Error] 'PI' undeclared (first use in this function)
[Note] each undeclared identifier is reported only once for each function it appears in

可见,到计算面积的时候,PI并没有被定义。原因是#undef终结了作用域。

  1. 宏定义允许嵌套。
2、带参数的宏定义
  • 例:输出两个数中较大的那个
# include <stdio.h>

#define MAX(x, y) (((x) > (y)) ? (x) : (y)) 
  
int main(){
	int x, y;
	
	printf("请输入两个数:");
	scanf("%d%d", &x, &y);
	printf("两个数中较大的那个是:%d", MAX(x,y));
	
	return 0;
}

输出结果为:

请输入两个数:9 8
两个数中较大的那个是:9

注意:MAX(x, y)这里的MAX和(x,y)中间不能有空格。

((x) > (y)) ? (x) : (y)   //这里是条件语句,是不是有点忘记了呢?
3、补充:#和##运算符
  • 在带参数的宏定义中,#运算符后面应该跟一个参数,预处理器会把这个参数转换成一个字符串
  • 例:
# include <stdio.h>
  
# define STR(s)  #s
int main(){
	printf(STR(Hello  %s), STR(World));
	
	return 0;
}

运算结果:

Hello World
  • ##运算符被称为记号连接运算符,比如我们可以使用##运算符连接两个参数。
  • 例:
# include <stdio.h>
  
# define LINK(x, y) x ## y
int main(){
	printf("%d\n", LINK(5, 21));
	
	return 0;
}

输出结果:

521

十、内联函数

引入内敛函数来解决程序中函数调用的效率问题。

内联函数虽然节省了函数调用的时间消耗,但由于每一个函数出现的地方都要进行替换,因此增加了代码编译的时间。另外,并不是所有的函数都能变化曾内联函数。

现在的编译器也很聪明,就算你不写inline,它也会自动将一些函数优化成内联函数。

所以说,内联函数了解即可!

十一、结构体

1、结构体声明、定义、变量访问及初始化

可以使用结构体(Struct)来存放一组不同类型的数据。

  • 结构体声明
struct  结构体名称
{
     结构体成员1;
     结构体成员2;
     结构体成员3;
     ......
};
  • 定义结构体类型变量
struct  结构体名称  结构体变量名
  • 访问结构体变量

需要用到 . 运算符。比如book.title就是引用book结构体的title成员,它是一个字符数组。

  • 例:设计一个简单的图书录入系统
# include <stdio.h>
  
struct Book
{
	char title[120];
	char author[40];
	float price;
 } ;
int main(){
	struct Book book;
	
	printf("请输入书名:");
	scanf("%s", book.title);
	printf("请输入作者:");
	scanf("%s", book.author);
	printf("请输入价格:");
	scanf("%f", &book.price);
	
	printf("数据录入完成\n");
	
	printf("书名:%s\n", book.title);
	printf("作者:%s\n", book.author);
	printf("价格:%.2f\n", book.price);
	
	return 0;
}

输出结果:

请输入书名:《从容的底气》
请输入作者:林清玄
请输入价格:39.90
数据录入完成
书名:《从容的底气》
作者:林清玄
价格:39.90
  • 初始结构体的指定成员值

例如,上例中,我们只初始化Book的price成员:

struct  Book  book  =  {.price = 50};
2、结构体数组
  • 定义的第一种方法,在声明结构体的时候进行定义
struct  结构体名称
{
    结构体成员;
} 数组名[长度];
  • 定义的第二种方法,先声明一个结构体类型,再用此类型定义一个结构体数组
struct  结构体名称
{
    结构体成员;
};
struct  结构体名称  数组名[长度];
  • 初始化结构体数组
  • 例如:
struct Book book[3] = {
     {"三体Ⅰ", "地球往事"},
     {"三体Ⅱ", "黑暗森林"},
     {"三体Ⅲ", "死神永生"}
};
3、结构体指针
  • 通过结构体指针访问结构体成员的两种方法
第一种:(*结构体指针).成员名

第二种:结构体指针->成员名
4、传递结构体变量

两个结构体变量能直接赋值,但要求数据类型要一致。

  • 例:
# include <stdio.h>
  
int main(){
	struct Test{
		int x;
		int y;
	}t1, t2;
	
	t1.x = 3;
	t1.y = 10;  
	
	t2 = t1;  // 将t1的值赋给t2
	
	printf("t2.x = %d, t2.y = %d\n", t2.x, t2.y);
	
	return 0;
}

输出:

t2.x = 3, t2.y = 10
  • 例:一个小的图书录入系统
#include <stdio.h>

struct Date  //结构体声明 
{
	int year;
	int month;
	int day;
};

struct Book   //结构体声明 
{
	char title[120];  //定义一个字符数组 
	char author[40];
	struct Date data;
};

struct Book getInput(struct Book book); 
void printBook(struct Book book); //函数声明

struct Book getInput(struct Book book)  //录入信息子函数,该函数的返回值是一个结构体,参数也是一个结构体 ,只要是机构提,就要有struct关键字 
{
	printf("请输入书名:");
	scanf("%s", book.title);
	printf("请输入作者:");
	scanf("%s", book.author);
	printf("请输入购买日期:");
	scanf("%d-%d-%d", &book.data.year, &book.data.month, &book.data.day);
	
	return book;
}

void printBook(struct Book book)   //打印子函数 
{
	printf("书名:%s\n", book.title);
	printf("作者:%s\n", book.author);
	printf("购买日期:%d-%d-%d\n", book.data.year, book.data.month, book.data.day);
}

int main()
{
	struct Book b1, b2;  //定义结构体变量 
	
	printf("请输入第一本书的信息:\n");
	b1 = getInput(b1);
	putchar('\n');
	printf("请输入第二本书的信息:\n");
	b2 = getInput(b2);
	
	printf("\n\n现在信息已录入完毕,开始打印验证\n\n");
	
	printf("打印第一本书的信息:\n"); 
	printBook(b1);
	printf("打印第二本书的信息:\n");
	printBook(b2);
	
	return 0; 
}

输出结果为:

请输入第一本书的信息:
请输入书名:《远方的星》
请输入作者:星
请输入购买日期:2021-7-27

请输入第二本书的信息:
请输入书名:《星》
请输入作者:远方的星
请输入购买日期:2021-7-27


现在信息已录入完毕,开始打印验证

打印第一本书的信息:
书名:《远方的星》
作者:星
购买日期:2021-7-27
打印第二本书的信息:
书名:《星》
作者:远方的星
购买日期:2021-7-27
5、传递指向结构体变量的指针。
  • 例:将上例进行简单修改
#include <stdio.h>

struct Date  
{
	int year;
	int month;
	int day;
};

struct Book    
{
	char title[120];  
	char author[40];
	struct Date data;
};

void getInput(struct Book *book); 
void printBook(struct Book *book); 

void getInput(struct Book *book)  //由于用到指针,这里就不需要返回值了 
{
	printf("请输入书名:");
	scanf("%s", book->title);
	printf("请输入作者:");
	scanf("%s", book->author);
	printf("请输入购买日期:");
	scanf("%d-%d-%d", &book->data.year, &book->data.month, &book->data.day);  //因为前面没有把data定义为指针,所以data.year等地方不需要修改 
}

void printBook(struct Book *book)   
{
	printf("书名:%s\n", book->title);
	printf("作者:%s\n", book->author);
	printf("购买日期:%d-%d-%d\n", book->data.year, book->data.month, book->data.day);
}

int main()
{
	struct Book b1, b2;  //定义结构体变量 
	
	printf("请输入第一本书的信息:\n");
	getInput(&b1);   //传入地址 
	putchar('\n');
	printf("请输入第二本书的信息:\n");
	getInput(&b2);
	
	printf("\n\n现在信息已录入完毕,开始打印验证\n\n");
	
	printf("打印第一本书的信息:\n"); 
	printBook(&b1);
	printf("打印第二本书的信息:\n");
	printBook(&b2);
	
	return 0; 
}

结果与上例一致。

6、动态申请结构体

使用malloc函数为结构体分配存储空间

例:对上例继续做简单的改变

#include <stdio.h>
#include <stdlib.h>    //exit需要的头文件 
struct Date  
{
	int year;
	int month;
	int day;
};

struct Book    
{
	char title[120];  
	char author[40];
	struct Date data;
};

void getInput(struct Book *book); 
void printBook(struct Book *book); 

void getInput(struct Book *book)  //由于用到指针,这里就不需要返回值了 
{
	printf("请输入书名:");
	scanf("%s", book->title);
	printf("请输入作者:");
	scanf("%s", book->author);
	printf("请输入购买日期:");
	scanf("%d-%d-%d", &book->data.year, &book->data.month, &book->data.day);  //因为前面没有把data定义为指针,所以data.year等地方不需要修改 
}

void printBook(struct Book *book)   
{
	printf("书名:%s\n", book->title);
	printf("作者:%s\n", book->author);
	printf("购买日期:%d-%d-%d\n", book->data.year, book->data.month, book->data.day);
}

int main()
{
	struct Book *b1, *b2;  //定义结构体变量 
	
	b1 = (struct Book *)malloc(sizeof(struct Book));  //强制转换为一个指向Book的指针 
	b2 = (struct Book *)malloc(sizeof(struct Book)); 
	if (b1 == NULL || b2 == NULL) 
	{
		printf("内存分配失败!\n");
		exit(1);
	}
	
	printf("请输入第一本书的信息:\n");
	getInput(b1);   
	putchar('\n');
	printf("请输入第二本书的信息:\n");
	getInput(b2);
	
	printf("\n\n现在信息已录入完毕,开始打印验证\n\n");
	
	printf("打印第一本书的信息:\n"); 
	printBook(b1);
	printf("打印第二本书的信息:\n");
	printBook(b2);
	
	free(b1);
	free(b2);  //最后不能忘记释放 
	
	return 0; 
}

结果仍然一致!

十二、未完待续

这一阶段的笔记就先记录到这里,下一篇,应该是最后一篇啦,加油!

路漫漫其修远兮,吾将上下而求索。

大家一起加油吧!

在这里插入图片描述
在这里插入图片描述

作者:远方的星 CSDN:https://blog.csdn.net/qq_44921056

本文仅用于交流学习,未经作者允许,禁止转载,更勿做其他用途,违者必究。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、函数初识
  • 2、函数的参数和输出值
  • 3、指针函数
  • 4、函数指针
  • 二、全局变量和局部变量
    • 1、局部变量
      • 2、全局变量
      • 三、作用域
      • 四、链接属性
      • 五、生存期
      • 六、存储类型
      • 七、更灵活的内存管理方式
        • 1、mallloc
          • 2、free
            • 3、calloc
              • 4、realloc
              • 八、C语言的内存布局规律
                • 1、堆
                  • 2、栈
                    • 3、堆和栈的区别
                    • 九、宏定义
                      • 1、不带参数的宏定义
                        • 2、带参数的宏定义
                          • 3、补充:#和##运算符
                          • 十、内联函数
                          • 十一、结构体
                            • 1、结构体声明、定义、变量访问及初始化
                              • 2、结构体数组
                                • 3、结构体指针
                                  • 4、传递结构体变量
                                    • 5、传递指向结构体变量的指针。
                                      • 6、动态申请结构体
                                      • 十二、未完待续
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档