学习C语言 8.3 函数

目录

一、一维整型数组做函数参数

1.一维整型数组做函数参数

2.一维字符型数组做函数参数

二、二维数组做函数参数

1.二维整型数组做函数参数

2.二维字符数组做函数参数

三、标识符的作用域与可见性问题

1.作用域

2.可见性

3.时间角度:生命周期

4.存储类别的关键字


一、一维整型数组做函数参数

1.一维整型数组做函数参数

形参  数组形式 + 数组长度 
实参  数组名   + 数组长度 

2.一维字符型数组做函数参数

一维字符型数组--- 用来存放字符串 

因为处理的是字符串数据,字符串操作的 依据,主要看结束标志 '\0'
而不是 数组长度
注意:
   一维字符型数组做函数参数
   形参  数组形式
   实参  数组名 

封装一个gets函数:

#include <stdio.h>

void mygets(char s[])//形参:数组形式,编译器理解为char *s
{
	int i=0;
	do
	{
		scanf("%c",&s[i]);
		++i;
	}while(s[i-1]!='\n');//因为先进行了++i

	s[i-1]='\0';//将\n覆盖
	
}

int main(void)
{
	char a[100];

	mygets(a);//实参,函数名(数组名)

	puts(a);

	return 0;
}

封装一个puts函数

#include <stdio.h>

void myPuts(char s[])
{
	int i=0;
	while(s[i]!='\0')
	{
		printf("%c",s[i]);
		++i;
	}
	putchar('\n');
}

int  main(void)
{
	char s[100]="hello";

	myPuts(s);

	return 0;
}

封装strcpy、strcat、strcmp函数

#include <stdio.h>

int mystrlen(char s[])
{
	int i=0;
	while(s[i]!='\0')
	{
		++i;
	}
	int ret = i;

	return ret;
}

void mystrcop(char dest[],char src[])
{
	int i=0;
	while(src[i]!='\0')
	{
		dest[i]=src[i];
		++i;
	}
	dest[i]='\0';
}

void mystrcat(char dest[],char src[])
{
	int i=0;
	int j=0;
	while(dest[i]!='\0')
	{
		++i;
	}
	while(src[j]!='\0')
	{
		dest[i]=src[j];
		++j;
		++i;
	}
	dest[i]='\0';
}

int mystrcmp(char s1[],char s2[])
{
	int i=0;
	while(s1[i]==s2[i]&&s1[i]!='\0'&&s2[i]!='\0')
	{
		++i;
	}

	return s1[i]-s2[i];
}

int main(void)
{
	char a[100];
	char b[100];
	char c[100];

	gets(a);
	gets(c);

	int ret = mystrlen(a);
	printf("%d\n",ret);

	mystrcop(b,a);
	puts(b);

//	mystrcat(a,c);
	puts(a);

	int m=mystrcmp(a,c);
	printf("strcmp(a,c)=%d\n",m);

	return 0;
}

二、二维数组做函数参数

1.二维整型数组做函数参数

总结:
    形参   --- 二维数组形式 + 行数    //本质上:一维数组 + 长度
    实参   --- 数组名 + 行数 

#include <stdio.h>

void printfArray(int (*a)[4],int row)//形参:二维数组形式,长度
{
	int i=0;
	int j=0;

	for(i=0;i<row;++i)
	{
		for(j=0;j<4;++j)
		{
			printf("%d ",a[i][j]);
		}
		putchar('\n');
	}
}

int add(int (*a)[4],int row)
{
	int sum=0;
	int i=0;
	int j=0;
	for(i=0;i<row;++i)
	{
		for(j=0;j<4;++j)
		{
			sum=sum+a[i][j];
		}
	}

	return sum;
}

int max(int (*a)[4],int row)
{
	int max=a[0][0];
	int i=0;
	int j=0;
	for(i=0;i<row;++i)
	{
		for(j=0;j<4;++j)
		{
			if(i==j&&a[i][j]>max)
			{
				max=a[i][j];
			}
		}
	}

	return max;
}

