C语言快速入门(一)

153 阅读12分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情

二进制

二进制加减法和十进制加减法的思想是类似的:

  • 对于十进制,进行加法运算时逢十进一,进行减法运算时借一当十;
  • 对于二进制,进行加法运算时逢二进一,进行减法运算时借一当二。

下面两张示意图详细演示了二进制加减法的运算过程。

1、二进制加法:1+0=1、1+1=10、11+10=101、111+111=1110

image.png

2、二进制减法:1-0=1、10-1=1、101-11=10、1100-111=101

image.png

基本数据类型

原码、反码和补码

首先介绍一下符号位的概念:指二进制数字从左往右第一位,0表示正数,1表示负数。通过看最高位是0还是1就可以辨别正负数了。

原码

所谓原码就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。

在数值前直接加一符号位的表示法。

例如: 符号位 数值位

[+7]原= 0 0000111 B

[-7]原= 1 0000111 B

注意:

数0的原码有两种形式:

[+0]原= 00000000B

[-0]原= 10000000B

位二进制原码的表示范围:-127~+127

反码

反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。

正数:正数的反码与原码相同。

负数:负数的反码,符号位为“1”,数值部分按位取反。

例如: 符号位 数值位

[+7]反= 0 0000111 B

[-7]反= 1 1111000 B

注意:

a. 数0的反码也有两种形式,即

[+0]反=00000000B

[- 0]反=11111111B

b. 8位二进制反码的表示范围:-127~+127

补码

补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。

正数:正数的补码和原码相同。

负数:负数的补码则是符号位为“1”。并且,这个“1”既是符号位,也是数值位。数值部分按位取反后再在末位(最低位)加1。也就是“反码+1”。

例如: 符号位 数值位

[+7]补= 0 0000111 B

[-7]补= 1 1111001 B

a.正数的补码即是它所表示的数的真值,而负数的补码的数值部份却不是它所表示的数的真值。

采用补码进行运算,所得结果仍为补码。

b. 与原码、反码不同,数值0的补码只有一个,即

[0]补=00000000B。

整数类型

整数就是不包含小数点的数据,比如13,等数字。整数包含以下几种数据类型:

  • int:占用4字节,默认一般使用此类型。
  • long:占用8字节。
  • short:占用2字节。

浮点类型

浮点类一般用于保存小数。

  • float:单精度浮点,占用4个字节。
  • double:双精度浮点,占用8个字节。

字符类型

每一个字符都可以使用字符类型来保存:

  • char:占用1个字节(-128~127),可以表示所有的ASCII码字符,每一个数字对应的是编码表中的一个字符:
  • 某些无法直接显示的字符(比如换行,换行也算一个字符),需要使用转义字符。

image.png

image.png

C程序基本格式

在后面,将进行代码的编写,所以在此简单介绍一下c语言程序的基本格式以供了解。

main.c

#include <stdio.h> // 引入系统库为我们提供的函数,以后编写一个C语言程序,就按照固定模式:int main() { // C语言程序入口点就是`main`函数(
    printf("Hello World!");
    return 0;
}

变量

在学习变量之前,请先了解占位符。(就目前阶段而言,常用的有%d和%s这两个)

格式控制符说明
%c输出一个单一的字符
%hd、%d、%ld以十进制、有符号的形式输出 short、int、long 类型的整数
%hu、%u、%lu以十进制、无符号的形式输出 short、int、long 类型的整数
%ho、%o、%lo以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数
%#ho、%#o、%#lo以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数
%hx、%x、%lx %hX、%X、%lX以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。
%#hx、%#x、%#lx %#hX、%#X、%#lX以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。
%f、%lf以十进制的形式输出 float、double 类型的小数
%e、%le %E、%lE以指数的形式输出 float、double 类型的小数。如果 e 小写,那么输出结果中的 e 也小写;如果 E 大写,那么输出结果中的 E 也大写。
%g、%lg %G、%lG以十进制和指数中较短的形式输出 float、double 类型的小数,并且小数部分的最后不会添加多余的 0。如果 g 小写,那么当以指数形式输出时 e 也小写;如果 G 大写,那么当以指数形式输出时 E 也大写。
%s输出一个字符串

