你的编程元年
回顾
我假设你又认真的看我前面写的文章,而且很认真的自己去写了程序,然后现在应该知道怎样快速的建立一个自己的工程了。像下面这样
#include<stdio.h>
int main()
{
return 0;
}
现在你有一个什么都不干的程序了。
总的干点什么吧
然而即使这个程序能够打印一串文字,这也算不得编程。一个程序,无非就是进行各种计算的,那么我们先编个算算数的程序吧,不是用计算机自带的计算器哦。
首先,我们来重新认识一下printf,之前我们打印都是打印的字符串,其实printf是可以用格式控制打印很多东西的,以后你会发现这个功能有多好用。
重识printf
printf是我们从stdio.h中找到的一个标准函数,我们可以在printf打印的字符串中加几个百分号,百分号后面跟几个特殊符号,然后后面相应的加几个参数,像这样 Printf(“hello world %s%d%f”,a,b,c);就可以打印指定格式的东西。这个特殊格式如下表:
控制符 | 说明 | 例子 |
---|---|---|
%d | 按十进制整型数据的实际长度输出。 | printf("%d",1234) ; |
%ld | 输出长整型数据。 | printf("%ld",1234567890); |
%md | m 为指定的输出字段的宽度。如果数据的位数小于 m,则左端补以空格,若大于 m,则按实际位数输出。 | printf("%4d",12); |
%u | 输出无符号整型(unsigned)。输出无符号整型时也可以用 %d,这时是将无符号转换成有符号数,然后输出。但编程的时候最好不要这么写,因为这样要进行一次转换,使 CPU 多做一次无用功。 | printf("%u",-12); |
%c | 用来输出一个字符。 | printf("%c",63); |
%f | 用来输出实数,包括单精度和双精度,以小数形式输出。不指定字段宽度,由系统自动指定,整数部分全部输出,小数部分输出 6 位,超过 6 位的四舍五入。 | printf("%f",1.1); |
%.mf | 输出实数时小数点后保留 m 位,注意 m 前面有个点。 | printf("%.5f",1.1234567);printf("%.5f",1.1); |
%o | 以八进制整数形式输出,这个就用得很少了,了解一下就行了。 | printf("%o",10); |
%s | 用来输出字符串。用 %s 输出字符串同前面直接输出字符串是一样的。但是此时要先定义字符数组或字符指针存储或指向字符串,这个稍后再讲。 | printf("%s",“1234”); |
%x | 或 %X 或 %#x 或 %#X) 以十六进制形式输出整数,这个很重要。 |
这里比较多,可以先不要全记,我们就用第一个%d
printf("%d",123)
加
程序中的加法,使用的符号是+,按下"shift" + “=“可以打出来加号,打印一个加法的结果
printf(”%d”,1+2);
减
程序中的减法,使用的符号是-,按下"shift" + “-“可以打出来加号,打印一个减法的结果
printf(”%d”,1-2);
乘
程序中的乘法,使用的符号是*,按下"shift" + “8"可以打出来加号,打印一个乘法的结果
printf(”%d",1*2);
除
程序中的除法,使用的符号是/,在英文输入模式下按带问号的键,可以打出来除号,打印一个除法的结果
printf("%d",1/2);
试试看除号右边写个0看看?^ _ ^
取余
计算1/2的时候,我们的结果为0,这是为什么呢,原来,我们进行的是整数运算,计算的结果也被取整了,1/2等于0.5,我们取整数部分,就会得到0,这个不是四舍五入,而是直接截断取整的算法,那么我们不是就丢了一个1了吗,怎么获取我们丢的这个数呢,计算机给我们提供了取余这个符号,让我们处理这个被丢的部分,符号取余为%,也就是shift + 5。
printf("%d",1%2);
我们就会获得1,相应的printf("%d",7%5);也就是2,很多时候,我们就是通过一个数对2取余看这个数是奇数还是偶数的,试试看更多的取余算法吧。
加点输入,初识scanf
这样的算法,每次都输出相同的值,是不是很没有意思?不如做个可以读取输入的程序。我们可以用scanf把键盘输入导入到我们的程序,同样的,这个函数也在stdio.h中。
scanf格式如下scanf("%d",&a);
这里我们要补充几个概念:
- 变量,我们上一篇说我们可以自己定义标识符,其中有一种就是变量,所谓变量,就是在程序中可以变化的量。
- 取地址符&,我们程序定义的变量会有自己的内存空间,我们可以通过这个符号获取到这个地址,然后改变用这个地址改变他的值。
这里scanf获取到我们自己定义的变量的地址,在程序获得输入的时候,就把值放到这个变量里面了。输入结束之后按enter键结束输入。
那么我们试试看带输入的加法吧。
这里由于某些原因,程序会编译不过,要在代码最开始加一段话#define _CRT_SECURE_NO_WARNINGS,以后如果有类似问题,我们也会这样操作,虽然实际应用中不会这样使用,但是学习阶段,屏蔽掉这个报错会方便很多。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int a;
int b;
scanf("%d",&a);
scanf("%d",&b);
printf("%d",a+b);
return 0;
}
现在我们的程序可以接受输入的数字进行计算了。同样的,其他计算也可以这样获取,然后我们这里对输入还可以再进一步。其实这两个数值是可以一句话就输入的。
像这样:scanf("%d,%d",&a,&b);
如果要这样使用,那么两个数字之间的间隔符也必须和双引号里面的格式一样,比如这个例子里面,使用的是逗号分隔,那么我们的输入也要是逗号分隔的,这个逗号也可以换成空格等,可以自己试一试。然后三个四个要一起输入的话也是一样的往上加就可以了。
复杂计算,算法的优先级
进行了简单的加减乘除计算,你是不是发现其实这和你在草稿纸上写的加减乘除没什么区别。实际上确实是这样的,只不过我们把乘除的符号改了一下而已,相应的,加减乘除的优先级也和我们平时一样,如果不加上括号,计算都是先乘除后加减,如果你想先计算加减,那么,加上括号吧。
数据类型
上面我们其实就已经说了数据的类型,我们说我们实现的是整数的计算,另外格式控制还可以打印字符,实数等等,那么数据类型是什么,都有哪些数据类型呢。
有哪些类型
这里的字节数和值范围主要可以看一下各种类型之间的相对大小关系,有些类型的大小和机器有关。想要知道自己的哪个类型占用几个自己,可以使用sizeof()查看,比如sizeof(int)
整数
类型 | 存储大小 | 值范围 |
---|---|---|
char 1 | 字节 | -128 到 127 或 0 到 255 |
unsigned char | 1 字节 | 0 到 255 |
signed char | 1 字节 | -128 到 127 |
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
浮点型
类型 | 存储大小 | 值范围 | 精度 |
---|---|---|---|
float | 4 字节 | 1.2E-38 到 3.4E+38 | 6 位小数 |
double | 8 字节 | 2.3E-308 到 1.7E+308 | 15 位小数 |
long double | 16 字节 | 3.4E-4932 到 1.1E+4932 | 19 位小数 |
void 类型
一般指没有类型,用在返回值表示不需要返回值,或者参数。如果定义一个void的指针,一般也不能直接用,在用的时候要转化成相应的类型,这个讲指针的时候再拓展。
选择合适的类型
这些类型,在使用的时候要知道自己需要的值是整数还是小数,然后精度范围以及需要支持多大的值,是否需要不能是负数,然后确定自己定义的数据的类型,比如unsigned char,最大值只能是255,如果你用255 + 1,那么就会让结果归零,这就叫做溢出。除此之外,没有太大的区别。
一个字节是什么
特别的需要了解的是char类型,char的大小为1个字节,也就是内存存储的最小单元。一个字节有8个bit,一个bit就是一个表示0或者1的二进制位,计算机里面的所有数据,都是用0,1组合表示的。一个char,在内存中就是这样子的01010010。这所有的组合,组成了整个程序世界,而我们最早通过assic表来确定每种组合的含义。
ASSIC码表
ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 |
---|---|---|---|---|---|---|---|
0 | NUT | 32 | (space) | 64 | @ | 96 | 、 |
1 | SOH | 33 | ! | 65 | A | 97 | a |
2 | STX | 34 | " | 66 | B | 98 | b |
3 | ETX | 35 | # | 67 | C | 99 | c |
4 | EOT | 36 | $ | 68 | D | 100 | d |
5 | ENQ | 37 | % | 69 | E | 101 | e |
6 | ACK | 38 | & | 70 | F | 102 | f |
7 | BEL | 39 | , | 71 | G | 103 | g |
8 | BS | 40 | ( | 72 | H | 104 | h |
9 | HT | 41 | ) | 73 | I | 105 | i |
10 | LF | 42 | * | 74 | J | 106 | j |
11 | VT | 43 | + | 75 | K | 107 | k |
12 | FF | 44 | , | 76 | L | 108 | l |
13 | CR | 45 | - | 77 | M | 109 | m |
14 | SO | 46 | . | 78 | N | 110 | n |
15 | SI | 47 | / | 79 | O | 111 | o |
16 | DLE | 48 | 0 | 80 | P | 112 | p |
17 | DCI | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 | 50 | 2 | 82 | R | 114 | r |
19 | DC3 | 51 | 3 | 83 | S | 115 | s |
20 | DC4 | 52 | 4 | 84 | T | 116 | t |
21 | NAK | 53 | 5 | 85 | U | 117 | u |
22 | SYN | 54 | 6 | 86 | V | 118 | v |
23 | TB | 55 | 7 | 87 | W | 119 | w |
24 | CAN | 56 | 8 | 88 | X | 120 | x |
25 | EM | 57 | 9 | 89 | Y | 121 | y |
26 | SUB | 58 | : | 90 | Z | 122 | z |
27 | ESC | 59 | ; | 91 | [ | 123 | { |
28 | FS | 60 | < | 92 | / | 124 | |
29 | GS | 61 | = | 93 | ] | 125 | } |
30 | RS | 62 | > | 94 | ^ | 126 | ` |
31 | US | 63 | ? | 95 | _ | 127 | DEL |
特殊符解释
特殊符 | 特殊符 | 特殊符 |
---|---|---|
NUL空 | VT 垂直制表 | SYN 空转同步 |
STX | 正文开始 | CR 回车 |
ETX 正文结束 | SO 移位输出 | EM 纸尽 |
EOY 传输结束 | SI 移位输入 | SUB 换置 |
ENQ 询问字符 | DLE 空格 | ESC 换码 |
ACK 承认 | DC1 设备控制1 | FS 文字分隔符 |
BEL 报警 | DC2 设备控制2 | GS 组分隔符 |
BS 退一格 | DC3 设备控制3 | RS 记录分隔符 |
HT 横向列表 | DC4 设备控制4 | US 单元分隔符 |
LF 换行 | NAK 否定 | DEL 删除 |
现在很多语言支持unicode和宽字节,Unicode是完全兼容assic码的,所以也可以使用,只是有时候需要进行一些简单的转换。
char里面的值也是整数,可以和整数进行一些计算,比如你可以吧字符a 加上1,他就可以变成b,减法也是一样的,而字符b减去字符a,就等于1。这个表不需要记忆,如果需要可以百度一下就好了,或者,你也可以收藏到自己的文件中方便以后查找。
如果上面这些你都懂了,那么我们是不是可以自己写一个计算任何类型数值的的计算器,算法包括加减乘除。试试吧。
类型之间的转化
上面我们说的计算,都是相同类型的数据在进行计算,实际上,不同类型也可以放在一起计算,你可以试试看任意两种不同类型的数据,放在一起使用加减乘除是什么现象。
你会发现,如果你使用1 + 1.1,的结果是2.1,答案是不是没有问题?其实就是看你怎么理解,这个结果实际就是我们在计算中,实际也会以最精确的那个数为准进行计算,计算机计算两个数的时候,会偷偷的将1 + 1.1 转化为1.0 + 1.1,看起来没有什么区别,实际上却不一样,这个和底层计算机实现有关系。也就是说在我们正常的计算中,其实你不了解类型的转化,使用起来也没有什么问题,那么为什么我们还要讲类型转化呢?基于以下两点:
- 显示的转化。有时候我们需要1 + 1.1算出来是个整数,那么我们就需要把1.1强制转成整数。显示转化的语法是在变量前面小括号加类,(int)a;就是把a转化为整数)
- 有时候隐式的转化并不是我们需要的,我们要避免,另外,隐式转化也是消耗性能的,虽然很小
实际上,除了数值转化之外,还有其他类型转化,基于的原理都差不多,后面慢慢讲,这里我们需要知道是类型不是固定不变的,是可以转换的,而转化分为显示的和隐式的。
note
- 熟悉格式化输出printf,尝试不同格式显示效果
- 尝试用不同类型实现加减乘除,了解算法的优先级
- 尝试使用格式化输入scanf
- 数据类型,c语言是强类型语言,每个变量都必须有类型,变量的命名决定了变量的位置,而变量的类型决定了怎么解析这段内存,所以变量一定要和类型匹配
- 了解字节的含义,对常见类型的内存表示有初步印象
- 了解类型间的转化关系
- 了解基本的ASSIC编码,知道原来char也是整数
附录
vs的快捷键
操作 | 功能 |
---|---|
F7 | 编译 |
vs的设置
设置vs使用table表示四个空格。
Tools -> Options -> Text Editor -> [C/C++] -> Tabs
这个页面两个选择,选成和我一样就好了。
第一个参数
none:换行的时候没有缩进
block:缩进到和{}也就是当前块一样
smart:比block再多缩进一个
第二个参数:两个值分别为插入空格和保留tab,也就是按table的时候,文件里面记录的到底是四个空格,还是一个table,后面我们会介绍字符的编码的时候,会发现这其实是不一样的。