int main(void)
{
	int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

	int row=sizeof(a)/sizeof(a[0]);//同样需要在主函数里计算数组的长度,不能在函数里计算

	printfArray(a,row);

	printf("sum=%d\n",add(a,row));//调用函数,求二位数组每个元素的累加求和

	printf("max=%d\n",max(a,row));//调用:函数名(数组名,长度)

	return 0;
}

与一维数组相同,不能在函数里计算数组的长度,因为在函数里,编译器是当指针理解的,如:int (*a)[4]。注意列数是不能省略的,可以理解为一维数组类型,也就是int [4]型,如果省略,那么数据类型将不完整。所以在函数里使用sizeof(a)计算长度时,编译器会当做地址而不是整个数组的大小。可以认为sizeof(a[0])就是一行的大小。

2.二维字符数组做函数参数

二维字符数组做函数参数与二维整型数组做函数参数的形参和实参的使用方式相同 


   注意:
      1.不要和一维字符型数组传参搞混 
        一维字符型数组,主要用来存放 字符串数据 
        而字符串数据有结束标志('\0'),所以传参时,不需要传长度 
      2.二维字符型数组,用来存储多个字符串
       要操作时,往往都是操作多个字符串,而多个字符串没有所谓结束的标志。
       看的是数组长度(行数)

实现一个输入多个字符串函数 inputStr()

#include <stdio.h>

void inputStr(char a[][10],int row)//可以写成char a[][10]的二维数组形式
{
	int i=0;
	for(i=0;i<row;++i)
	{
		gets(a[i]);
	}
}

void printfStr(char a[][10],int row)
{
	int i=0;
	for(i=0;i<row;++i)
	{
		puts(a[i]);
	}
}

int main(void)
{
	char a[3][10];

	int row=sizeof(a)/sizeof(a[0]);

	inputStr(a,row);
	printf("---------------\n");
	printfStr(a,row);

	return 0;
}

注意:二位字符型数组是操作多个字符串,没有结束标志,所以要给行数,否则就不知道什么时候字符串输入完毕。

形参里,可以写成char a[][10]的二维数组形式,编译器是当成char (*a)[ ]来处理的。

给字符串排序,使用插入排序

#include <stdio.h>
#include <string.h>

void insert(char a[][10],int row)
{
	int i=0;
	int j=0;
	char t[10];
	for(i=1;i<row;++i)
	{
		strcpy(t,a[i]);
		j=i;
		while(j>0&&strcmp(a[j-1],t)>0)
		{
			strcpy(a[j],a[j-1]);
			--j;
		}
		strcpy(a[j],t);
	}
}

void printfStr(char a[][10],int row)
{
	int i=0;
	for(i=0;i<row;++i)
	{
		puts(a[i]);
	}
}

int main(void)
{
	char a[][10]={"hello","hell","help"};

	int row=sizeof(a)/sizeof(a[0]);

	insert(a,row);

	printfStr(a,row);

	return 0;
}

使用查找算法,找字符串

#include <stdio.h>
#include <string.h>

int find(char a[][10],int row,char s[])
{
	int begin=0;
	int end=row-1;
	int mid=0;
	int ret=-1;
	while(begin<=end)
	{
		mid=(begin+end)/2;
		if(strcmp(s,a[mid])<0)
		{
			end=mid-1;
		}
		else if(strcmp(s,a[mid])>0)
		{
			begin=mid+1;
		}
		else
		{
			ret=mid;
			break;
		}
	}
	return ret;
}

int main(void)
{
	char a[][10]={"a","b","c","d","e"};
	int row = sizeof(a)/sizeof(a[0]);

	char s[10];
	gets(s);

	int m=find(a,row,s);

	if(m==-1)
	{
		printf("not found\n");
	}
	else
	{
		printf("found. a[%d] is %s\n",m,s);
	}

	return 0;
}

总结:
   1.一维整型数组 做函数参数
   形参 --- 数组形式 + 数组长度 
   实参 --- 数组名   + 数组长度    
   2.一维字符型数组 做函数参数
   形参 --- 数组形式  
   实参 --- 数组名   
   原型: 一维字符型数组 主要用来存储字符串数据 
   3.二维整型数组 做函数参数
   形参 --- 数组形式 + 行数 //本质 就是一维数组的长度 
   实参 --- 数组名   + 行数 
   4.二维字符型数组 做函数参数
   形参 --- 数组形式 + 行数 //本质 就是一维数组的长度 
   实参 --- 数组名   + 行数 