变量的使用

声明变量的格式为:数据类型 变量名称 = 初始值; 示例:

int a = 10;   //变量类型为int(常用),变量名称为a,变量的初始值为10
int a = 10, b = 20;   //多个变量可以另起一行编写,也可以像这样用逗号隔开,注意类型必须是一样的

变量命名有以下规则:

  • 不能重复使用其他变量使用过的名字。
  • 只能包含英文字母或是下划线、数字,并且严格区分大小写。
  • 虽然可以包含数字,但是不能以数字开头。
  • 不能是关键字。

初始值可以是一个常量数据(比如直接写10、0.5这样的数字)也可以是其他变量,或是运算表达式的结果,这样会将其他变量的值作为初始值。

我们可以通过给变量赋值,然后进行一些简单的计算。

#include <stdio.h>int main() {
    int a = 10;  //将10作为a的值
    int b = 20;
    int c = a + b;   //注意变量一定要先声明再使用,这里是计算a + b的结果,并作为c的初始值
}

以上代码就是简单对变量进行赋值,下面我们想知道变量的值到底是多少,那么就需要进行打印这个操作

#include <stdio.h>
​
int main() {
    int a = 10;
    int b = 20;
    int c = a + b;
​
    printf(c);   //直接打印变量c
}

发现并没有任何结果在控制台输出,因为在C语言中,输出内容需要使用占位符,若有遗忘,请往上看。以下才是正确操作。

printf("c的结果是:%d", );   //使用%d来代表一个整数类型的数据(占位符),在打印时会自动将c的值替换上去

下面是小数的计算

#include <stdio.h>int main() {
    double a = 0.5;
    float b = 2.5f;   //直接写2.5默认表示的是一个double类型的值,需要再后面加一个f或是F表示是flaot类型值
​
    printf("a + b的结果是:%f", a + b);   //根据上表,小数类型需要使用%f表示,这里我们可以直接将a + b放入其中
}

再来看字符类型

char c = 'A';   //字符需要使用单引号囊括,且只能有一个字符,不能写成'AA',这就不是单个字符了
//注意这里的A代表的是A这个字符,对应的ASCII码是65,实际上c存储的是65这个数字
#include <stdio.h>int main() {
    char c = 'A';
    printf("变量c的值为:%c 对应的ASCII码为:%d", c, c);   //这里我们使用%c来以字符形式输出,%d输出的是变量数据的整数形式,其实就是对应的ASCII码
}

当然,我们也可以直接让char存储一个数字(ASCII码),同样也可以打印出对应的字符:

#include <stdio.h>int main() {
    char c = 66;
    printf("变量c的值为:%c 对应的ASCII码为:%d", c, c);
}

那要是我们不对变量设定初始值呢?那么变量会不会有默认值:

#include <stdio.h>int main() {
    int a, b, c, d;
    printf("%d,%d,%d,%d", a, b, c, d);
}

根据结果发现并不会,可以看到,虽然定义变量但是我们没有为其设定初始值,那么它的值就是不确定的了(原理是内存分配机制)

下面再看一种特殊情况:

#include <stdio.h>int main() {
    char c = 127;    //已经到达c的最大值了
    c = c + 1;   //再进行+1的操作
    printf("%d", c);    //这时会得到什么结果?
}

由于位数不够,导致运算结果值溢出:

  • 127+1= 01111111+1
  • 由于现在是二进制,满2进1,所以最后变成
  • 10000000 = 补码形式的 -128

在我们的运算中,可能也会存在一些一成不变的值,比如π的值永远都是3.1415....,在我们的程序中,也可以使用这样不可变的变量,我们成为常量。

定义常量和变量比较类似,但是需要在前面添加一个const关键字,表示这是一个常量:

可以看到,常量在一开始设定初始值后,后续是不允许进行修改的。

无符号数

