C语言学习笔记
目录
第一章
复习题:
源代码包含了程序员所编写的使用任何编程语言编写的代码。
目标文件包含了机器语言代码
可执行程序是完成的机器语言代码
定义程序目标-->设计程序-->编写代码-->编译-->运行程序-->测试和调试程序-->维护和修改程序
编译器的任务:把源代码转化成等价的机器语言。
链接器的任务:把编译好的目标代码和库代码以及启动代码链接起来,生成可执行文件
5.:练习题(code_0):
#include <stdio.h>
int main()
{
double a;
printf("Please input a value whose unit is inch: ");
scanf_s("%lf",&a);
getchar();
a = a*2.54;
printf("\n%.3f",a);
getchar();
}
第二章
C代码的构成:
C_Source_Code = {预处理指令(如:#include)–>
int main():{语句(函数的内容)}–>
function a():{语句}–> function b():{语句}–>… }
语句 = {标号语句,复合语句,表达式语句,选择语句,迭代语句,跳转语句}
数据类型:见下一章节
代码分析:以该段代码例
a = a*2.54
是一个赋值语句,其中左边的a称为左值(lvalue),右边的值称为右值(rvalue).=
右边用于计算并读取值,然后在把这个值传递给=
左边的变量,作为其新的值。
printf()
中实参数为"\n%.3f"
,其中\n
为换行符, %3.f
中%用于提醒程序此处用于打印一个变量,3.f
意思是打印一个保留小数点后三位的浮点数。此处效果类似于python中的
a = 2.3434343
print(f"\n{'{:.4}'.format(a)}") # outputs: 2.343
并且printf();
是一个表达式语句。
C的关键字
auto | extern | short | while |
---|---|---|---|
break | float | signed | _Alignas |
case | for | sizeof | _Alignof |
char | goto | static | _Atomic |
const | if | struct | _Bool |
continue | inline | switch | _Complex |
default | int | typedef | _Generic |
do | long | union | _Imaginary |
double | register | unsigned | _Noreturn |
else | restrict | void | _Static_assert |
enum | return | volatile | _Thread_local |
关键字不可以被用作变量名或者函数命,比如你定义了一个函数叫做printf()那么它就会在编译的时候覆盖原本的printf()的作用。
eg:
#include "stdio.h"
void scanf(int num);
int main()
{
int a = 1;
scanf(a);
return 0;
}
void scanf(int num)
{
printf("The number a is : %d",num);
}
outputs:error: conflicting types for 'scanf'
scanf()
函数会读取缓冲区(buffer)中的数据,如果遇到空白符,那就跳过当前的,并进入下一个变量。注意你在输入结束的时候数据流结尾会带上一个\n
,这就导致你在循环中反复使用scanf时会出错。
e.g.
#include<stdio.h>
#include <stdlib.h>
int main(void)
{
char a,b;
while(scanf("%c %c",&a,&b) != EOF)
{
printf("a = ");
putchar(a);
printf(" b = ");
putchar(b);
}
return 0;
}
/*
a 输入
b 输入
a = a b = ba 此处输入了一个a
a =
b = a
*/
先输入a再输入b,结尾此时缓冲区里面有a,b,\n。之后输出前两个a,b。之后再输入a,缓冲区里面\n后面接了一个a,此时按下enter,那么缓冲区中a后面再多一个\n。那么scanf把\n传到a的地址中,'a’传到b的地址中。从而有了
a =
b = a
这样的输出。(以上默认你的输入和它要求的格式匹配)。
本章复习题:
-
C语言的基本模块是函数
第三章
浮点数: 浮点数在C中的储存方式为
符号(+/-) | exponent(指数):x | 尾数:.xxxxxx |
---|
如3.1415被记录为 + . 31415 ∗ ( 1 0 1 ) + .31415*(10^1) +.31415∗(101)。
int类型: 范围 − 2 31 ∼ 2 31 − 1 -2^{31}{\sim}2^{31}-1 −231∼231−1,占4个字节32bit,
打印 unsigned int
类型的值的时候要使用%u
,若是usigned long int
用%lu
,usigned long long int
用%llu
.
%x
是打印为对应的16进制整数,%o
是打印为对应的8进制整数。
#include <stdio.h>
int main(void)
{
unsigned int un = 3000000000;
short end = 200;
long big = 65537;
long long verybig = 12345678908642;
printf("un = %u and not %d\n",un,un);
printf("end = %hd and %d\n",end,end);
printf("big = %ld not %hd\n",big,big);
printf("verybig = %lld not %d\n",verybig,verybig);
return 0;
}
//结果 编译器版本为clang 13.0 系统:macos 12.2
un = 3000000000 and not -1294967296
end = 200 and 200
big = 65537 not 1
verybig = 12345678908642 not 1942899938
h
修饰符是把一个整型阶段short类型. big = 65537 = 00000000 00000001 00000000 00000001(二进制)被截断为short类型后按照00000001打印出来,所以结果为1,同理其他的。
char类型
注意,如果是单个字符
char a;
那么有
a = 't' ;//True
a = "t" ;//False,because character is not string,the \"",means a string
a = t ;//False,because t is a variable here
char 变量实际上是被储存为一个整数,其值为字符对应的ASCII码。
i.e.
char a = 65;
printf("%c",a); //outputs: A
转义字符
\a,\f,\n,\r,\t,\v,\\,\',\",\?,\0oo,\xhh
用宏定义进行整数格式化输出
#include <stdio.h>
#include <inttypes.h>
int main()
{
int32_t me32; //me32 is a 32-bit signed integer variable
me32 = 45933945;
printf("me32 = %" PRId32 "\n",me32); // PRID32 是一个宏定义,会被替换为inttypes中定义的d,这样的好处是一定是按照32位来输出的,不会因为计算机位数改变而改变
printf("me32 = %d\n",me32);//outputs:45933945 上面一行输出也一样,测试环境同上
}
float、double and long double
float可以保证至少6位的有效数字,注意是保留上面***浮点数***一节中的小数中间的前6位数字如0.123456789,只保证123456的精度。
对于233.3434343也是只保证233.343这几个的精度。
double至少可以保证10位有效数字,为64位的变量(8字节)。
浮点数的声明方法:
double a,b,c,d,e;
a = 3232.322; //赋值方法一
b = 2.32E+12;//赋值方法二
c = 2.99e-3;//赋值方法(表示方法)三
d = .2; //d = 0.200000
e = 100. // outputs: 100.000000
//Remark: c = 2.99 e-3 是错误的,和E(e)之前不可以有空格
Remark: 浮点数常量默认被编译器认为是double类型,在浮点数常量后添加后缀f或者F可以使其被识别为float类型,同理加上后缀l或L,会被识别为long double
e.g.
#include <stdio.h>
int main(void)
{
float a = 6.f; //6f不可以,因为6被识别为int类型常量,f后缀对int类型常量无效,同理,L.
long double b = 0.323l;
printf("%f %Lf\n",a,b); //outputs: 6.000000 0.323000
printf("%d",a); // 0,这里并不是将小数点部分以后截断,而是按照转换int的内存信息的方式转换这块记有float a的内存的信息,然后输出,所以结果为0
return 0;
}
浮点数格式化输出方法: %f
,%a
,%e
e.g.
double c;
c = 5.32e-5;
printf("%f\n",a);
printf("%a\n",a);
printf("%e\n",a);
// outputs:
0.000053
0x1.be46214c80e21p-15
5.320000e-05
%a
是把浮点数以16进制输出,后面的p-15意思是
0
x
1.
b
e
46214
c
80
e
21
∗
2
−
15
0x1.be46214c80e21*2^{-15}
0x1.be46214c80e21∗2−15
浮点数的范围
#include <stdio.h>
#include <float.h>
int main(void)
{
printf("%e %e\n",FLT_MAX,FLT_MIN);
printf("%e %e",DBL_MAX,DBL_MIN);
return 0;
}
/*outputs:
3.402823e+38 1.175494e-38
1.797693e+308 2.225074e-308
*/
其中FLT_MAX
,FLT_MIN
,DBL_MAX
,DBL_MIN
为头文件float.h中的宏定义,其分别代表了float类型的最大值和最小值,double类型的最大(小)值。
所以浮点数是有范围的,如果浮点数的值超过了它的最大值,那么那么称之为上溢(overflow),在C中,该数会被赋值为inf
#include <stdio.h>
#include <float.h>
int main(void)
{
printf("%Le",LDBL_MAX+1.00e+308);
return 0;
}
//outputs: inf
假设a是一个float所能够表示的最小的float变量,然后对a进行一次运算使其指数指数进一步减小,此时a的有效位数会下降,既a损失了最高精度,称这个为下溢(underflow),如a = 0.1234E-10; a/10;outputs:a = 0.0123E-10(这只是一个例子,不适用于实际上的C).其中损失了最后一位的4,因为此时已经到达了浮点数的指数所能显示的最小值-10(我们假定是-10,实际上不是,而且情况会更加复杂),那么为了实现a/10的效果只能改变E前面0.1234变更为0.0123(向右移动一位),此时精度发生损失。
下面是取a =1.175494e-38(float能准确描述的最小值)的时候,a的精度是如何损失的
#include <stdio.h>
int main(void)
{
float a = (float)1.175494E-38;
printf("%e",a);
for(int ix = 1;ix <10;ix++)
{
a = a/10;
printf("\n%e",a);
}
return 0;
}
outputs: | 损失的精度位数
1.175494e-38 | 0
1.175493e-39 | 1
1.175493e-40 | 1
1.175549e-41 | 3
1.175689e-42 | 3
1.177091e-43 | 4
1.121039e-44 | 6
1.401298e-45 | 7
0.000000e+00 | all
0.000000e+00 | all
其它浮点数计算的误差
#include <stdio.h>
int main(void)
{
float a ,b;
double c;
a = 2.0E20f;
c = 2.0E20;
b = (2.0E20f) +1.0f;
printf("%f\n",c);
printf("%f\n",a);
a = b - (float)2.0E20;
printf("%.26f",a);
return 0;
}
outputs:
200000000000000000000.000000 c作为double变量就没有问题,因为c的精度更高
200000004008175468544.000000 这一步有误差
0.00000000000000000000000000 这一步没有误差,与C Primer Plus上说的不一致,可能是编译器版本比较新的关系
复数
#include <stdio.h>
#include "complex.h"
int main(void)
{
complex float a;
a = 1.0+2.0I;
printf("%f+%fi", crealf(a), cimagf(a));
return 0;
}
在complex中用I(大写的 i 表示
−
1
\sqrt{-1}
−1),用i也可以,但是a = 1.0 + 2.0 * i
不可以这样i会被编译器认为是变量,不再是
−
1
\sqrt{-1}
−1.
布尔值
_Bool
类型,在C中用1表示true,0 表示false. 所以_Bool其实也是整型。
小结
C中一共有11种关键字:
int | long | short | unsigned | char | float | double | signed | _Bool | _Complex | _Imaginary |
---|
对于同一个整型,unsigned的类型所能够表示的范围(在正整数域上,要大于signed类型),因为第一位不再用于表示正负,也用来表示大小了。
long double的精度不一定高于double,得看你的电脑是几位的,但是最高不超过8字节。
可以使用sizeof()
函数查看当前变量类型占用几个字节,或者查看limits.h 和float.h中有相关的类型限制的信息。
复数和虚数的声明方法(不引入complex.h)的情况下
float _Complex
double _Complex
long double _Complex
float _Imaginary
double _Imaginary
long double _Imaginary
小技巧:
在命名变量名的时候前面加上一些表示比如unsigned short变量有前缀表示它是us_ 可以帮助自己通过变量命了解这个变量的类型,提高代码可读性。
字符串的格式化输入/输出
C存储字符串的方式之一是使用字符数组char a[]
注意,每一个字符占据一个比特,且在这个字符数组中,其末尾一定是\0
,\0
是一个非打印字符,C在打印到\0时会停止打印
#include<stdio.h>
int main(void)
{
char a[] = {'a','b','b','\0','v','d'};
printf("%s",a);
return 0;
}
/* outputs:
abb
*/
可以从示例中看到\0后面的字符不再被打印。
并且scanf在读取字符串的时候读到空格‘ ’,就会停止。
#include<stdio.h>
int main(void)
{
char a[20];
scanf("%s",a);
printf("%s",a);
return 0;
}
/*
input: hoiwdh nwdiw
output: hoiwdh
*/
C在limits.h 和floats.h中有一些符号常量,用于告诉用户,该整型和浮点数的大小限制(位数,字节数等)。详情见C Primer Plus 67页
目前先更新到这里,润了😇
·