C语言学习笔记(基于C语言程序设计:现代方法(第二版))8.6.22版

C语言学习记录

stdio.h:它“包含”了C语言标准输入/输出库的相关信息,我们以#include <stdio.h>将其导入至程序之中

C语言的执行步骤

1.预处理。首先程序会被送交给预处理器(preprocessor)。预处理器执行以#开头的命令(通常称为指令)。预处理器有点类似于编辑器,它可以给程序添加内容,也可以对程序进行修改。

2.编译。修改后的程序现在可以进入编译器(compiler)了。编译器会把程序翻译成机器指令(即目标代码)。然而,这样的程序还是不可以运行的。

3.链接。在最后一个步骤中,链接器(linker)把由编译器产生的目标代码和所需的其他附加代码整合在一起,这样才最终产生了完全可执行的程序。这些附加代码包括程序中用到的库函数(如printf函数)。

编译C语言程序

cc -o name target.c #使用该语句在UNIX系统中编译target.c,其输出的名称则为name,其中 -o name 可以被省略
gcc -o name target.c #使用gcc编译target.c,name

C程序的基本格式

指令
int main(void) #此处的void表示该函数不存在参数,如果是int改为void则表示该函数不返回值
{
    语句
}

指令:在编译C程序之前,预处理器会首先对其进行编辑。我们把预处理器执行的命令称为指令

其中#incude就是一个指令意为将指定的信息(如<stdio.h>)在编译之前包含在程序之中,而如<stdio.h>这样的就称之为头(header),每个头都包含一些标准库的内容。所有指令都是以字符#开始的。这个字符可以把C程序中的指令和其他代码区分开来。指令默认只占一行,每条指令的结尾没有分号或其他特殊标记

函数:它们是用来构建程序的构建块

函数分为两大类:一类是程序员编写的函数,另一类则是作为C语言实现的一部分提供的函数。我们把后者称为库函数(library function),因为它们属于一个由编译器提供的函数“库”。。在C语言中,函数仅仅是一系列组合在一起并且赋予了名字的语句。某些函数计算数值,某些函数不这么做。计算数值的函数用return语句来指定所“返回”的值。

虽然一个C程序可以包含多个函数,但只有main函数是必须有的。main函数是非常特殊的:在执行程序时系统会自动调用main函数

注: main函数的名字是至关重要的,绝对不能改写成begin或者start,甚至写成MAIN也不行。

注: 当我们把一个包含小数点的常量赋值给float型变量时,最好在该常量后面加一个字母f(代表float)

占位符

%d 打印整形十进制数据

%c 打印字符格式的数据

%f 打印浮点数字(打印小数),我们可以使用%.nf 的格式使其保留n为小数

%p 以地址的形式打印

%x 打印十六进制数字

%s 打印字符串

%fl 打印长整型

%e 打印科学计数法

%g 打印小数或者科学计数法 (且不输出毫无意义的零)

%ld 打印长整型

%u 打印十进制数输出unsigne型数据(无符号数)

%o 打印八进制数字整数

初始化式(initializer)

当我们声明了一个变量后可以对其进行赋值或者在声明时直接赋值,那么这个被赋予的值便称之为一个初始化式:

int height=8; //数值8即为一个初始化式

我们也可以这样声明多个同类型的变量:

int height=8,length=12,width=10;

此时8,12,10都是各自变量的一个初始化式,所以这几个变量的初始化式都是独立的。也就意味着

int height,length,width=10;

此时仅有width被初始化了,而其他两个变量却未被赋值。

scanf(‘%d’,&x):获取用户输入的值将其赋给x并将x的值赋给前面字符串所对应的%d,这里可以含有多个值,但是它们需要按照先后顺序一一对应

定义常量

在C语言中我们需要使用#define命令来定义一个常量,这也就意味着它并非是定义在方法之中的,而是与#include <stdio.h>是类似的。

当我们使用#define命令定义一个常量时,可以采用称为宏定义(macro definition)的特性给常量命名:

#define FINAL_VALUE value

注意此时我们的value前不可以有’=‘号,否则在调用该常量时这个常量值前面将同样包含一个’=',初次之外若我们希望该常量是一个表达式,则这个值应由一个括号包含,如:

#define FINAL_VALUE 3+2
int main()
{
 printf(FINAL_VALUE); /*实际运算为3+2*3+2=11*/
 return 0;
}

注1: 宏的名字只用了大写字母。这是大多数C程序员遵循的规范,但并不是C语言本身的要求。

