【C语言航路】第一站:初识C语言(二)

简介: 【C语言航路】第一站:初识C语言(二)

三、变量与常量

1.定义变量的方法

int age = 150;
float weight = 45.5f;
char ch = 'w';

如上所示,类型加一个自定义的名字,并且同时给其赋值,即为定义变量。

2.变量的命名

(1)变量的名字需要遵循以下五条规则

1.只能由字母(包括大写和小写)、数字和下划线( _ )组成。

2.不能以数字开头。

3.长度不能超过63个字符。

4.变量名中区分大小写的。

5.变量名不能使用关键字。

(2)几个经典的错误,标准的零分

int #ac    //不满足规则1,使用了#
int 2b     //不满足规则2,使用了数字开头
int char   //不满足规则5,使用了关键词

3.变量的分类

变量分为局部变量和全局变量

区分方法:判断在main函数的大括号外面还是大括号里面

#define _CRT_SECURE_NO_WARNINGS 1
int a = 10;
int main() 
{
  int b = 20;
  return 0;
}
int c = 30;

显然a和c为全局变量,b为局部变量

那么如果出现下面这种情况会发生什么呢?

#include<stdio.h>
int a = 10;
int main()
{
  int a = 20;
  printf("%d", a);
  return 0;
}

也就是说

局部变量和全局变量名字冲突了,此时遵循“局部优先”这条规则。

运行结果如下

这里有两个小建议

①尽量少使用全局变量

②避免局部变量与全局变量产生冲突

那么变量改如何使用呢?

#include<stdio.h>
int main()
{
  int num1 = 0;
  int num2 = 0;
  int sum = 0;
  scanf("%d %d", &num1, &num2);
  sum = num1 + num2;
  printf("%d", sum);
  return 0;
}

这是一个加法的程序

运行结果如图所示。

4.变量的作用域和生命周期

(1)作用域

首先给出作用域的概念:

作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

①局部变量的作用域是变量所在的局部范围。

然后我们运行一下以下代码

int main()
{
  {
    int a = 1;
  }
  printf("%d", a);
  return 0;
}

这个代码编译时会产生报错。也就是说,a出了他的大括号后会被报错。他的作用域就是他所属的大括号。

②全局变量的作用域是整个工程。

#include<stdio.h>
int g_a;
void test()
{
  printf("test:%d\n", g_a);
}
int main()
{
  printf("%d\n", g_a);
  test();//调用函数
  return 0;
}

这两段代码中g_a的生命周期都是整个工程文件。

这里先拓展以下extern这个的意思是什么?

extern——这个关键词可以声明同一个工程项目中的外部变量,同上图所示,我们在test_10_16这个工程中创建了两个.c文件,我们在add.c文件中定义了一个一个变量,然后在test.c中我们使用了extern这个关键词来声名变量a,这时候我们就可以在test.c这个文件中使用a这个变量了。

③几个经典的错误,标准的零分

在这里我们有可能会出现一些由于概念不清,导致出现了很多奇葩的错误,在这里我举出几个较为经典的错误。

a.如下列代码所示,声名变量放在了使用后面,这是一个经典的因果倒置错误,我们的代码首先是先经过程序编译以后才会运行的,在编译的时候,我们的代码在test函数中的g_a还没有声名,就已经被使用,所以在此处产生了编译错误。

#include<stdio.h>
void test()
{
  printf("test:%d\n", g_a);
}
int g_a = 2022;//定义
int main()
{
  printf("%d\n", g_a);
  test();//调用函数
  return 0;
}

b.在下面的这段代码中也产生了一个很严重的问题,因为有一个printf不在任何一个函数里面,在运行的过程中根本运行不到他上面,这就自然编译器会报错了。

#include<stdio.h>
int g_a = 2022;//定义
void test()
{
  printf("test:%d\n", g_a);
}
int g_a = 2022;//定义
printf("%d", g_a);
int main()
{
  printf("%d\n", g_a);
  test();//调用函数
  return 0;
}

④声明和定义的区别

在上文中我们说了很多名词,其中有时候说声明,有时候说定义,那这两个有区别吗

其实,我们说,是有区别的。那区别又在哪里呢?

我们这里给出一段代码,来区分一下

int g_a;//声明
void test()
{
  printf("test:%d\n", g_a);
}
int g_a = 2022;//定义
int main()
{
  printf("%d\n", g_a);
  test();//调用函数
  return 0;
}

       在这段代码中,我们直接对其进行int g_a=2022时,也就是第七行,此时称作定义。无论这个定义在前排还是靠后的位置,都称作定义,但是有可能会出现一个问题,我的变量定义在使用的后面了,这个时候编译器就会直接报错。那么为了处理这种情况,我们在第一行补上一个int g_a,但是没有给其赋值,此时,称作声明,也就是为了防止我的编译器报错,我先声明一下,我有这个变量,然后后面在对其定义。当然我也可以将定义放在第一行,此时的定义就等同于声名了。

       这一块跟函数的声明和定义类似,函数我们将在后面的文章中讲到。

(2)生命周期

变量的生命周期指的是变量的创建到变量的销毁之间的这一个时间段

①局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束

如图所示a的生命周期仅仅在内层的大括号里面,所以第一个printf可以正常打印,但第二个会报错

②全局变量的生命周期是:   整个程序的生命周期

也就是程序什么时候结束,全局变量什么时候销毁,从而释放他所占用的内存。

5.常量

(1)常量的分类

我们将变量分为以下四种:

1.字面常量

2.const修饰的常变量

3.#define修饰的标识符常量

4.枚举常量

(2)字面常量

#include<stdio.h>
int main()
{
    //1           整数字面常量
  //300         整数字面常量
  //3.14        浮点数字面常量
  //'a'         字符字面常量
  //"abcjdksa"  字符串字面常量
  return 0;
}

(3)const修饰的常变量

对于这个const修饰的常变量,有很多人都比较疑惑,这名字起的很奇怪,到底是什么意思呢,其实,常变量,常是修饰词,是由const进行修饰的,变量才是名词,才是主语。

也就是说,const修饰的常变量本质是一个变量,只不过由于有了一个const修饰,给其赋予了常属性(也就是不可改变的,如果改变,编译器则会报错)。具有变量的一些性质。这一点类似于我们英语中的动名词。

#include<stdio.h>
int main()
{
  const int num =100 ;
  num = 200;
  return 0;
}

如图所示,在这里我们已经将num,使用const修饰了,但是我们还想要强迫改变num,那么结局只有一个,那便是编译器报错。

这里也是一个经典的错误,标准的零分,许多玩家都会在这里掉坑里。

(4)#define修饰的标识符常量

#define M 200
#include<stdio.h>
int main()
  int a[M] = { 0 };
  printf("%d", M);
  M = 100;
    return 0;
}

如图所示代码中,我们使用define将M全部替换成200,注意define这条指令是在预处理阶段进行的,将所有M全部替换成200。

我们知道,数组中的大小如果在定义时要指定的话,则里面必须是一个常数。显然第一条语句是正确的。这说明M本质是一个常数

我们第二跳语句中打印M,打印出来结果正好是200,这说明M被替换成200

我们第三条语句中想要强行修改M的值,很遗憾,报错了。这再次说明M是一个常数,具有常属性,不可被修改

(5)枚举常量

这个概念我们在数学中肯定是遇见过的,我们数学中有一种是枚举法,一般用于计算古典概型时候使用。

枚举在c语言中也是一样的,是一一列举的意思。跟数学的古典概型有区别的是,数学中的古典概型是有限个 ,或者是无限可列的但必须满足某种规律下(比如几何分布,巴斯卡分布等)。而我们c语言中的枚举仅仅代表前者,是有限个。比如血型,性别,三原色等等。

枚举的标识符是enum

#include<stdio.h>
enum Color {
  RED,//0
  //RED,GREEN,BLUE是枚举类型Color的可能取值,同时也是常量,所以叫枚举常量
  GREEN,//1
  BLUE//2
};
int main()
{
  enum Color c = BLUE;
  printf("%d\n", RED);
  printf("%d\n", GREEN);
  printf("%d\n", BLUE);
  printf("%d", c);
}

如上代码所示,我们定义一个枚举类型,并执行以下语句。输出的结果分别是0 1 2 2,这说明枚举常量中第一个被默认赋值为0,后面每一个依次递增1。

关于枚举常量,我在后面的文章中还会更加详细的展开讲述。

(6)几个经典的错误,标准的零分

①define 定义标识符常量后额外加了分号,有很多人在这块掉入了坑里,其实这块是不需要加分号的

②对const修饰词理解不够到位,误以为常变量是一个常量,其实是一个变量,不妨我们证明一下。我们知道,定义数组时的长度为一个常量,如果是变量则会报错,由这条结论我们便可推出

编译器说这块报错了,那么证明完毕,N不可以使用在数组上,因为N本质是一个变量。

四、 字符串+转义字符

1.字符串

(1)定义

"hello world.\n"

形如这种由双引号引起来的一串字符称为字符串字面值,或者简称字符串

(2)字符串的结束标志

字符串的结束标志是一个\0的转义字符。在计算字符串长度时候\0是结束标志,不算做字符串内容

(3)字符串在数组中的存储

我们先来思考一下以下代码结果是如何呢?

#include<stdio.h>
int main()
{
  char arr1[] = "hello";
  char arr2[] = { 'h','e','l','l','o' };
  printf("%s\n", arr1);
  printf("%s\n", arr2);
  return 0;
}

运行结果如下

为什么会第二个数组会出现这种情况呢?

这是因为字符串的结束标志是\0,第一个数组中,由于字符串常量默认最后一个是\0,所以可以正常打印。而对于第二个就比较糟糕了,因为他是一个字符一个字符输入进去的,这就导致了他的后续可能还有一堆其他乱七八糟的东西,也会一直打印出来,直到出现第一个\0。

这是监视窗口里面的数据,再次印证了字符串在数组中的存储。

(4)字符串的长度

   c语言中提供了一个库函数,叫做strlen,这个函数可以计算字符串的长度,统计的是\0之前的字符个数。strlen------string lenth ,属于string这个库中。

#include<stdio.h>
int main()
{
  char arr1[] = "abc";
  char arr2[] = { 'a','b','c' };
  printf("%d\n", strlen(arr1));
  printf("%d\n", strlen(arr2));
}

运行结果为

原因为,第一个数组默认有一个\0,而第二个c后面具体是什么位置,所以会一直不断的找到\0为止,所以第二个其实是一个随机值。

2.转义字符

(1)什么是转义字符?

我们先来看这两段代码

#include<stdio.h>
int main()
{
  printf("abcdnf");
  return 0;
}
#include<stdio.h>
int main()
{
  printf("abcd\nf");
  return 0;
}

输出结果分别为

      此时我们会发现,由于这个\n导致了我们原本n的意思发生转变。因此,转义字符,就是将原来的字符转变了一个意思。

(2)都有哪些转义字符

转义字符 释义
\? 在书写连续多个问号时使用,防止他们被解析成三字母词
\' 用于表示字符常量'
\"

用于表示一个字符串内部的双引号

\\

用于表示一个反斜杠,防止它被解释为一个转义序列符

\a

警告字符,蜂鸣

\b 退格符

\f

进纸符
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符
\ddd ddd表示1~3个八进制的数字。如\130X
\xdd dd表示2个十六进制的数字。如\x30 0

这就是所有的转义字符,这里详细讲解一下每一个转义字符

①    转义字符  \?

这个转义字符,主要是避免三字母词使用的,那什么是三字母词呢。这里给出一段代码。

int main()
{
  //    ??) - 三字母词 - ]
  printf("(are you ok??)");
  return 0;
}

在一些编译器上会将??)这个转变成  ] 这个字符

为了避免这种情况,我们可以使用\?这个转义字符

int main()
{
  //    ??) - 三字母词 - ]
  printf("(are you ok\?\?)");
  return 0;
}

这样的话在任何编译器上都不会有问题了

②转义字符\'  \”  \\

  这三个转义字符的作用是在想要打印出' " \这三种字符时候,我们无法直接打印出来,得用转义字符来表达这些字符

#include<stdio.h>
int main()
{
  //printf("%c\n", ''');
  printf("%c\n", '\'');
  //printf("%c\n", '"');
  printf("%c\n", '\"');
  return 0;
}

如上代码所示,只有加上转移字符后,才能运行成功,否则报错。

③转义字符\ddd  \xdd

这两个转移字符跟其他的转义字符有些区别

\ddd这个转义字符中,他会将后面的1~3个八进制数字先转换成十进制数字,然后在ASCII表中找到对应的字符。这个字符就是最终的结果

同理

\xdd这个转义字符也是将后面的2个十六进制数先转换成十进制数,然后在ASCII表中找出对应的字符。这个字符就是最终的结果。

这里附上ASCII表

下面给出几个例子

#include<stdio.h>
int main()
{
  printf("%c\n", '\073');//073是8进制数字
    printf("%c\n", '\x46');
  return 0;
}

运行结果为

#include <stdio.h>
int main()
{
  printf("%d\n", strlen("abcdef"));
  printf("%d\n", strlen("c:\test\628\test.c"));
  return 0;
}

运行结果为

(3)字符'0',数字0,以及字符'\0'的对比

'0'的ASCII值为48

0和'\0'本质一样,只是同一种东西的不同表现形式,0是数字形式,'\0'是字符形式

比如下面这段代码中

int main()
{
  char arr[] = { 'a', 'b', 'c' ,0};
  printf("%s\n", arr);
  return 0;
}

这个数组在内存中为

所以说0和'\0'为同一东西的不同形式。

类似的还有一个例子

#include<stdio.h>
int main()
{
  printf("%d\n", 100);
  printf("%c\n", 100);
  return 0;
}

这说明了100这个数字在数字中和字符中的不同表现形式


总结

本节主要讲述了变量,常量,字符串,以及转移字符等知识点的讲解。

不要忘记留下你的赞赞和关注哦

本站未完,欲知后事,请看下节

相关文章
|
6月前
|
编译器 Linux C语言
【C语言航路】第十五站:程序环境和预处理(下)
【C语言航路】第十五站:程序环境和预处理(上)
42 0
|
6月前
|
存储 自然语言处理 编译器
【C语言航路】第十五站:程序环境和预处理(上)
【C语言航路】第十五站:程序环境和预处理
43 0
|
6月前
|
存储 编译器 C语言
【C语言航路】第十四站:文件(下)
【C语言航路】第十四站:文件
44 0
|
6月前
|
存储 编译器 数据库
【C语言航路】第十四站:文件(上)
【C语言航路】第十四站:文件
39 0
|
6月前
|
程序员 C语言 C++
【C语言航路】第十三站:动态内存管理(下)
【C语言航路】第十三站:动态内存管理
36 0
|
6月前
|
编译器 C语言
【C语言航路】第十三站:动态内存管理(上)
【C语言航路】第十三站:动态内存管理
50 0
|
6月前
|
编译器 Linux C语言
【C语言航路】第十二站:自定义类型:结构体、枚举、联合体
【C语言航路】第十二站:自定义类型:结构体、枚举、联合体
41 0
|
6月前
|
存储 编译器 C语言
【C语言航路】第十一站:字符串、字符和内存函数(下)
【C语言航路】第十一站:字符串、字符和内存函数
52 0
|
6月前
|
算法 安全 编译器
【C语言航路】第十一站:字符串、字符和内存函数(中)
【C语言航路】第十一站:字符串、字符和内存函数
59 0
|
6月前
|
IDE 开发工具 C语言
【C语言航路】第十一站:字符串、字符和内存函数(上)
【C语言航路】第十一站:字符串、字符和内存函数
34 0