C语言语法
变量
变量对应这内存中的一段内存。 从内存的角度看变量,变量包括:内存地址(变量首地址)、变量名、变量类型(即变量大小)、变量值等。
关键字
auto
- 自动变量:是函数的参数 ,和 在函数体内定义的局部变量。
函数的形参及代码块中定义的变量都属于auto变量,这是C语言中应用最广的一种变量,这类变量是栈分配的,是动态分配存储空间的。
举函数形参为例,当调用该函数时,为形参分配存储空间,当函数调用结束时,系统就自动释放这些存储空间。
对**代码块中定义的变量(包含函数中定义的变量),当执行到变量声明语句时,系统为这些auto变量分配空间,当程序流程离开代码块时,这些变量被自动撤销,其占用的内存空间被系统自动释放**。
-
C语言中,默认所定义的变量是auto自动变量。C语言编译器自动加上auto
void func(int num)
{
int a;
float b;
}
-
auto 变量不仅指普通内置类型的变量,还包括**块中定义的数组、结构体变量和指针变量**等复合结构。 -
全局变量不是auto自动变量。 -
auto自动变量一定要初始化。 哪怕是先定义,在使用前初始化。
某些编译器并不会自动为auto变量初始化,这项工作必须在变量定义时由程序员显式完成,否则,变量的值是随机不确定的。
不论是指针还是普通变量,时刻提醒自己注意初始化,能有效防止一些稀奇古怪错误的发生。
{
int num;
num = 0;
}
-
auto 用于软件开发工程规范, 让代码清晰易懂。 如果只是为了让程序跑起来, 可加可不加。
static修饰符
static静态修饰符。
static修饰变量
静态局部变量 静态全局变量
- 静态变量:是指内存位置在程序执行期间一直不改变的变量,用关键字static修饰。代码块内部的静态变量只能被这个代码块内部访问,代码块外部的静态变量只能被定义这个变量的文件访问。
-
static变量的定义格式为:static数据类型 变量1[=初始化表达式], 变量2[=初始化表达式]……; 与extern变量都是全局变量不同,static变量有静态全局变量和静态局部变量之分:
-
静态局部变量,除了生存期是整个程序执行期间(与程序共存亡)外,其作用域与可见域与普通auto变量完全一样。 Static局部变量只有定义,没有声明。
- auto变量和静态局部变量的区别:假设auto变量和static变量在同一个循环体内,则每次循环开始,auto变量都会被初始化为初始值,而static变量因为上次循环块结束后不会被销毁,保留这上次循环后的值,所以,再次循环开始后使用的上次循环后保留的值,而不会被再次初始化static变量了。
-
静态全局变量和extern变量的不同体现在作用域上,extern作用域是本程序的所有源代码文件,只要在一个文件中定义,在其他文件中使用时只要对其进行声明即可。而静态全局变量其作用域仅限于从定义位置起到本文件结束的一段代码区域,不能被其他文件中的函数所使用,即使在其他文件中进行extern声明也不被引用。 -
静态全局变量实际上是对extern变量破坏封装性和可靠性的一种改良。静态全局变量使得代码便于移植,代码的独立性可靠性得到保证。因为不是static,在移植时,有可能还需要移植定义非static全局变量的文件。 总结: 对局部变量用static声明,把它分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间始终存在。常使用静态局部变量在函数调用间歇保存某些变量的值。 静态局部变量定义在快内部,让代码的移植性更强,直接复制到需要用的地方就可以了。 所以,static变量在块内可以保留中间结果 对全局变量用static声明,则该变量的作用域只限于本文件模块(即被声明的文件中)。 -
当省略初始化表达式时,编译器自动以0初始化静态变量。对于数组或结构,编译器将其中的每个元素或成员的所有二进制位都初始化为0。
? C语言中static修饰符的意义
在C语言中,static通常有2种含义:1)定义变量的生命周期;2)定义变量或者函数的作用域。
变量的生命周期是指,相对于程序运行的进程生命周期,变量存在的时间段。变量的生命周期由变量的存储类型(位置)决定。因此static的第1种含义也可以理解为,static定义了变量的存储类型(位置)。
C语言中,变量存储位置分为栈、全局静态区、堆。栈用来做函数调用,参数传递等,在程序的运行过程中是操作最频繁的数据区。全局静态区,是变量一直存在于内存中,在程序整个运行过程中不会被销毁。堆,是由操作系统维护的大空间内存伸缩区,进程可以主动向操作系统申请使用堆的存储,在程序运行过程中动态申请。
由static修饰的变量,其存储类型为全局静态区,变量在程序的整个运行过程中不会被销毁,并且只会被初始化1次。
//在内存中观察auto变量和static变量的值的变化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9u0nxQll-1648965888673)(C:\Users\Miller\AppData\Roaming\Typora\typora-user-images\image-20220302175341163.png)]
代码1:static修饰变量的生命周期(存储类型)
[](javascript:void(0)😉
void func1()
{
static int var1 = 1; // 在全局静态区分配变量var1的存储空间
int var2 = 1; // 在栈上分配var2的存储空间
var1 = var1+1;
var2 = var2+1;
printf("var1=%d, var2=%d", var1, var2)
}
void main()
{
func1();
func1();
func1();
}
[](javascript:void(0)😉
代码输出:
“var1=2, var2=2”
“var1=3, var2=2”
“var1=4, var2=2”
static修饰函数
内部函数
static函数也叫文件内部函数, 只能在定义static函数的本文件内调用,直接调用或被本文件内的其他函数调用,而不能被其他源文件中的函数调用。
C语言默认调用函数时,直接可以跨文件调用在其他文件中定义的函数,无需声明。
但是.cpp文件不可以。
作用域是指变量/函数的可见区域,可被引用区域。在C语言中,不加static修饰的函数对所有的源文件都是可见的,不加static修饰的全局变量对所有的源文件都是可见的。通过static修饰,可以把函数的作用域限定在本源文件内,把全局变量的作用域限定在本源文件内。
由于全局变量和函数的作用域是所有源文件,所有源文件引用的是同一个全局变量和函数,因此C语言不允许在2个源文件内定义相同的全局变量、函数,在代码链接阶段会提示链接错误。通过添加static修饰,可以让2个源文件定义相同的全局变量和函数,但是注意这2个源文件引用的全局变量和函数是独立的。
static常见使用场合
[](javascript:void(0)😉
static int var1 = 1; // 限定全局变量var1仅在本源文件可见
static void func1() // 限定函数仅在本源文件可见
{
static int var2 =1; // var2是静态变量,不是栈变量,只被初始化一次
}
void main()
{
func1();
}
[](javascript:void(0)😉
块,代码块
所谓代码块,是指用两个花括号包裹起来的代码行,函数只是代码块的一种,常见的代码块还有if结构、for结构等等,哪怕只是两个成对花括号,也能构成一个独立代码块。
//块 代码块
{
int a=10; //a是局部自动变量, 作用域:定义开始,到右花括号右边。 块结束作用域就结束,且a被销毁,内存被回收。
}
-
代码块可以嵌套应用。 形成一定的层次结构,内层代码块中可定义同名变量,屏蔽内层代码块之前的同名变量。 //块1
{
//块1.1
{
}
//块1.2
{
//块1.2.1
{
}
}
}
-
同一块内部,定义的变量不能同名重名, 即使他们是不同的类型也不可以。
return
总结:
python函数使用return语句返回 ‘返回值’
所有函数都有返回值,如果没有return语句,隐式调用return None
return 语句并不一定是函数的语句块的最后一条语句
一个函数可以存在多个return语句,但是只有一条可以被执行。如果没有一条return语句被执行到,隐式调用return None
如果有必要,可以显示调用return None,可以简写为return
如果函数执行了return语句,函数就会返回,当前被执行的return语句之后的其它语句就不会被执行了
返回值的作用:结束函数调用、返回“返回值”
函数不能同时返回多个值
return 1,3,5看似返回多个值,隐式的被python封装成一个元组
x,y,z=shownlist()使用解构提取返回值更为方便
———————————————— 版权声明:本文为CSDN博主「都枯槐」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_36883141/article/details/89286366
register 登记;注册
寄存器,在CPU中。寄存器分好几种类型,都有各自的功能。
- 寄存器说明符 register 只能用于说明函数中的变量和函数中的形参。
- 不允许将全局变量 、 静态变量说明为 register
汇编编程
#include<stdio.h>
#include<stdlib.h>
void main070()
{
int num = 10;
num += 15;
printf("%d\n", num);
getchar();
}
//用汇编实现上诉计算
void main()
{
int num = 10;
printf("%x", &num); //打印num地址
//汇编语言编程。
_asm
{
mov eax, num //CPU的控制器用来操作内存。控制器找到num的内存地址,把num的值赋值到CPU中的寄存器eax中。 内存中的数据必须载入寄存器中,才能计算。
add eax, 15 //相加, 寄存器eax中的值 增加15
mov num, eax //控制器把寄存器中的值赋值移动到num的内存地址中。
}
printf("%d\n", num);
getchar();
}
在寄存器中查看变量
- 打印变量num地址
- 插入断点
- 调试
- 在内存窗口输入地址,可以在内存中查看内存值
- 调试—窗口—寄存器窗口: 在寄存器窗口查看变量。
寄存器变量
定义寄存器变量的需求背景和意义:
程序运行时,根据需要到内存中调用相应的变量,如果一个变量在程序中频繁使用,例如循环变量,那么,系统就需要多次访问内存中的该单元, 影响程序的执行效率。
因此,C/C++语言定义了一种变量, 不是保存在内存中,而是直接存储在CPU中的寄存器里,这种变量称为 寄存器变量。
register int a= 10;
在寄存器中 变量没有地址。//
//定义一个寄存器变量
void main()
{
for (register int i = 0; i < 10; i++)
{
printf("\n%d", i);
}
getchar();
}
寄存器变量的用途
- 寄存器变量在CPU内部, 速度很快
- 所以,频繁使用的变量,需要放在寄存器中,可以提高速度
- VC编译器会自动优化,即使没有声明寄存器变量,所以,对于频繁使用的变量,VC也会自动优化成寄存器变量
- gcc编译器, 就不会自动优化。 MinGW 是gcc编译器移植到Windows的版本。
所以,用VC编译器 时, 加不加register(定义寄存器变量), 对于所有时长是一样的。
用gcc编译器时,例如Code::Blocks IDE, 把频繁使用的变量定义成寄存器变量,会减少计算所用时长,极大优化效率
- 寄存器是与机器硬件密切相关的, 不同类型的计算机,寄存器的数目是不同的,通常是2到3个, 对于在一个函数中说明的多余2到3个的寄存器变量,C编译器会自动将寄存器变量变为自动变量。
- .c文件C语言寄存器变量不可以取地址, .cpp文件C++可以, 因为C++会在内存中给C语言寄存器保留副本。
extern
C语言规定,任何定义或声明在块外的变量,都当做全局变量。 编译器把全局变量默认成extern 变量
int a=10; //全局变量定义(实体)。 编译器会自动解释为 extern int a;
extern int a= 10; //等价形式
int a; //全局变量声明
extern 变量定义
定义时:隐式定义省略extern时才能省略初始化表达式,显式定义加上extern 则必须有初始值。
//块外
extern int m = 100; //初始值表达式不可以省略
{
//块内
}
int n ; //省略extern时才能省略初始化表达式
显式定义extern变量时,初始化表达式不可以省略, 定义语句通知编译器在静态内存中 开辟一块指定类型大小的内存区域,用于存储该变量。 省略了,就是声明,变量如果只有声明没有定义, 会出错。
可以省略初始化表达式的情况: 定义全局变量时, 当且仅当省略了extern时, 初始化表达式才可省略。系统默认初始化为0。 对于定义全局数组或结构体, 编译器把其中的每个元素或成员都所有位都初始化为0。
想想: 可以在块内定义extern变量吗??
extern 声明
-
可以在任何需要的地方做 外部全局变量的声明。 -
extern变量在块内声明时,不可以被赋值(不可以定义)。 声明后,extern变量可以被赋值: num=10; -
在块内做extern声明时, 作用域到块结束。 -
若在全局变量定义处之前想要访问这个变量,可以在相应的地方用“extern”关键字提前声明此变量,从而将在后面定义的全局变量作用范围扩展到前面来。 -
外部源文件也可以使用全局变量, 但是,外部源文件在使用全局变量之前, 需要对变量做extern 声明。 在任何块外声明变量时,extern可以省略, 编译器会自动添加extern。 -
全局变量extern声明后, 一直到源文件末尾,都是其作用域。 //其他源文件中...
extern int a; //声明外部全局变量
//使用 a3
外部函数
参考 大纲中的 C语言规定的函数调用。
变量的初始化
- extern 变量(即全局变量) 和static变量的初始化, 只能使用常量表达式来初始化extern变量和static变量。
常量表达式包括 直接常量、#define常量、枚举常量和sizeof()运算符
- 定义常量,不可以作为初始值。 因为C语言的const 是伪常量,仅仅适用于编译器禁止无法修改。const不是真正意思上的常量。
int num; //编译器默认初始化0
int num2 = sizeof(double); //合法,因为double类型大小是固定的。
#define X 6
int d = x; //define常量可以接受
//枚举常量也可以作为初始值
enum info{ A, B, C};
int f = A ;
//不接受const定义的常量作为初始值。
const int a=10;
int b=a; //错误。
一些重要概念说明
定义 、声明
定义、声明时,应该思考各种变量、函数的可以定义、声明的位置。位置包括:块内块外、头文件源文件、
-
同一块内,只能有一个定义,可以有多个声明。 -
一般为了叙述方便,把建立存储空间的变量声明称定义,而把不需要建立存储空间的声明称为声明 -
在函数中出现的对变量的声明(除了用extern声明的以外)都是定义。 -
定义 或声明,都是作用域的开始。 -
不同文件中,不能定义同名的函数名、变量名 。 但是加上static 修饰符后,可以定义同名函数名,变量名。 因为static限制 函数、变量在定义的文件中可用。
调用
-
C 语言规定, 无需函数声明,就可以跨文件调用定义在其他文件中的函数。 包括在其他文件的函数体内调用定义在其他文件中的函数。 -
//但是,软件工程规范, 调用外部函数, 一般需要外部声明。 -
//软件工程规范, 本文件内定义的函数的函数声明无需加extern, 文件外定义的函数的声明需要加extern。 外部函数的声明可以去掉extern,但是为了软件工程规范,应该加上去。
void fun( );
extern void fun( );
头文件
<stdio.h> 中包含了很多函数的声明, 调用库函数的时候,挨个挨个写出库函数是不现实的, 所以,将输入输出函数封装到<stdio.h> 方便调用。 这样的话, 调用某个函数时, #include <stdio.h> 包含某个头文件即可。
限制 函数、变量在定义的文件中可用。
调用
-
C 语言规定, 无需函数声明,就可以跨文件调用定义在其他文件中的函数。 包括在其他文件的函数体内调用定义在其他文件中的函数。 -
//但是,软件工程规范, 调用外部函数, 一般需要外部声明。 -
//软件工程规范, 本文件内定义的函数的函数声明无需加extern, 文件外定义的函数的声明需要加extern。 外部函数的声明可以去掉extern,但是为了软件工程规范,应该加上去。
void fun( );
extern void fun( );
头文件
<stdio.h> 中包含了很多函数的声明, 调用库函数的时候,挨个挨个写出库函数是不现实的, 所以,将输入输出函数封装到<stdio.h> 方便调用。 这样的话, 调用某个函数时, #include <stdio.h> 包含某个头文件即可。
|