1:
gcc 默认编译生成 a.out----可以自己指定
调试信息:直观的打印输出信息
printf("FILE = %s,LINE = %d,func = %s\n",__FILE__,__LINE__,__func__);
__FILE__ :当前文件名 --%s
__LINE__ :当前行号 --%d
__func__ :当前函数 --%s
2:关键字:
数据类型关键字:(5)
voidcharintfloatdouble
类型修饰关键字: (4)
shortlongsignedunsigned
复杂类型关键字: (5)
structunionenumtypedefsizeof
存储级别关键字: (6)
autostaticregisterexternconstvolatile
流程控制关键字: (4)
returncontinuebreakgoto
分支结构关键字: (5)
ifelseswitchcasedefault
循环结构关键字: (3)
fordowhile
3:数据类型长度:sizeof() 字节数
基本类型 (32位系统) (64位系统)
char : 1 1short (int) : 2 2int : 4 4long (int) : 4 8long long (int): 8 8char * (指针) : 4 8float : 4 4double : 8 8
4:数据类型转换:
double ← float
↑
unsigned long ← long
↑
unsigned ←unsigned short
↑
int ← char , short
5:进制标识:
八进制: 0开头
二进制: 0b开头
十六进制: 0x开头
6:负数在计算机内的存储格式:
1.先忽略负号直接将其绝对值转换为二进制
2.再取反二进制
3.最后再加1就是其存储值
结果是:int c = -7;//0x000000007(绝对值)---0xfffffff8(取反)---0xfffffff9(加1)printf("signed c = %#x\n",c);
7:小数(float double)的存储方式:signed c = 0xfffffff9
符号位 指数位底数位
float 1 + 8 + 23
double 1 + 11 + 52
8.25
1.整数部分:8---1000
8 / 2 = 4 -- 0 ↑4 / 2 = 2 -- 0 ↑
2 / 2 = 1 -- 0 ↑
1 / 2 = 0 -- 1 ↑
2.小数部分:0.25 -- 01
0.25 * 2 = 0.5 --0 ↓
0.5 * 2 = 1 --1 ↓
3.结果是:1000.01
4.转换为符号和指数与底数模式
1.00001 * 2^3 (底数:00001 指数:3 + 127(01111111) = 130 --- 10000010)
符号位1 指数位8位底数位23
5.计算机存储数据为-- 0 10000010 0000 1000 0000 0000 0000 000
-8.25
符号位1 指数位8位底数位23计算机存储数据为-- 1 10000010 0000 1000 0000 0000 0000 000
举例:
-12.5
第一步:1100.1 = 1.1001 * 2^3第二步:符号位 1,指数: 3 + 127 = 130 (10000010),底数: 1001第三步:1100 0001 0100 1000 0000 0000 0000 0000第四步:0xc1 48 00 00
17.625
第一步:10001.101 = 1.0001101 * 2^4第二步:符号 0 ,指数: 4 + 127 = 131 (10000011) 底数: 0001101第三步:0100 0001 1000 1101 0000 0000 0000 0000第四步:0x 41 8D 00 00
8:>> <<
>>(算术右移)
低位抛弃
无符号数:高位全部补0
有符号数:负数(高位全部补1),正数(高位全部补0)
int c = -7;//0x000000007(绝对值)---0xfffffff8(取反)---0xfffffff9(加1)
int d = 1000;//0x0000003e8(绝对值)
printf("signed c = %#x\n",c >> 4);
printf("signed d = %#x\n",d >> 4);
结果是:
signed c = 0xfffffff9
signed d = 0x0000003e
<<(算术左移)
低位全部补0
9:需要注意的运算符号:
表达式1 && 表达式2 (若1为假将不执行2)
表达式1 || 表达式2 (若1为真将不执行2)
异或
a^a=0;
a^0=a;
三目运算符:
表达式1 ? 表达式2 : 表达式3--(若1为真将返回2,否则返回3)
逗号:
表达式1,表达式2,,表达式3,......,表达式n (最后的值是表达式n的值)
10:for循环执行步骤:
for(int i=0;i<20;i++){
循环体
}
执行步骤是:
1、i=0 初始化初值;
2、进行条件判断i是否<20,如果条件为真,则继续执行;
3、执行循环体的代码;
4、i++ 变量i自增一次;
5、回到第2步,一直循环,直到第2步条件为假时, 退出循环,结束
11:printf()函数注意点:
printf(""); (运算从右到左,打印输出从左到右)
比如:
int i = 10;
printf("%d,%d\n",i++,i);
1.其先从右边计算i为10
2.再计算i++表达式值为10
3.结果输出为10,10
------------------------
int i = 10;
printf("%d,%d\n",i,i++);
1.其先从右边计算i++为10,但是i变成了11
2.再计算i已为11
3.结果从左边开始输出为11,10
顺序,选择,循环
switch(表达式){//表达式的结果必须是整型值或者字符型值,否则报错
case 'a' :
....;
break;
case 1 :
....;
break;
......
default :
....;
}
-----------------
if(){
} else if (){
} else {
}
------------
循环语句:
while(){} //先判断再执行
do{}while(); //先执行一次再判断
for(;;){} //执行步骤在 九
continue;(结束本次循环,继续下次循环)
break;(跳出当前整个循环)
return;(结束当前函数,并且返回一个值)
13:变量的存储周期与作用域:
普通局部变量:存储周期(模块内,模块执行完成就销毁),作用域(模块内)
普通全局变量:存储周期(整个程序期间),作用域(所有模块)
静态变量(static修饰):不同模块定义的将会被存储在不同区域,也就是代表不同变量
局部:存储周期(整个程序期间),作用域(模块内)
全局:存储周期(整个程序期间),作用域(所有模块)
局部变量 会 局部屏蔽 同名 的 全局变量
比如:
int a = 1;//全局变量
int main(){
printf("%d\n",a);
int a = 10;//属于main{}内的局部变量,它将在main中屏蔽前面定义的int a=1
printf("%d\n",a);
{//这大括弧不加的话,系统报错,认为有两个cc同时存在
int a = 100; //这个a就是{}局部变量,只在这个{}之间使用
printf("%d\n",a);
}
a++;
printf("%d\n",a);
return 0;
}
结果输出是:
1
10
100
11
14:指针:
int a = 10;
(int *)p = &a; (int *) 为指针符号
b = *p; (此时*p为 a的值,表示将a赋给b)
*p = 100; (此时*p指向a,代表a的存储空间,以后a就为100)
指针的类型: int *
指针指向的类型: int
野指针:
指针定义后若没有指向变量地址,
此时它就只是一个单一变量,
其值随机,
访问它非常危险!
int *p = NULL;(避免危险,空指针不能访问)
char *p, t; (表示p为指针变量,t表示char 变量)
char *p, *t;(表示p与q都是指针变量)
15:指针的运算:
int a[10];//数组在内存中存储是连续的
int *p = &a[0];
int *q = &a[8];
p + 1;
p + 1 - 1;
指针 加减(地址偏移) (偏移量:n * sizeof(*p)---与指针指向地址存储数据类型有关)
p++,++p (*p++,*++p)
p--,--p (*p--,*--p)
p - q, q - p (个数(矢量-有正负))
(*p)++;
表示将p指向地址空间的内容加 1,
以后p指向的地址空间的内容在随后使用中都被改变了的,
指针没有发生偏移
*p++; //*(p++)
先取出当前指针指向内存地址的数值,
再将p指向的地址偏移一个存储单位
*++p; //*(++p)
先将p指向的地址偏移一个存储单位
再取出偏移后指针地址的数值,
注意:运算过程中 p 与 *p 的区别运算符的优先级等
16:内存开辟函数:
(void *) 解引用之前必须先类型强制转换
比如:
int main(){
int a = 10;
void *p = NULL;
p = &a;//任何指针都可以赋给void *指针
printf("%d\n",*p);//该语句错误,*p 在这里直接解引用了一个void *指针
printf("%d\n",*(int *)p);//这里是先将void *指针p强制转换为int *指针,再对其解引用,无误
return 0;
}
下面的这些函数返回值都是定义的void * ,所以在解引用时要先强制转换
p = malloc(a);
分配a个字节空间
返回这个空间的地址指针 p
这个空间没有初始化,其值为随机数,常使用memset(p,20,'0')来初始化
若a = 0 则返回 NULL 或者一个确定的唯一指针,让free释放
free(p);
释放函数创建的指针p指向的空间
p = calloc(a,b);
分配a个b大小的存储空间
返回这个空间的地址指针p
初始化为0
若a = 0 则返回 NULL 或者一个确定的唯一指针,让free释放
p = realloc(q,a);
重新分配大小
返回原有地址指针
比如:
int main{
int *p = NULL;
p = (int *)malloc(100);//因为malloc返回的是void * 类型,这里就将它强制转换为int * 了,后面可以直接使用
*p = 100;
printf("%d,%p\n",*p, p);
return 0;
}
17:指针指向 字符串常量 与 字符数组 的区别:
char *s = "12345";
s就是指向一个字符常量"12345",
常量是不能够修改的,所有s[2]='e';或者*(s+1) = 'e';的操作都是错误的
char s[10] = "12345";
char *p = s;
s 是一个字符数组,可以修改
此时指针p可以去修改了,*(p+1)='e'; 相当于s[1]='e';
18:二级指针:
必须存储一个地址的地址(也就是一级指针的地址)
比如:
int a =10;
int *p = &a; //p存储的是a的地址,指向a
int **q = &p; //q存储的是p的地址,指向p
*q 表示解引用,取p存储的值,也就是a的地址
*(*q) 同理
19:指针数组:
定义的数组元素为指针
(char *)b[10];
数组b的类型为指针类型,
也就是说数组b中定义的10个元素都是指针类型
char *b[10] = {"abcd","hello","efgh"}; 特别注意点: 其中元素都是字符串常量,只能根据地址读取,不能修改其值
上面语句相当于:
char *b[10] = {NULL};
b[0] = "abcd"; //b[0]指向字符串首地址
b[1] = "hello"; //b[1]指向字符串首地址
b[2] = "efgh"; //b[2]指向字符串首地址
也就是将指针指向一个个字符串常量,常量是不能修改的,只能读取
*(b[2]) = 'q';这样的赋值语句就会出现段错误
----------------------
可以修改为如下:
char *b[10] = {NULL};
char s1[4] = "abcd";
char s2[5] = "hello";
char s3[4] = "efgh";
b[0] = s1;
b[1] = s2;
b[2] = s3;
此时是将指针指向字符数组,就可以通过指针修改数组!
*(b[0] + 1) = 'd';就s2[1]修改了
*(*(b + 2) + 2) = 'a';就将s3[2]修改了
----------------------------------
b[0],b[1],b[2]....都是代表对应的首地址
比如:
b[0]--(&a), b[1]--(&h)
b[0]+1,b[0]+2,b[1]+1,b[1]+2...也是代表地址(地址偏移)
比如:
b[0]+1--(&b), b[0]+2--(&c), b[2]+1--(&f)
b,b+1,b+2,b+3...都是代表 地址的地址
比如:
b--(&b[0]), b+1--(&(b[1])), b+2--(&(b[2]))
所以有:
*(b+3) :b+3 代表b[3]的地址(&b[3]),对其解引用 b[3](还是地址) 则 *(*(b+3)) 为 *b[3] (NULL (自动补0的原因))
20:数组指针:
指向一维数组的指针 int (*p)[];
int a[3][4];
int (*p)[4];
将二维数组a[3][4]看做
a[0]:(a[0][1],a[0][1],a[0][2],a[0][3])
a[1]:(a[1][1],a[1][1],a[1][2],a[1][3])
a[2]:(a[2][1],a[2][1],a[2][2],a[2][3])
其中a[0],a[1],a[2]都是表示其对应的一维数组的首地址
a可以看做 数组a[0],a[1],a[2]的首地址,a指向a[0]
a[0],a[1],a[2]也都表示首地址
p代表a[0]地址,p+1代表a[1]地址,p+2代表a[2]地址
a[0]+1代表a[0][1]地址 ,a[2]+2代表a[2][3]地址
*(a[0]+1)表示a[0][1]
*(*(p+1)+1) 表示a[1][1]
举例
int main(){
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,0,10,11}};
int (*p)[4] = a;
printf("%d,%d,%d\n",**p, *(*(p + 1) +1), *(*p + 1));
//*p指向a的首地址,**p就是a[0]数值
//p + 1 指向a[1]的首地址,*(p + 1)就是a[1]的首地址,再对其加1,地址偏移到a[1][1],解引用为a[1][1]
//*p指向a[0]的首地址,*p + 1指向a[0][1],*(*p + 1))就是a[0][1]
return 0;
}
21:绝对地址写数据:
向绝对地址 0x12345678 写入字符 'a'
char *p = (char *)0x12345678;//强制转换
*p = 'a';
向绝对地址 0x12345678 写入int型 1000
char *p = (int *)0x12345678;
*p = 1000;
22:
extern 声明外部变量
static :
修饰全局变量:
只能在该模块被使用 (普通全局变量在整个程序中能被任意模块使用)
修饰局部变量:
生命周期变为静态 (普通局部变量的生命周期为定义到该模块结束)
修饰函数:
该函数只能在该模块被调用
const :
修饰变量:
常变量,该变量只能在定义的时候初始化,其他任何时候都不能直接去修改它的值
可以通过指针去间接修改
比如:
int main(){
int const a = 10;
int *p = &a;
a = 100;//该语句会提示错误,因为a是常变量
*p = 100;//指针可以修改,但是会提示警告
printf("%d\n",a);
return 0;
}
const全局变量存储在全局存储空间,其值只有可读属性,不能修改;
比如:
int const a = 10;
int *p = &a;
int main(){
a = 100;//该语句会提示错误,因为a是常变量
*p = 100;//该语句也会错误
printf("%d\n",a);
return 0;
}
const局部变量存储在堆栈中,可通过指针修改其值;
const变量在预处理是处理,编译器只对其值读取一次。
修饰函数:其他模块不能使用该函数
const int *p;
const 修饰的是 指针*p 代表地址存储的值,也就是说指针p指向的地址内的值不能通过 指针p 去修改
但是 p 可以重新指向其它地址
int *const p;
const 修饰的是 变量p 代表地址,也就是说 p指向的地址不能改变,也就是说不能将这个指针变量指向其它地址
但是可以通过 *p 来修改其地址内的值
volatile :
23:函数传参:
单向的值传递--传入的值在函数中改变后,在退出函数后,改变的值不会返回
int main(){
int a =10;
int b = 100;
printf("%d,%d\n",a,b);//10,100
fun(a,b);//11,101--函数里面对a,b进行处理,但是离开这个函数就没有影响a,b的值
printf("%d,%d\n",a,b);//10,100
return 0;
}
int fun(a,b){
++a;
++b;
printf("%d,%d\n",a,b);
return 0;
}
传地址:传首地址与数据个数--改变了传入的值
int main(){
int a = 10;
int b = 100;
printf("%d,%d\n",a,b);//10,100
fun(&a, &b); //11,101 --通过地址改变值
printf("%d,%d\n",a,b); //11,101 --结果都改变了,双向传输
return 0;
}
int fun(int *p, int *q){
*p = 11;
*q = 101;
printf("%d,%d\n",*p, *q);
return 0;
}
24:sizeof() 字节数 注意点:
int a = 10;
int b[10] = {1,2,3,4};
char *c = "12345";
printf("%d,%d,%d\n",sizeof(a), sizeof(b), siezof(c));
//sizeof(a) :a是int 类型 4字节
//sizeof(b) :b数组10个int类型的元素 4 * 10= 40 字节
//sizeof(c) :c代表字符串首地址,是一个地址,32位系统地址 4字节
特别的:
int fun(int a[100]){//这里的int a[100] 等价于 int *a
printf("%d\n",sizeof(a)); //所以也是一个地址 4字节
return 0;
}
25:
printf(); 输出到终端
i,d :十进制整数
x,X :十六进制无符号整数
o :八进制无符号整数
u :无符号十进制整数
c :单一字符
s :字符串
e,E :指数形式浮点小数
f :小数形式浮点小数
g :e和f中较短一种
%% :% 本身
------------
m :数据宽度,m小于实际长度将按照实际长度输出
.n :
对实数:指定小数位
对字符串:指定实际输出位
- :左对齐
+ :有符号数正数显示+号
0 :左边补0
# :加0,0x
l :指定输出精度
-------------
int a = 100;
printf("%#5.3lx\n",a);
fprintf();输出到文件
sprintf(); 指定格式转换到指定字符串
snprintf();
atoi(); 指定字符串转换为int
int main(){
char *s = "1357";//若char *s = "13a7";会自动切断a和其后面数据,输出13
int ss = 0;
ss = atoi(s);
printf("%d\n",ss);//输出 1357
return 0;
}
atol(); 指定字符串转换为long
atoll(); 指定字符串转换为long long
atop(); atoll()函数的老式名称