目录

  • 1. 关于命名与代码规范
  • 2.关于输入与输出
  • 3.关于数据类型
  • 4. 关于运算符
  • 5. 分支
  • 6.循环
  • 7.数组
  • 8.模块化程序设计
  • 标准库函数

1. 关于命名与代码规范

  • 简单变量名最多可以包含31个字符,建议使用student_name这种形式进行命名,当然也可以使用studentName这种形式。给简单变量命名,第一个字母一定小写。
  • 宏常量采用大写字母,如PI。
  • 函数的命名:采用一个单词命名时采用小写,若采用多个单词命名函数,则将后面每个单词的首字母大写,也有的人采取所有单词的首字母大写。我觉得第一种方式用的更多一些。
  • if语句前面留一行,if条件内的语句整体向后缩进。
  • 循环语句的前后各空一行,增加程序的可读性

2.关于输入与输出

  • 关于scanf:https://blog.csdn/qq_38048756/article/details/115530687
  • 关于printf:https://blog.csdn/qq_38048756/article/details/115598738
  • scanf格式串中的空格符:意味着跳过零个或多个空白字符(空格、回车、制表符等)
  • scanf函数是有返回值的,返回值表示成功输入变量的个数,当输入结束标志时,scanf函数无法再读取值,返回-1,因此我们会见到如下这种形式的代码:
while(scanf("%d", &x) == 1) { }
或者
while(scanf("%d", &x) != EOF) { }
/*上述代码表示利用while循环去读取键盘输入的数据并赋值给x
但是如果输入1 2 3 4 按下回车后,会发现没有显示结果,这是
因为程序还在等待输入,按回车并不意味着输入的结束,windows下
输入完毕后先按Enter,再按Ctrl+Z键,最后再按Enter键即可结束
输入,Linux下,输入完毕后按Ctrl+D即可结束输入 */
  • while(scanf()!=EOF),其作用和while(scanf("%d", &x) == 1)相同,意思是当遇到输入结束标志时,结束循环,输入结束的标志就是EOF,EOF是stdio.h中预定义的一个符号常量,通常值为-1,windows下输入EOF的方法是按下Ctrl+Z组合键,Linux系统下是按Ctrl+D.
  • 关于输入输出重定向:https://blog.csdn/qq_38048756/article/details/117201318

3.关于数据类型

  • 无符号整数类型主要应用于底层编程,在一般应用中不会使用。
  • 各种数据类型所占字节:https://blog.csdn/qq_38048756/article/details/115533752
  • bool数据类型,在C99中提供了一个头文件 <stdbool.h> 定义了bool,true代表1,false代表0。只要导入 stdbool.h ,就可以操作布尔类型了。
  • 需要注意的是c99并没有规定int类型的确切大小,c99只是规定了int至少是16位,没有规定具体值。但是目前大部分平台下int都是32位整数

  • 浮点数据

  • 浮点数据的存储:
    对于带小数点的数据,这类数据在计算机中以浮点(小数点是浮动的)格式进行存储,存储浮点数时,分为符号位、阶码和尾数三部分。
    例如浮点数-13.625,符号位为1,整数部分和小数部分转化为二进制,得到 ( 1101.101 ) 2 (1101.101)_2 (1101.101)2,所以需要将小数点左移3位(使整体变为一个刚比1大的数据),故阶码是 ( 11 ) 2 (11)_2 (11)2,尾数为 ( 1.101101 ) 2 (1.101101)_2 (1.101101)2,以IEE754标准的double格式存储,占8个字节,内容如下:

    号位(1位)阶码(11位)尾数(52位)
    00000000001100000000000000000000000000000000000000000000001101101

