工作需要,前同事很多程序是用C编写的,而自己之前只是会有Perl、Python、R,没有接触过C语言的编程,都说C是底层编程语言,虽然在编写上会麻烦,但是执行效率会甩高级语言好几条街,所以想借着这个机会,学习一下C语言的编程,虽然没时间学精学熟,但是至少以后如果有些对效率需求比较的数据处理,可以通过C语言进行简单的实现。
编译过程简介
和之前学习的Perl、Python之间最大的不同主要就是C语言在编写后执行前需要对代码编译,这也是C语言的一个被诟病的地方,尤其是在进行小规模的数据处理时,代码编写调试过程中,会在编译过程中浪费大量的时间,所以一般简单的小程序,可能用Perl、Python在实现上更简单一些。
看介绍,c语言在执行前,需要先编译然后进行连接,但是我在学习测试的时候,发现gcc会默认同步完成的,当然也可以通过-c参数分开进行执行。对于我们一些小程序来讲可能没有多少影响,但是在进行大型项目的时候,可能可以有效的帮助进行错误的判断。因为我是一步编译的,所以目前还不是很确定差异。
编辑的C语言程序必须是 .c 为后缀, 在测试中,发现如果缺少后缀的话,是不能直接通过gcc进行编译的。这个很奇怪,因为一直感觉在Linux系统中,文件后缀其实是个摆设,这个还需要后期又时间详细了解一下原因。
1 | gcc -o helloWorld helloWrold.c #多数情况下我们可以一步到位的编译+链接程序; |
链接成功后就会生成一个可执行程序
C语言的结构简介
第一个c语言程序代码如下:
1 | #include<stdio.h> |
其中第一行 #include<stdio.h>
#号不代表注释,而是一个预处理的标志,include相当于Python的import,perl的use,相当于通过include会导入一个头文件,头文件中定义了一些基本函数的说明。
后面main()中的内容则是每个程序的一个主程序,每个c程序,都有且只有一个main()函数,
C语言常用函数
1. 格式输出函数 printf
一般形式:printf(格式控制,输出表列)。例如:printf("%d,%d",a,b);
括号内包含两个部分:
- “格式控制”是用双撇号括起来的一个字符串,称“转换控制字符串”,简称“格式字符串”,它包括两个信息:
- 格式声明:格式声明由 % 和格式字符组成,如 %d (%d 代表输出整数,%f 代表输出实数),它的作用是将输出的数据转换为指定的格式然后输出。格式声明总是由 % 字符开始。
- 普通字符:普通字符即在需要输出时原样输出的字符。例如上例中的 printf(“Please enter a value:”);中的 Please enter a value: 即为原样输出。
(2)“输出表列”是程序需要输出的数据。看下面例子:
1 | printf("I love %d and %d",x,s); |
第一个 %d
对应的是x
的值,第二个 %d 对应的是 s 的值。 I love 和 and (注意这里包括空格)都是 普通字符会原样输出。 假如 x 的值是 3,s 的值是 4,这条语句将会输出“ I love 3 and 4 ”。
常用格式字符
1 | %d整型输出, |
2. 格式输入函数 scanf()
一般形式:scanf(格式控制,地址表列)。“格式控制”的含义同 printf 函数。“地址表列”是由若干地址组成的表列,可以是变量的地址。
看下面的例子:
1 | scanf("a=%d,b=%d",&a,&b); |
在格式字符串中除了有格式声明的 %d 以外,其它普通字符原样输出(如“ a= ”,“ b= ”和“,”),假如给 a 和 b 分别赋值 5 和 6 ,将显示“ a=5,b=6 ”。
注意:scanf()函数中的表列是地址表列。 scanf(“a=%d,b=%d”,&a,&b); 中 a 和 b 前面的 & 不能省掉,这一点要和 printf 作区分。**
printf() 函数和 scanf() 函数我们会在以后的“数据的输入与输出”版块继续讲述。
3. 输出函数putchar(),输出一个字符
一般形式:putchar(c); 功能:输出变量 c 所代表的一个字符; 说明:c 为字符型变量或整型变量。
4.输入一个字符
一般形式:getchar(); 功能:要求用户从终端(键盘)输入单个
ss注意:运行程序时,系统等待用户输入,注意回车也是一个合法字符。字符; 说明:返回值为从输入设备上得到的字符。
5. 注释
位于“ /…….. / ”中的和“ // ”后面的内容为注释,用来对程序进行说明;注释在编译时会被自动忽略。
这是一个简单的计算程序,通过定义变量让用户可以自由设定 a 和 b 的值,之后通过 c=a+b; 这条语句实现把 a 和 b 的和计算出来并赋值给 c 。究竟什么是变量,什么是常量呢?接下来我们来一一讲述。
常量
1.整型常量,如:整数
2.实型常量,如:实数。
小数还可以用指数形式表现,如32.23e3(表示 32.2310^3), -323.34e-6(表示 323.3410^-6), 由于计算机无法表示上角和下角,所以规定以字母 e 或者 E 代表以 10 为底的指数。
注意:e 或者 E 之前必须有数字,且 e 或者 E 后面必须为整数,不能是 12e4.1 或者 e3 这种形式。
字符常量
- 普通字符
用单撇号括起来的一个字符,如 ‘a’ 、’E’ 、’%’ 、’3’ 。不能写成 ‘ab’ 、’12’ 。字符常量只能是一个字符,不包括单撇号。
- 转义字符
除了以上形式的字符常量外,C 语言还允许用一种特殊形式的字符常量,就是以字符 \ 开头的字符序列,比如我们本节课的 3-1.c 中,\n 代表的就是换行符,显示跳转到下一行。这是一种在屏幕上无法显示的“控制字符”。
常用转义字符
字符 | 含义 |
---|---|
\n | 换行 |
\r | 回车(不换行) |
\t | 制表符 |
\f | 换页 |
\b | 退格 |
变量
变量代表一个有名字的、具有特殊属性的存储单元。它可以用来保存数据。变量的值是可以改变的。变量在程序中定义的一般形式就是: <类型名称> <变量名称>。例如:int a; int b; int a,b; int price;等。C 语言规定变量名只能由字母、数字和下划线构成,且第一个字符必须为字母或下划线。
数据类型
C语言包含的数据类型
数组
在C语言中不存在字符串类型的概念,字符串都是存储在一个字符数组中,同时数组中的每个字符以ASCII的形式存储在存储单元中。
int student[10]; # 定义一个包含10个元素的数组,从student[0]开始到student[9] ; 在数组定义时要指定元素个数。元素个数的定义不能包含变量。
char string[10] ; #定义一个字符串,可以省略数组维数(10);
数组常用函数
函数 | 说明 |
---|---|
puts(字符数组) | 其作用是讲一个字符串输出到终端,因此该函数用的不是很多,我们可以编写小程序来体验。 |
gets(字符数组) | 其作用是从终端输入一个字符串到字符数组,并且得到一个函数值。 |
strcat(字符数组1,字符数组2) | 把两个字符数组中的字符串连接起来,把字符串2接到1后面,结果放到字符串1中。注意字符串1必须足够长,不然长度会溢出。 |
strlen(字符数组) | 测量字符串长度的函数。函数的值为字符串中的实际长度,比占用长度小(没有计算字符串结尾的 “\0”) |
strcpy(字符串 1,字符串 2) | 作用是将字符串 2 复制到字符串 1 中。 |
strcmp(字符串1,字符串2) | 比较字符串1和字符串2 (从左向右逐个比较每个字符的ASCII码) |
strlwr 函数 | 转换为小写的函数 |
strupr 函数 | 转换为大写的函数 |
整数类型
基本类型(int):
编译系统分配给 int 类型数据 2 个字节或者 4 个字节(由具体的编译系统自行决定)。我们使用的 gcc 编译器为每个整数类型分配四个字节(32 个二进位)。在存储单元中的存储方式是:用整数的补码形式存放。所以当 4 个字节的整数类型取值范围是 -2^31~(2^31-1)。无符号的基本整型表示为 unsigned int,和 int 类型占有的字节数相同,取值范围是 0~(2^32-1)。短类型(short 类型):
短整型的类型名为 short,gcc 编译系统分配给 short 类型 2 个字节,存储方式和 int 类型一样,也是补码的形式存储,取值范围是 -2^15~(2^15-1),无符号短整型 unsigned short 的取值范围是 0~(2^16-1)。长整型(long 类型):
gcc 编译系统分配给 long 类型 8 个字节,存储方式和 int 类型一样,也是补码的形式存储,取值范围是 -2^63~(2^63-1),无符号长整型 unsigned long 的取值范围是 0~(2^64-1)。- 同类型占用的空间:在这里大家可以通过 sizeof() 运算符查看各类型的常量占据多少字节。
浮点型数据
单精度浮点数(float)
gcc编译器为每个浮点数分配4个字节(32个位),每个浮点数的存储结构分为3个部分,+or- | 小数部分 | 指数部分
但是小数部分和指数部分所分配的位数根据编译器进行确定。双精度浮点数(double)
为了扩大数据的范围,用8个字节存储一个double类型的数据,可以编程查看 double 极限值,符号的下限为:DBL_MIN,上限为 DBL_MAX。
指针
指针变量的类型说明
1.对指针变量的类型说明包括三个内容:
- 指针类型说明,即定义变量为一个指针变量;
- 指针变量名;
- 变量值(指针)所指向的变量的数据类型。
1 | int *p1;表示p1是一个指针变量,它的值是某个整型变量的地址。 |
应该注意的是,一个指针变量只能指向同类型的变量,如P3 只能指向浮点变量,不能时而指向一个浮点变量, 时而又指向一个字符变量。
2.指针变量的赋值
(1)指针变量初始化的方法
1 | int a; |
(2)赋值语句的方法
1 | int a; |
不允许把一个数赋予指针变量,故下面的赋值是错误的: int p;p=1000; 被赋值的指针变量前不能再加“”说明符,如写为*p=&a 也是错误的
3.指针变量的运算
指针变量只能进行赋值运算和部分算术运算及关系运算。
- 取地址运算符&
- 取内容运算符
用来表示指针变量所指的变量,在运算符之后跟的变量必须是指针变量。需要注意的是指针运算符和指针变量说明中的指针说明符 不是一回事。 - 指针变量的运算
- 赋值运算
指针变量的赋值运算有以下几种形式:- 指针变量初始化赋值,前面已作介绍。
- 把一个变量的地址赋予指向相同数据类型的指针变量。
- 把一个指针变量的值赋予指向相同类型变量的另一个指针变量。
- 把数组的首地址赋予指向数组的指针变量。
- 把字符串的首地址赋予指向字符类型的指针变量。
- 把函数的入口地址赋予指向函数的指针变量。
- 加减算数运算
指针变量的加减运算只能对数组指针变量进行, 对指向其它类型变量的指针变量作加减运算是毫无意义的。指针变量的加减运算,相当于以指针为原点,通过相对位置寻找数组中的其他元素。 - 两个指针变量之间的运算只有指向同一数组的两个指针变量之间才能进行运算, 否则运算毫无意义。
- 赋值运算
数组指针变量的说明和使用
1
2int a[5],*pa;
pa=a;pa,a,&a[0]均指向同一单元,它们是数组a的首地址,也是0 号元素a[0]的首地址。pa+1,a+1,&a[1]均指向1号元素a[1]。类推可知a+i,a+i,&a[i]指向i号
元素a[i]。应该说明的是pa是变量,而a,&a[i]都是常量。引入指针变量后,就可以用两种方法来访问数组元素了。
第一种方法为下标法,即用a[i]形式访问数组元素。
第二种方法为指针法,即采用*(pa+i)形式,用间接访问的方法来访问数组元素。
字符型数据
字符常量
C中字符型的基本类型是char,这是一个字符型常量,字符型常量是指用单引号扩起来的一个字符,每个字符在所有编译系统中均占用1个字节(8个位),利用ASCII码的形式进行存储,因此每个字符型常量都可以进行数学运算,运算时就是调用的每个字符对应的ASCII码值。同样在进行输出时,系统也会根据输出格式,确定输出字符还是输出其对应的ASCII码值。字符串常量
字符串常量是用一对双引号括起来的零个或多个字符组成的序列,如 “hello”,”China”,”b” 都是字符串常量。 字符串常量的存储与字符常量的存储是不同的。字符串中的每个字符占用一个字节,在存储字符串常量时还要自动在其末尾加上 ‘\0’ 作为字符串结束的标志。 “How do you do.” 存储示意如下。
因此字符常量和字符串常量’b’ 和 “b” 是完全不同的。前者是字符常量,在内存中占用的字节数为 1;而后者是字符串常量,在内存中占用的字节数为 2,包含字符 ‘b’ 和 ‘\0’。注意:在 C 语言中没有专门的字符串变量,如果你想要将一个字符串存放在变量中,必须使用字符数组,数组中每一个元素存放一个字符,数组的内容我们会在以后的课程中和大家详细讲述。
基础运算符
四则运算
1 | x + y:将x与y相加 |
- x/y 中,两个实数(亲!注意说的是实数)相除的结果是双精度实数,两个整数相除的结果为整数。如 5/3 的结果为 1,舍去小数部分。
- % 运算符要求参加运算的对象为整数,结果也是整数。如 7%3,结果为 1,除了%以外的运算符的操作数都可以是任何算数类型。
自增自减
作用是使变量的值加 1 或减 1 ,例如:++i ,–i(在使用 i 之前,先使 i 加(减)1 );i++,i–(在使用 i 之后,使 i 的值加(减)1 )。
不同数据类型混合运算
在程序中经常会遇到不同类型的数据进行运算,比如 7*3.5。如果一个运算符的两侧数据类型不同,则先进行类型的转换,使两者具有同一种类型,然后进行运算。如下表,先转换成优先级较高的数据类型然后进行运算。
类型 | 优先级 |
---|---|
long double | 高 |
double | |
float | |
long | |
unsigned int | |
int | |
short | |
char | 低 |
如果 int 类型的数据和 float 或 double 型数据进行运算时,先把 int 型和 float 型数据转换为 double 型数据,然后进行运算,结果为 double 型。其他的大家可以按照上图来做。
字符 (char) 型数据和整形数据进行运算,就是把字符的 ASCII 代码与整形运算。如 4+’B’,由于字符 ‘B’ 的 ASCII 代码是 66,相当于 66+4=70。字符型数据可以直接和整形数据进行运算。如果字符型数据和浮点型数据运算,则将字符的 ASCII 码先转化为 double 型,然后在进行运算。
强制转换类型
在运算前,可以根据需要对数据格式进行强制转换,一般形式就是(数据类型名)(表达式)如下:
1 | (double)a // (将a转换成为double型) |
基础结构
switch
if是一个但分支的逻辑结构,当我们有多个筛选条件时,如果用if会不断使用elsif,
switch(表达式) { case 常量1:语句1 case 常量2:语句2 . . . case 常量n :语句n default : 语句n+1 }
while循环
1 | while(expression) |
do…while
1 | do |
for 循环
1 | for(表达式 1;表达式 2;表达式 3) 语句 |
break
退出循环,执行后续命令。
continue
退出本轮循环进行下一轮循环,(跳过循环中的后续命令)。
函数
函数声明
指定函数的类型为 void,表示函数无类型,即无函数值,也就是说,执行这函数不会返回任何值
如果函数的定义之前被引用了的话,则在引用前进行声明。
1 | #include<stdio.h> |