https://www.runoob.com/cprogramming/c-tutorial.html
编译:通过编译器,识别源文件中的语句,将其转换成计算机能够识别的二进制形式
编译后,生成的是目标文件,不是最终的可执行文件
目标文件(中间文件),是二进制形式(.o
或.obj
)链接:通过链接器,链接生成可执行文件。编译只是对自己编写的源文件进行编译,链接是将源文件的目标文件和标准库、动态链接库等结合起来,组成最红的可执行文件
即,将所有二进制形式的目标文件和系统组件组合成一个可执行文件
嵌入式编译器:CCS (DSP)
gcc test.c test2.c -o main.out
file.out
是可执行文件
头文件包含若干函数
使用外部函数,要引入对应的头文件
通常,头文件只包含函数的说明,即函数怎么用;而函数本身保存在其他文件,在链接时会找到
#include
;#include
的作用就是将头文件中的文本赋值到当前文件,然后一起被编译器编译
对于不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为NULL(所有字节的值都是0),其他所有变量的初始值是未定义的
需要建立存储空间的变量:变量在声明的时候就已经建立了存储空间
无需建立存储空间:通过extern
关键字声明变量名,而不定义他,这种可以在别的文件中定义
变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储
声明与定义???
extern int i
是声明,不是定义
int a
是声明,也是定义
左值:指向内存位置的表达式, 叫做左值表达式
右值:内存中某些地址的数值
字面量:常量 – 不允许进行修改
整型常量、浮点常量、字符常量、字符字面值、枚举常量、定义常量
定义常量
#define
预处理器const
关键字
// #define
#define Variable value // 注意没有分号 可以定义字符型、字符串型的常量
// const
const type variable=value; // 注意有分号,且必须直接赋值
- 在函数或块内部的局部变量
在某个函数或块内部声明的变量称为局部变量,只能被函数或该代码块内部的语句使用
- 在所有函数外部的全局变量
全局变量在整个声明周期内都是有效的,在任意的函数内部都能访问全局变量
- 形参
被当做函数内的局部变量
命名冲突
局部作用域内,优先使用局部变量
全局变量:保存在内存的全局存储区中,占用静态的存储单元
局部变量:保存在栈中,只有在所在函数被调用时才动态的为变量分配存储单元
初始化局部变量和全局变量
局部变量被定义时,系统不会对其初始化(或随机初始化,根据编译器的不同策略不同),必须手动对其初始化
全局变量被定义时,系统会自动对其初始化,默认是0,(因为全局变量存储在内存分区中的全局数据区,这个区域中的数据在程序载入内存后会初始化为0)
int 0
;char
-\0
;float 0
;double 0
;pointer NULL
未初始化的变量会导致一些在内存位置中已经可用的垃圾值
a=1
b=2
c=4
def f():
print(c)
c=a+b
f()
print(c)
# c为局部变量,会报错!!!
存储类:定义c程序中变量/函数的范围(可见性)和声明周期
说明符,放在所修饰类型的类型之前
- auto
- register
- static
- extern
所有局部变量的存储类,只修饰局部变量,只用在函数内
int func()
{
// 下面两种写法都表示的是局部变量
int mount;
auto int month;
}
定义存储在寄存器中而不是RAM中的局部变量,意味着变量的最大只存等于寄存器的大小(一个字节),且不能也对它应用一元运算符???
寄存器只用于需要快速访问的变量,如计数器等(定义了register,并不意味着变量将被存储在寄存器中,只是意味着变量可能存储在寄存器中,这取决于硬件和实现的限制)
int func()
{
register int miles;
}
https://www.runoob.com/w3cnote/cpp-static-usage.html – 还没看
指示 编译器 在 程序 的生命周期内 保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁,因此使用
static
修饰局部变量可以在函数调用之间保持局部变量的值
static
也可以修饰全局变量,当修饰全局变量的时候,会是变量的作用域限制在声明它的文件中。全局声明的一个static变量或方法可以被任何函数或方法调用,只是必须与被修改的变量或方法在同一个文件中
int func()
{
// num 是局部变量,但是只被初始化一次,每次调用函数func的时候,num不会被重置
static int num=10
num--;
}
提供一个全局变量的引用,全局变量对所有程序文件都是可见的
使用extern
无法初始化变量,会把变量名指向一个之前定义过的存储位置
其实就是 A文件中定义了一个可以在B文件中使用的全局变量或函数,那么在B文件中就可以使用extern来得到A文件中定义的变量或函数的引用(用在在另一个文件中声明一个全局变量或函数)
进阶:A/B文件可以存在相互的使用extern
// A.c
#include
int count;
extern void write_extern();
int main()
{
count=5;
write_extern();
}
// B.c
#include
extern int count;
void write_extern(void)
{
printf("count is %d\n",count)
}
只能被赋值一定的离散整数值变量
类型说明符,表明没有可用的值
指针类型、数组类型、结构类型、共用体类型和函数类型
其中:函数类型指的是函数返回值的类型
char
1个字节,[-128,127] 或[0,255]
unsigned char
1个字节,[0,255]
int
2或4字节,(win 4字节)
unsigned int
2或4字节
short
/unsigned short
2字节
long
/unsigned long
4字节
double
8字节
前缀:表示进制关系
后缀:表示有符号(默认)和无符号整型U、长整型L,大小写任意,顺序任意
有符号和无符号
当以有符号数的形式输出时,printf会读取数字所占用的内存,并把最高位作为符号位,把剩下的内存作为数值位,但是对于有符号的正数而言,最高位恰好是0,所以可以按照无符号数的形式读取,且不会有任何的数值影响
当以无符号的形式输出时,printf也会读取数字所占用的内存,并把所有的内存都作为数值位对待
0b 0B # 二进制数
0x 0X # 十六进制数
0 #八进制
注意标准编译器不支持0b的二进制写法,只是某些编译器在扩展后支持
注意printf 输出形式
整型只控制字节长度,和数值范围
进制数才是实际数值的表现形式
一个数字不管以何种进制来表示,都可以 以任意进制的形式输出,因为数字在内存中始终以二进制的形式存储
其他进制的数字在存储前都必须转换成二进制形式,而在输出前也需要反转
原码:数值的二进制数,包括符号位
反码:正数的反码=原码
负数的犯法=符号位不动,其他位置取反补码:
正数的补码=原码
负数的补码:是反码加1
-128的补码就是1000 0000
补码 1000 0000 就表示-128 记录就可以了
深究里边,是因为符号位会被数值的高位进行多次覆盖
计算机内存中,整型一律采用补码的形式存储,因此读取整数时还要采取逆向的转换,也就是将补码转换为原码
float
4字节,32位
double
8字节,64位
对于小数默认是double类型
long double
16字节
float x=1.2 # 赋值的时候,会先从double类型转换为float类型
# 后缀
float x=1.2f # 赋值的时候直接是默认的float类型
注意所有的浮点型都是以双精度double进行运算的,即使是float类型,也要先转换成double类型
定义了宏,可以在程序中使用这些值和其他有关实数二进制表示是的细节
#include
FLT_MIN # 最大值
FLT_MAX # 最小值
FLT_DIG # 精度值
单引号
char
,unsigned char
是一个字符类型,用来存放字符,但是同时也是一个整数类型,也可以用来存放整数!!!,注意取值范围
char
1个字节,[-128,127]
unsigned char
1个字节,[0,255]
char
类型只能存储ASCII字符,不能存储其他字符,根据上面的定义,也可以存放范围以内的进制数
由于
char
类型只能存储ASCII字符,字符集及其编码其实本质上就是二进制数,(本质上与整型没有区别)
定义一个char
类型字符,会先转换成ASCII字符对应的编码再存储
而存储一个整型的时候,不需要任何转换直接存储
由于数据类型不同,所以需要的字节位数不同,因此在存储和读取的时候占用的内存不一样,所以在读取 字符型和整型 的时候并不会有冲突
字符型与进制数
由于char可以存放整型,所以可以声明和定义取值范围内的进制数
下面的例子中,就可以把char
想象成整型(但是要注意取值范围)
char a=0x32 #
printf("%c",a) // 2
printf("%d",a) // 50 这是因为由十六进制转换为的十进制
// 与python类别
// '2'.encode().hex() # 32
// int('32',16) # 50
// chr(50) # '2'
对于
char
类型的字符,(由于是窄字符,且只占一个字节,所以可以使用ASCII编码),(又由于ASCII编码的结果就是一个二进制数),可以利用转义的进制数进行表示
下面的例子,就可以把其当成字节对象(字节串对象),想当于python中的bytes对象,
char a='\x31' // a是字符数值1
char b='\x61' // b是字符a
char *str1="\x31\x32\x33\x61\x62\x63" // 字符串"123abc"
// 类别python
// '123abc'.encode().hex() # '313233616263'
输出字符的专用函数
printf("\"string\'") // "string'
对于中文字符应该使用宽字符的形式,并加上前缀,加上前缀后,所有的字符都变成了宽字符的存储形式
wchar_t
类型,在不同的编译器下对不同的语言使用不同的长度
#include
wchar_t a=L'中'
char
类型的窄字符,使用ASCII编码
char
类型的窄字符串,通常来讲是UTF8编码,(其实不同的编译器在不同的平台上编码方式不同)
wchar_t
类型的宽字符或宽字符串,使用UTF16或32编码
见文档
双引号
在内存中占一块连续的内存
其实字符串数据,依靠的是数组和指针来简介的存储字符串
char s1[]="string1";
char *s2="string2"
专门用于输出字符串数据
获得当前平台上的对象或类型的存储字节大小
printf("%lu",sizeof(int))
printf("%d",sizeof(instance))
数据类型存在强制转换
原则浮点数赋给整型:浮点数小数部分会被舍去(注意不是四舍五入)
整数赋值给浮点数,数值不变,但是会被存储到相应的浮点型变量中
强制转换
注意,强制转换是临时性的,并不会影响数值本身的数据类型和值
(类型说明符)(表达式)
(int)(x+y)
下面的例子中 由于都是
int
类型,所以即使是算出了float
值,也会变成int
类型,只不过在printf
变成了对应的格式
因此必须在先前就想结果类型
int a=1;
int b=2;
float c;
c=a/b;
printf("%f",c); // 0.000000
%
取余运算,只能作用于整型,不能作用于浮点型注意:正负数,只根据
%
左边的数值符号决定!!!
++
自增--
自减num++; // 先运算后+1
num--; // 先运算后+1
//例子
v=num++; // v=num;num+=1
num++; // 先运算后+1
++num; // 先+1后运算
&&
– and
||
– or
!
– not
&
|
^
异或~
取反<<
左移>>
右移如果条件为真?则值为x:否则值为y
注意结合性
函数返回为空
函数参数为空,不接受任何参数,int main(void)
指针指向void:类型为void*
的指针 代表对象的地址,而不是类型
https://www.runoob.com/cprogramming/c-function-printf.html – 没看完
http://c.biancheng.net/view/1793.html?from=pdf_website_1_0 – 没看
%d //十进制有符号整数
%u //十进制无符号整数
%f //float浮点数,默认保留六位小数,不足六位以0补齐,超过六位按四舍五入阶段
%lf //double
%c //字符
%s //字符串
%p //指针的值
%e //指数形式的值
%x //十六进制 无符号
%lu //32位无符号整数
...
// printf的高级写法,用于区分不同的进制数字
print("a=%#hx",0x1d)
整型、短整型、长整型,分别有对应的printf输出形式
printf
中不能输出二进制数
需要某些转换函数
一个数字不管以何种进制来表示,都可以 以任意进制的形式输出,因为数字在内存中始终以二进制的形式存储
其他进制的数字在存储前都必须转换成二进制形式,而在输出前也需要反转
注意事项
有符号和无符号
当以有符号数的形式输出时,printf会读取数字所占用的内存,并把最高位作为符号位,把剩下的内存作为数值位,但是对于有符号的正数而言,最高位恰好是0,所以可以按照无符号数的形式读取,且不会有任何的数值影响
当以无符号的形式输出时,printf也会读取数字所占用的内存,并把所有的内存都作为数值位对待
因为通过格式控制符进行数值输出的时候,其实并不会检查定义的数值是有符号还是无符号数,只会按照格式控制符对数值进行特定的解释
"\\"
,%%
从控制台读取数据
if (condition)
{
...
}
else if (condition)
{
...
}
else ()
{
...
}
switch-expression 必须是一个常量表达式,一个整型或一个枚举类型
expression1/2 必须是与expression类型相同的数据,且必须是常量或字面量
如果case不加break,控制流会继续后面的case,直到遇到break
当所有的case都不匹配的时候,有一个可选语句default,default语句中的break不是必须的
swtich(expression)
{
case expression1:
...
break;
case expression2:
...
break;
default:
...
}
while(condition)
{
...
}
int
会首先被执行一次,且只会被执行一次;这一步可以声明并初始化任何循环控制变量,也可以不写任何语句,只有一个分号即可
然后会判断condition,如果为真则执行循环主体,如果为假,则不执行循环主体,且控制流会跳转到紧接着for循环的下一条语句
执行完for循环主体后,控制流会跳回increment,该语句可以留空,只要在条件后有一个分号出现即可
条件再次被判断,如果为真,则执行循环,条件变为假的时候,for循环终止
for(int;condition;increment)
{
...;
}
函数体至少会被执行一次
do
{
...;
}while(codition);