阶码所占的位数决定了浮点类型的表示范围,尾数所占的位数决定了浮点类型的有效位数,在这种存储格式中,尾数部分小数点前的1被省略,不占存储位(因为毕竟已经明确知道尾数小数点前一定是1)。

  • c中提供了三种浮点类型,以适合不同需要:
    float:单精度浮点类型
    double:双精度浮点类型
    long double:扩展精度浮点类型
    c标准中没有明确规定以上三种类型的精度到底是多少,不同的计算机可以用不同的方法存储浮点数,大多是计算机遵循IEEE754标准,该标准用4个字节表示float类型,8个字节表示double类型,long double类型随机器不同而不同,常见的是80位和128位,在vc++ 6.0中,占64位,8个字节。
  • 浮点数精度问题:和整数类型不同的是,c中的浮点类型是一种近似表示,比如十进制的1.2,其二进制形式为1.001100110011…,这是一个无限循环小数,所以计算机无法精确表示,因为任何类型数据都有固定长度,所以这个小数只能截断保存部分,所以浮点类型是不精确的数据类型。
  • 所以,不能用关系运算符“==”来判断两个浮点数是否相等,例如判断两个浮点数a,b是否相等可采用fabs(a-b) < 1e-10的形式

  • c语言中的字符数据
    • c中的字符型数据,实际存储的是字符的ascii码
    • 字符型数据像整数类型一样也分为有符号型和无符号型,c标准中没有说明char默认的类型是无符号型还是有符号型。有的编译器默认char为有符号型,有的编译器默认char为无符号型。visual c++默认char有符号型。通过有符号字符型取值范围为-128~127,而无符号型字符的取值范围为0~255,所以要注意避免字符类型溢出的错误。
    • 字符常量:字符常量是用单引号括起来的单个普通字符或转义字符:‘a’, ‘B’, ‘3’, ‘$’, ‘\0’, ‘\n’
    • 字符型数据:当今最常用的是ASCII字符集,它用7个二进制位表示128个字符,字符型数据在内存中一般占1个字节,字节最高位为0.
    • 字符变量:字符变量定义形式为:char 变量名;例如char ch;可以将一个字符型常量存入字符变量,也可以将-128至127之间的整数存入字符变量,例如ch=65;既可以将该变量的内容解释为字符,也可以将改变量的内容解释为字符。当算术运算中出现char类型时,编译器会对该值进行类型提升,转换为int型,然后再进行计算,如:‘a’+1的值为98。
    • c语言是把字符当成整数来处理的。
    • 输入输出:读入char型数据,可以采用:scanf("%c", &ch);也可以采用ch = getchar();输出char类型的数据,可以采用printf("%c", ch);也可以采用putchar(ch);
    • getchar()和putchar()函数是c标准库函数中专门用于字符输入和输出的函数,getchar()的功能是从系统隐含指定的输入设备(即终端键盘)输入一个字符,函数返回输入的字符,对于字符型数据而言,所有的输入都是有效输入,包括空格和回车等空白字符,所以在每次读入字符时,要提前对输入流中的换行符和空格等空白符进行处理。
    • 可以使用赋值抑制符“ * ”跳过读入的字符,char ch; scanf("%*c%c", &ch);
    • 处理字符的函数:
函数功能
tolower()转换为小写字母
toupper()转换为大写字母
islower()判断是否是小写字母
isupper()判断是否是大写字母
isalpha()判断是否是大写字母
isdigit()判断是否是十进制数字(‘0’ - ‘9’)
isxdigit()判断是否是十六进制数字(‘0’ - ‘9’, ‘a’ - ‘f’, ‘A’ - ‘F’)
isblank()判断是否是空白字符(空格,‘\t’)

  • 极限常量
  • 头文件<limits.h>定义了每种类型极限值的符号常量,如下表
    类型下限上限
    charCHAR_MINCHAR_MAX
    intINT_MININT_MAX
    longLONG_MINLONG_MAX
    floatFLT_MINFLT_MAX
    doubleDBL_MINDBL_MAX
  • 无符号整数类型的下限都是0,所以没有特定的符号,无符号类型的上限符号常量是对应带符号类型上限常量名前加“U",分别为UCHAR_MAX、UINT_MAX、ULONG_MAX

