C语言诞生于贝尔实验室,基于B语言设计
C语言相对紧凑,运行速度很快
C语言具有强大的可移植性,C编译器将C代码转换给计算机可识别的汇编指令,因为不同操作系统(windows,macintosh等)都自带不同种类的C编译器,因此我们所C语言具有强大的可移植性
功能强大的unix操作系统,大部分都是C语言写的,其它语言(fortran,python等)的编译器和解释器都是用C语言写的
C语言是为了程序员而设计,可以利用C语言访问硬件,操作内存中的位
C语言的缺点就是,C指针出现错误往往难以察觉
C++是在C语言的基础上嫁接了面向对象编程工具,因此C程序差不多就是C++程序
C语言是嵌入式系统编程的流行语言
K&R标准是鼻祖,C90标准随后,C99标准其次,C11标准是最新,本书只讨论C99标准
机器语言就是计算机能认识的数字0和数字1,这些数字存储在寄存器中,寄存器就是与或非路搭成的逻辑电路,这个逻辑电路根据高低电平触发信号和时钟信号,来实现存储数字,从输出端可以取出这些数字,这些数字组成了一串数字,这一串数字叫做机器语言,用一个符号代替这一串数字,这个符号叫做符号语言=汇编语言,编译器要做的就是将C语言转化为汇编语言再转化为机器语言也可以说是一串数字,不同CPU的机器指令不同,如intel指令集和arm指令集,但本质上都是0和1,因此此时,编译器就显得尤为重要,可以开发出一种编译器,可以将同样的C语言语句编译成inte和arm都可识别的机器指令,然后再将相应的机器指令转化为数字0和1
名词辨析:机器语言,机器指令(汇编语言中操作码和操作数构成),汇编语言,高级语言
1定义程序的目标,
2设计程序,
3编写代码,
4编译,
5运行程序,
6测试和调试程序,
7维护和修改代码,
8说明,初学者往往忽略前两步的重要性
来自搜狗:risc=精简指令集reduced instruction set computing,cisc=复杂指令集complex
c代码储存在文件中,基本名.扩展名=文件名=源代码文件=文件,虽然C语言可以运行在大部分操作系统,但是不同的操作系统的环境由有所区别,比如有些unix系统限制文件名不能超过14个字符,而windows要求最多255,Startup code=启动代码
C通过编译=编译器和链接=链接器实现程序的执行,首先C编译器将上面的文件也即是源代码文件=C语言代码,转化成中间代码,链接器再把中间代码和其它代码(启动代码和库函数代码)合并,生成可执行文件,好处就是,假如某一个编译环节出现问题,不需要更改其他的编译环节,就是说,这个源文件出现问题,只需要单独修改这个源文件不需要动其他的,分而治之,总-分-总
上面我们已经得到了中间代码≠可执行文件(中间代码+其它代码),可执行文件=基本名.exe文件=计算机可运行,中间代码到可执行文件之间有很复杂的过程,首先中间代码(0和1)≠C代码,,我们把它放在目标代码文件中=基本名.obj文件(0和1),此时目标代码文件缺少启动代码(充当程序和不同操作系统之间的接口),很显然,不同系统,目标obj文件相同,启动代码一定不同,此时还缺少库函数(如printf函数调用),于是链接器将目标文件的目标代码和启动文件中的启动代码和库函数文件中的库函数代码,三个共同合并成一个文件=可执行文件(0和1)=基本名.exe文件=计算机可以直接运行的文件
名词辨析:目标文件(存放目标代码)=基本名.obj≠可执行文件=基本名.exe
Unix系统介绍:C语言因unix系统而生,unix中基本名.out文件=pc中基本名.exe文件
Linux系统介绍:是一个开源,类似于unix的操作系统,可在PC和Mac或其它计算机平台上运行,安装linux时可选择是否安装GCC,什么是GCC?gcc=工具=c编辑器=多种版本(以适应不同硬件平台和操作系统),这个编辑器就是为了解释C语言=C代码,因为Linux系统也是用C语言来开发,所以要通过gcc安装C编译器来解释C语言供系统运行
PC的命令行编译器
IDE=编译器=解释C语言
Stdio.h是c编译器软件包的标准部分,它提供键盘输入和屏幕输出的支持
头文件=# include “stdio.h”=把stdio文件中的所有内容都输入到这一行所在的位置=拷贝粘贴=预处理指令,C编译器在编译C代码之前会对源代码进行预处理,为什么不把这些内置在系统中呢?学过单片机的就明白了,省资源啊,用到什么调用什么
原来void main()不是标准啊,竟然才知道,不过无所谓,一般这样写不会出错(注意1)
(注意2)
Varible=变量,C语言并没有所谓的“赋值语句”,只有赋值表达式,“=”号是赋值运算符
意思就是说,如果直接使用enter健,则系统有可能会跳出这一行语句,误认为这句不是源代码语句,而是你手动点击产生的换行,于是不能这样写(注意7)
原来这个return语句可以省略,为了规格统一,我们一般不省略,刚刚我在思考如果是void main呢,这种情况当然不需要返回0了,因为不需要返回,但是int main时可以返回也可以不返回,为了统一我们返回0,(注意8)
函数调用语句=表达式语句(注意9)
所有的C程序都是从main()开始执行,不论main()在什么位置,原型=函数声明
这倒是没怎么注意到,请检查上一行(注意10)
我当时学习时,就很不是理解,为什么输完数字后还要按下enter健,其实记住就行了,没有那么多为什么(注意11)
字=word,字节=8位=1byte=8bit,字长=根据计算机32还是64而定=32位就是32位=4byte,位=0/1=1bit,字长就是字的长度=字的位数,所以字长不是什么很神奇的概念,字长=word长度,64位计算机的word长=64位=字长,word是计算机的储存单位,字长才是实际意义,就好比,m只是单位,10m才是实际意义
名词辨析:字=word是概念是单位,字长=实际意义=计算机位数,字节=8bit,位=1bit
这代表一个数字8储存在一个字节中
计算机粗储存浮点数,是将浮点数分成小数部分和指数部分来表示,分开存储两个,对于一些算术运算,浮点数损失的精度更多,过去,浮点运算很忙,比整数运算慢,现在CPU都包含浮点处理器,缩小了速度上的差距
这里应该对字长有了新的认识
就是说,后面两个是垃圾值,为什么是垃圾值,因为此时是内存中的随机值,你不知道内存中是什么数字,有可能是计算机在运行中刚好存了一个运算的中间值刚好赋给了它,于是它就是垃圾值了(注意13)
这下明白了,short int=int=long int=long long int=有符号整型,注意文中说的都是可能,而不是一定
所以就是,short int≤int short int简称为short, int≤long int,同样long int简称为long
但是short int是否有可能=long int=long long int呢?这个问题有待以后深究?
这里的0202L是表示long类型的用八进制表示的数字,0x10L是long类型的用十六进制表示的数字,这下一切都明白了,这都是单片机中的我以前看不太懂的代码部分(注意),这里也要注意,前缀和后缀无影响,12=12D=无前缀,1B=0b1=二进制,01=1O=八进制,0x12=0X12=12H=十六进制,终于明白了
注意,这里,i是头尾循环开始,j也是循环开始,只不过从0开始了
注意,这几句话很严谨,就是说常量后缀可以大小写,比如3l=3L=long int 类型数字3=long类型数字3=十进制数字3,但是在格式打印时,也就是说格式转化时,必须是小写前缀,所以我有一个疑问,如果不是格式转换呢?也就是说打印类型和原数据类型一样呢?应该是统一小写
注意注意,0=O=八进制=进制,B=0b=二进制=进制,D=十进制,0x=0X=H=十六进制,而int,char,float,double,long=long int=l=L ,short int=short=h,u=U=unsigned是数据类型,我们格式化打印输出的是进制的数字,它们本质上都是0和1,只是数据类型不同,上面所说的小写是指的数据类型小写,如%ld(十进制打印输出long int类型的数据值)而不能是%Ld
我不知道这里short中的h是不是为了跟十六进制中的H作区别
我猜测这里的%lu表示的是默认十进制表示
注意,注意,我觉得这一块很重要,也很细节,所以截图出来,尤其是第三个printf很重要,因为此时ld表示十进制输出32位long类型数字,hd表示十进制输出16位short类型数字,于是会出现意想不到的错误出现,注意第二个printf中,short=short int 它会默认转为int类型,所以输出都是200
哦,这里终于明白了char为啥是8位了,因为不谈商业方面因素,就是char其实也是整型,它存储ascii值的字符,而常用的ascii值的字符就0-127,八位无符号char是0-255,绰绰有余啊
这里需要注意两点,其一是,上面不好的编程风格,其二是最下面的32位变成8位,我觉得上面翻译有问题,没有说清,我的理解就是上面的B是字符本质上是八位ascii码值它应该是系统默认了八位存储在32位int中的,但是char型很特殊,当32位FATE赋给char时,那么其实只有E给了char
这个也很容易理解了,就是\ ’ ’’ 三个字符,因为他们三个字符都构成了arcsii码,系统不知道你用这三个字符是想用它门的arcsii值还是它们的转义字符(加入\后面还有个n呢,是想输出换行还是只是打印这两个字符==arcsii值,’r’是想打印它还是把他们看成arcsii值打印,所以此时前面统一加上\,也就是\ \’ \’’表示只是打印这些字符,注意注意注意
通常打印输出普通字符很简单,比如打印输出字符a,但是如何打印输出转义字符?(顾名思义,转义字符=字符,只是不是单一字符),那么有三种方法表示转义字符,一是把ascii赋给char型变量,二是利用单引号’’,三是利用\,书本上竟然没有说第三种方法
这些知识点有些杂和细碎,先留着
就是说,如果打印转义字符就是打印输出’\n’就是打印输出换行字符=转义字符,如果是在双引号内那么不需要加’ ’,也就是”aaa\n”而不是”aaa’\n’”此时打印aaa之后再换行=转义字符,如果\n打印这个应该就是将转义字符打印输出\n普通字符而不是换行
我之前就一直搞不明白这里的float到底是个啥?现在知道了,float至少能表示六位有效数字,一个浮点数占用32位,其中8位用来存储指数的值和符号,剩下的24位才用来存储剩下的非指数部分
就是小数点和指数不能同时省略/整数和小数不能同时省略,我的问题来了,加入小数点省略了,指数存在,那么谁知道剩下的数字是整数还是小数呢
注意,反正细节不用在意,就是假如大了就是上溢,会打印输出iinf或者infinity表示无穷大,小了会损失精度会比正常的应该得到的值更小
哦哦这一部分,我现在明白了,就是说即使2后面有20个0,但是呢,根据计算机的存储机制,只储存指数和尾数(也就是非指数部分),因此计算机不可能把1准确加到个位上,于是出现错误,反正我还是有一点不太明白,前面说了,float至少存储小数点后6位有效数字,然后又说8位用来存储指数和指数正负,24位用来存储非指数部分,那么这里才21位小于24应该说可以啊,算了,就假如还是六位或7位八,所以不准,而下面的5位就准
在调用傅里叶fft算法时,那么就需要用到复数和虚数
注意,小细节,size of 就是字节大小,注意不等于字长,前面有辨析过
由这里我似乎能解释上面的疑惑了,就是虽然float类型用24位来存储尾数部分,但是我们只保证六位有效数字也就是六位有效精度,反正还是不太懂
刚刚看了一篇微信公众号文章,写得很好,就是讲述了十进制和二进制存储浮点数机制,里面我看懂了大部分,最后环节仍旧没有看懂,因为对于十进制转化为二进制而言,整数部分就是2取余,小数部分是/2取整,那么这就解释了十进制0.9,用浮点数表示可能就是0.8999…无限循环了,详情可看那个链接,然后十进制表示科学计数法就是0.910的6次方(这是规定),二进制,也可以这样表示,总的来说就是,符号位1,阶码位8,尾数23,总共32位,文章也解释了为啥float类型数据能够表示十进制的六位有效精度,特殊情况下可表示7位有效精度(这种我没看懂)
这条语句可以好好分析,很有助于理清思路
\a是警报,\b是后退一格,\r是当前行起始,\t实现tab健,\n换行
总之,刷新屏幕有三种方式,缓冲区满或者遇到换行字符或者需要输入的时候
注意不要再双引号中的字符串中间断开,这很重要,我刚刚在编译器中尝试过会报错
这样比较规范
建议符号常量,也就是define替换时,都用大写,毕竟这样是规范用法
注意第一个是转义字符,它本身也是字符,有ascii值,只是看着像两个字符而已
我还是第一次见到这个,似乎在单片机程序中也见到过这个,define中是常量,const中是变量,明示常量=符号常量
这就是想打印%时的做法
统一称之为“修饰符”,所以,修饰符有很多种类,上图中只给出了四种,其实也就是三种
注意const定义的是只可读“变量”,然后很细节的printf,要注意,至少懂得这么回事
_ _ 0 0 6应该是指3位有效数字,所以前面填了两个0,00006是用0填满前面的位置
所以这就是为什么前面我愿意称之为,格式转换的原因
这里在空格字符和characters之间断行,那么肯定是不行的
第二个是不是空格太多了呢(上面都是打印printf,下面都是输入scanf)
因为空格也有ascii值,也属于字符,所以只有%c格式转换例外
这就是为什么要%d %d %d之间要隔开距离,但是还是不太明白这样
总之,输入除了%c会读取空格字符外,其他都还好,输出呢最好%d %d之间要隔开,至于原因我没有看懂
看到这里,就会发现文字诙谐易懂,比课本强多了
截断,截断,截断啊
注意,注意,这里很需要注意,我刚刚还犯错了,这段话错误,下面有解释
下面是我自己运行的程序
# include
int main()
{
int i=5;
int j;
j = (i++ < 6);
printf("%d\n",j);
return 0;
}
1
--------------------------------
Process exited after 0.06542 seconds with return value 0
请按任意键继续. . .
# include
int main()
{
int i=5;
int j;
j = (++i < 6);
printf("%d\n",j);
return 0;
}
0
--------------------------------
Process exited after 0.04267 seconds with return value 0
请按任意键继续. . .
所以所以,上面的红字部分,应该是错误的,错误的
# include
int main()
{
int i=1;
int j;
//j = (++i < 6);
//printf("%d\n",j);
while(++i < 5)
{
printf("%d\n",i);
}
return 0;
}
2
3
4
--------------------------------
Process exited after 0.03472 seconds with return value 0
请按任意键继续. . .
2
3
4
5
--------------------------------
Process exited after 0.03537 seconds with return value 0
请按任意键继续. . .
这是另一种情况,果然上面红字部分是错误的
这种风格,我见到很多人使用这种风格,包括蓝澜老师也是如此,讲java时
所以说,scanf函数返回读取到的项的数量,假如是两个输入的话,那么就是返回2
这两种程序,我在程序中验证过了
下面这句话很重要
一般我会加上括号,因为我自己都不相信自己的水平,为了保险起见
While和for循环都是入口条件循环,而do while则是出口条件循环
嵌入式中,使用for循环更多,其实也都差不多
我发现这本书中,很喜欢用const来表示常量,不知道这是不是一种好的编程习惯
C 语言中并不存在字符串这个数据类型,而是使用字符数组来保存字符串。那么,字符数组就一定是字符串吗?
对于这个问题,大多教科书中的回答是“是”。其实不然,字符数组和字符串是完全不相同的两个概念,千万不要混淆。分析如下所示的示例代码。
#include
#include
int main(void)
{
/*字符数组赋初值*/
char cArr[] = {'I','L','O','V','E','C'};
/*字符串赋初值*/
char sArr[] = "ILOVEC";
/*用sizeof()求长度*/
printf("cArr的长度=%d\n", sizeof(cArr));
printf("sArr的长度=%d\n", sizeof(sArr));
/*用strlen()求长度*/
printf("cArr的长度=%d\n", strlen(cArr));
printf("sArr的长度=%d\n", strlen(sArr));
/*用printf的%s打印内容*/
printf("cArr的内容=%s\n", cArr);
printf("sArr的内容=%s\n", sArr);
return 0;
}
运行结果为:
cArr的长度=6
sArr的长度=7
cArr的长度=7
sArr的长度=6
cArr的内容=ILOVEC’
sArr的内容=ILOVEC
首先,从概念上讲,cArr 是一个字符数组,而 sArr 是一个字符串。因此,对于 sArr,编译时会自动在末尾增加一个 null 字符(也就是’\0’,用十六进制表示为 0x00);而对于 cArr,则不会自动增加任何东西。
记住,这里的 sArr 必须是“char sArr[7]=“ILOVEC””,而不能够是“char sArr[6]=“ILOVEC””。
其次,“sizeof()”运算符求的是字符数组的长度,而不是字符串长度。因此,对于“sizeof(cArr)”,其运行结果为 6;而对于 sizeof(sArr),其运行结果为 7(之所以为 7,是因为 sArr 是一个字符串,编译时会自动在末尾增加一个 null 字符)。因此,对于以下代码:
上面这些是我从网上找到的资料,所以C语言没有字符串类型,也就是说sArr不是一种数据类型,它和int float double char等等都不同,它叫做字符串和字符串数组不一样,系统自动在字符串尾部添加一个’\0’字符,于是长度自然加1,而数组长度不变
名词辨析:数组下标=索引=偏移量
这里ch+1,就是1是整型,所以ch也强制转换成为整型,实际上这个是读取字符的,所以这个整型就相当于ascii值而已
哇,学了这么多年,我还是第一次看到这种解释,感觉以前都是白学了啊
与离他最近的进行配对
# include
# include
int main()
{
float i=1.4646;
double j=11.2536367774;
printf("%f\n",i);
printf("%lf",j);
return 0;
}
1.464600
11.253637Press any key to continue
总结:书中说float至少表示6位有效数字=小数点后六位,double至少表示10位,韦道计那本书中说默认输出6位,上面编程也是如此,另外%f可打印输出float或者double,%lf可打印输出double或者long int型,实测%lf也可以打印输出float型数据(这个两本书上都没有说)
刚刚我用vc++6.0实操测试得出,’”’这样可以正常打印,而’和\前面都要加\号,这样就和上面说过的两个冲突,其实也不冲突,’\”’这样结果一样,就统一记住好了
原来这种是语义错误,而不是语法错误,我一直以为是语义错误
# include
# include
//# include //我电脑编译器环境没有这个头文件
# define STOP '|'//这是符号替换=符号常量=明示常量,也叫作宏定义
int main(void)
{
/*
float i=1.4646;
double j=11.2536367774;
printf("%f\n",i);
printf("%lf",j);
*/
/*
char i = '\"';
printf("%c\n",i);
*/
char c;
char prev;//用来检测是否存在不完整的行,也就是一行没到头就输入|结束的,也许是第一行,也许是最后一行
long n_chars = 0L;//我太不清除为啥用这个long int型变量
int n_lines = 0;
int n_words = 0;
int p_lines = 0;
bool inword = false;//false
printf("Enter test to be analyzed (| to terminate):\n");
prev = '\n';//这是换行符字符=字符常量=ascii值的换行符=转义字符
while((c = getchar()) != STOP)//这是为了一直卡在while循环里面,C语言的序列点,自左向右执行
{
n_chars++;//这是检测所有的字符数
if(c == '\n')//如果出现了一次,那么就是说明已经过了一行,此时行数加一
n_lines++;
if(!isspace(c) && !inword)//1注意这里与下面是相反执行,就是说,假如inword=0时,那么检测是否进入这个if语句
{//此时下面那个if语句根本不会检测
inword = true;
n_words++;
}
if(isspace(c) && inword)//2相反如果上面那个if语句进入时,那么久检测进入这个语句,也就是1和2交替检测进入
inword = false;//1是用来检测单词数,如果是空格自然不计入内,此时进入2
//假如第一次遇到空格,此时进入2,那么下次只会检测1也就是字符,如果还是空格就不进入1,直到遇到字符才进入1
//如果进入1之后,单词数加1,此时不会在进入这个函数,检测2,直到下一个空格出现才又进入1
prev = c;
}
if(prev != '\n')//这就是说,有一行结束时没出现换行,是第一行也许是最后一行
p_lines = 1;
printf("characters = %ld,words = %d,lines = %d,",//注意是单词数,不是字符数
n_chars,n_words,n_lines);
printf("partial lines = %d\n",p_lines);
return 0;
}
这个可以看懂,if语句代替了continue跳出本次循环语句
而这里不会跳过跳过++阶段,所以输完之后会跳到ch == getchar()语句
这一种输入不了换行符,因为会跳过++阶段,
char count,ch;
for (count = 0; count < 10; count++)
{
ch = getchar();
if (ch == '\n')
continue;
putchar(ch);
}
这一种可以输入换行符
char count = 0,ch;
while(count < 10)
{
ch = getchar();
if (ch == '\n')
continue;
putchar(ch);
count++;
}
我自己写代码实测发现,两者都显示啊,不太明白
巧用continue语句,也很重要,虽然不跳过switch语句,但是它跳过循环语句啊
这句话终于读懂了,六六六,就是读取字符再舍弃的行为,具体可看代码清单
用不用都还行
这就是C语言语法的宽泛了
Switch语句就是运行速度要快一些,这点我深有体会
Eorror简称为EOF(我猜测的,哈哈哈)
原来这个是重定向的意思啊,就是说程序读取文件呗
我终于明白这个contiinue了,就是如果输出c23之后再输入换行,无论几个换行,它都会卡在这里continue的while循环,直到不是换行,于是开始最开始的while循环,所以说很有用
我不知道缓冲输入是个什么意思?,而且上面代码我竟然发现了里面的小错误,就是第二个getfisrt(),上面是get_first()函数
放在主函数里面我倒是第一次听说
下面的解释很显而易见
由前面可知,字符串就是尾部自动加一个空字符,getchar是打个字符,gets是字符串
所以是,stdin=标准输入
Gets函数读取一整行输入,丢弃换行符,末尾添加空字符,使其成为字符串,缺点无法输入过长字符串
Fgets函数读入n-1个字符(第二个参数=n),存储换行符,
第一次知道还有这种用法
数组下标越界,就是长度超过了数组长度,数组中,如果不满,则后面默认都是\0,如果刚好满,而字符串中默认后面加\0,如果不赋值就是垃圾值,如果只赋值部分值,则后面都是,1.0在C语言中可以写成1.、0.1在C语言中可以写成.1、e前e后必有数,e后必为整数,‘0’是一个字符,占一个字节,“0”是一个字符串,占两个字节;字符是可以进行算术运算的,比如‘0’-0=48,
这个数组下标越界,带来的引起其他变量值发生变化,挺奇怪的,第一次了解,还有这一题中,下标能够从-1开始计数是我始料未及的