目录
前言
以下内容使用Visual Stdio 2022集成开发环境,配置环境位debug版本32位操作数。
读前须知
①c语言常见内存分布
②由代码到可执行程序需要经过 编译——>链接——>可执行程序
正文
一、关键字
Ⅰ、static关键字
① 、static修饰局部变量
static修饰局部变量的时候改变了局部变量的存储类型:本来一个局部变量是存储在栈区的,而被static修饰的局部变量是存储在静态区的。
存储在静态区的变量出了它的作用域(以下例子add函数为z的作用域)变量也不销毁,所以生命周期较长,但是局部变量的作用域是不被改变的。如果想在作用域外部调用这个静态变量,则需要传出(返回)静态变量的地址。
static修饰的变量生命周期与程序的生命周期相同,即程序运行完毕static修饰的变量才销毁。
正常情况下:
而被static修饰之后:
②、static修饰全局变量
static修饰全局变量的时候,改变了全局变量的链接属性。
本来一个全局变量是具有外部链接属性的,但是被static修饰之后就变成了内部链接属性。这时static修饰的全局变量只能在本源文件(.c)中使用,其他文件无法使用。
注:内外部链接属性:一个源文件能否通过代码使用另一个源文件中的变量或者函数。能使用就称这个变量(或函数)具有外部链接属性,不能的话就称这个变量(或函数)具有内部链接属性。
正常情况:
外部符号被static修饰:
注:extern关键字是用来声明外部符号的。
③、static修饰函数(与修饰全局变量同理)
static修饰函数和修饰全局变量是类似的,一个函数本来也是具有外部链接属性的。当被static修饰的时候,外部链接属性就变成了内部链接属性,这个函数就只能本源文件内部使用,其他文件不能使用了
正常情况:
被static修饰后:
Ⅱ、(define)定义常量和宏
①、定义常量与宏
注:定义的常量和宏,在源文件编译时,编译器做的第一步就是将define定义的常量或宏替换文件中所有出现常变量(例如SUM)的位置
注:定义宏的时候每个变量都要加上括号,因为宏变量是直接替换表达式,可能会遇见操作符优先级跟预想中的不一样,导致出错,例如:
原本你想ADD(1,1)返回的应该是2,输出为4,但是这里是宏,应该替换表达式,所以
a = 2 * 1 + 1 = 3;
二、内存(指针)
Ⅰ、从内存条到指针
①、内存条
内存条如下:
注:0x是十六进制的前导,意思是表示后面一串数字是十六进制的形式
地址怎么来的?————><地址详解>
比如我们创建一个int类型的变量b,与一个char类型的变量c:
②、指针和指针变量的大小
指针就是地址,一串二进制,如上图(32位操作系统)0xFFFFFF11 就是二进制的
11111111 11111111 11111111 00010001
每个指针都指向一个字节,就把一个字节看作一个房间,地址(指针)就是这个房间的门牌号。
指针变量的大小由操作系统或者由配置的地址操作数决定
32位操作数的指针变量大小为4个字节,64位操作数的指针变量大小为8个字节如:
32位操作数:11111111 11111111 11111111 11111111
(32位,4个字节)=0xFFFFFFFF
64位操作数:11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111
(64位,8个字节) =0xFFFFFFFF FFFFFFFF
(稍微能理解为什么64位操作数的软件为啥不能兼容32位的了吧!)
③、指针解引用操作(* 解引用操作符)
如下代码和内存分布:
b的二进制为:00000000 00000000 00000001 00000000
c存放的是b的首个字节的地址:0xFFFFFF11
解引用就是访问c指向的字节,具体访问多少字节由指针变量的类型决定
指针变量c的类型为char* ,那么c解引用只访问一个字节,也就是说*c = 00000000 等于十进制的0,其他类型比如 int*解引用访问内存中4个字节,由此可以做到一些特殊操作。
注:1、Visual Studio采用的是小端字节序,底位字节放在底地址处,高位字节放在高地址处
三、拓展:大小端字节序
小端字节序:数据的低位放在低地址处,数据的高位放在高地址处。(我们使用的vs使用的就是小端存储)
大端字节序:数据的低位放在高地址处,数据的高位放在低地址处。
四、原码,反码,补码
注:内存条中存放的都为补码。
Ⅰ、原码
原码是字面值的二进制,比如
255的原码是 00000000 00000000 00000000 11111111
-255的原码是 10000000 00000000 00000000 11111111
(有符号二进制的第一位是符号位,0表示数据为正数,1表示负数)
Ⅱ、反码
反码是在原码的基础上,除了符号位,其他位按位取反,比如
255的反码是 00000000 00000000 00000000 11111111
-255的反码是 11111111 11111111 11111111 00000000 (1比0窄,这里没问题)
注:正数的原码、反码和补码都是相同的。
Ⅲ、补码
补码是在反码的基础上加一,比如
255的反码是 00000000 00000000 00000000 11111111
-255的反码是 11111111 11111111 11111111 00000001 (1比0窄,这里没问题)
(补码也是存放在内存中的数据)
原码、反码与补码的转换关系: