C语言变量入门

191 阅读10分钟

程序设计的一个重要内容,就是合理的储存数据,其中,变量能够帮助我们完成数据的储存,而变量的类型则规定了变量储存内容的解析方式。

使用变量储存数据

在C语言中,想要使用变量,就必须先声明变量,这一语法的基本形式如下:

type var_name;

对于变量的命名而言,有一些重要的规则需要遵循:

  • 变量名需要以英文或下划线开头
  • 不能使用 C语言 中的关键字

学习这些规则的最好办法,就是编写程序,因此,我们编写了一些示范的小程序。
在这里插入图片描述

/*
    var.c
    Show the usage of var
    BeginnerC
*/
#include <stdio.h>
int main()
{
    // Define a var named number, int type
    int number = 100;
    // Define a var named PI, double type
    double PI = 3.14;
    // Define a var named letter, char type
    char letter = 'A';
    printf("%d %lf %c\n", number, PI, letter);
    return 0;
}

在这个简单的小程序中,我们声明并定义三个变量,选择不同的数据类型来储存不同的数据,并在最后使用 printf 函数输出了他们。

其中涉及到的知识点如下

  1. C 语言中的数据类型
    C 语言提供了数种内置类型进行数据的储存,但从大的分类来看,主要是整数、小数、字符
  2. printf 格式输出
    printf 函数不是一个单纯输出字符串的函数,它可以根据格式指示符(%d %lf %c 这一类)进行对应的数据输出

我们将 C语言 中的内置数据类型罗列如下表

数据类型作用说明具体类型指示符
字符用于表示可以阅读的符号(比如字母)char
小数表示小数double float
整数表示整数int

在当下的程序设计中,我们使用 char double int 三种类型对 字符、小数与整数进行储存。

同时,C语言也提供了一些变量限定符,用于更加规范地使用变量。

变量限定符作用举例
long使得变量占用的储存空间尽可能大long double
short使得变量占用的储存空间尽可能小short int
unsigned使得变量按照无符号方式进行解析unsigned int
signed使得变量按照有符号方式进行解析signed int
const使得变量不可以被修订const double PI = 3.1415926;

当然,在现实情况中的程序设计中,情况往往更加复杂,这种状况下,我们就需要灵活选择(用实践作为支撑)

而对于 printf 函数的格式输出,我们也准备了一张对应的表格(描述常用的格式控制符号)

指示符号具体含义示例代码
%c输出字符printf(“%c”, ‘c’);
%s输出字符串printf(“%s”, “Hello World”);
%d %ld %u输出整数(以 10 进制的形式)printf(“%d”, 123);
%f %lf输出小数printf(“%lf”, 3.14);
%x %o输出整数(以 16 进制 或 8 进制的形式)printf(“%x”, 0x0001);

学习 printf 函数的最好方式,就是动手实践,比起拘泥于一些名词与表格,我们更倾向于鼓励你写出一些验证的小程序进行测试。

对数据进行基本处理

程序的本质就是处理数据的工具,在完成数据的储存之后,我们就要开始对我们储存的数据进行处理。

在 C语言 中,这被称为运算。