注2: 在C语言之中如果两个整数相除,那么C语言会对结果向下取整。所以如2/3=0,为了解决这个问题我们可以使用2.0f/3.0f代替

注3: 在C语言中,标识符可以含有字母、数字和下划线,但是必须以字母或者下划线开头。

注3: 我们的函数语句可以都写在一行,但是不包括预处理指令,因为预处理指令要求独立成行

格式化输入和输出

printf函数

printf函数被设计用来显示格式串(format string)的内容,并且在该串中的指定位置插入可能的值。调用printf函数时必须提供格式串,格式串后面的参数是需要在显示时插入到该串中的值:

printf(格式串,表达式1,表达式2,...);

显示的值可以是常量、变量或者更加复杂的表达式。调用printf函数一次可以打印的值的个数没有限制。

格式串包含普通字符和转换说明(conversion specification),其中转换说明以字符%开头。转换说明是用来表示打印过程中待填充的值的占位符。跟随在字符%后边的信息指定了把数值从内部形式(二进制)转换成打印形式(字符)的方法,这也就是“转换说明”这一术语的由来。

案例:

int i,j;
float x,y;
i=10;
j=20;
x=43,2892f;
y=5527.0f;
printf("i=%d,j=%d,x=%f,y=%f\n",i,j,x,y);
/*输出结果为:i=10,j=20,x=43.289200,y=5527.000000*/

格式串中的普通字符被简单复制给输出行,而变量i、j、x和y的值则依次替换了4个转换说明。

然而语言编译器不会检测格式串中转换说明的数量是否和输出项的数量相匹配,所以表达式和转换说明之间应当保持一致,否则将会编译失败:

printf("%d %d\n",i); /*转换说明大于输出项*/
printf("%d\n",i,j); /*输出项大于转换说明*/
/*两行均会编译失败*/

此外,C语言编译器也不检测转换说明是否适合要显示项的数据类型。如果程序员使用不正确的转换说明,程序将会简单地产生无意义的输出。

当我们显示格式化一个数字时,基本格式为:“%a.bt”:

其中a代表的时数字的位数,如果是3,则数字为一位数到两位数时,数字将会右对齐,如果此时加上一个符号,则可以左对齐。如果位数大于等于三位时则自动增加位数,并不会出现精度丢失的情况。此称之为最小字段宽度。而且a可以被省略。

b代表的是小数点后精确的位数,如果是2则会精确两位,以此类推,注意当该数字被省略时,小数点也应该被省略。如果这里的类型是一个整数型,那么此时的a表示的仍是位数,但是b此时表示显示的位数,位数不足以0填充,如%5.5d即表示,总位数为五,位数需要显示五位,此时位数不足以0填充,因此显示"40‘时结果为"00040"

t代表的是数字的类型,如果是f,则是浮点数。

scanf函数

就如同printf函数用特定的格式显示输出一样,scanf函数也根据特定的格式读取输入。像printf函数的格式串一样,scanf函数的格式串也可以包含普通字符和转换说明两部分。scanf函数转换说明的用法和printf函数转换说明的用法本质上是一样的。

int i,j;
float x,y;
scanf("%d%d%f%f",&i,%j,&x,&y);

假使输入的值为: 1 -20 .3 -4.0e3

此时1,-20,0.3,-4000.0分别被输入到i、j、x和y中

scanf的工作原理

像printf函数一样,scanf函数是由格式串控制的。调用时,scanf函数从左边开始处理字符串中的信息。对于格式串中的每一个转换说明,scanf函数从输入的数据中定位适当类型的项,并在必要时跳过空格。然后,scanf函数读入数据项,并且在遇到不可能属于此项的字符时停止。如果读入数据项成功,那么scanf函数会继续处理格式串的剩余部分;如果某一项不能成功读入,那么scanf函数将不再查看格式串的剩余部分(或者余下的输入数据)而立即返回。

:在寻找数的起始位置时,scanf函数会忽略空白字符(white-space character,包括空格符、水平和垂直制表符、换页符和换行符)

scanf会一值监听,直到它收集到指定数量的数字才会罢手,此外,根据它的识别识别方法,使用空格可以分开两个数字,如12 23它可以识别为12和23。当有符号时,也可以写作:12-23此时获得12和-23

:printf格式串经常以\n结尾,但是在scanf格式串末尾放置换行符通常是一个坏主意。对scanf函数来说,格式串中的换行符等价于空格,两者都会引发scanf函数提前进入到下一个非空白字符。例如,如果格式串是"%d\n",那么scanf函数将跳过空白字符,读取一个整数,然后跳到下一个非空白字符处。像这样的格式串可能会导致交互式程序一直“挂起”直到用户输入一个非空白字符为止。

