C语言学习笔记

http://c.biancheng.net/cpp/     c语言中文网

https://my.oschina.net/u/3138096/blog/1083282   对于win7下VC++6.0出现不能导入文件到工程的错误解决


1.1, C语言的编译器

所谓C语言编译器,就是把编程得到的文件,比如.c,.h的文件,进行读取,并对内容进行分析,按照C语言的规则,将其转换成系统可以执行的二进制文件
其本质在于对文件的读入,分析,及处理。这些操作,C语言都是可以实现的。
所以用C语言来做C语言的编译器是完全可行的。

但是,历史上的第一个C语言编译器,肯定不是C语言写的,因为在没有编译器时,无法把C语言转换成可执行文件【如果第一个是C编写的,那么编译器本身无法被编译】。
只要有了第一版其它语言的编译器,就可以用C语言写编译器了。
事实上,目前大多数的C语言编译器,都是用C语言写的。
【另一个问题:c语言的标准库函数是用c语言实现的还是用汇编实现的?】
这个问题其实就和先有鸡还是先有蛋一样,也跟好多人问的最早的编译器是什么语言写的一样,都是探寻本源的,但是,在现有的知识下,探寻探寻着就陷入混乱了,:-)
我的blog: http://blog.csdn.net/band_of_brothers/ 有些探讨此类问题的文章,lz可以看看。
回到这一问题,c的库函数是c语言写的,某些部分为了提高性能,是用汇编写的,可以具体下一个 glibc 的源码来看看。

lz说的 “那么用来实现的函数又是用什么写的呢”,lz这句话并没有表达出自己的意思,混乱了吧 :-) ,其实不需要什么实现函数的东西啊,把用c语言代码写的函数编译下,就可以了,只要世界上有一个c语言的编译器存在,那么用c语言写的函数就可以编译成机器码的,明白了吗 ?你的潜意识中,迷惑的其实是:这第一个c语言的汇编器又是怎么来的?还是建议看下在下的blog里的《蛋鸡问题,先有鸡还是先有蛋。顺便回答第一个编译器是怎么来的。》,我在这里简单说下,其实编译器这个东西,是一个运行在机器里的一堆机器指令的集合,他的功能就是把源文件中的用ascii字符编码堆积的xx语言语句,转换成机器指令,就这么简单。

那么第一个编译器,也就是“运行在机器里的一堆机器指令的集合”,可以用机器指令来实现,比如你可以用纯机器指令写出一个c语言的编译器,然后用某些读入设备(比如纸带机,读卡机)等直接读入内存,那么这个编译器就可以工作了,然后你再用c语言的语法语句,写一个编译器,存储为文本文件形式,然后把这个文本文件送给早已在内存中等候的那“第一个编译器”,编译后,就又得到一个新的编译器了,也就是说,只要世界上诞生了第一个xx语言的编译器后,从此,人们再也不用用可怕的机器码写程序了,转而可以用xx语言来写程序了,当然编译器本身也是个程序,所以你可以用xx语言来写一个新的语言的编译器了,比如用c写pascal的编译器、写c++的编译器,写c自身的编译器。

1.2,编译机制:

  将源文件转换为可执行程序分为两步:编译和链接。编译器将源代码转换为中间代码,链接器将此中间代码与其他代码相结合来生成可执行文件。中间文件有多种选择形式,最一般的形式是将源代码转换为机器语言代码,将结果放置在一个目标代码文件(简称目标文件)中。虽然此目标文件中包含机器语言代码,但此文件还不能运行。目标文件包含源代码的转换结果,但它还不是一个完整的程序。

  目标文件中缺少两个元素:一是一种叫做启动代码的东西,此代码相当于程序和操作系统之间的接口。二是库例程(即库函数)的代码,即书写的程序中用到的函数的实际代码并不在目标文件中。

  链接器的作用是将这三个元素(目标代码、系统的标准启动代码和库代码)结合在一起,并将它们放在单个文件中,即可执行文件中。对库代码来说,链接器之提取书写的程序中用到的函数代码。

  在大部分的系统上,编译器可以自动启动链接器,所以只需给出编译命令即可.

编译器和链接器的作用如下图:


1.3, 字长

平常我们说的32位机,64位机,说的就是32字长,64字长,英文叫word size。是设计计算机时给定的自然存储单位。