学习数据处理的最好办法,就是动手实践,因此,我们同样会给出一组示例的程序,作为参考。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2HIDSUyo-1683023217334)(https://foruda.gitee.com/images/1677754968075414777/a9329ac7_871414.png "1672804375050.png")]

/*
    calc_circle_area.c
    Calc the circle area
    BeginnerC
*/
#include <stdio.h>
int main()
{
    double pi = 3.14;
    float r = 2;
    printf("The area of circle is :%f\n", pi * r * r);
    return 0;
}

这个程序负责计算圆的面积,可以看出,程序非常顺利地达到了我们的目标。

这种包含多个变量运算的式子,被称为表达式,他们是我们数据处理的基石。

C 语言提供了许多种有用的变量运算,我们也如之前一般,整理如下表

加减乘除

符号作用举例
+使两个变量相加a + b
-使两个变量相减a - b
*使两个变量相乘a * b
/使两个变量相除a / b

学习一种运算,最好的办法,就是动手实践。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J81uq1LG-1683023217335)(https://foruda.gitee.com/images/1677754981029951751/c5701366_871414.png "1672878861547.png")]

/*
    calc_base.c
    Show the usage of the calc
    BeginnerC
*/
#include <stdio.h>
int main()
{
    int a = 1, b = 2;
    printf("%d + %d = %d\n", a, b, a + b);
    printf("%d - %d = %d\n", a, b, a - b);
    printf("%d * %d = %d\n", a, b, a * b);
    printf("%d / %d = %d\n", a, b, a / b);
    printf("%d / %d = %f\n", a, b, a / (double)b);
    return 0;
}

可以发现,程序非常顺利地完成了计算任务,唯一的差别在于最后的除法上。

在这里,我们有必要引入表达式类型与类型转化的概念。

  • 表达式的类型
    首先,我们明确,在 C语言表达式 的最后结果上,有且只有一种数据类型
    而当参与运算的变量类型不一致时,就需要涉及到类型转换

    • 两种类型转换
      类型转换分为自动类型转换与强制类型转换

      • 自动类型转换
        自动转换用“相对更大的储存空间"保证“不损失信息",它往往取决于参与运算的数据类型中,哪一者的数据类型“占用空间最多"
      • 强制类型转换
        强制类型转换由程序设计者们主动写入,语法为
        (type)var

掌握一项程序设计知识点的最好办法,就是动手实践,在此,我们继续编写有关的案例。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uYOcTvOL-1683023217336)(https://foruda.gitee.com/images/1677754992304906393/62d3b631_871414.png "1672881314067.png")]

/*
    calc_base_second.c
    Show the usage of the calc
    BeginnerC
*/
#include <stdio.h>
int main()
{
    printf("%d\n", 3 / 5);
    printf("%f\n", 3 / 5);
    printf("%f\n", 3 / (double)5);
    printf("%f\n", 3 / 5.0);
    return 0;
}

可以发现,同样是 3 与 5 相除,四条式子的运算结果却不能一致,这是因为

  1. 整数除法的结果往往采用舍尾法,即完全忽略小数部分因此 3 / 5 = 0.6 时,后面的小数部分被完全舍去,只剩下整数部分,也就是 0
  2. printf 函数只负责按照指定的格式输出数据,对于数据的类型(储存方式、占用空间),printf 函数没有决定权
    所以,我们尽管在第二个式子中让 printf 函数以小数形式输出数据,也只能得到 0 的结果
  3. 在第三个式子中,我们使用强制类型转换,让 5 的类型直接提升到小数型
    所以 0.6 被正确的计算了出来
  4. 在第四个式子中,我们将 5 变成小数类型,从而让自动类型转化发生,顺利地实现了 0.6 的计算结果

同时,对于字符与整数之间的互相转换,我们也提供一个示例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kla3Txtm-1683023217337)(https://foruda.gitee.com/images/1677755005745971984/dfcc0096_871414.png "1672926703948.png")]

/*
    char_int.c
    Show the relation of the char & int
    BeginnerC
*/
#include <stdio.h>
int main()
{
    printf("%d = %c\n", 'A', 'A');
    return 0;
}

在这个案例之中,我们很明显的发现,字符就是数字的另一种表现形式

而这种“字符-数字"的对应关系,被称为字符集,C语言一般使用 Ascil 字符集

% 取余运算

取余运算只针对于整数,它负责计算一次除法以后的余数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Knw2KLv1-1683023217337)(https://foruda.gitee.com/images/1677755017525103063/d5a00dbd_871414.png "1672881978170.png")]

/*
    mod.c
    Show the usage of mod
    BeginnerC
*/
#include <stdio.h>
int main()
{
    int a = 7, b = 3;
    printf("%d\n", a % b);
    return 0;
}

逻辑运算

在 C语言 中,逻辑运算是一种对于数据的比较,它的运算结果遵循一个法则:非0为真

我们将所有的逻辑运算整合为一张表格,罗列如下

符号含义举例
>=判断前者的数据是否大于等于后者5 >= 3
判断前者的数据是否大于后者1 > 2
<=判断前者的数据是否小于等于后者6 <= 9
<判断前者的数据是否小于后者0 < 1
==判断两个数据是否相等1 == 1
!=判断两个数据是否不相等0 != 1
!将逻辑运算的结果取反!(1 != 1)

学习一项程序设计知识的最好办法就是动手实践,因此,我们亦给出与之相关的程序

/*
    logical.c
    Show the usage of logical calc
    BeginnerC
*/
#include <stdio.h>
int main()
{
    int result = 0;
    result = 2 >= 1;
    printf("%d\n", result);
    result = 0 > 1;
    printf("%d\n", result);
    return 0;
}

在这个案例中,我们展现了其中两种逻辑运算,可以很清晰的发现,逻辑运算的结果就是一个整数,判断为真的时候,为1,判断为假的时候,就是0

我们鼓励你更多的用自己编写程序的方式,验证我们上述的观点。

同时,我们指出,这些最基本的逻辑运算,还可以通过组合的方式,构成更加复杂的逻辑关系式。

我们将这些组合的符号,也罗列如下表

符号名称含义举例
&&(1 != 2) && (3 == 4)
(1 != 2)(3 == 4)

他们的区别,我们也用一个程序展示

/*
    or_and_compare.c
    Show the difference of && and ||
    BeginnerC
*/
#include <stdio.h>
int main()
{
    int result;
    result = (1 != 2) && (3 == 4);
    printf("%d\n", result);
    result = (1 != 2) || (3 == 4);
    printf("%d\n", result);
    return 0;
}

如您所见,&&(与运算)需要参与表达式同时为真(不为0)时,才可以判断为真;而||(或运算))则只需要其中一个为真即可。