4. 关于运算符

  • 模运算的符号与第一个操作数相同

  • 执行算术运算,通常需要要求操作数具有相同的数据类型(相同的字节数和相同的存储方式)。计算机可以直接将两个32位整数相加,但是无法将一个16位整数与一个32位整数直接相加,也无法将一个32位整数和一个32位浮点数直接相加。不过c语言允许在表达式中混合使用基本数据类型,在这种情况下,编译器会对某些操作数进行类型转换,以便硬件可以对表达式进行计算。

  • 类型转换:一共有三种方式

    • 第一种是算数转换:当算数运算、关系运算或逻辑运算的两个操作数不同,精度低的向精度高的自动转换,也就是类型提升。char->int->float->double
    • 赋值时类型转换:编译器在对此种情况下会遵循:将赋值运算符右侧表达式的值转换为赋值运算符左侧变量的类型。赋值时右侧数据类型高于左侧,将丢失部分数据,造成精度降低,或发生数据溢出,导致结果错误。如:double a = 1; //把1.0赋给a 而int i = 1.5 //把1.5转为整型1,赋给i
    • 强制类型转换:这种形式的类型转化并不会改变操作数本身,例如:double a = 2.5; int n; n = (int) a; 此时n的值为2,而a的值仍为2.5。
  • 关于逻辑运算:逻辑表达式的值即逻辑运算的结果只有真和假两个值,C语言本身是没有提供布尔类型的,运算结果为真用int数值类型的1来表示,运算结果为假用0表示。但是在需要判断一个数值表达式真假的时候,由于任意一个数值表达式的值不局限于0和1,因此c是根据表达式的值为非0还是0来判断真假的。如果表达式的值非0则为真,否则为假。例如:!2为0即假。

  • c语言中的逻辑运算符都是短路运算符,一旦能确定整个表达式的值,就不再继续计算。

  • 对于浮点运算时可能存在误差的,所以在对浮点数进行比较时,要考虑浮点误差。

  • 浮点数对应的四舍五入的实现:通过floor(x+0.05)实现,可以想象成在数轴上把一个单位区间往左移动0.5个单位的距离,floor(x)等于1的区间为[1, 2),而floor(x+0.5)等于1的区间为[0.5, 1.5).

  • 运算符优先级与结合性总表:https://blog.csdn/qq_38048756/article/details/115729561

  • >>右移运算符,右移n位表示除以 2 n 2^n 2n,<<左移运算符,左移n位表示乘 2 n 2^n 2n,更详细的参考https://blog.csdn/qq_15037231/article/details/76999926

  • i++与++i的区别:
    二者都会给i加1,但是当他们用在一个表达式当中时,二者是不同的,i++会使用加1前的值计算表达式,而++i会使用加1后的值计算表达式。
    例子:a = 8; b = a++;结果:b = 8; a = 9;
    a = 8; c = ++a;结果:c = 9; a = 9;
    所以++ i 是先加后赋值(比较);i ++ 是先赋值(比较)后加

  • 逗号表达式的求解过程是:先求解表达式1,再求解表达式2。整个逗号表达式的值是表达式2的值。
    例子: a = 3 ∗ 5 , a ∗ 4 a=3 * 5,a*4 a=35a4; 先计算a = 3 * 5然后再计算a*4 = 60,该逗号表达式的结果就是60(a仍为15).

  • !运算符是一个逻辑运算符,用来反转一个条件的值,该运算符只需要一个操作数,若操作数为真,则该逻辑表达式为假,该操作数为假,则该逻辑表达式为真。

5. 分支


  • 嵌套的if语句实现多分支结构:
if (表达式1)  语句1
else if (表达式2)  语句2
...
else if (表达式n)  语句n
else  语句n+1
多分支if语句的执行流程:
(1)依次计算并判断表达式1~n,若成立则执行后面的语句
(2)若所有的if条件都不成立,执行语句n+1
(3)无论执行完哪个语句分支,都不会执行其他分支

  • switch多分支
  • 当问题需要处理的分支情况较多时(一般大于3种),通常使用开关语句switch语句来代替条件语句简化程序的设计。开关语句就像多路开关一样,使程序控制流程形成多个分支,根据一个表达式的不同取值,选择其中一个或几个分支执行。
    switch语句的一般形式
switch(表达式)
{	case 常量表达式1: 语句序列1
	case 常量表达式2: 语句序列2
	...
	case 常量表达式n: 语句序列n
	default:语句序列n+1
}

(1)控制表达式。switch后边必须跟着由圆括号括起来的整型表达式,c把字符当成整数处理,所以switch语句中可以对字符进行判定,但是表达式不能用浮点数和字符串。
(2)分支标号。每个分支的开头都有一个标号,格式为case 常量表达式:常量表达式的值必须是整数或者字符
(3)语句序列。每个分支后边可以跟任意多条语句,而且不需要用花括号括起来,每组语句的最后一条通常是break语句。

  • switch语句执行的流程:首先计算switch后的表达式的值,然后将该值依次与case后的常量值进行比较,当它们相等时,执行相应case后面的代码段,case相当于一个语句标号,程序从此标号下开始向下执行,直到遇到break语句,则跳出switch语句,否则程序将会执行到switch语句结束
  • switch中要求不允许使用重复的分支标号,否则会编译出错,但是对分支的顺序是没有要求的,default分支不一定要放在最后。
  • switch语句要比if语句执行速度块,执行效率高,特别是在有许多情况需要判定的情况下。(因为switch语句在编译的时候,会有一个map进行映射,所以直接查表一步找到特定分支,而if多分支是按顺序比较,如满足条件,则执行对应的代码,否则跳转到下一个分支再进行比较。)此部分的解释具体可参考:https://wwwblogs/idorax/p/6275259.html