字长:CPU一次操作可以处理的二进制比特数(0或1)。计算机的字长越大,其数据转移越快,允许的内存访问也更多。人处理信息时是一个字一个字的读,计算机也一样,一个字长一个字长的处理。


第三章         整型和浮点型  

1, 整型和浮点型在计算机中的区别是它们的存储方式不同。计算机把浮点数分为小数部分和指数部分开来存储。7和7.0数值相同,但一个是整型一个是浮点型

2, 整型:

  2.1, int类型是有符号整型。其取值范围依计算机系统而异。理论上讲 存储一个int要占用一个机器字长【实际牵涉到64位机时并非如此,实际上数据类型占用几个字节是由编译器在编译期间说了算。一般的64字长的机器(64位机)其int类型占用的一般还是4个字节32位而非理论的64位】因此早期的16位IBM PC兼容机使用16位来存储一个int值,其取值范围为:-2^15 ~2^15-1[-32768~32767]。目前的个人计算机一般是32位,因此用32位表示一个int值。64位机则用64位表示一个int值。ISO C 规定int的取值范围最小为-32768~32767。一般而言,系统使用一个特殊位的值表示有符号整数的正负号。

  2.2, C语言提供了3个附属关键字修饰基本整数类型:short, long, 和unsigned。

    同义词:short,   short int,   signed short,   signed short int  都表示的是同一种类型。

  2.3, C语言只规定了short占用的空间不能多于int,long占用的空间不能少于int。现在个人计算机上最常见的设置是:long long 占64位, long占32位,short占16位,int占16或32位(依计算机的自然字长而定)。原则上,这四种类型代表4种不同的大小,但是实际使用中,有些类型之间通常有重叠。

  2.4, 注意一点:如果在32位机上    [此机器上int和long都占用32位]    需要用32位的整数时,应使用long类型,以便把程序移植到16位机上后仍然可以正常工作。

  2.5, 通常,程序代码中使用的数字字面量被存储为int类型。如果数字过大,编译器会依次认定为:long int-->unsigned long int --> long long --> unsigned long long。如果需要编译器以long类型存储一个小数字,可以在值的末尾加上l或L。同理ll 或LL表示long long ,u或U表示unsigned。 例如:5uLL  [或5LLU顺序和大小写无关紧要]  表示 这个5是以unsigned long long 类型存储的。

  2.6, 当存储的值超过类型的范围时会出现溢出行为。比较有代表性的溢出行为是超出最大值1就是最小值(归零)。但是C标准并未规定溢出规则,各种溢出的情况都有可能发生。

3, char类型

  3,1, char类型用于存储字符(字母和标点符号)。实际上计算机使用的是数字编码来存储字符,如若用ASCII码,则A在计算机中用数字65表示。所以char类型实际上也是整型,因为char存储的实际上是整数而不是字符。

  3,2, C语言把一个字节定义为char类型占用的位数。C语言规定,char类型所占用的位数叫一个字节。注意这是人为的规定!通常char类型是占用8个位,所以一个字节就是8个位。但是也有可能有些系统上char类型占用16个位(一些字符集可能使用),此时,在这个系统上一个字节就是16位,那么在此系统上,如果double类型占用64位,那么double类型占用的字节数是4而不再是8。

  3,2, C语言中,把用单引号括起来字符称为字符常量。编译器一遇到‘A'就将其转化为相应的代码值(65)。不用单引号编译器认为其是变量,用双引号则编译器认为其是字符串。奇怪的是,C语言把字符常量视为int型而非char型,因此直接打印字符常量 ’AB' 则会显示 16706(这是65的二进制和66的二进制连接在一起时的十进制值)。如果把‘AB'这个字符常量赋值给char类型的变量ccc,则只有最后8位有效。因此变量ccc的值是'B'。

    注意下面这个细节:

int main(void)
{
    char c = 'ABCD';
    printf("%c
", c);//这里打印出的是D,即最后8位有效
    scanf("%c", &c);
    printf("%c
", c);//输入ABCD,这里打印出的是A

    return 0;
}

  3.3,  char beep = 7 / 07 / 0x7 / '7' / 'x7'   都是表示ascii码 7(beep),后面两种属于转义序列(escape sequence),可以嵌入C语言的字符串中。char b = '7'表示字符7,即ascii码55。例如:printf("hello!7");//打印hello!7;  而printf("hello7");//打印hello!并发出一声蜂鸣 

   char型表示方法汇总: 

  •     直接用ascii数字表示:    char beep= 7 / 07 / 0x7
  •     用ascii码的8进制或16进制表示;'oo'  / 'xhh': char beep = '07' 或 '7' 或'7'[八进制可以省略前导0]  或'x07'
  •     用字符表示,有可细分为可打印字符入:char a = 'A',和不可打印字符,用转义序列表示:char es = ‘ ' 或 'a'等。

  3.4, 有些C编译器把char类型实现为有符号类型,即char的范围为-128~127,而有的C编译器把char类型实现为无符号类型,即char的范围为0~255。C语言允许在关键字char前面使用signed和unsigned。这样无论编译器默认的char类型是什么类型,signed char表示有符号类型而unsigned char表示无符号类型。这在用char处理小整数时很有用。如果只用char处理字符,那么char前面无需任何修饰符。

4, 可移植类型:stdint.h 和 inttypes.h头文件

  4.1,C语言中的许多类型名在不同系统中的功能不一样,因此C99新增了这两个头文件来来确保C语言的类型在各系统中的功能相同。

  4,2, 【exact-width integer type:精确宽度整数类型】,如:int32_t 表示32位有符号的整型,在32位int系统中,int32_t是int的别名,而如果在int为16位,long为32位的系统中,int32_t则变为了long的别名。

  4,3 【minimum width type:最小宽度】,如:int_least8_t表示可容纳8位有符号整型类型中宽度最小的类型的一个别名。如果一个系统中的最小整数类型是16位,可能不会定义int8_t类型,但该系统仍可使用int_least8_t类型,但是可能把此类型实现位16位的整型。

  4.4 【fast minimum width type:最快最小宽度类型】,如: int_fast8_t被定义为系统中对8位有符号值运算最快的整型的别名

  4.5 【最大整数类型】,如:intmax_t可存储任何有效的有符号值。uintmax_t表示最大的无符号整数类型。这些类型可能比unsigned long long 类型还大,因为C编译器可以实现一些标准之外的类型。如一些编译器在标准引入long long类型之前就已经提前实现了该类型。

  4.6,printf()函数要求类型匹配,C提供了一些字符串宏来显示可移植类型。如inttypes.h文件中定义了PRId32字符串宏代表打印32位有符号值的合适转换说明(d或l)。如:

    printf("int32_t variable aaa =%" PRId32 " ", aaa);//会把PRId32替换为d或l ;

5, 浮点型:float ;double;long double; 

  5.1, C标准规定,float类型必须至少能表示6位有效数字,且取值范围至少是10E-37~10E37前一个规定是指 float类型必须至少精确表示小数点后的6位有效数字,如33.222222。通常,系统存储一个float浮点数要占用32位。其中8位用于表示指数的值和符号,剩下的24位用于表示非指数部分(也叫尾数 或 有效数) 及其符号。

  5.2, C标准规定,double(双精度)类型与float类型的最小取值范围是一样的,但至少必须能表示10位有效数字。一般情况下double占64位。多出的位可以全部表示非指数部分,也可以分一部分到指数部分。

  5.3, C提供的第三种浮点类型是long double ,以满足比double类型更高的精度要求。不过C 只保证了long double类型 至少 与double类型精度相同。

  5.4, 浮点型常量:标准形式是【3.12E+5】,可以没有小数点【3e5】,也可以没有指数部分【19.12】,但是不能两者同时没有。

  5.5, 默认情况下,编译器默认浮点型字面量是double类型的精度。在浮点型常量后面加上后缀f或F可以覆盖默认设置,将其转换位float型。使用l或L后缀使数字称为long double类型。C99标准添加了一种新的浮点型常量格式----用十六进制表示浮点型常量。如0xa.1fp10[或0Xa.1fp10],其中p代表的是2的幂而非10的幂。

  5.6, 浮点值的上溢和下溢:当浮点值超过其类型的最大值时,会发生上溢行为。现在的C语言规定上溢时,将一个表示无穷大的特定值(如inf, infinity等)赋给这个变量;而当值过小时又会发生下溢行为(underflow)。下溢会产生一个低于正常的浮点值。还有一个特殊的浮点值:NaN(not a number)。给asin()函数传一个大于1的值就会产生NaN。  

6, 复数和虚数类型

7, 其他类型:C语言没有字符串类型,但也能很好的处理字符串。C语言还有一些从基本类型衍生的其他类型,包括数组,指针,结构和联合。

8, 类型大小:sizeo运算符    [注意:sizeof 是运算符!!]

  8.1, sizeof是C语言的内置运算符,以字节位单位给出运算对象(也叫操作数,operand)的大小。当sizeof用于返回类型占用的字节大小时必须使用sizeof(),即带括号的形式,而sizeof 用于普通的操作对象时可以不用括号,但是最好还是用括号。  C99开始提供%zd转换说明匹配sizeof的返回类型。不支持的可以用%u或%lu代替%zd。如:printf("Type long long has a size of %zd bytes. ", sizeof(long long));

9, 参数

现在C语言通过函数原型机制检查函数调用时参数的个数和类型是否正确,但是该机制对printf()和scanf()不起作用。因为这两个函数与一般函数不同,它们的参数时可变的。这两个函数通过第一个字符串参数中的转换说明出现的个数来表明后续后多少个参数。程序员有责任保证使用这两个函数时 前后的个数和类型都要一一匹配。

10, 转义序列:表示一些非打印字符

  • : 退格符。使光标向左移动一个位置。通常,退格键不会擦除退回所经过的字符,但有些编译器实现使擦除的。
  • : 回车符。使光标回到当前行的起始处。
  • :换行符。使光标移至下一行的起始处。

11, 输出刷新

  11.1, printf()何时把输出发送到屏幕上呢?printf()语句把输出发送到一个叫做缓冲区(buffer)的中间存储区域,然后缓冲区中的内容再不断被发送到屏幕上。C标准明确规定了何时把缓冲区中的内容发送到屏幕上:1,当缓冲区满、2,遇到换行字符、3,需要输入时,如scanf()(从缓冲区把数据发送到屏幕或文本被称为刷新缓冲区)。另外使用fflush()函数也可以刷新缓冲区。


1.4,printf()函数

1, printf()函数中的转换说明决定的是数据的显示方式,而不是数据的存储方式。比如,一个float类型的值用%d来打印,是将计算机中以float形式存储的值当作int型读取出来。注意区分:赋值时,int aaa = 12.99;//将一个double型常量赋值给int变量,编译器会把此double值转换成int型12(直接截断而非四舍五入)。

2, 还是书上整理的全面,截图如下:

指定了如何把数据转换成可显示的形式的符号被称为 转换说明(convesion specification)。

 修饰符:在%和转换字符之间插入修饰符可修饰基本的转换说明。如果要插入多个修饰符,其书写顺序应和表4.4中列出的顺序一致!

标记

 


 1.4.1: scanf()函数 

1,除了%c之外,scanf()中其余的各种转换说明之间是否由空格没有任何影响。在%c之前放一个空格【scanf(" %c", &b)】,可以使编译器跳过所有的空格从第一个非空格处开始读取。

2,


1.5,https://zhidao.baidu.com/question/582366108.html     float,double到底保留几位有效数字

http://blog.csdn.net/cppptr/article/details/573372 【浮点数是如何存储的】
这篇文章里有一点需要注意,C语言中的二进制可能并不是文章中【针对java】所说的Round to even方法,有可能是0舍1入或者 恒置1 法。 浮点数在计算机中存在误差的原因就是,
float在计算机中只有23位(按照标准,省略了最前面的1,实际最多可以有24位由来存储)用来存储尾数,而大部分的小数换算成2进制时是无限位的(典型的如0.1),所以计算机存储时
只能截取到整个数(包括整数部分和小数部分在一起的所有二进制位)的第24位【从1开始计算的】,剩下的全部被用各种方法”舍入”
(比如这篇文章中的round to even方法就是一个方法,
此方法具体为:如果第25位是0,那么从第25位以及25位之后的将直接被舍弃,如果第25位为1,那么在可以舍或入的两个数中取尾数为0的那个数,其实就是看第24位是0还是1,如果24位是0直接舍,
如果是1则将第25位及之后进1后舍弃)。于是存储后再转化为浮点数就会有误差。

http://blog.csdn.net/jjj19891128/article/details/22945441 【浮点数如何存储的,这篇更清晰】
http://blog.csdn.net/rsp19801226/article/details/3085343【关于浮点数的精度与取值范围的问题】
从这篇文章看,貌似float是到小数点后6位(24/4=6), double是到小数点后13位(53/4=13...1)。但是为什么4位二进制可以精确一个小数位?
float是32位,double是64位
float32位中,有1位符号位,8位指数位,23位尾数为
double64位中,1位符号位,11位指数位,52位尾数位

第四章      字符串和格式化输入/输出

1, 字符串简介


第五章    运算符、表达式和语句

1,递增运算符(++)

  1.0, 递增和递减运算符都有很高的结合优先级,只有圆括号的优先级比它们高!因此:x*y++表示的是 (x)*(y++)而不是(x*y)++,而且后者也是无效的,因为递增和递减运算符只能影响一个变量(或者更普遍的说,只能影响一个可修改的左值),而组合 x*y不是可修改的左值。

  1.1, 前缀模式(++a)和后缀模式(a++): 共同点--->最终都使a递增1;不同点--->整个表达式的值不同,表达式++a的值是a+1,表达式a++的值是a.

  1.2, 递增和递减运算符不能乱用,可能会出现意想不到的情况。在C语言中,编译器可以自行选择先对函数中哪个参数求值。这样做提高了编译器的效率,但是也会出现一些问题。如下例所示:  

int main()
{
    int num = 1;
    while (num < 21) {
        printf("%10d %10d 
", num, num * num++);
    }

    return 0;
}
/**
* 理想情况是打印num, 计算num * num,最后把num递增1。但实际上只会在某些系统上是如此运行的。有些编译器可能从右往左执行:所以结果是:6, 6×5.
*/

   遵循一下规则,可以避免类似问题:

  •   如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增和递减运算符;
  •        如果一个变量多次出现在一个表达式中,不要对该变量使用递增和递减运算符。

       1.3 ,一个例子

    

#include <stdio.h>

int main(void)
{

    int x = 100;
    while (x++ < 103) //这里注意:当x=103时,while条件不再满足,但是while条件仍然执行了,所以还是让x增到104.
        printf("%4d
", x);
    printf("%4d
", x);//这个值注意下!

    return 0;
}

最终结果是:101 102 103 104

2, 除法运算

  2.1,  整数除法和浮点数除法不同。浮点数除法的结果是浮点数,而整数除法的结果是整数。C语言中,整数除法的结果的小数部分被丢弃,这一过程成为截断(truncation)。在C99之前,不同的实现有不同的截断方法,但是C99规定使用“趋零截断”。如-3.8趋零截断后是-3。


3, 类型转换

  2.0, C 允许编写混合类型的表达式,但是算术运算要求运算对象都是相同的类型。因此C会进行自动类型转换。尽管如此,不要养成依赖自动转换的习惯,应该显式选择合适的类型或使用强制类型转换。

  2.1, 自动类型转换:

    需要自动转换时,按如下规则自动转换:

    • 在表达式里或者作为函数的参数(函数原型除外,后面会介绍 函数原型会覆盖自动升级转换)  char和short[无论时signed还是unsigned] 会被升级(promotion,对应demotion降级)为int类型,如有必要会被转换为unsigned int(如果shrot和int大小相同,unsigned short就比int大,此时,unsigned short就被转换为unsigned int)。   
    • float类型在函数参数中会被自动升级为double类型,同样,函数原型除外。 在K&R C(不是ANSI C)下,表达式中的float也会被升级为double类型。
    • 涉及两种类型的运算,两个值会被自动转换为类型较大的那个类型。
    • 赋值表达式中, 如果将一个大的整数赋值给一个小的无符号整型,则只保留后面的位数,前面多余的位数去除。如:将int类型的值赋值给unsigned char,则只保留最好8位(效果是int的值对256求膜:256是1 0000 0000, 只有后面8位是256的余数)。

  2.2, 强制类型转换:

    • 强制类型转换运算符(cast operator):  通用形式为: (type)。如 (int) i_count

4,


第六章  C控制语句:循环

1,

2,

3,

4,

5,


第七章  C控制语句:分支和跳转

1,

2,

3,

4,

原文地址:https://www.cnblogs.com/everest33Tong/p/6415271.html