C语言-细节
- Fang XS.
- 1452512966@qq.com
- 如果有错误,希望被指出,学习技术的路难免会磕磕绊绊,不定期更新
摘要
- 小技巧
- 宏定义中的
#
##
- 副作用与序列点
- 数字的前缀与后缀
- 关于变量
- 关键字
const
- 关键字
static
- 关键字
volatile
- 关键字
extern
- 结构体
小技巧
- 在C中,任何数据类型都不占内存,只有数据实体占内存。
- 整形常量可以使用匿名枚举
enum {eTest =5};
,这样比#define
定义的整形更安全。 - 浮点型常量可以使用
const
定义。
宏定义中的#
##
#
的作用啊将括号中的参数转换成字符串。#
在宏定义中一般形式为:#define ToString(x) (#x)
- 就可以这样使用:
printf("%s\n",ToString(TestString)); // ToString(TestString) 被替换成了 "TestString"。 // 相当于: // printf("%s\n","TestString");
##
是连接符,将前后两个东西连接成一个整体。- 例如:
#define func(NAME,NUM) void NAME##NUM(void) func(test,1) // 相当于 void test1(void) { printf("this is a test function.\n"); } //在main函数中就可以这样调用: test1();
副作用与序列点
- 副作用
- 副作用是指对数据对象或文件的修改。
- 对于表达式来说, 主要作用是求表达式的值, 其他作用为副作用。
- 例如,
a=3;
C会对表达式进行求值,表达式值为3
,而a
这个对象的值也被修改成3
。
- 序列点
- 序列点有: 完整表达式(full expression), 表示语句结束的分号, 逻辑运算符 && 和 ||, 逗号运算符。
- 程序执行到序列点时, 所有副作用都在序列点之前发生。
- 例如,
a=3;
,程序修改了a
的值就是副作用,这个副作用在这个语句的序列点;
之前发生。
- C标准
- C标准规定:在两个序列点之间,一个对象所保存的值最多只能被修改一次。
- C标准规定:在两个序列点之间,副作用的执行顺序是不确定的。
- C标准规定:两个序列点之间的执行顺序是任意的。当然这个任意是在不违背操作符优先级和结合特性的前提下的,这个规定的意义是为编译器的优化留下空间。
- 总结
- 这样看来,我们写C程序大多数情况就是在用C的副作用。
- 尽量不要这样写
a = i++ + ++j;
这样的代码,碰到考试或者面试,先分析表达式的运算优先级,i++
先取值后自增,++j
先自增再取值。
数字的前缀与后缀
- 在数据前后加前后缀通常是为了指明常量的数据类型,以便与变量的数据类型保持一致或保证数据运算的正确性.
- 前缀(大小写都一样)
0x
表示该数值是十六进制表示的。0
表示该数值是八进制表示的。
- 后缀(大小写都一样)
U
表示该常数用无符号整型方式存储,相当于unsigned int
.L
表示该常数用长整型方式存储,相当于long
.F
表示该常数用浮点方式存储,相当于float
.
关于变量
- C语言变量定义格式
存储类型 特征修饰 数据类型 变量名;
- 存储类型:决定变量的存储位置。
auto
用来修饰局部变量,具有局部作用域,局部生命周期,存储在栈上,一般局部变量默认为auto
类型。static
修饰的:(下文关键字详述)- 局部变量具有局部作用域,全局生命周期,存储在全局区。
- 全局变量具有模块作用域,全局生命周期,存储在全局区。
extern
修饰的全局变量具有全局作用域,全局生命周期,存储在全局区。register
用来修饰局部变量,具有局部作用域,局部生命周期register
修饰变量不能被取地址。- 因为CPU寄存器有限,可能会存储在CPU寄存器中,CPU寄存器的速度比存在内存中快。
- 特征修饰:决定变量的特征属性。
const
修饰的变量具有只读的属性。(下文关键字详述)volatile
修饰的变量,提醒编译器随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。(下文关键字详述)
- 数据类型:决定变量的数据类型。
- C中的基本数据类型包括整型和浮点型。不同的基本数据类型修饰的变量,区别是所占存储空间不一样,所表示的数值范围也不一样。
- 变量名:决定变量的引用标识。
- 遵循标识符命名规则:
- 不使用关键字,系统预定义标识符等。
- 由数字,字母,下划线组成的,且数字不可以作为开头。
- 严格区分大小写。
- 不同编译器,不同标准,标识符长度限制不一样,最好小于32个字符。
- 遵循标识符命名规则:
- 存储类型:决定变量的存储位置。
关键字const
- 关于
const
修饰变量- 在C中,
const
所修饰的变量,具有只读属性。 - 在C中,
const
所修饰的变量,本身还是变量,只是被限定为只读,不可修改。 - 在C中,
const
所修饰的变量,被限定为只读属性,通过非常规操作还是可以改,这里不讨论。
- 在C中,
- 关于
const
修饰指针const int *p
修饰指针p
所指向的值不可改,常量指针。int const *p
修饰指针p
所指向的值不可改,常量指针。int * const p
修饰指针p
不可改,指针常量。const int * const p
修饰指针和指针p
所指向的值不可改,指向常量的指针常量。
- 总结
const
修饰指针,分3种情况,修饰指针指向的值,修饰指针,修饰指针和指向的值。- 修饰指针不可改,
const
放在*
的右边。 - 修饰指针指向的值不可改,
const
放在*
的左边。 - 修饰指针和指向的值都不可改,
const
在*
的左边和右边。 - 在函数中,
const
一般用来修饰形参,来确保传进的值或指针,不被本函数修改。
关键字static
- 在C中,
static
使用恰当能够大大提高程序的模块化特性,有利于扩展和维护。 - 在C中,
static
用来修饰变量 称静态变量- 函数内局部变量
- 函数内局部变量,只具有局部作用域,局部生命周期,在栈区分配内存空间。
- 被
static
修饰的局部变量称静态局部变量。- 具有局部作用域,全局生命周期。
- 静态局部变量,只被初始化一次,在全局数据区分配内存空间。
- 函数外全局变量
- 函数外全局变量,具有全局作用域,全局生命周期,在全局数据区分配内存空间。
- 被
static
修饰的全局变量称静态全局变量- 具有文件作用域,全局生命周期,只在模块内访问,在全局数据区分配内存空间。
- 函数内局部变量
- 在C中,
static
用来修饰函数 称静态函数- 静态函数只能在声明它的文件中可见,其他文件不能引用该函数。
- 不同的文件可以使用相同名字的静态函数,互不影响。
- 不被
static
修饰的函数,隐含被声明为extern
,其他文件可以访问。
关键字volatile
- 翻译过来是 adj.易变的,动荡不定的,反复无常的;
volatile
声明的变量,提醒编译器随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。- 现在的编译器趋向于智能化,经过时间的沉淀,bug越来越少,越来越智能,甚至有的变量,编译器可能优化读取和存储,来提升程序的速度和性能。
- 在嵌入式中,有些指针存储的是寄存器的地址,而寄存器的值可以不仅仅被程序改变,也可以被外设,或者引脚电平变化而随之改变,这样一来,如果编译器对其进行优化,暂时使用寄存器中的值,那么编译器优化保存的值,跟实际的值是不一样的,会出现bug。
关键字extern
- 在C中,
extern
修饰的全局变量,具有全局作用域,全局生命周期,存储在全局区,可以被跨文件使用。 - 在C中,
extern
指示编译器去别处查询其定义。 - 在C中,用
extern
关键字声明定义在其他文件中的函数。这样做是为了表明当前文件中使用的函数被定义在别处。除非使用static
关键字,否则一般函数声明都默认为extern
。