我们知道,所有的数据底层都是采用二进制来进行保存的,而第一位则是用于保存符号位,但是如果我们不考虑这个符号位,那么所有的数都是按照正数来表示,比如考虑了符号位的char类型:

  • 考虑符号表示范围:-128~127
  • 不考虑符号:0~255

我们也可以直接使用这些不带符号位的数据类型:

int main() {
    unsigned char c = -65;   //数据类型前面添加unsigned关键字表示采用无符号形式
    printf("%u", c);    //%u以无符号形式输出十进制数据
}

结合我们前面学习的基础知识,我们来看看为什么得到的是191这个数字。首先char类型占据一个字节,8个bit位:

  • 00000000 -> 现在赋值-65 -> -65的补码形式 -> 10111111
  • 由于现在没有符号位,一律都是正数,所以,10111111 = 128 + 32 + 16 + 8 + 4 + 2 + 1 = 191
#include <stdio.h>int main() {
    int i = -1;
    printf("%u", i);    //%u以无符号形式输出十进制数据
}

得到无符号int的最大值。

类型转换

一种类型的数据可以转换为其他类型的数据,这种操作我们称为类型转换,类型转换分为自动类型转换强制类型转换,比如我们现在希望将一个short类型的数据转换为int类型的数据:

#include <stdio.h>int main() {
    short s = 10;
    int i = s;   //直接将s的值传递给i即可,但是注意此时s和i的类型不同
    float a = 3;    //包括这里我们给的明明是一个int整数3但是却可以赋值给float类型,说明也是进行了自动类型转换
}

这里其实就是一种自动类型转换,自动类型转换就是编译器隐式地进行的数据类型转换,这种转换不需要我们做什么,我们直接写就行,会自动进行转换操作。

如果我们使用一个比转换的类型最大值都还要大的值进行类型转换,比如:

#include <stdio.h>int main() {
    int a = 511;
    char b = a;   //最大127
    printf("%d", b);
}

很明显char类型是无法容纳大于127的数据的,因为只占一个字节,而int占4个字节,如果需要进行转换,那么就只能丢掉前面的就只保留char所需要的那几位了,所以这里得到的就是-1:

  • 511 = int -> 00000000 00000000 00000001 11111111
  • char -> 11111111 -> -1

我们也可以将整数和小数类型的数据进行互相转换:

#include <stdio.h>int main() {
    int a = 99;
    double d = a;
    printf("%f", d);
}

不过这里需要注意的是,小数类型在转换回整数类型时,会丢失小数部分(不是四舍五入,是直接丢失小数)

#include <stdio.h>int main() {
    double a = 3.14;
    int b = a;    
    printf("%d", b);
}

除了赋值操作可以进行自动类型转换之外,在运算中也会进行自动类型转换,比如:

#include <stdio.h>
​
int main() {
    float a = 2;
    int b = 3;
    double c = b / a;
    printf("%f", c);
}

下图是类型转换的规则:

下面介绍强制类型转换,我们可以为手动去指定类型,强制类型转换格式如下:

(强制转换类型) 变量、常量或表达式;

比如:

#include <stdio.h>int main() {
    int a = (int) 2.5;   //2.5是一个double类型的值,但是我们可以强制转换为int类型赋值给a,强制转换之后小数部分丢失
    printf("%d", a);
}

我们也可以对一个算式的结果进行强制转换

#include <stdio.h>int main() {
    double a = 3.14;
    int b = (int) (a + 2.8); 
    printf("%d", b);
}

在我们需要得到两个int相除之后带小数的结果时,强制类型转换是一种有效的方式

#include <stdio.h>int main() {
    int a = 10, b = 4;
    double c = a / b;    //不进行任何的类型转换,int除以int结果仍然是int,导致小数丢失
    double d = (double) a / b;   //对a进行强制类型转换,现在是double和int计算,根据上面自动类型转换规则,后面的int自动转换为double,结果也是double了,这样就是正确的结果了
    printf("不进行类型转换: %f, 进行类型转换: %f", c, d);
}