:在printf中我们可以使用%i或者%d来表示整数,但是在scanf中,前者可以用一匹配十进制,十六进制和八进制的数,但是后者只可以匹配十进制的数。如果输入的数有前缀0(如056),那么%i会把它作为八进制数来处理;如果输入的数有前缀0x或0X(如0x56),那么%i会把它作为十六进制数来处理

:如果printf函数在格式串中遇到两个连续的字符%,那么它将显示出一个字符%

:水平制表符之间的距离通常是8个字符宽度,但C语言本身无法保证这一点

:格式串中的\g和\e都会将小数转为科学计数法的形式,如果所精确的小数位全部为0,那么\g会将小数位省略,如果小数位不全为0,那么\g会将小数位显示出来,而e将始终显示所有小数位,即使小数位全部为0

:当我们使用scanf时,我们有空格和无空格时有所区别的:

 scanf("%d /%d",&num1,&denom1);  //有空格
 scanf("%d/%d",&num1,&denom1);   //无空格
 /*此时前者当我们输入2 /3时,它可以正常接收到2/3,将其分别赋予num1和denom1;而后者当我们输入2/3时,它只能接收到2,将其赋予num1,而denom1被设置为一个随机值,而如果输入时2/3则两者时没有区别的。也就是说,当我们输入的数据间如果存在一个符号且可能存在空格,那么我们需要在格式串中预留一个空格*/

:scanf不可以像printf那样控制浮点数的精度,但是可以规定数字的位宽,包括了浮点数和整数:

scanf("%3d",&a); //只接收3位位宽的整数,s输入123456,将获得123
scanf("%4f",&a); /*只接收4位位宽的浮点数,其包含了小数点的四位位,但是如果整数部分位宽已经大于或等于设置位宽,则不会获取到小数点为,如1234.56,将获取浮点数1234.0,实际获取了1234,'.0'是浮点数自动追加的;而如果整数部分位宽小于设置位宽,则会获取到小数点及以后以达到设置位宽位置,如1.23456,将获取浮点数1.23,实际获取'1.23',共占据4个位宽*/

C语言的运算符

·运算符/可能产生意外的结果。当两个操作数都是整数时,运算符/会丢掉分数部分来“截取”结果。因此,1 / 2的结果是0而不是0.5。

· 运算符%要求操作数是整数。如果两个操作数中有一个不是整数,程序将无法编译通过

·把零用作/或%的右操作数会导致未定义的行为

·当两个数进行取余运算时,结果的符号以左翼的运算数的符号为其符号,如a%b,那么意味着如果a为正数,则结果为正数,反之亦然

·当运算符/和运算符%用于负操作数时,其结果难以确定。根据C89标准,如果两个操作数中有一个为负数,那么除法的结果既可以向上取整也可以向下取整。(例如,-9/7的结果既可以是-1也可以是-2。)在C89中,如果i或者j是负数,i%j的符号与具体实现有关。(例如,-9%7的值可能是-2或者5。)但是在C99中,除法的结果总是向零截取的(因此-9/7的结果是-1),i%j的值的符号与i的相同(因此-9%7的值是-2)。

术语 由实现定义:C标准故意对C语言的部分内容未加指定,并认为其细节可以由“实现”来具体定义。所谓实现是指程序在特定的平台上编译、链接和执行所需要的软件。因此,根据实现的不同,程序的行为可能会稍有差异。C89中运算符/和运算符%对负操作数的行为就是一个由实现定义行为的例子

算术运算符的相对优先级

最高优先级:+ -(一元运算符)

* / %

最低优先级:+ -(二元运算符)

作为通用规则,C语言允许在所有表达式中用圆括号进行分组。其起到的运算优先级的分组与Java一致:

i + j * k 等价于 i + ( j * k )

-i * -j 等价于 (-i) * (-j)

+i + j / k 等价于 (+i) + ( j / k )

**一元运算符:**表示操作数的正负

当表达式包含两个或更多个相同优先级的运算符时,仅有运算符优先级规则是不够用的。这种情况下,运算符的结合性(associativity)开始发挥作用。如果运算符是从左向右结合的,那么称这种运算符是左结合的(left associative)。二元算术运算符(即*、/、%、+和-)都是左结合的,所以

i - j - k 等价于 (i - j ) - k

i * j / k 等价于 (i * j ) / k

如果运算符是从右向左结合的,那么称这种运算符是右结合的(right associative)。一元算术运算符(+和-)都是右结合的,所以

- + i 等价于 -(+i)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值