三、标识符的作用域与可见性问题

1.作用域

作用域:限定名字的可用性的代码范围就是这个名字的作用域

a.局部作用域 
        { 
        }  //花括号范围内 就叫局部作用域 
        
        在局部作用域 定义的变量 --- 局部变量 
        在全局作用域 定义的变量 --- 全局变量 
 b.全局作用域 
       不在 任何一个 { } 范围之内 


2.可见性

可见性:程序运行到某一个位置 哪些名字可以被使用(被看见)

标识符的可见性的规则:
1.先定义,后使用 
2.同一作用域中,不能有同名标识符
3.在不同的作用域,同名标识符,相互之间没有影响 
4.如果是不同的作用域,但是作用域之间存在嵌套关系,
 那么内层的作用域的同名标识符,会屏蔽外层的作用域的同名标识符。
 (就近原则) 

C语言的程序5个区


堆                          [数据区]
字符串常量区 
全局区(静态区)
--------------------------------------
代码区                   [代码区]


局部变量
   特点:
       局部变量 空间 一般都开栈上 
       如果不初始化,局部变量中的值是随机值(垃圾值)
全局变量
   特点:
       全局变量 空间  全局区
       如果不初始化,默认初始化为0

3.时间角度:生命周期

   int a; //什么时候a的空间被开辟,什么时候a的空间被销毁 
   
  局部变量的生命周期:
      从程序运行到定义处开始存在,到程序运行到 它作用范围结束 时 销毁
  全局变量(静态变量)的生命周期:
      从程序开始运行时就存在了,直到整个程序运行结束时,销毁 
      注意:
          全局变量和静态变量,不能用"变量"进行初始化

#include <stdio.h>

int a=100;
int b=a;

这两行代码如果在main函数里,是可以执行的,但是这里的a和b都是全局变量,编译时会报错

这里的意思是说初始化的元素不是常量,因为这里的将a给b,a是个变量。全局变量是从程序运行(即./a.out时)就存在的,所以可以认为a,b是同时存在的,那么在编译器看来,这两行代码是没有先后顺序的,它就不知道这个值应该给谁。所以全局变量只能用常量初始化。

4.存储类别的关键字

[存储类别] 类型 变量名;

auto          //表示它是一个自动变量 (局部变量)  ---  空间自动申请 自动释放 

static        //static 修饰局部变量  此时会被放在 全局区(静态区)
                // 此时局部变量的生命周期被延长,但作用域还是在{ }内 
                // 注意:
                    1.static 修饰局部变量 --只会被初始化一次 
                    2.static 修饰的变量 -- 具有继承性
                    3.static 修饰的变量 -- 只能用常量初始化 (不能用变量初始化)  

register    // 寄存器  
                // 表示,把变量存储寄存器中
                //使用此关键字具有建议性

        注意:寄存器在CPU中,所以register修饰的变量不能&(取地址) ,因为数据没存在内存里,存于寄存器中 

extern      //外部的  ---表示你的变量 是存在外部的,不在当前文件中  
                //用于多文件编程
                //只能 声明 全局变量 
                
                //static 修饰全局变量 
                        表示限定全局变量的作用域为本文件,别的文件不能通过extern来声明使用 
                //用途: 保护私有数据,防止被引用 
                
                
extern修饰函数       //声明函数是在别处的定义的(函数的作用域都是全局的) 
                               //static 修饰函数作用和修饰全局变量作用相同
                                限定作用域为本文件,别的文件不能通过extern来声明使用 

总结static  
           修饰变量 
                1.局部变量 
                   表示将局部变量存放到静态区。
                   延长生命周期。
                   存在静态区的变量
                   a.不能用变量初始化 
                   b.只会被初始化一次 
                   c.反复使用该变量时,值具有继承性。
                2.全局变量   
                    限定作用域为本文件,别的文件不能通过extern来声明使用 
           修饰函数     
                   限定作用域为本文件,别的文件不能通过extern来声明使用

  • 19
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值