# include <stdio.h>
void hello(); //函数声明
void hello() //函数定义
{
printf("你好哇,nice to meet you");
}
int main(){
hello(); //函数调用
return 0;
}
输出结果为:
你好哇,nice to meet you
①、函数声明
有时候,函数声明也可以省略(子函数放在主函数前面的时候)
但在初学阶段,为了养成良好的习惯,建议写上函数声明。
②、函数定义
类型名 函数名(参数列表) //参数列表可以空,但是()不能省
{
函数体
}
- 例:编写一个函数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,是有两个返回值吗?不是的,只有一个!
整型参数
**,并**返回
**它们中较大的值。# 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函数)中的参数名是不冲突的,可以一样,也可以不一样。==
函数也可以返回指针类型的数据
# 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
因此,返回局部变量的指针是不可取的
像数组指针一样,它是一个指针。
# 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;
}
在函数里边定义的变量
在函数外边定义的变量
Q:哪些情况下,会使用全局变量呢?
A:当一个程序中,多个函数都需要使用一个变量,那么就会用到全局变量
==全局变量的一些特点:==
# 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。
==全局变量不建议大量使用,原因:==
多个文件中声明的同名标识符表示同一个实体
单个文件中声明的同名标志符表示同一个实体
声明的同名标识符被当作独立不同的实体
只有具备文件作用域的标识符才能拥有external或internal的链接属性,其他作用域的标识符都是none属性。
默认情况下,具备文件作用域的标识符拥有external属性。也就是说该标识符允许跨文件访问。对于external属性的标识符,无论在不同文件中声明多少次,表示的都是同一个实体。
使用static关键字可以使得原先拥有external属性
的标识符变为internal属性
。==有两点需要注意==:
①、使用static关键字修改链接属性,支队具有文件作用域的标识符生效(对于拥有其他作用域的标识符时另一种功能)
②、链接属性只能修改一次,也就是说一旦将标识符的链接属性变为internal,就无法变为external。
C语言的变量拥有两种生存期:
具有文件作用域的变量属于静态存储期,函数也属于静态存储期。属于静态存储期的变量在程序执行期间将一致占据存储空间,知道程序关闭才释放。
具有代码块作用域的变量一般情况下属于自动存储期。属于自动存储期的变量在代码块结束时将自动释放存储空间。
C语言提供了五种不同的存储类型。
在代码块中声明的变量默认的存储类型就是自动变量,使用关键字auto来描述。
将一个变量声明为寄存器变量,那么该变量就有可能被存放于寄存器中。
寄存器变量和自动变量在很多方面是一样的,它们都拥有代码块作用域,自动存储期和空连接属性。
将变量声明为寄存器变量,那么就没办法通过取址运算符获得该变量的地址。
使用static来声明局部变量,那么就可以将局部变量指定为静态局部变量。
static的局部变量具有静态存储期,所以它的生存期与全局变量一样,直到程序结束才释放。
extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。
也就是说,extern关键字是用于告诉编译器这个变量或函数在别的地方已经定义过了,不要急着报错
==后续补充==
申请动态内存空间
释放动态内存空间
申请并初始化一系列内存空间
重新分配内存空间
函数原型:
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
函数原型:
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函数释放)
函数原型:
void *calloc(size_t nmemb, size_t size);
calloc函数在内存中动态地申请nmemb个长度为size的连续内存空间(申请的总空间尺寸为nmemb*size),这些内存空间全部被初始化为0。
==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));
函数原型:
void *realloc(void *ptr, size_t size);
以下几点是需要注意的:
# 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
代码段通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
通常用来存放已经初始化的去那句变量和局部静态变量。
通常是指用来存放程序中未初始化的全局变量的一块内存区域。这个区段中的数据在程序运行前将被自动初始化为数字0。
堆是用于==存放进程运行中被动态分配的内存段==,它的大小并不固定,可动态扩展或缩小。当进程调用malloc等函数分配内存时,新分配的内存就动态添加到堆上;当利用free等函数释放内存时,被释放的内存从堆中被删除。
栈是函数执行的内存区域,通常和堆共享一片区域。
堆由程序员手动申请
栈由系统自动分配
堆由程序员手动释放
栈由系统自动释放
堆的生存周期由动态申请到程序猿主动释放为止,不同函数之间均可自由访问
栈的生存周期由函数调用开始到函数返回时结束,函数之间的局部变量不能互相访问
堆是从低地址向高地址发展
栈是由高地址向低地址发展
# 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
# 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终结了作用域。
# 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) //这里是条件语句,是不是有点忘记了呢?
# 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,它也会自动将一些函数优化成内联函数。
所以说,内联函数了解即可!
可以使用结构体(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};
struct 结构体名称
{
结构体成员;
} 数组名[长度];
struct 结构体名称
{
结构体成员;
};
struct 结构体名称 数组名[长度];
struct Book book[3] = {
{"三体Ⅰ", "地球往事"},
{"三体Ⅱ", "黑暗森林"},
{"三体Ⅲ", "死神永生"}
};
第一种:(*结构体指针).成员名
第二种:结构体指针->成员名
两个结构体变量能直接赋值,但要求数据类型要一致。
# 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
#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;
}
结果与上例一致。
使用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 删除。