学习,最重要的还是实践,因此,我们鼓励你写出与之相关的程序,验证自己的想法。

条件表达式

条件表达式根据逻辑运算的结果决定所执行的代码,如您所见

/*
    condition_statement.c
    Show the usage of condition statement
    BeginnerC
*/
#include <stdio.h>
int main()
{
    1 > 0 ? puts("1 > 0"), puts("That's true") : puts("1 <= 0"), puts("That's false");
    return 0;
}

可以发现,这个程序既顺利,又不顺利的完成了任务。

顺利的在于,条件表达式正确判断出了 1 > 0 的结果,输出了 1 > 0

而不顺利的在于,后面本应该不被输出的 That's false 也被输出了

这是因为,我们使用了 逗号表达式,它的作用在于分割语句为一个个独立的模块。

而在这里,实际上我们的语句被按照如下的方式进行“理解"

1 > 0 ? puts("1 > 0"), puts("That's true") : puts("1 <= 0")puts("That's false");

换而言之,不论如何,后面的 That's false 都要被输出。

解决的办法,在于用括号进行包裹。

/*
    condition_statement_final.c
    Show the usage of condition statement
    BeginnerC
*/
#include <stdio.h>
int main()
{
    1 > 0 ? puts("1 > 0"), puts("That's true") : (puts("1 <= 0"), puts("That's false"));
    return 0;
}

事实上,这种办法非常容易引发不必要的麻烦,也降低了代码的可读性,因此,我们更推荐你使用 if 语句 进行替代。

condition ? statement_true : statement_false;
// Equal to
if (condition)
{
	statement_true;
}
else
{
	statement_false;
}

if语句 具有更好的可理解性与可阅读性

位运算

在计算机中,一切的数据实际上都是二进制数据,而位运算,就是专注于二进制数字的运算。

我们也将他们罗列如下表

运算符含义举例
>>右移运算,将二进制位向右移动一位,高位补01 >> 2
<<左移运算,将二进制位向左移动一位,低位补01 << 2
&按二进制比特位进行与运算(非逻辑运算)8 ^ 9
按二进制比特位进行或运算(非逻辑运算)8 & 9
按二进制比特位进行异或运算(非逻辑运算)8 ^ 9

学习一项知识点的最好办法,就是动手实践,因此,我们也将给出程序,进行验证

/*
    bit_calc.c
    Show the usage of bit calc
    BeginnerC
*/
#include <stdio.h>
#include <string.h>
/*
    PrintNumber
        Print the number in binary form
    Argument
        number
            The number want to print
    Return Value
        No value will return
*/
void PrintNumber(int number)
{
    char buffer[1024] = {};
    char bit[2] = {};
    if (0 == number)
    {
        printf("0");
    }
    while (number)
    {
        sprintf(bit, "%d", number % 2);
        strcat(buffer, bit);
        number /= 2;
    }
    for (int i = strlen(buffer) - 1;i >= 0;i--)
    {
        printf("%c", buffer[i]);
    }
    puts("");
}
int main()
{
    /* Show the usage of >> & <<  */
    PrintNumber(8);
    PrintNumber(8 >> 1);
    PrintNumber(8 << 1);
    /* Show the usage of & and ^ and | */
    PrintNumber(4);
    PrintNumber(4 & 8);
    PrintNumber(4 ^ 8);
    PrintNumber(4 | 8);
    return 0;
}

这个程序涉及到了许多超前的知识点,比如循环语句,比如函数,在这里我们只需要清楚一点

PrintNumber 函数会打印出一个十进制数字的二进制数字形式。

而如我们所见,8 的 二进制形式 是 1000,4 的 二进制形式 是 100

先解析前2条语句。

  1. 8 >> 1,右移一位,所以变成了 0100,也就是图中输出的 100
  2. 8 << 1,左移一位,所以变成了 10000,也就是图中输出的 10000

这就是左移与右移运算的基本理解,实际情况下(比如遇到有符号与无符号数字)时会更加复杂,在这里,我们更鼓励你通过实践去掌握他们。

再看后面的三条语句。

  1. 4 & 8
    也就是 0100 & 1000,因为与运算的准则就是:同时为1才是1,所以这一运算的结果为 0000,也就是最后输出的二进制 0

  2. 4 ^ 8
    也就是 0100 ^ 1000,因为异或运算的准则是:参与对比的两个比特位不一样时,为1,所以这一运算的结果为 1100

  3. 4 | 8

    也就是 0100 | 1000,因为与运算的准则是:两个位中的一位为1,结果就是1,所以这一运算的结果为 1100