6.循环

  • 循环分为for循环、while循环和do_while循环。for循环适合指定循环次数的循环;while循环在循环体执行之前测试控制表达式;do循环在循环体执行之后测试控制表达式。
  • for(表达式1;表达式2;表达式3)
    其执行的过程为:
  • do while循环,循环体至少执行一次
  • 有些情况下,循环的准确次数无法事前预知,例如:每次循环都包含输入数据的语句,循环是否结束由输入决定,这种情况下可以采用while循环实现,如:
while(scanf("%lf", &number), number > 0)
//此处采用了逗号表达式读入数据和判断数据的合法性作为循环控制的表达式。
  • break和continue:break的作用是提前结束所在循环,continue的作用可以中断循环体的本次循环(即跳过循环体中尚未执行的语句),立即开始执行下一次循环。
  • goto语句:goto语句可以跳到函数中任何有标记的语句处,格式为:goto 语句标号;语句标号由一个标识符加一个冒号组成,它标识程序的一个特定位置,一般放在一段可执行语句的左边。

例如:求1-100的累加和。

sum = 0; i = 1;
loop: sum +=1;
i++;
if(i < 100)
	goto loop;
printf("%d", sum);
  • goto语句一般不建议使用,因为它会破坏程序的结构,程序可读性差,容易带来隐患。不过当需要从多重循环中退出,或从包含switch语句的循环中退出时,用goto语句比较合理

7.数组

  • 比较大的数组尽量声明在main函数外,否则程序可能无法运行
  • c语言数组不能够进行赋值操作,如果要从数组a复制k个元素到数组b,可以这样做memcpy(b, a, sizeof(int) * k)。当然如果数组a和b是浮点型的,复制时要写成memcpy(b, a, sizeof(double)*k)。如果需要把数组a全部复制到数组b中,可以通过memcpy(b, a, sizeof(a))实现。
  • memset(a, o, sizeof(a))可以将数组a清零,虽然也可以使用循环完成,但是memset方便又快捷。
  • 关于memcpy与memset函数https://blog.csdn/qq_38048756/article/details/115831128
  • c中的字符串用字符数组来表示
    • char s[20]; scanf("%s", s); 其中scanf("%s", s)用来读入一个不含空格、tab和回车字符的字符串,存入字符数组s中,注意s前没有&。
    • strchr()函数用来在一个字符串中查找单个字符,例如 strchr(s, 'a');表示在在字符数组s中寻找字符‘a’,若找到返回该位置,否则返回0

