学习编程需要掌握程序设计语言,我们先来学习C语言。学了C语言再学其他语言也比较好学了。
C语言是一种面向过程的计算机程序设计语言,至于什么是面向过程,什么是面向对象,我们先按下不表。有了C语言,我们就能和计算机交谈了。比如,你想让它说话,你就可以这样写:
#include<stdio.h>
int main(){
printf("Hello World!");
return 0;
}
编译运行后它就会输出内容啦!
那么这些代码该写在哪里呢?
我们需要一个编译软件,笔者用过的有Dev-C++和Visual Studio,你也可以有其他的选择
C语言属于高级语言,计算机是不能直接看懂的,所以要经过编译之类的操作转化为机器语言。
机器语言:又称二进制代码语言,需要编程人员记忆每条指令的二进制编码。机器语言是计算机唯一可以直接识别和执行的语言。
汇编语言:用英文单词或其缩写代替二进制的指令代码,更容易为人们记忆和理解。使用汇编语言编辑的程序,必须经过一个称为汇编程序的系统软件的翻译,将其转变为计算机的机器语言后,才能在计算机的硬件系统上执行。
高级语言:是为方便程序设计人员写出解决问题的处理方案和解题过程的程序。通常高级语言需要经过编译程序编译成汇编语言程序,然后经过汇编操作得到机器语言程序,或直接由高级语言程序翻译成机器语言程序。
代码解释
-
#include <stdio.h>就是一条预处理命令, 它的作用是通知C语言编译系统在对C程序进行正式编译之前需做一些预处理工作。
-
函数
就是实现代码逻辑的一个小的单元。 -
一个C语言程序有且只有一个主函数,即main函数。
-
C程序就是执行主函数里的代码,也可以说这个主函数就是C语言中的唯一入口。
-
而main前面的int就是主函数的类型.
-
printf()是格式输出函数,这里就记住它的功能就是在屏幕上输出指定的信息。而引号里的内容就是它输出的内容。
C语言规范
-
一个说明或一个语句占一行,例如:包含头文件、一个可执行语句结束都需要换行。
-
函数体内的语句要有明显缩进,通常以按一下Tab键为一个缩进。
-
括号要成对写,如果需要删除的话也要成对删除。
-
当一句可执行语句结束的时候末尾需要有分号。
-
代码中所有符号均为英文半角符号。
注释
注释是写给程序员看的,不是写给计算机看的,因为它们会被编译器忽略
为什么要写注释?
先讲个笑话”我平生最讨厌两种人:一种是不写注释的,一种是让我写注释的“
当然,这种想法并不可取。注释还是要好好写的。
1.写给自己,因为我们不可能对自己写过的所有代码都有印象,日后回过来再看代码时可以用注释帮助自己回想。
2.写给其他程序员,便于别人看懂你的代码,因为代码是按你自己想法写的,一些名称也是你命名的,你自己知道的内容和功能之类的,别人未必知道。尤其是一起做项目或者交接项目就更有必要了。
注释的格式:
//单行注释
/*
注释1
注释2
注释3
...
多行注释
*/
计算机除了“能说话”,最起码得有计算的功能吧,那我们应该怎样实现呢?
我们以加法为例,如果我们使用计算器的话,是以下步骤;
1.输入一个数
2.点击加号
3.输入另一个数
4.点击等于号
5.输出结果
而C语言中加法的计算是自带的,我们要解决的是怎么表示第1、3、5步中的数。
我们要做的其实就是定义整型变量,那什么是整型,什么是变量呢?我们稍后就会进行说明。
那这三个数得区分一下吧?我们得给它们起不同的名字。
标识符
C标识符是用来标识变量、函数或其他用户自定义项目的名称。一个标识符以字母A-Z或a-z或下划线"_"开始,后面跟零个或多个字母、下划线和数字(0-9)
C标识符内不允许出现标点字符($可以)
C是区分大小写的编程语言。因此在C中,Cat和cat是两个不同的标识符
下面列举几个有效的标识符:
Zara
move_name
a_123
_temp
a23b9
需要注意的是:关键字不能作为标识符
比如上面出现的 int return
数据类型
在C语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。
基本类型:它们是算术类型,包括两种类型:整数类型(我们说的整型就是它)和浮点类型
枚举类型:它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量
void类型:类型说明符void表明没有可用的值
派生类型:它们包括:指针类型、数组类型、结构类型、共用体类型和函数类型(数组类型和结构类型统称为聚合类型。函数的类型指的是函数返回值的类型)
PS:有些概念(包括本篇文章和后续文章)我们目前没涉及到,不理解没关系,随着知识的增多,慢慢会理解的。
整数类型
类型 | 存储大小 byte(s) | 值范围 |
---|---|---|
char | 1 | -128到127或0到255 |
unsigned char | 1 | 0到255 |
signed char | 1 | -128到127 |
int | 2或4 | -32768到32767或-2147483648到2147483647 |
unsigned int | 2或4 | 0到65535或0到4294967295 |
short | 2 | -32768到32767 |
unsigned short | 2 | 0到65535 |
long | 4 | -2147483648到2147483647 |
unsigned long | 4 | 0到4294967295 |
浮点类型
类型 | 存储大小 byte(s) | 值范围 | 精度 |
---|---|---|---|
float | 4 | 1.2E-38到3.4E+38 | 6 |
double | 8 | 2.3E-308到1.7E+308 | 15 |
long double | 10 | 3.4E-4932到1.1E+4932 | 19 |
为了得到某个类型或某个变量在特定平台上的准确大小,可以使用sizeof运算符。表达式sizeof(type)得到对象或类型的存储字节大小。
#include<stdio.h>
int main(){
printf("%d\n",sizeof(char));
printf("%d\n",sizeof(int));
printf("%d\n",sizeof(long));
printf("%d\n",sizeof(float));
printf("%d\n",sizeof(double));
//"\n"是转义字符,表示换行
/*
“%d”表示按十进制整型输出
“%f”表示按小数形式输出单、双精度实数
“%c”表示按字符型输出
*/
return 0;
}
void类型
void类型指定没有可用的值。它通常用于以下三种情况:
1.函数返回为空
C中有各种函数都不返回值,或者可以说它们返回空。不返回值的函数的返回类型为空。
eg: void exit(int status);
2.函数参数为空
C中有各种函数不接受任何参数。不带参数的函数可以接受一个void
eg: int rand(void);
3.指针指向void
类型为void*
的指针代表对象的地址,而不是类型。例如,内存分配函数void* malloc(size_t size);
返回指向void的指针,可以转换为任何数据类型。
变量
变量就是可以变化的量,而每个变量都会有一个名字(标识符)。变量占据内存中一定的存储单元。使用变量之前必须先定义变量。
变量定义的一般形式为:数据类型 变量名;
多个类型相同的变量:数据类型 变量名1, 变量名2, 变量名3…;
变量的赋值分为两种方式:
-
先声明再赋值
-
声明的同时赋值(初始化)
不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为NULL(所有字节的值都是0),其他所有变量的初始值是未定义的。
#include<stdio.h>
int main(){
int a;
a=10;
int b=20;
float d1,d2,d3;
d1=1.1;
d2=2.2;
d3=3.3;
printf("%d\n%d\n%f\n%f\n%f\n",a,b,d1,d2,d3);
return 0;
}
变量的作用域
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
-
局部变量的作用域是变量所在的局部范围。(就是大括号里边的变量只能在大括号里用)
-
全局变量的作用域是整个工程。(就是大括号外边的变量在哪都能用)
变量的生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段。
-
局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。(大括号大括号!)
-
全局变量的生命周期是:整个程序的生命周期。 (在哪都行!)
注:后面的存储类也会涉及到。
上面的代码中"int b=20;
" "="是赋值运算符,在这里表示把20赋值给b,那么为什么不能写成“int 20=b;
”呢?
左值和右值
C中有两种类型的表达式:
左值(lvalue):指向内存位置的表达式被称为左值表达式。左值可以出现在赋值号的左边或右边。
右值(rvalue):术语右值指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。
了解了以上这些之后我们就可以实现加法啦!
比如我们想计算456+789的和
#include<stdio.h>
int main(){
int a=456;
int b=789;
int c=a+b;
printf("%d\n",c);
return 0;
}
这样我们就可以实现啦!
有的小伙伴此时可能会问,计算不同的值每次需要改a和b的值,那能不能从键盘输入数去计算呢?
这也是可以的,有请我们的 scanf 出场
#include<stdio.h>
int main(){
int a;
int b;
scanf("%d %d",&a,&b);
int c=a+b;
printf("%d\n",c);
return 0;
}
scanf函数是格式化输入函数,用于接受从键盘输入的数据,用户输入数据完成后,按回车键(Enter)结束输入。格式和printf类似,多一个“&”。
printf和scanf函数的具体用法后续会补充。
实现加法了,那减法和乘法只需要改个符号就可以了,注意,乘法符号是“ * “。那除法(符号是" / ")呢?为什么我没说,是有什么不一样吗?
如果是沿用上面的456/789,你会发现结果是0
而1/2是0 5/2是2 -7/3是-2 -8/-3是2
这是因为C语言使用向零取整方式
在C语言中规定,两个整数相除,如果不能整除,那么结果的小数部分的处理应该向0靠近;如果两个异号整数进行取模运算,那么结果的符号应该与被除数相同。
取模运算
取模运算也叫取余运算,在C中用%来表示, 数学中叫mod。 x mod y = x%y
x%y = x - y[x/y], for y!=0.
数学中的余数概念和我们的计算机中的余数概念一致,但实现却不一致。 其中 [x/y] 代表的是 x/y 的最小下界。 例:
-3 mod 2
= -3 - 2[-3/2]
= -3 - 2[-1.5]
= -3 - 2(-2)
= -3 + 4
= 1
而我们的计算机是怎么做的呢:
-3%2
= -3 - 2(-3/2)
= -3 - 2*(-1)
= -3 - (-2)
= -1
所以计算机中的取余实际上是:
x%y = x - y(x/y), for y!=0.
这就是二者的区别。这个区别,对于正数,二者计算出的结果是相等的,但是负数就不相等了。
类型转换
自动类型转换
将一种类型的数据赋值给另外一种类型的变量时就会发生自动类型转换,在编译的时候自动完成,不需要程序员干预。
自动类型转换有以下几个规则:
- 字节小(级别低)的可以向字节大(级别高)的自动转换,但字节大的不能向字节小的自动转换
- 无符号类型级别高于有符号类型(unsigned > signed)
- 浮点型级别高于有符号、无符号整型(与浮点运算,结果都为浮点型)
- 双精度浮点级别高于单精度浮点(double > float)
#include <stdio.h>
int main(){
float PI = 3.14159;
int r = 5;
int s1;
double s2;
s1 = r*r*PI; //s1=78
/*
在计算表达式r*r*PI时,r和PI都被转换成 double 类型,
表达式的结果也是 double 类型。
但由于 s1 为整型,所以赋值运算的结果仍为整型,
舍去了小数部分,导致数据失真。
*/
s2 = r*r*PI; //s2=78.539749
printf("s1=%d s2=%f\n", s1, s2);
return 0;
}
强制类型转换
格式为:(type_name) expression
(数据类型) 表达式
其作用是把表达式的运算结果强制转换成类型说明符所表示的类型
在使用强制转换时应注意以下问题:
-
数据类型和表达式都必须加括号, 如把
(int)(x/2+y)
写成(int)x/2+y
则成了把x
转换成int
型之后再除2
再与y
相加了。 -
转换后不会改变原数据的类型及变量值,只在本次运算中临时性转换。
#include <stdio.h>
int main(){
float x=1.5f,y=2.0f;
//小数后加f,表示这个小数是float类型数据
printf("%d\n",(int)(x+y));
//强制转换的是表达式的值,x自身是不变的
printf("x=%f\n",x);//x=1.500000
printf("%d\n",(int)(x));
printf("x=%f\n",x);//x=1.500000
return 0;
}
在C语言中,有些类型既可以自动转换,也可以强制转换,例如 int 到 double,float 到 int 等;而有些类型只能强制转换,不能自动转换,例如以后将要学到的 void * 到 int *,int 到 char * 等。 可以自动转换的类型一定能够强制转换,但是,需要强制转换的类型不一定能够自动转换。
可以自动进行的类型转换一般风险较低,不会对程序带来严重的后果,例如,int 到 double 没有什么缺点,float 到 int 顶多是数值失真。只能强制进行的类型转换一般风险较高,或者行为匪夷所思,例如,char * 到 int * 就是很奇怪的一种转换,这会导致取得的值也很奇怪,再如,int 到 char * 就是风险极高的一种转换,一般会导致程序崩溃。
就先讲到这里吧,已经有很多内容了,好好消化一下吧。
要热爱编程、热爱生活哟!