C 函数详解
一、函数的定义
#include <stdio.h>
/*
#函数的定义
类型名 函数名(参数列表){函数体}
类型名是函数返回值的类型,如果函数没有返回值,类型名为void
*/
/*
# 函数的申明
所谓申明,就是告诉编译器我要使用这个函数,你现在没有找到它的定义不要紧,请不要
报错,稍后我会把定义补上
如果函数的定义写在函数的调用之后,必须写
如果函数的定义写在函数的调用之前,可以不写
*/
void print_c();//函数的申明
void print_c(){
printf(" ###### \n");
printf("## ##\n");
printf("## \n");
printf("## \n");
printf("## ##\n");
printf(" ###### \n");
}
int main(){
print_c();//函数的调用
printf("\n");
print_c();
printf("\n");
print_c();
return 0;
}
二、函数的参数和返回值
#include <stdio.h>
//函数的参数和返回值
/*
# 函数的参数
*/
int sum(int n);
int sum(int n){
int sum = 0;;
while(n>=0){
sum+=n;
n--;
}
return sum;
}
int cmp(int a,int b);
int cmp(int a,int b){
if(a>=b){
return a;
}else{
return b;
}
}
int main(){
int n;
printf("请输入n的值:");
scanf("%d",&n);
int result;
result = sum(n);
printf("和为:%d\n",result);
int max;
max = cmp(5,4);
printf("较大值为:%d\n",max);
return 0;
}
三、参数和指针
#include <stdio.h>
//一个函数仅实现一个功能
//参数和指针
/**
# 传值和传址
指针也是一个变量,你可以通过参数传递给函数
*/
void swap(int x ,int y);
void swap(int x ,int y){
int temp;
printf("In swap互换前为x=%d,y=%d\n",x,y);
temp = x;
x = y;
y = temp;
printf("In swap互换后为:x=%d,y=%d\n",x,y);
}
void swap2(int *a, int *b);
void swap2(int *a, int *b){
printf("In swap2互换前为a=%d,b=%d\n",*a,*b);
int temp;
temp = *a;
*a = *b;
*b = temp;
printf("In swap2互换后为:a=%d,b=%d\n",*a,*b);
}
int main(){
int x = 3,y = 5;
printf("In main互换前为x=%d,y=%d\n",x,y);
swap(x,y);
printf("In main互换后为:x=%d,y=%d\n",x,y);
printf("-------------------------------\n");
int a = 3,b = 5;
printf("In main互换前为a=%d,b=%d\n",a,b);
swap2(&a,&b);
printf("In main互换后为:a=%d,b=%d\n",a,b);
return 0;
}
四、指针函数
#include <stdio.h>
//指针函数 与 函数指针
/**
#指针函数:使用指针变量作为函数的返回值
不要
*/
char *getWord(char c);
char *getWord(char c){
switch(c){
case 'A':
return "Apple";
case 'B':
return "Banana";
case 'C':
return "Cat";
case 'D':
return "Dog";
default:
return "NULL";
}
}
int main(){
char input;
printf("请输入一个字母:");
scanf("%c",&input);
printf("%s\n",getWord(input));
return 0;
}
五、函数指针
#include <stdio.h>
//函数指针
//指向函数的指针
//int (*p)()
int square(int num);
int square(int num){
return num*num;
}
int main(){
int num;
//申明一个函数指针
int (*fp)(int);
printf("请输入一个整数:");
scanf("%d",&num);
fp = square;
printf("%d\n",(*fp)(num));
return 0;
}
六、局部变量和全局变量
#include<stdio.h>
/**
局部变量和全局变量
*/
//1、不同函数的变量无法相互访问
//2、在函数内部定义叫做局部变量,在函数外部定义叫做外部变量,也叫全局变量
//如果不对全局变量进行初始化,那么它会自动初始化为0
//如果在函数的内部存在一个与全局变量同名的局部变量,编译器并不会报错,而是在函数中屏蔽全局变量(也就是说在这个函数中全局变量不起作用)
int main_1(){
int i = 520;
printf("before,i=%d\n",i);//520
for(int i = 0;i<10;i++){//此时的i只争对本代码块内有效
printf("%d\n",i);
}
printf("after,i=%d\n",i);//520
return 0;
}
//有时候,我们可能需要在多个函数中共同使用一个变量,那么就会用到全局变量。因为全局变量可以被本程序中其他函数共用的。
int count = 0;//这就是一个全局变量
void a();
void b();
void c();
void a(){
count++;
}
void b(){
count++;
}
void c(){
count++;
}
int main_2(){
a();
b();
c();
b();
printf("小郭今天被抱了%d次\n",count);//小郭今天被抱了4次
}
//extern关键字
//用extern关键字告诉编译器这个变量我在后边定义了,你先别急着报错
void incr();
void incr(){
extern incrNum;//关键字
incrNum++;
}
int incrNum = 0;
int main(){
incr();
printf("%d\n",incrNum);
return 0;
}
/**
不要大量使用全局变量
1.使用全局变量会使你的程序占用更多的内存,因为全局变量从被定义时候开始,直到程序退出才被释放。而局部变量当函数被调用之后就释放了。
2.会污染命名空间,虽然局部变量会屏蔽全局变量,但这样以来也会降低程序的可读性,人们往往很难一下子判断出每个变量的含义和作用范围。
3.提高了程序的耦合性,牵一发而动全身,时间久了,代码长了,你都不知道全局变量被哪些函数修改过。
*/
七、作用域和链接属性
#include<stdio.h>
//作用域和链接属性
/**
作用域:
1、当变量被定义在程序的不同位置时,它的作用范围是不一样的,这个作用返回就是我们所说的作用域。
2、C语言编译器可以确认4种不同类型的作用域:
----代码块作用域
----文件作用域
----原型作用域(少用)
----函数作用域(少用)
*/
/**
代码块作用域:
1、在代码块种定义的变量,具有代码块作用域。作用范围是从变量定义的位置开始,到标志该代码块结束的右大括号处
2、尽管函数的形式参数不在打括号内定义,但其同样具有代码块作用域,隶属于包含函数体的代码块。
*/
int main_1(){
int i = 100;//i1
{
int i = 110;//i2
{
int i = 120;//i3
printf("i = %d\n",i);//120
}
{
printf("i = %d\n",i);//110
int i =130;//i4
printf("i = %d\n",i);//130
}
printf("i = %d\n",i);//110
}
printf("i = %d\n",i);//100
return 0;
}
/**
文件作用域(file scope):
1、任何在代码块之外声明的标识符都具有文件作用域,作用范围是从他们的声明位置开始,到文件的结尾处都是可以访问的。
2、另外,函数名也具有文件作用域,因为函数名本身也是在代码块之外。
*/
/**
原型作用域:
1、原型作用域只适用于那些在函数原型中声明的参数名。函数在声明的时候可以不写参数名字(但参数的类型是必须要写上的),其实函数原型的参数名还可以随便写一个名字,不必与形式参数相匹配(当然,这样做没有任何意义!)。
void func(int a,int b,int c);
void func(int d,int e,int f){...}
*/
/**
函数作用域:
1、函数作用域只适用于goto语句的标签,作用将goto语句的标签限制在同一个函数内部,以及防止出现崇明标签。
*/
//定义和声明
/**
1、当一个变量被定义的时候,编译器为变量申请内存空间并填充一些值。
2、当一个变量被声明的时候,编译器就知道该变量被定义在其他地方。
3、声明是通知编译器该变量名及相关的类型已存在,不需要再为此申请内存空间。
4、局部变量及时定义又是申明。
5、定义只能来一次,否则就叫做重复定义某个同名变量;而声明可以有很多次。
*/
//链接属性
/**
1、external(外部的):多个文件中声明的同名标识符表示同一个实体
2、internal(内部的):单个文件中声明的同名标识符表示同一个实体
3、none(无):申明的同名标识符被当作独立不同的实体。
*/
/**
1、只有具备文件作用域的标识符才能拥有external或internal的链接属性,其他作用域的标识符都是none属性。
2、默认情况下,具备文件作用域的标识符拥有external属性。也就是说该标识符允许跨文件访问。对于external属性的标识符,无论再不同文件中声明多少次,表示的都是同一个实体。
*/
/**
使用static关键字可以使得原先拥有external属性的标识符变为internal属性。这里有两点需要注意:
1、使用static关键字修改链接属性,只对具有文件作用域的标识符生效(对于拥有其他作用域的标识符是另一种功能)
2、链接属性只能修改一次,也就是说一旦标识符的链接属性变为internal,就无法变回external了。
*/
八、生存期和存储类型
#include<stdio.h>
//生存期和存储类型
//生存期
/**
C语言中的变量拥有两种生存期:
1、静态存储期(static storage duration)
2、自动存储期(automatic storage duration)
具有文件作用域的变量属于静态存储期,函数也属于静态存储期。属于静态存储期的变量在程序执行期间将一直占据存储空间,直到程序关闭才释放。
具有代码块作用域的变量一般情况下属于自动存储期。属于自动存储期的变量在代码块结束时将自动释放存储空间。
*/
int A;//文件作用域(静态存储期)
static int B;//文件作用域(静态存储期)
extern int C;//文件作用域(静态存储期)
//文件作用域(静态存储期)
void func(int m,int n){
int a,b,c;//代码块作用域(自动存储期)
}
//文件作用域(静态存储期)
void func_tmp(void){
int i,j,k;//代码块作用域(自动存储期)
}
//存储类型
/**
存储类型其实就是指存储变量值的内存类型,C语言提供了5种不同的存储类型
1、auto(自动变量)
2、register(寄存器变量)
3、static(静态局部变量)
4、extern
5、typedef
*/
/**
1、auto
在代码块种声明的变量默认的存储类型就是自动变量,使用关键字auto来描述。
由于这是默认的存储类型,所以不写auto是完全没有问题的。
*/
int main_1(){
auto int i,j,k;
return 0;
}
/**
2、register
将一个变量声明为寄存器变量,那么该变量就 有可能 被存放在CPU的寄存器中。
寄存器变量和自动变量在很多方面时一样的,他们都拥有代码块作用域,自动存储期和空链接属性。
不过这里有一点需要注意的是:当你将变量声明为寄存器变量,那么你就没办法通过取址运算符获得该变量的地址。(CPU的寄存器地址一般是不允许获取的)
*/
int main_2(){
register int i = 520;
//printf("Addr of i:%p\n",&i);//报错:不会允许获取CPU寄存器的地址
return 0;
}
/**
3、static
使用static来声明局部变量,那么就可以将局部变量指为静态局部变量。
static使得局部变量具有静态存储期,所以它的生存期与全局变量一样,知道程序结束才释放。
*/
void f(void);
void f(void){
static int count = 0;
printf("count=%d\n",count);
count++;
}
int main(){
int i;
for(i=0;i<10;i++){
f();
}
return 0;
}
/**
4、static和extern
作用于文件作用域的static和extern,static关键字使得默认具有external链接属性的标识符编程internal链接属性,而extern关键字是用于告诉编译器这个变量或函数在别的地方已经定义过了,先去别的地方找找,不要急着报错。
*/
/**
5、typedef
结构体再说。
*/
九、递归
#include<stdio.h>
//递归
/**
注意“递归必须要有结束条件,否则程序将崩溃
*/
void recursion(void);
void recursion(void){
printf("------");
static int count = 10;
printf("Hi,%d\n",count);
if(--count){//这里是终止递归的关键
recursion();
}
}
int main_1(){
recursion();
return 0;
}
//递归求阶乘
/**
循环
*/
long fact(int num);
long fact(int num){
long result;
for(result = 1;num>1;num--){
result *= num;
}
return result;
}
int main_2(void){
int num;
printf("请输入一个正整数:");
scanf("%d",&num);
printf("%d的阶乘是:%d\n",num,fact(num));
return 0;
}
/**
递归实现
*/
long fact1(int num);
long result = 1;
long fact1(int num){
result *= num;
if(num>1){
fact1(num-1);
}
return result;
}
int main(void){
int num;
printf("请输入一个正整数:");
scanf("%d",&num);
printf("%d的阶乘是:%d\n",num,fact1(num));
return 0;
}
十、汉诺塔
#include<stdio.h>
//汉诺塔
void hanoi(int n,char x,char y,char z);
void hanoi(int n,char x,char y,char z){
if(n==1){
printf("%c --> %c\n",x,z);
}else{
hanoi(n-1,x,z,y);
print("%c --> %c\n",x,z)
hanoi(n-1,y,x,z);
}
}
int main(void){
int n;
printf("请输入汉诺塔的层数:");
scanf("%d",&n);
hanoi(n,'X','Y','Z');
return 0;
}
十一、快速排序
#include<stdio.h>
//快速排序
/**
分治法:
大事化小,小事化了(递归就是分治法的一种实现)
*/
/**
快速排序算法的基本思想是:通过一趟排序将待排序数据分割成独立的两部分,其中一部分的所有元数据均比另一部分的元素小,然后分别对这两部分继续进行排序,重复上述步骤直到排序完成。
*/
void quick_sort(int arr[],int left,int right);
void quick_sort(int arr[],int left,int right){
int i = left,j = right;
int temp;
int pivot;//基准点
pivot = arr[(left+right)/2];
while (i<=j) {
//从左到右找到大于等于基准点的元素
while(arr[i] < pivot){
i++;
}
//从左到右找到小于等于基准点的元素
while(arr[j] > pivot){
j--;
}
//如果i<=j则互换
if(i<=j){
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
if(left<i){
quick_sort(arr,left,j);
}
if(i<right){
quick_sort(arr,i,right);
}
}
int main(void){
int arr[] = {71,107,110,118,101,70,105,115,104,67,46,99,111,109};
int i,length;
length = sizeof(arr)/sizeof(arr[0]);
quick_sort(arr,0,length-1);
for(i=0;i<length;i++){
printf("%d ",arr[i]);
}
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论