1.review
2.变量的作用域
变量之分
1.局部变量
凡是大括号{}以内
的变量,都是局部变量。
附:形式参数也是局部变量。其作用域,起始于定义处,截止于所在的大括号。
若未赋值,其值是随机的。
2.全局变量
凡是大括号以外的变量,都是全局变量。
其作用域起始于定义处,截止于本文件结束。
若未赋值,系统将其初始化为,零。
全局量,在多文件编程中,可以通extern声明
的方式,将作用域扩展到其它文件中去。
- 凡是在{}以内的变量称为局部变量,也包含形式参数
- 凡是在{}以外的变量称为全局变量
- 作用域这个概念
- {}以内的作用域,称为局部作用域
- {}以外的作用域,称为全部作用域
- 同一作用域内,不可以用重名的变量。
- 同一作用域内,不可以用重名的函数。
- 同一作用域内,不可以用重名的标识符
作用域叠加而是指的是,全局变量同局部变量的
作用域叠加
当小范围内的作用域和大范围的作用域,出现叠加,且有重名变量的时候,
小范围作用域内的变量名会覆盖
大范围作用域内的变量名。
也就是通常所说的,局部变量同全局变量重名的问题。
强龙不压地头蛇。
局部变量的作用域,起始于定义处,直到所在的大括号结束
全局变量的作用域,起始于定义处,直到本文件的结束
更小的作用域
func()
{}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int a = 10000;
{
int a =10;
printf("%d\n",a);
}
return 0;
}
全局命名污染
不同作用域的局部变量,既使重名也是无所谓,不会对程序的编译,造成任何的影响。
全局变量则不然!!!!!!!!
全局变量是指同一工程内的所有c文件中的全局变量,在全局作用域内,如果有重名则会造成重定义。
当一个项目,大到由n(n>1000)多个文件,有m(m>500)多个人参与开发。
在进行编译和调试的过程中会带来很多的麻烦,所以尽量少用全局变量
。
除非,有人负责对全局变量进行统一管理。
3.变量生命周期和修饰符
生命周期(Duration)
局部变量的生命周期:
局部变量的生命周期,同其所在的函数。局部变量随着函数的执行而有生命,随着函数的执行结束而生命截止。
#includ99e <stdio.h>
#include <stdlib.h>
#include <time.h>
// 函数的生命周期,起于调用,结束于调用结束
// 局部变量的生命周期,起于调用,结束于调用结束
//main开始==》进程的开始
//main函数的结束==》进程的结束
//main函数的生命周期就是程序的生命周期
//全局变量的生命周期,起于main调用,结束于main调用结束,等同于程序的生命周期
//特殊的局部变量:main函数中的局部变量,生命周期和main一样
int a =999 ;
void func()
{
int a;
}
int main()
{
func();
return 0;
}
修饰符(Storage Description )
修饰符放在,变量的定义的类型之前,或改变生命周期,或改变存储区域,或者均有所改变
。格式如下∶
auto int a;
auto
只能修饰局部变量,可以省略,局部变量若无其它的修饰,则默认为auto。它修饰的变量的特点是,随用随开,用完即消。
auto只能用于修饰局部变量,表示该变量存储于栈stack
存储于栈上的数据在有什么特点:随有随开,用完消失
默认的局部变量,就是auto类型的,所以通常将其省略
register
register存储于cpu的一个变量,速度很快,但数量有限,所以通常被优化。
只能修饰局部变量
,原则上,将内存中的变量升级到CPU寄存器中存储,这样访问速度会更快。
但由于CPU寄存器数量相当有限,通常会在程序优化阶段,被优化为普通的auto类型变量。
可以通过汇编代码来查看,优化过程(具体优化,与平台和编译相关)。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//register存储于cpu的一个变量,速度很快,但数量有限,所以通常被优化。
int main()
{
register int a = 5;register int b = 6;register int c = 7;register int d = 8;register int e =9;register int f = 0;register int aa = 15;register int ab = 52;register int ac = 53;register int ad = 54;register int ae = 55;register int af = 56;register int ba = 57;register int bb = 58;
printf("%d %d %d %d %d %d %d %d %d %d %d %d %d %d",
a,b,c,d,e,f,aa,ab,ac,ad,ae,af,ba,bb);
return 0;
}
extern
只能用来修饰全局变量,全局变量本身是全局可用的,但是由于文件是单个完成编译,并且编译是自上而下的
所以说,对于不是在本范围内定义的全局变量,要想使用必须用extern 进行声明
如果不加上extern ,就会造成重定义。
注意,经extern声明的变量,不可以再初始化。
如果main.c中没有extern int a,但是由于后面又使用了变量a,则会报错。
如果main.c中有extern int a,则编译通过,如果在链接中,其它文件中有定义,则顺利链接通过,否则的话,则会链接失败。
全局变量,本质作用域内通用,即所有的c文件中均可使用,但是由于,单文件编译,则需要声明。extern有省略用法,不讲,也不推荐。
同文件使用
#include <stdio.h>extern int a;
extern int mm;
int main(int argc,char *argv[]){
printf("%d\n",a);
printf("mm = %d\n" ,mm);return o;
}
int mm = 300;
声明和定义的区别
声明: int a;
定义: int a =100;
但是 int a;有可能是定义,也有可能是声明
4.static修饰符
static修饰局部变量
static修饰,局部变量,即修改了存储区域,又修改了局部变量的生命周期
。
使其生命周期同进程或是main()函数。
static变量若未初始化,则系统初始化为零,并且只进行一次初始化。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// static修饰局部变量,默认初始化为零
int main()
{
static int a;
printf("%d",a);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// static修饰局部变量,默认初始化为零
// auto修改的局部变量,存储在栈上,随有随开,用完即消
// static修改的局部变量,存储在data段上,生命周期等同于进程
// static变量的初始化,只执行一次。
void func()
{
int a =1;
printf("a = %d\t",a++);
static int b =1;
printf("b = %d\n",b++);
printf("+++++++++++++++++++++++++\n");
}
int main()
{
for (int i=0;i<10;i++) {
func();
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// static修饰局部变量,默认初始化为零
// auto修改的局部变量,存储在栈上,随有随开,用完即消
// static修改的局部变量,存储在data段上,生命周期等同于进程
//控制执行次数,记录打印某函数的调用次数
void print(int num, int age,char sex,float score)
{
static int flag = 1;
if(flag == 1)
{
printf( "num\tage\tsex\tscore\tidx\n" );
flag = 0;
}
static int count = 0;
printf("%d \t%d \t%c \t%.2f\t%d\n",num ,age,sex,score,++count);
}
int main()
{
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
print(1001,21,'x',99.9);
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMovie>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on__start_clicked()
{
static QMovie movie("E:\\WorkPlace\\C\\untitled\\cats.gif");//硬盘资源只需要加载一次
ui->_show->setMovie(&movie);
ui->_show->setVisible(true);
movie.start();
}
void MainWindow::on__stop_clicked()
{
ui->_show->setVisible(false);
}
static修饰全局变量
全局变量,会带来命名污染,全局可用,避免传参。
一个全局变量,天然的拥有外延性,可以被其它的文件所引用,extern
static修饰全局变量以后,处延性消失,变成本文件内部的全局变量
static 全局变量 也存储在 data段
也适用于函数
other.c
static int a=100;
static void func(){
printf( "hello world\n" );
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//全局变量,会带来命名污染,全局可用,避免传参。
//一个全局变量,天然的拥有外延性,可以被其它的文件所引用,extern
//static修饰全局变量以后,处延性消失,变成本文件内部的全局变量
//static 全局变量 也存储在 data段
//也适用于函数
static void func(){
printf( "hello world\n" );
}
static int a=100;
int main()
{
func();
printf("%d",a);
}
5.字符串常量
字符串是,是由双引号,引起的一串字符。
系统在字符串常最后添加了一个字符‘\0’
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//字符串是,是由双引号,引起的一串字符。
//系统在字符串常最后添加了一个字符‘\0’
#if 0
stack
heap
data: 全局变量 static局部变量 常量
text
endif
int main()
{
printf( "sizeof(\"major\") = %d\n",sizeof("major")) ;
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//字符串是,是由双引号,引起的一串字符。
//系统在字符串常最后添加了一个字符‘\0’
int main()
{
printf( "sizeof(\"major\") = %d\n",sizeof("major")) ;
char *p = "major";
printf("p = %p p+1 =%p p[0]=%c 2[p] = %c\n",p,p+1,p[0],2[p]);
printf(" = %p =%p =%c = %c\n","major","major"+1,"major"[0],2["major"]);
return 0;
}
c语言,是如何处理常量字符串的。将其处理一个向data段这段字符串的首地址
对比数组,三要素,起始地址,步长,范围
char*能不能代表了整个字符串,起始地址 、步长、 ‘\0’
可以带表整个字符串
6.字符串与字符数组
//寻求改变,我要改变这些面的数据。
//正是因为字符数组和字符串,他们具有相同的性质,
//所以为了要改变字符串的内容,能常将常字符串,拷贝字符数组中去。
// arr[6]是栈上的空间 “china”是data一块常量区域
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//寻求改变,我要改变这些面的数据。
//正是因为字符数组和字符串,他们具有相同的性质,
//所以为了要改变字符串的内容,能常将常字符串,拷贝字符数组中去。
// arr[6]是栈上的空间 “china”是data一块常量区域
int main()
{
char arr[6] = "major";//他们之间存在着某种等价关系
printf("sizeof(arr)= %d arr= %p arr+1 = %p\t\n",
sizeof(arr) , arr , arr+1);
printf("sizeof( \"china\")= %d \"china\"= %p \"china\"+1 = %p\t\n",
sizeof("china") , "china" , "china"+1);
return 0;
}
//字符数组和字符串这间的相同性质。并不代表字符串可以与任意的字符数组之间划等号
//等价条件:sizeof(字符数组)的大小>=sizeof(字符串)的大小
//sizeof(字符数组)的大小< sizeof(字符串)的大小此时不存在等价关系字符数组,此时只是字符数组而己
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//字符数组和字符串这间的相同性质。并不代表字符串可以与任意的字符数组之间划等号
//等价条件:sizeof(字符数组)的大小>=sizeof(字符串)的大小
//sizeof(字符数组)的大小< sizeof(字符串)的大小此时不存在等价关系字符数组,此时只是字符数组而己
int main()
{
char arr[2] = "major";
printf("arr =%s\n",arr);
printf("%s\n","ma\0jor");
return 0;
}
//自适应
char[] = "mmmmmmmmmmmmmmmmmmmmmmaaaaaaaaaaaaaajjjjjjjjjjjjjjoooooooooooooooooorrrrrrrrrrrr";
7.字符串的输入与输出
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//字符数组和字符串这间的相同性质。并不代表字符串可以与任意的字符数组之间划等号
//等价条件:sizeof(字符数组)的大小>=sizeof(字符串)的大小
//sizeof(字符数组)的大小< sizeof(字符串)的大小此时不存在等价关系字符数组,此时只是字符数组而己
//输入的字符串,一般越数组界,此时的数组,仅仅是字符数组而已。
//scanf遇到空格结束输入。,除非 scanf("%[^\n]s",arr); 或者用gets(arr)
//fgets比较安全
int main()
{
char arr[100] = "major";
printf("%s",arr);
puts("");
puts(arr);
printf("********************");
puts(""); // 换行
char arr1[6] = "major";
fgets(arr1,10,stdin);
printf("%s",arr1);
return 0;
}