----- <a href="http://www.itheima.com" target="blank">ios培训</a>、<a href="http://www.itheima.com" target="blank">ios培训</a>、期待与您交流! ---------
变量的作用域
1.局部变量
1>定义:在函数内部定义的变量,称为局部变量。形式参数也属于局部变量。
2>作用域:局部变量只在定义它的函数内部有效,即局部变量只有在定义它的函数内部使用,其它函数不能使用它。
float(int a)
{int b ,c;
} //b,c在函数float有效
int main()
{int m,n;
return 0;
}m,n在main函数有效
2.全局变量
1>定义:在所有函数外部定义的变量,称为全局变量。
2>作用域:全局变量的作用范围是从定义变量的位置开始到源程序结束,即全局变量可以被在其定义位置之后的其它函数所共享。
int p = 1,q = 5;//p,q全局变量,整个程序都有效
float(int a)
{int b ,c;
}//b,c只在float函数中有效
int main()
{int m,n;
rerurn 0;
}m,n在main函数有效
局部变量的存储类型
变量的存储类型就是指变量存储在什么地方。有3个地方可以用于存储变量:普通内存、运行时堆栈、硬件寄存器。变量的存储类型决定了变量何时创建、何时销毁以及它的值能保持多久,也就是决定了变量的生命周期。
* C语言根据变量的存储类型的不同,可以把变量分为:自动变量、静态变量、寄存器变量。
1.自动变量(auto变量)
1.定义:自动变量是存储在堆栈中的。
2.哪些是自动变量:被关键字auto修饰的局部变量都是自动变量,但是极少使用这个关键字,基本上是废的,因为所有的局部变量在默认情况下都是自动变量。
3.生命周期:在程序执行到声明自动变量的代码块(函数)时,自动变量才被创建;当自动变量所在的代码块(函数)执行完毕后,这些自动变量就会自行销毁。如果一个函数被重复调用,这些自动变量每次都会重新创建。
2.静态变量(static变量)
1. 定义:静态变量是存储在静态内存中的,也就是不属于堆栈。
2. 哪些是静态变量:
· 所有的全局变量都是静态变量
· 被关键字static修饰的局部变量也是静态变量
3. 生命周期:静态变量在程序运行之前创建,在程序的整个运行期间始终存在,直到程序结束。3.寄存器变量(registe变量)
1. 定义:存储在硬件寄存器中的变量,称为寄存器变量。寄存器变量比存储在内存中的变量访问效率更高(默认情况下,自动变量和静态变量都是放在内存中的)
2. 哪些变量是寄存器变量:
· 被关键字register修饰的自动变量都是寄存器变量
· 只有自动变量才可以是寄存器变量,全局变量和静态局部变量不行
· 寄存器变量只限于int、char和指针类型变量使用
3. 生命周期:因为寄存器变量本身就是自动变量,所以函数中的寄存器变量在调用该函数时占用寄存器中存放的值,当函数结束时释放寄存器,变量消失。
4. 使用注意:
· 由于计算机中寄存器数目有限,不能使用太多的寄存器变量。如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理
· 为了提高运算速度,一般会将一些频繁使用的自动变量定义为寄存器变量,这样程序尽可能地为它分配寄存器存放,而不用内存
全局变量的存储类别
1. 在一个文件内扩展外部变量的作用域
1.如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件结束
2.关键字extern对变量作“外部变量声明”,表示把该外部变量的作用域扩展到此位置
2. 将外部变量的作用域扩展到其它文件
3. 将外部变量的作用域限制在本文件中
file.c file2.c
static int A; extern A;
int main() void fun(int n)
{ {
…… A = A*n // 出错
} }
这种加上static声明,只能用于本文件的外部变量称为静态外部变量
结构体
定义结构体类型
struct 结构体名
{成员列表};
定义结构体变量的3种方式
1> 先定义类型,再定义变量(分开定义)
struct Student
{
int age;
};
struct Student stu;
2> 定义类型的同时定义变量
struct Student
{
int age;
} stu;
struct Student stu2;
3> 定义类型的同时定义变量(省略了类型名称)
struct
{
int age;
} st;
结构体类型的作用域
定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
结构体注意点
1.不允许对结构体本身递归定义
2.结构体内可以包含别的结构体
3.定义结构体类型,只是说明了该类型的组成情况,并没有给它分配存储空间,就像系统不为int类型本身分配空间一样。只有当定义属于结构体类型的变量时,系统才会分配存储空间给该变量
4.结构体变量占用的内存空间是其成员所占内存之和,而且各成员在内存中按定义的顺序依次排列
结构体和数组
数组:只能由多个相同类型的数据构成
结构体:可以由多个不同类型的数据构成
指向结构体的指针
每个结构体变量都有自己的存储空间和地址,因此指针也可以指向结构体变量
结构体指针变量的定义形式:struct 结构体名称 *指针变量名
有了指向结构体的指针,那么就有3种访问结构体成员的方式
1. 结构体变量名.成员名
2. (*指针变量名).成员名
3. 指针变量名->成员名
枚举类型
声明形式
enum [枚举名]{枚举元素列表};
enum Sex { Man, Woman, Unkown};
1.所有的预处理指令都是以#开头
2.预处理指令分3种: 宏定义,条件编译,文件包含
3.预处理指令在代码翻译成0和1之前执行
4.预处理的位置是随便写的
5.预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6.宏名一般用大写或者以k开头,变量名一般用小写
一、不带参数的宏定义
1.一般形式
#define 宏名 字符串
比如#define ABC 10
右边的字符串也可以省略,比如#define ABC
2.作用
它的作用是在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。
接下来写个程序根据圆的半径计算周长
3.使用习惯与注意
1> 宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误
2> 对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。
3> 在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查
4> 宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef命令
5> 定义一个宏时可以引用已经定义的宏名
二、带参数的宏定义
1.一般形式
#define 宏名(参数列表) 字符串
2.作用
在编译预处理时,将源程序中所有宏名替换成字符串,并且将字符串中的参数 用宏名右边参数列表中的参数替换
3.使用注意
1> 宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串
2> 带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。
3> 计算结果最好也用括号括起来
宏定义与函数的区别
从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:
1> 宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
2> 函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率
利用条件编译防止头文件多次包含
#ifndef LISI_H
#define LISI_H
int sum(int a, int b);
#endif
typedef声明
1.简单地用一个新的类型名代替原有的类型名
typedef int Integer;//指定Integer为类型名,作用域int相同
typedef float Real;//指定Real为类型名,作用域float相同
2.命名一个简单地类型名代替复杂的类型表示方法
typedef struct
{int month;
int day;
int year;
}Date;//类型名Date代表上面的一个结构体类型
Date birthday;//定义结构体变量birthday
利用以上知识完成一个小程序
/*找出多个字符串中的最大公共子字符串*/
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#define N 6 //输入的字符串个数
//返回string字符串中从第start字符开始后length个字符
char * string(char * string,int start,int length)
{
char *string3;
int i,j=0;
string3=(char *)malloc(100);
for(i=start;i<start+length;i++)
{
string3[j]=*(string+i);
j++;
}
string3[j]='\0';
return string3;
}
//将第一个字符串与最短的字符串交换
void swap(char (*pStr)[100],int i)
{
char temp[100];
strcpy(temp,pStr);
strcpy(pStr,pStr+i);
strcpy(pStr+i,temp);
}
int main()
{
char str[N][100];//定义二维字符数组
int i,min=0,len,len0;
int minlen,j,k,maxlen=0;
char * maxStr;
char * tmpStr;
char (* pStr)[100];
printf("请输入%d个字符串:\n",N);
for(i=0;i<N;i++)//输入N字符串
gets (str[i]);
pStr=str;
minlen=strlen(str[0]);//把第一个字符串的长度赋给minlen
//找出输入的字符串中长度最小的串,并把最小串序号记在min中
for(i=0;i<N;i++)
{
len=strlen(str[i]);
if(len<minlen)
{
minlen=len;
min=i;
}
}
swap(pStr,min);
len0=strlen(str[0]);//把长度最小的串的长度赋给len0
//对字符串数组中第一个子串,求出其可能的子串值,如果剩余子串长度小于maxlen则不用去求了,for循环中给出了限制
for(i = 0; i < len0 && maxlen <= len0 - i-1; ++i)
{
for(j = 1; j <= len0-i; ++j)
{
tmpStr=string(str[0],i,j);
//将子串tmpStr与参与匹配的字符串比较,判断tmpStr是否为剩余串的子串,如果不是则break出循环
for(k = 1; k < N; ++k)
{
if(strstr(str[k],tmpStr))
continue;
else
break;
}
if(k == N)//说明子串tmpStr是其他参与匹配的子串的子串
{
if(strlen(tmpStr)>maxlen)//tmpStr如果是当前最大的子串,则记录下来
{
maxlen = strlen(tmpStr);
maxStr = tmpStr;
}
}
}
}
printf("最长子字符串为:\n");
puts(maxStr);//输出maxStr
return 0;
}