取地址运算

&var 可以提取出一个变量的内存地址,这一点对于指针而言颇为重要。

自增运算与自减运算

var++ 将当前变量的数值加一,var--,将当前变量的数值减一

var++++varvar----var的区别在于最终表达式的结果(是原变量数值,还是自增/自减后的结果),这一点,我们鼓励你写出明确的程序于以测试。

运算的优先级问题

每一种运算都有先后次序之分,比如,1 + 2 * 3 的次序就是先计算 2 * 3,再计算 两者 的和。

我们认为,单纯背诵优先级表没有多大的意义,最好的办法就是:

  1. 使每个表达式都尽可能简单
  2. 对于需要优先运算的表达式,用括号进行包裹
  3. 动手实践,自己测试

计算表达式的占用空间

很多时候,许多复杂的表达式难以估算其类型的占用空间,对此,C语言提供了 sizeof 运算符帮助我们进行评估。

/*
    sizeof_try.c
    Use the sizeof to solve the problem
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char c = 'A';
    printf("%d\n", sizeof(3.1415926));
    printf("%d\n", sizeof(3.14));
    printf("%d\n", sizeof('A'));
    printf("%d\n", sizeof('A' + 'B'));
    printf("%d\n", sizeof(c));
    printf("%d\n", sizeof(5 + 1));
    return 0;
}

可以发现,很多时候,表达式所占用的字节空间,是出乎我们的意料的,因此,实践动手才是解决问题的唯一方案。

register 寄存器变量

寄存器变量相对于一般的变量而言,具有更高的性能。

因为寄存器变量在寄存器中进行读写,而一般的自动变量在内存中进行读写。

/*
    register.c
    Use the register value
    BeginnerC
*/
#include <stdio.h>
#include <time.h>
int main()
{
    int start = 0, end = 0;
    register int count_1 = 0;
    int count_2 = 0;
    start = clock();
    for (int i = 0;i < 1000000;i++)
        count_1++;
    end = clock();
    printf("%f s\n", (double)(end - start) / CLOCKS_PER_SEC);
    start = clock();
    for (int i = 0;i < 1000000;i++)
        count_2++;
    end = clock();
    printf("%f s\n",(double)(end - start) / CLOCKS_PER_SEC);
    return 0;
}

值得注意的是,寄存器变量不能使用取地址运算

数据的读取

在能够设计出合理的数据方案之后,我们也要关注数据的读取。

C语言标准函数库 stdio.h 提供了许多有用的函数,帮助我们读取数据。

学习一门技术的最好方式,就是动手实践,下面,我们集中展示他们的使用

/*
    read.c
    Read the data to the value
    BeginnerC
*/
#include <stdio.h>
int main()
{
    int number = 0;
    double number_2 = 0;
    char c = '\0';
    scanf("%d %lf %c", &number, &number_2, &c);
    printf("%d %f %c\n", number, number_2, c);
    return 0;
}

在这一案例中,我们使用标准库函数 scanf 函数为我们读入 整数 小数 字符 三种类型的数据

而作为一种格式输入函数,scanf 函数也提供了许多其它的用法

/*
    read_2.c
    Read the data to the value
    BeginnerC
*/
#include <stdio.h>
int main()
{
    int ip_first = 0;
    int ip_second = 0;
    int ip_third = 0;
    int ip_fourth = 0;
    scanf("%d.%d.%d.%d", &ip_first, &ip_second, &ip_third, &ip_fourth);
    printf("%d %d %d %d\n", ip_first, ip_second, ip_third, ip_fourth);
    return 0;
}

在这一案例之中,我们用’.‘与’%d’格式字符串进行组合,最后让我们能够顺利地实现 IP地址(IPV4)的输入

对于 scanf 函数的输入,我们整理如下表

符号含义
%%百分号
%lf小数
%d %u %i整数
%s %c字符串/字符
%x %o十六进制数字或八进制数字

值得注意的是,字符串实际上就是多个相连的字符

/*
    read_3.c
    Use the scanf to read the string
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char string[256] = {};
    scanf("%s", string);
    printf(string);
    return 0;
}

在这个程序之中,我们使用了数组来保存字符串,可以发现,这个数组的最大容量是 256 个字符。

在我们使用 %s 读取字符串,可以发现,在这个案例之中,我们并没有使用取地址运算,这是因为,数组名本身就是数组元素的起始地址

/*
    read_4.c
    Show the usage of getchar/putchar
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char c = '\0';
    c = getchar();
    putchar(c);
    return 0;
}

在这个程序中,我们使用 getchar 函数读取单一的字符,并用 putchar 输出他们