8.模块化程序设计

  • 从软件工程的视点,关于函数有以下几点说明:
    (1)一个函数的规模一般不要超过一页,最好控制在半页内容,小规模的函数可以提高程序的可用性
    (2)一个程序最好设计成一些小函数的组合,这样易于编写、排错、维护和修改
    (3)如果一个函数形参列表中的参数数目很大,则说明这个函数功能过多,可以考虑将其分解为若干具有单一功能的小函数,函数头的长足最好控制在一行之内
    (4)函数原型、函数头和函数调用语句三者应该在形参和实参的输了、类型和顺序以及返回值的类型上严格保持一致。

  • 关于函数原型声明的问题:
    • 函数声明是为了使编译器可以先对函数进行概要浏览,而函数的完整定义以后再给出,这种声明成为函数原型。
    • 一般而言,如果函数定义在函数调用之前,是不需要函数原型声明的,那么为什么还会有函数原型声明这一说法:这是因为对于大型程序而言,一个程序中可能包含多个函数,而某些函数之间可能是相互调用的,当把所有的函数定义放在main之前,这时候需要去斟酌它们的顺序,防止出现调用未定义的函数,而如果两个函数相互调用(这种形式称之为间接递归),这时候无论先定义哪个函数,都会导致未定义的函数。而且当程序达到一定规模的时候,在一个文件中放置所有的函数是不可行的,所以这时,就需要函数原型告诉编译器在其他文件中定义的函数。
    • 因此,建议在程序中包含所有被调用的函数的函数原型,这样可利用c语言的类型检查功能。
    • 函数原型一般放在程序的开头处,这样会对程序中出现在它后面的所有对这个函数的调用语句起作用。而在一个函数内部的函数原型,将只对在该函数中出现的调用语句起作用,这样做的好处是满足"最小权限原则"。

  • 关与函数调用与内存管理的内容:https://blog.csdn/qq_38048756/article/details/110475125
  • 按值传递机制:函数定义时的参数成为形式参数(简称形参),函数调用时的参数称为实际参数(简称实参),在程序运行中遇到函数调用时,将实参的值依次传送给形参,这就是参数传递,这种参数传递是单向的,程序会为实参创建一个副本,作为被调用函数的形参,实参和形参各有不同的存储单元。被调函数中对这个副本的修改不会影响到主调函数中实参变量的原始值,这种传递方式被称为按值传递机制。

  • 变量的存储类型:
    • 变量除了数据类型,还有其他属性:存储类型、作用域和链接等。
    • 变量的存储类型决定了它的存储周期、作用域和链接。变量的存储周期是指变量存在于内存的时间。有些变量存在时间短、有的变量反复被创建、收回,有些则在程序的整个运行期间都驻留在内存中。
    • 变量的作用域是指变量在程序能够被访问到的区域
    • 变量的链接属性是针对多个源文件组成的程序而言的,旨在说明这个变量能否被其他源文件访问
    • 变量的存储类型决定了为变量分配内存和释放内存的时间,变量的存储类型共有4种:auto、static、extern和register
    • auto存储类型:
      • auto存储类型的变量只在所在块有效,在所在块被执行时获得内存单元,并在块终止时释放内存单元。例如:auto int n;这句代码表明n是具有自动存储周期的局部变量,auto存储类型几乎从来不用明确表明,因为函数的局部变量(函数体内声明的变量和形参列表中的变量)都默认为auto存储类型。
      • 自动存储是一种节约内存的手段,符合“最小权限原则”,因为auto变量只在需要它们的时候才占用内存。
    • static存储类型:
      • 具有静态存储周期的变量所占用的存储单元是从程序运行的开始时刻分配和初始化的,并且只分配和初始化一次。
      • 可以将局部变量声明为static存储类型,称为静态局部变量,在整个程序执行期间拥有最久的存储单元,都会保留变量的值。
      • 外部变量具有静态存储周期,将一个变量声明为外部变量的方法是将其声明在所有函数体之外,外部变量在整个程序运行期间始终存在,但只可以被位于其声明语句之后的函数访问。
    • extern存储类型
      - extern存储类型可以使几个源文件共享一个变量,提醒编译器需要访问定义在别处的变量,可能是同一文件稍后的位置,也可能在另一个文件中。
      - 注意:extern不是变量定义,只是声明一个定义在别处的变量,例如:
#include<stdio.h>
extern int x;
int main()
{
     printf("%d", x);
}

int x = 10;

  • register存储类型
    • 当程序被执行时,数据通常存储在内存,当需要计算或其他处理时才装入CPU的寄存器中。而声明变量为register类型就要求编译器把变量存储在寄存器中。
      • 寄存器是驻留在CPU中的存储单元,具有比内存更高的存取速度。
      • 一般将诸如循环变量和累加和变量等这类需要频繁访问的变量声明为寄存器类型。例如:register int sum;
      • 如今,register声明常常是多余的,今天的编译器已经具有很好的优化能力,编译器会找出被频繁访问的变量,并将其驻留在寄存器中,而无需register声明。

标准库函数

  • <math.h>

    • abs(x): 求整数x绝对值
    • fabs(x): 求实数x绝对值,返回double
    • pow(x, y): 求 x y x^y xy,x,y与返回值都为double
    • exp(x): 求 e x e^x ex,x与返回值均为double
    • log(x): 求自然对数ln(x),x与返回值均为double
    • log10(x): 求以10为底的对数,x与返回值均为double
    • 三角函数sin(x),cos(x),tan(x),x与返回值均为double
    • fmax(a, b): 比较a与b的大小,返回较大的那个,a,b与返回值均为double
    • fmin(a, b): 比较a与b的大小,返回较小的那个,a,b与返回值均为double
  • <string.h>

    • memset与memcpy函数:https://blog.csdn/qq_38048756/article/details/115831128
  • <time.h>

    • clock()计时操作:https://blog.csdn/qq_38048756/article/details/102613004

更多推荐

c语言知识点总结(常更)建议收藏