C语言深度解剖学习笔记
《C语言深度解剖》前言:如果本书上面的问题能真正明白80%,作为一个应届毕业生,肯怕没有一家大公司会拒绝你。
第一章关键字
什么是定义?什么是声明?
什么是定义:所谓的定义就是(编译器)创建一个对象,为这个对象分配一块内存并给它取上一个名字,这个名字就是我们经常所说的变量名或对象名。但注意,这个名字一旦和这块内存匹配起来,它们就同生共死,终生不离不弃。并且这块内存的位置也不能被改变。一个变量或对象在一定的区域内(比如函数内,全局等)只能被定义一次,如果定义多次,编译器会提示你重复定义同一个变量或对象。
什么是声明:有两重含义,如下:
第一重含义:告诉编译器,这个名字已经匹配到一块内存上了,下面的代码用到变量或对象是在别的地方定义的。声明可以出现多次。
第二重含义:告诉编译器,我这个名字我先预定了,别的地方再也不能用它来作为变量名或对象名。
定义声明最重要的区别:定义创建了对象并为这个对象分配了内存,声明没有分配内存
C语言标准定义的32个关键字
关键字意义
auto声明自动变量,缺省时编译器一般默认为auto
int声明整型变量
double声明双精度变量
long声明长整型变量
char声明字符型变量
float声明浮点型变量
short声明短整型变量
这六个关键字代表C语言里的六种基本数据类型
在32位的系统上short内存大小是2个byte;int内存大小是4个byte;long内存大小是4个byte;float内存大小是4个byte;double内存大小是8个byte;char内存大小是1个byte。(注意这里指一般情况,可能不同的平台还会有所不同,具体平台可以用sizeof关键字测试一下)
signed声明有符号类型变量
unsigned声明无符号类型变量
正负数:最高位如果是1,表明这个数是负数。如果最高位是0,表明这个数是正数
struct声明结构体变量
不要认为结构体内不能放函数\
union声明联合数据类型
union只配置一个足够大的空间以来容纳最大长度的数据成员
enum声明枚举类型
成员都是常量,也就是我们平时所说的枚举常量(常量一般用大写)。enum变量类型还可以给其中的
常量符号赋值,如果不赋值则会从被赋初值的那个常量开始依次加1,如果都没有赋值,它们的值从0开始依次递增1
static声明静态变量
第一个作用:修饰变量。
1、静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用extern声明也没法使用他
2、静态局部变量,在函数体里面定义的,就只能在这个函数里用。由于是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。
第一个作用:修饰函数
函数前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
switch用于开关语句
case开关语句分支
default开关语句中的“其他”分支
break跳出当前循环
如果分支很多……请用switch、case
字符串数组怎么转成byte
case后面只能是整型或字符型的常量或常量表达式【C语言中,字符常量的字面值是整型,表达式运算时,字符型也会自动提升为整型。这也就是说,在switch或case中写的表达式,其值是整型:】
register声明寄存器变量
这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率。注意是尽可能,不是绝对
register变量必须是能被CPU寄存器所接受的类型。意味着register变量必须是一个单个的值,并且其长度应小于或等于整型的长度
const声明只读变量
volatile说明变量在程序执行中可被隐含地改变
用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可
以提供对特殊地址的稳定访问。
typedef用以给数据类型取别名(当然还有其他作用)
extern声明变量是在其他文件正声明(也可以看做是引用变量)
return子程序返回语句(可以带参数,也可不带参数)
void声明函数无返回值或无参数,声明空类型指针
void*任何类型的指针都可以直接赋值给它,无需进行强制类型转换
continue结束当前循环,开始下一轮循环
do循环语句的循环体
while循环语句的循环条件
for一种循环语句(可意会不可言传)
if条件语句
else条件语句否定分支(与if连用)
C语言有这样的规定:else始终与同一括号内最近的未匹配的if语句结合
goto无条件跳转语句
goto语句可以灵活跳转,如果不加限制,它的确会破坏结构化设计风格;其次,goto 语句经常带来错误或隐患。它可能跳过了变量的初始化、重要的计算等语句
sizeof计算对象所占内存空间大小
sizeof是关键字不是函数。sizeof在计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略
第二章符号
国际C语言乱码大赛(IOCCC)这是IOCCC1988年获奖作品,作者是IanPhillipps。
#include<stdio.h>
main(t,_,a)char*a;{return!0<t?t<3?main(-79,-13,a+main(-87,1-_, main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2? _<13?
main(2,_+1,"%s%d%d\n"):9:16:t<0?t<-72?main(_,t,
"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+, /+#n+,/#\
;#q#n+,/+k#;*+,/'r:'d*'3,}{w+Kw'K:'+}e#';dq#'l\
q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n'; d}rw'i;#\
){nl]!/n{n#';r{#w'r nc{nl]'/#{l,+'K{rw'iK{;[{nl]'/w#q#n'wknw'\ iwk{KK{nl]!/w{%'l##w#'i;:{nl]'/*{q#'ld;r'}{nlwb!/*de}'c\
;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w!nr'/
')}+}{rl#'{n'')#\
}'+}##(!!/")
:t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+ 1)
:0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,
"!ek;dci@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m.vpbks,fxntdCeghiry"),
a+1);}
接续符和转义符
C语言里以反斜杠(\)表示断行。编译器会将反斜杠剔除掉,跟在反斜杠后面的字符自动接续到前一行。但是注意:反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格。
花括号
char a[10]={“abcde”};[正确]char a[10]{=“abcde”};[错误]
用花括号是为了把一些语句或代码打个包包起来,使之形成一个整体,并与外界绝缘,
++、--操作符
inti=3;
(++i)+(++i)+(++i);
表达式的值为多少?15吗?16吗?18吗?其实对于这种情况,C语言标准并没有作出规定。有点编译器计算出来为18,因为i经过3次自加后变为6,然后3个6相加得18;而有的编译器计算出来为16(比如VisualC++6.0),先计算前两个i的和,这时候i自加两次,2个i 的和为10,然后再加上第三次自加的i得16。其实这些没有必要辩论,用到哪个编译器写句代码测试就行了。但不会计算出15的结果来的。
贪心法
C语言有这样一个规则:每一个符号应该包含尽可能多的字符。也就是说,编译器将程序分解成符号的方法是,从左到右一个一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。这个处理的策略被称为“贪心法”。需要注意到是,除了字符串与字符常量,符号的中间不能嵌有空白(空格、制表符、换行符等)
按照这个规则可能很轻松的判断a+++b表达式与a+++b一致
第三章预处理
ANSI标准定义的C语言预处理指令:
另外ANSI标准C还定义了如下几个宏:
_LINE_表示正在编译的文件的行号
_FILE_表示正在编译的文件的名字
_DATE_表示编译时刻的日期字符串,例如:"25Dec2007"
_TIME_表示编译时刻的时间字符串,例如:"12:30:55"
_STDC_判断该文件是不是定义成标准C程序
如果编译器不是标准的,则可能仅支持以上宏的一部分,或根本不支持。当然编译器也有可能还提供其它预定义的宏名。注意:宏名的书写由标识符与两边各二条下划线构成。
第四章指针和数组
三个问题:
A),什么是指针?
B),什么是数组?
C),数组和指针之间有什么样的关系?
指针
一个基本的数据类型(包括结构体等自定义类型)加上“*”号就构成了一个指针类型的模子。这个模子的大小是一定的,与“*”号前面的数据类型无关。“*”号前面的数据类型只是说明指针所指向的内存里存储的数据类型。所以,在32位系统下,不管什么样的指针类型,其大小都为4byte。可以测试一下sizeof(void*)
如何将数值存储到指定的内存地址
int*p=(int*)0x12ff7c;需要注意的是将地址0x12ff7c赋值给指针变量p的时候必须强制转换
左值和右值
简单而言,出现在赋值符“=”右边的就是右值,出现在赋值符“=”左边的就是左值C语言引入一个术语-----“可修改的左值”。意思就是,出现在赋值符左边的符号所代表的地址上的内容一定是可以被修改的。换句话说,就是我们只能给非只读变量赋值
指针与数组
A),char*p=“abcdef”;
B),char a[]=“123456”;
例子A)定义了一个指针变量p,p本身在栈上占4个byte,p里存储的是一块内存的首地址。这块内存在静态区,其空间大小为7个byte,这块内存也没有名字。对这块内存的访问完全是匿名的访问
1)以指针的形式:*(p+4)。
2)以下标的形式:p[4]
以下标的形式访问在本质上与以指针的形式访问没有区别,只是写法上不同
对指针进行加1操作,得到的是下一个元素的地址,而不是原有地址值直接加1。所以,一个类型为T的指针的移动,以sizeof(T)为移动单位
指针数组和数组指针

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。