C基础补习

来源:互联网 发布:volatile 变量 java 编辑:程序博客网 时间:2024/04/28 15:50

1:
gcc 默认编译生成 a.out----可以自己指定

调试信息:直观的打印输出信息
printf("FILE = %s,LINE = %d,func = %s\n",__FILE__,__LINE__,__func__);
__FILE__:当前文件名 --%s
__LINE__ :当前行号 --%d
__func__ :当前函数 --%s 

2:关键字:
数据类型关键字:(5)
void
char
int
float
double
类型修饰关键字: (4)
short
long
signed
unsigned
复杂类型关键字: (5)
struct
union
enum
typedef
sizeof
存储级别关键字: (6)
auto
static
register
extern
const
volatile
流程控制关键字: (4)
return
continue
break
goto
分支结构关键字: (5)
if
else
switch
case
default
循环结构关键字: (3)
for
do 
while

3:数据类型长度:sizeof()  字节数

基本类型(32位系统)(64位系统)
char: 11
short (int): 22
int: 44
long (int): 48
long long (int):8 8
char *  (指针): 48
float: 44
double: 88

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);

结果是:

signed c = 0xfffffff9


7:小数(float double)的存储方式:

符号位指数位底数位
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  100000100000 1000 0000 0000 0000 000


-8.25

符号位1指数位8位底数位23
计算机存储数据为-- 1  100000100000 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


12:三大结构:
顺序,选择,循环

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()函数的老式名称


0 0
原创粉丝点击