【C语言学习之路】第一篇:与C语言的初次见面


阅前说明

C语言学习之路系列博客,是博主自己在学习C语言的过程中所做的笔记,把知识框架整理记录下来,为了后续回顾与复习,同时也希望该博客可以帮助到一些正在学习C语言的小伙伴。博客内容如有错误或疏漏,请大家指出,谢谢啦,博主一定多多学习,及时改正过来。

使用的编译器:Visual Studio 2019(下载地址)


本篇前言

本篇大致介绍了C语言的基础知识,希望能让大家对C语言有一个大概的认识。每个知识点只是简单介绍,后续篇章都会一一详细讲解。


点击可直接跳转

文章目录

    • @[toc]
    • (1)什么是C语言?
      • 第一个C语言程序
    • (2)数据类型
      • 每种类型的大小是多少?
      • 计算机中的单位
      • 计算机中的进制
    • (3)变量、常量
      • 变量的定义
      • 变量的分类
      • 变量的作用域和生命周期
      • 常量
    • (4)字符串、转义字符、注释
      • 字符串
        • 1、什么是字符串:
        • 2、字符串的存储:
        • 3、字符串的结束标志:
        • 4、获取字符串的长度:
        • 5、计算字符数组/数组的长度:
        • 6、一定要区分开 '0' 和 '\0' 和 0
      • 转义字符
        • 1、什么是转义字符
        • 2、C语言中有哪些转义字符
        • 3、转义字符的应用
        • 4、ASCII编码
        • 5、测试题:
      • 注释
    • (5)操作符
      • 算数操作符
      • 移位操作符
      • 位操作符
      • 赋值操作符
      • 单目操作符
      • 关系操作符
      • 逻辑操作符
      • 条件操作符
      • 逗号表达式
      • 下标引用、函数调用、结构成员
    • (6)常见关键字
      • 1、关键字 `typedef`
      • 2、关键字 `static`
        • `static` 修饰局部变量
        • `static` 修饰全局变量
        • `static` 修饰函数
    • (7)`#define` 定义宏
    • (8)选择语句
    • (9)循环语句
    • (10)函数
    • (11)数组
    • (12)指针
        • 1、什么是内存
        • 2、获取变量的地址
        • 3、地址的存储 - 指针变量
        • 4、指针变量的大小
    • (13)结构体

(1)什么是C语言?

C语言广泛应用于底层开发(不做详细介绍,可百度)

【C语言学习之路】第一篇:与C语言的初次见面_第1张图片
  • 计算机语言发展过程

    • 二进制语言 0/1
  • 汇编语言 - 助记符 ADD/AND

    • 高级语言

第一个C语言程序

//test.c(testcode project)
#include 
int main()
{
     
  printf("hello C language\n");
  return 0;
}

main函数是程序的入口

一个工程/项目可以有多个.c文件,但只能有一个main函数


(2)数据类型

char     //字符数据类型 
short    //短整型 
int     //整型
long     //长整型 
long long  //更长的整型 
float    //单精度浮点数 
double    //双精度浮点数
  • C语言中没有没有字符串类型?

    没有

  • 为什么会出现这么多类型?

    更加丰富的表达生活中的各种值,可以提高空间利用率

每种类型的大小是多少?

这里需要用到 sizeof() 操作符 - 获取类型或者变量所占空间大小(以字节为单位)

#include
int main()
{
     
	//每种类型的大小是多少?
	printf("%d字节\n", sizeof(int));
	printf("%d字节\n", sizeof(short));
	printf("%d字节\n", sizeof(long));
	printf("%d字节\n", sizeof(long long));
	printf("%d字节\n", sizeof(char));
	printf("%d字节\n", sizeof(float));
	printf("%d字节\n", sizeof(double));
	printf("%d字节\n", sizeof(long double));
	return 0;
}

运行结果(x86平台):

【C语言学习之路】第一篇:与C语言的初次见面_第2张图片

C语言标准:规定只要sizeof(long)>=sizeof(int)就可以了,不需要一定大于

计算机中的单位

bit - 比特位 - 一个比特位存放一个二进制位 0/1

byte - 字节 - 一个字节 = 8bit

kb - 1kb = 1024byte

mb - 1mb = 1024kb

计算机中的进制

二进制:0 1

八进制:0 ~ 7(三位二进制等于一位八进制)

十进制:0 ~ 9

十六进制:0 ~ 9 , A ~ F(四位二进制等于一位十六进制)


(3)变量、常量

变量的定义

注:建议定义变量时给它初始化一个值

#include
int main()
{
     
	//推荐定义变量时给它初始化一个值,比如:
	int age = 0;
	double weight = 52.3;
	return 0;
}

变量的分类

  • 局部变量
  • 全局变量(不提倡使用,后续讲原因)
#include
int a = 100;  //全局变量 - { }外部定义的
int main()
{
     
	int a = 10;  //局部变量 - { }内部定义的
	printf("a = %d", a);  //输出a = 10
	return 0;
}

思考:为什么会输出 a = 10 呢?

当局部变量和全局变量名称冲突时,局部优先

不建议把全局变量和局部变量的名字写成一样(如上面代码中的 a)


变量的作用域和生命周期

变量的作用域

这个变量可以在哪里使用,哪里就是它的作用域

  • 局部变量的作用域

    变量所在的局部范围

#include
int main()
{
     
	int a = 10;
	printf("%d\n", a);
    
	{
     
		int b = 20;  //b的作用域为此 {} 内
	}
	//printf("%d\n", b);  //error: "b"是未声明的标识符
	return 0;
}
  • 全局变量的作用域

    整个工程(在同一工程的其它文件下使用需要用 extern 声明一下变量)

/*源文件demo2.c(test_4_5 project)*/

int x=5;
/*源文件demo.c(test_4_5 project)*/

#include
int main()
{
     
    //声明 demo2.c 文件中的全局变量
    extern int x;
    
    printf("%d\n", x);
    return 0;
}

变量的生命周期

变量的创建到销毁之间的时间段

//局部变量的生命周期:进入局部范围生命开始,出局部范围生命结束
//全局变量的生命周期:就是整个程序的生命周期

#include
int main()  //一个程序的生命周期就是 main 函数的生命周期
{
     
	{
       //变量 a 声明开始
		int a = 10;
		printf("%d\n", a);
	}  //变量 a 声明结束
	return 0;
}

常量

C语言中常量分为以下几种:

  • 字面常量
  • const修饰的常变量
  • #define定义的标识符常量
  • 枚举常量

1、字面常量

//字面常量
3.14;
10;
'a';  //字符常量 - 由一对单引号引起来的单个字符
"abcdef";  //字符串常量

2、const修饰的常变量

常变量 - 具有常属性(不能被改变的属性) - 本质上还是变量,不能当常量使用

这样是错误的(只有在C99语法下才支持变长数组)

【C语言学习之路】第一篇:与C语言的初次见面_第3张图片

这样是可以的

int arr[10] = {
      0 };
const int N = 9;
//思考:这两种写法正确吗?
arr[N] = 10;  //这样是可以的
N = 20;  //error

3、#define 定义的标识符常量

使用 #define 指令为程序中的常量赋予有意义的名称,它只是一个符号,在预编译阶段进行字符替换

(如下:所有 MAX 符号将被替换成 100)

#include
#define MAX 100  //#define 定义的标识符常量
int main()
{
     
    MAX = 200;  //error
    return 0;
}

4、枚举常量

枚举常量 - 可以一一列举的常量:比如星期、三原色、性别

//定义枚举常量Sex
enum Sex
{
     
	//枚举常量默认的值: 0 1 2 3 …… 依次递增 - 其值不可被更改
	Male,    // 0
	Female,  // 1
	Secret   // 2
};

#include
int main()
{
     
	enum Sex S1 = Male;
	printf("%d\n", Male);  	 //输出:0
	printf("%d\n", Female);  //输出:1
	printf("%d\n", Secret);  //输出:2
	return 0;
}

枚举常量不可以赋值,但可以在定义的时候 指定值,这个可以认为是定义值,而不是赋值。

enum Sex
{
     
	//这个可不是赋值哦
	Male = 3,
	Female = 100,
	Secret
};
printf("%d\n", Male);
printf("%d\n", Female);
printf("%d\n", Secret);

打印其值,运行结果为:
3
100
101


(4)字符串、转义字符、注释

字符串

1、什么是字符串:

字符串就是由双引号引起来的一串字符

"hello world"

2、字符串的存储:

因为C语言没有字符串这种数据类型,所以要用到「 字符数组 」来存储字符串(每个元素都是字符类型的数组)

#include
int main()
{
     
	char arr[] = "hello";
    //['h']['e']['l']['l']['o']['\0']
	return 0;
}

3、字符串的结束标志:

C语言规定,在每一个字符串常量的结尾,系统都会自动加一个字符 ‘\0’ 作为该字符串的“结束标志符”,系统据此判断字符串是否结束。计算字符串长度时,’\0’ 不算作字符串的内容哦;但是计算数组长度(或元素个数或所占内存空间大小),’\0’ 要算进去。

通过调试,打开监视窗口可看到字符数组内部情况

【C语言学习之路】第一篇:与C语言的初次见面_第4张图片 【C语言学习之路】第一篇:与C语言的初次见面_第5张图片
  • 接下来我们用代码来验证一下:
#include
int main()
{
     
	char arr1[] = "abc";  //字符串
	char arr2[] = {
      'a','b','c' };  //是字符数组,但不代表字符串,末尾没有'\0'
	
	printf("%s\n", arr1);
	printf("%s\n", arr2);
	return 0;
}

调试这段代码,我们发现,arr1 数组尾部有结束标志 ‘\0’ ,而 arr2 数组后面却没有 ‘\0’

【C语言学习之路】第一篇:与C语言的初次见面_第6张图片

再来看看运行结果,arr1 正常输出,而 arr2 后面却乱码了,输出了一堆奇怪的符号,这是为什么呢?

因为在输出 arr1 中的内容过程中,遇到了 ‘\0’ 是字符串的结束标志,所以停止输出;

而输出 arr2 中的内容时,输出完 abc 没有遇到字符串结束标志 ‘\0’ ,不知道什么时候停下来,而后面的内存空间的内容是未知的,放的是啥我们不知道,所以输出乱码。

![](https://img-blog.csdnimg.cn/img_convert/3984e84c9a5a3f9e3c9507928fe6bd98.png style="zoom:80%)

现在我们修改代码,主动在 arr2 后面放上 ‘\0’ ,输出看看效果

#include
int main()
{
     
    //定义并初始化字符串的两种方式
    //一定不要忘记结束标志 '\0' 了哦
	char arr1[] = "abc";
	char arr2[] = {
      'a','b','c','\0' };
    
	printf("%s\n", arr1);
	printf("%s\n", arr2);
	return 0;
}

运行结果:都能打印出 abc 由此可见,字符串的结束标志是至关重要的

【C语言学习之路】第一篇:与C语言的初次见面_第7张图片

4、获取字符串的长度:

这里要使用到一个函数 strlen() - string length - 调用该函数需要引用头文件

#include
#include  //调用strlen()函数
int main()
{
     
	char arr1[] = "abc";
	char arr2[] = {
      'a','b','c' };
	char arr3[] = {
      'a','b','c','\0' };
	//打印字符串的长度
	printf("%d\n", strlen(arr1));
	printf("%d\n", strlen(arr2));
	printf("%d\n", strlen(arr3));
	return 0;
}

运行结果:获取字符串的长度并输出时,字符数组 arr2 因为末尾没有字符串结束标志 ‘\0’ ,不知道长度到底是多少,所以输出随机值,获取不了字符串的长度。

注:在计算字符串长度的时候 ‘\0’ 是结束标志,不算作字符串的内容。

【C语言学习之路】第一篇:与C语言的初次见面_第8张图片

5、计算字符数组/数组的长度:

计算数组长度(或元素个数或所占内存空间大小), ‘\0’ 要算进去

#include
int main()
{
     
	char arr[] = "abc";
	int len = 0;
    //计算数组 arr 的长度:数组内存总大小(字节)/单个数组元素内存大小(字节)
	len = sizeof(arr) / sizeof(arr[0]);
	
    printf("len = %d\n", len);
	return 0;
}

运行结果:4(因为字符数组 arr 后面还有一个系统自动添加的结束符 ‘\0’ )

6、一定要区分开 ‘0’ 和 ‘\0’ 和 0

字符 ‘0’ —— ASCII码值为 48

转义字符 ‘\0’ —— 字符串的结束标志(不算作字符串的内容) - ASCII码值为 0

数字 0


转义字符

1、什么是转义字符

转义字符顾名思义就是转变了字符原来的意思

来段代码感受一下,如果我们想要在屏幕上打印一个目录:c:\test\test.c

#include
int main()
{
     
	printf("c:\test\test.c");
	return 0;
}

运行结果:很奇怪,和我们想象的不一样唉,没有打印出目录,却出现了两段空白,这个字符\和字符t不再是普通的字符的意思了,转变了原来的意思,变成了转义字符\t(水平制表符)

【C语言学习之路】第一篇:与C语言的初次见面_第9张图片

同理,字符\和字符n也不再是两个普通的字符了,而是转义字符\n(换行符)

【C语言学习之路】第一篇:与C语言的初次见面_第10张图片

2、C语言中有哪些转义字符

转义字符 释义
\’ 用于表示字符常量'
\" 用于表示一个字符串内部的双引号"
\\ 用于表示一个反斜杠,防止它被解释为一个转义序列符
\a 警告字符,蜂鸣
\n 换行
\r 回车
\t 水平制表符(键盘上的 Tab 键)
\v 垂直制表符
\ddd ddd表示 1-3 个八进制的数字,如:\130(从 \000 到 \377)
\xdd dd表示 2 个十六进制的数字,如:\x30
\b 退格符
\f 进制符
? 在书写连续多个问号时使用,防止他们被解析成三字母词

3、转义字符的应用

  • 思考问题:在屏幕上打印一个单引号'怎么做?

大家再没有学习转义字符之前可能会想直接输出打印就好啦,而写成下面这个错误示范了哦

错误示范:中间的你想要输出的那个单引号 ’ 和左边的单引号组成一对了,编译会 error

printf("%c\n", ''');

修改正确:为了防止其与左边的单引号组成一对,需要在单引号前加一个转义符

printf("%c\n", '\'');  //这里 \'被解析成一个转义字符

同理:在屏幕上打印一个双引号"就可以这样写啦

printf("%s\n", "\"");
  • 回到最初的问题:在屏幕上打印一个c:\test\test.c怎么做?
printf("c:\\test\\test.c");
  • \ddd 与 \xdd :使用编码值来间接的表示字符
//输出字符 A - 对应的ASCII码的8进制形式是101 - 8进制的130是十进制的65
printf("%c\n", '\101');

//输出字符 a - 对应的ASCII码的8进制形式是141 - 8进制的141是十进制的97
printf("%c\n", '\141');

//输出字符 B - 对应的ASCII码的16进制形式是42
printf("%c\n", '\x42');

//输出字符 b - 对应的ASCII码的16进制形式是62
printf("%c\n", '\x62');

对于转义字符来说,只能使用八进制或者十六进制。

转义字符的初衷是用于 ASCII 编码,所以它的取值范围有限:

  • 八进制形式的转义字符最多后跟三个数字,也即\ddd,最大取值是\177
  • 十六进制形式的转义字符最多后跟两个数字,也即\xdd,最大取值是\x7F

@参考文章:C语言转义字符

4、ASCII编码

补充知识:

计算机只认识 0 和 1 两个数字,数据在内存中以二进制形式存储,我们在屏幕上看到的文字,在存储之前都被转换成了二进制(0和1序列),在显示时也要根据二进制找到对应的字符。

那么,怎么将英文字母和二进制对应起来呢?这就需要有一套规范,一种专门针对英文的字符集 - ASCII编码就被设计出来了,为每个字符分配了唯一的编码值在C语言中,一个字符除了可以用它的实体表示,还可以用编码值表示。使用编码值来间接地表示字符的方式称为转义字符。

在 ASCII 编码中,大写字母、小写字母和阿拉伯数字都是连续分布的(见下表),这给程序设计带来了很大的方便。例如要判断一个字符是否是大写字母,就可以判断该字符的 ASCII 编码值是否在 65~90 的范围内。

@参考文章:ASCII编码,将英文存储到计算机

【C语言学习之路】第一篇:与C语言的初次见面_第11张图片

此图转载自百度

5、测试题:

#include
#include
int main()
{
     
	//程序输出什么呢?
	printf("%d\n", strlen("c:\test\328\test.c"));
	return 0;
}

运行结果:14(\t\32分别是一个转义字符)


注释

注释有两种风格:

在编写C语言源代码时,应该多使用注释,这样有助于对代码的理解。

  • C语言风格的注释(缺陷:不能嵌套注释)
/*xxxxxxxxxxxx*/
  • C++风格的注释(可以注释一行也可以注释多行)
//xxxxxxxxxxxx

(5)操作符

这里只是简单介绍一下各种操作符,目的是能够让大家在代码中识别出这些,后面我们会详细讲解和学习

算数操作符

+ - * / %

移位操作符

移位操作符 含义
>> 右移操作符 - 把二进制位向右移动 - 缺位补 0
<< 左移操作符 - 把二进制位向左移动 - 缺位补 0

举例说明:

int a = 1;
int b = a << 1;
//a - 00000000000000000000000000000001 --> a = 1
//b - 00000000000000000000000000000010 --> b = 2

位操作符

位操作符 含义
& 按位与
| 按位或
^ 按位异或

赋值操作符

=  //赋值符
+=  -=  *=  /=  &=  ^=  |=  >>=  <<=  //复合赋值符

举例说明:

int a = 0;
a = a + 1;
//等效于: 
a += 1;

单目操作符

单目操作符 含义
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制序列按位取反
前置、后置–
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换

注:

双目操作符:有两个操作数(比如:a + b)

单目操作符:只有一个操作数

补充知识:C语言中如何表示真和假?

0 表示假,非 0 表示真

下面来展开讲解几个操作符

  1. 按(二进制)位取反操作符:~

对一个数的所有二进制位取反,0 变成 1,1 变成 0

#include
int main()
{
     
	int a = 0;
	int b = ~a;  //按位取反
	printf("%d\n", b);  //输出的是 b 的原码
	return 0;
}

运行结果:-1

这个时候大家可能会有疑问了,为啥会是 -1 呀,别着急,仔细听我道来

这里需要引入原码 反码 补码的概念

整数在内存中存储的都是二进制形式的「 补码 」,输出打印时 要将其转换成「 原码 」

「 原码 」第一位是「 符号位 」表示 「 正负 」(1为负,0为正)

所以:

a - 00000000000000000000000000000000(补码)

对 a 的二进制位( ~ 按位取反)得到 b 的二进制形式的「 补码 」,因为输出的是「 原码 」所以我们进一步转换:

b - 11111111111111111111111111111111(补码)

b - 11111111111111111111111111111110(补码 -1得到反码)

b - 10000000000000000000000000000001(符号位不变,其它位按位取反得到原码)

而二进制序列 10000000000000000000000000000001 就是 -1

知识点:

整数(正数、负数)的原码反码补码:

  • 正数:原码 = 反码 = 补码 相同
int i = 2;
00000000000000000000000000000010 - 原码
00000000000000000000000000000010 - 反码
00000000000000000000000000000010 - 补码
  • 负数
二进制形式 转换规则
原码 按照整数正负 写出二进制序列
反码 「 原码 」符号位不变 其它位按位取反
补码 反码 + 1
int i = -2;
10000000000000000000000000000010 - 原码
11111111111111111111111111111101 - 反码
11111111111111111111111111111110 - 补码
  1. **自增自减操作符:++ – **
#include
int main()
{
     
	int a = 10;
	int b = ++a;  //前置++:先++ 后使用
	int c = a++;  //后置++:先使用 后++

	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

前置后置-- 同理

同时建议大家不要去研究这样的代码,浪费时间,在不同的编译器得到的结果都不一样,平时开发也几乎不会这样去写,可读性太差

int a = 1;
int b = (++a) + (++a) + (a++);
  1. 强制类型转换:(类型)

一般在类型不匹配的时候使用,不推荐,既然你会使用强制类型转换,说明一开始在编写程序的时候就没有设计好各变量的数据类型,导致程序有缺陷

int a = (int)3.14;

关系操作符

>
>=
<
<=
!=  用于测试“不相等”
==  用于测试“相等”

逻辑操作符

描述并且、或者的关系

逻辑 与 / 或 的结果为:真 / 假

&&   逻辑与
||   逻辑或

条件操作符

表达式 exp1 成立,整个表达式的结果是 exp2 的结果

表达式 exp1 不成立,整个表达式的结果是 exp3 的结果

exp1 ? exp2 : exp3  //也是三目操作符,有三个操作数

举例说明:

#include
int main()
{
     
	int a = 10;
	int b = 20;
	int max = a > b ? a : b;
	printf("max = %d\n", max);
	return 0;
}

运行结果:max = 20

逗号表达式

逗号隔开的一串表达式,从左向右依次计算的,整个表达式的结果是最后一个表达式的结果

exp1, exp2, exp3, …expN

举例说明:

#include
int main()
{
     
	int a = 1;
	int b = 2;
	int c = 5;
    //思考 d 的值是多少?
	int d = (a = b + 2, c = a - 4, b = c + 2);
	printf("d = %d\n", d);
	return 0;
}

运行结果:d = 2

思考题:

函数调用 exec( ( vl, v2 ), ( v3, v4 ), v5, v6 ); 中,实参的个数是:( 4 ) - 分别是v2 v4 v5 v6

下标引用、函数调用、结构成员

[]	下标引用操作符
()	函数调用操作符
.	结构体成员访问操作符
->

举例说明

【C语言学习之路】第一篇:与C语言的初次见面_第12张图片

(6)常见关键字

1.关键字是C语言提供的,不能自己创建关键字
2.关键字不能做变量名

auto(修饰局部变量,一般是省略的)  break   case  char  const   continue  default  
do   double	  else  enum(枚举)  extern(声明外部符号)  float  for   goto  if   int   
long  register(寄存器)   return   short   signed(有符号数)   unsigned(无符号数)  
sizeof(计算机类型/变量所占内存空间大小)   static(静态)   struct(结构体)   switch  
typedef(类型定义)   union(联合体/共用体)   void   volatile(C语言中暂时不讲)   while
  • 补充知识:

计算机中,数据可以存放到哪里呢?

寄存器(会被频繁调用的数据,放在寄存器中,可以提高效率,现在编译器已经能够自动识别这种数据并放在其中,register实用意义不大)

高速缓存

内存

硬盘

  • 思考:#define是不是关键字?include是不是关键字?

都不是,它们是预处理指令(预编译期间处理的指令)

关键字这次先简单介绍几个,后续遇到了再讲解

1、关键字 typedef

类型重定义,类型重命名,相当于取了一个别名

举例说明:

#include

//把 unsigned int 重命名为 u_int,现在 u_int 也是一个类型名了
typedef unsigned int u_int;
int main()
{
     
    //a 和 b 的类型相同
	unsigned int a = 10;
	u_int b = 20;
	
    return 0;
}

2、关键字 static

static 用来修饰变量和函数

1)修饰局部变量 - 静态局部变量

2)修饰全局变量 - 静态全局变量

3)修饰函数 - 静态函数

static 修饰局部变量

改变了局部变量的生命周期(本质上是变量的存储类型),让静态局部变量出了作用域依然存在,直到程序结束生命周期才结束

代码1:

#include
void test()
{
     
	int a = 1;  //局部变量 a
	a++;
	printf("%d ", a);
}
int main()
{
     
	int i = 0;
	for (i = 0; i < 10; i++)
	{
     
		test();
	}
	return 0;
}

运行结果:2 2 2 2 2 2 2 2 2 2(10个2)

代码2:

#include
void test()
{
     
	static int a = 1;  //静态局部变量a
	a++;
	printf("%d ", a);
}
int main()
{
     
	int i = 0;
	for (i = 0; i < 10; i++)
	{
     
		test();
	}
	return 0;
}

运行结果:2 3 4 5 6 7 8 9 10 11

为啥会出现这样的结果呢?

这里补充一点小知识,内存会被分为几个区域,今天在这里我们只讨论和这次C语言有关的几个区域,局部变量 a 开始放在栈区的,被 static 修饰后就被放到静态区去了,改变了它的存储类型,存在不同的区域,就有不同的特点了,静态变量 a 的生命周期和全局变量一样,为整个程序

【C语言学习之路】第一篇:与C语言的初次见面_第13张图片

static 修饰全局变量

static修饰全局变量,使得这个全局变量只能在自己所在的源文件(.c)内使用

不能在同一工程的其他源文件内使用

代码实例:

/*源文件demo2.c(test project)*/

//静态全局变量g_val
static int g_val = 2021;
/*源文件demo.c(test project)*/

#include
//声明静态全局变量 g_val
extern int g_val;
int main()
{
     
	printf("%d\n", g_val);
	return 0;
}

运行结果:error

【C语言学习之路】第一篇:与C语言的初次见面_第14张图片

运行程序,结果error了,那么 static 修饰全局变量的本质是什么呢?

全局变量,可以在同一工程的其他源文件内被使用,是因为全局变量具有外部链接属性

但是当被 static 修饰成静态全局变量,就变成了内部链接属性,其他源文件就不能链接到这个静态全局变量了

所以无法被其他源文件所使用,只能在自己所在的源文件内使用

static 修饰函数

static修饰函数,使得这个函数只能在自己所在的源文件(.c)内使用

不能在同一工程的其他源文件内使用

本质上是:static 将函数的外部链接属性变成了内部链接属性(和 static 修饰全局变量一样)

仔细对比代码1和代码2,理解 static 在修饰函数前后的意义

代码1:

【C语言学习之路】第一篇:与C语言的初次见面_第15张图片

代码2:

【C语言学习之路】第一篇:与C语言的初次见面_第16张图片

(7)#define 定义宏

#define 是C语言提供的宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。

在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。

还有就是要记住这是简单的替换而已,不要在中间计算结果,一定要替换出表达式之后再算。

//普通宏
#define PI (3.1415926)
//带参数的宏
#define ADD(a,b) ( (a) + (b) )
//关键是十分容易产生错误,包括机器和人理解上的差异等等

来看一个例子:

int a = 2 * ADD(2, 3);

预处理阶段之后,ADD(2, 3) 则会被替换成 ( (2) + (3) )

int a = 2 * ( (2) + (3) );  //结果为 10

如果没有括号

#define ADD(a,b) a + b

替换之后的结果是:

int a = 2 * 2 + 3;  //结果为 7

(8)选择语句

为了先将C语言拉通介绍一遍,能够对其有一个整体的认识,这里的内容只进行一个简单的介绍,后续会详细讲解

生活中的选择无处不在,那么在计算机中,怎么用C程序设计语言表示呢?

如果(if)就……

否则(else)就……

这就是选择!

#include
int main()
{
     
	int a = 10;
	int b = 20;
	if (a > b)
	{
     
		printf("较大值:a\n");
	}
	else
	{
     
		printf("较小值:b\n");
	}
	return 0;
}

如果 a 比 b 大,输出 a,否则就输出 b


(9)循环语句

生活中有些事,要一直做,重复做,比如学习、吃饭、睡觉等等,如何用C语言实现循环呢?

  • while 语句
  • for 语句
  • do…while 语句

后面会详细讲解


(10)函数

假设(我是 main函数)(张三是 add 函数),有一天,我想让张三给我带饭,所以得告诉他我想带什么饭,并给他带饭的钱,(蛋炒饭,10元),张三接收到信息和拿到钱,最后返回(return)给我一份蛋炒饭

实例:用函数实现求两数的和

#include

int add(int x, int y)  //求两数的和
{
     
	return x + y;
}

int main()
{
     
	int a = 10;
	int b = 20;
	int sum = add(a, b);
	printf("sum = %d\n", sum);
	return 0;
}

函数的特点就是简化代码,代码复用


(11)数组

我们需要存储 10 个数字,怎么办呢?如果去定义 10 个变量来存储,那也太麻烦了,这时我们可以用数组

C语言中数组的定义:一组相同数据类型的元素的集合

  • 数组的定义与初始化
int arr1[10] = {
      1,2,3,4,5,6,7,8,9,10 };  //完全初始化
int arr2[5] = {
      1,2,3 };  //不完全初始化,剩余的默认为 '\0'
  • 数组元素的访问(通过数组下标访问,数组的下标是从 0 开始的)
arr1[0] = 1;
arr1[1] = 2;
……
arr1[9] = 10;

(12)指针

指针是C语言中非常非常重要的内容,我们要谈指针,必须先要搞明白内存

1、什么是内存

内存是电脑上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的。

为了有效的使用内存,就把内存划分成一个个小的内存单元,「 每个内存单元的大小是1个字节 」。

为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该「 内存单元的地址 」。

【C语言学习之路】第一篇:与C语言的初次见面_第17张图片
  • 内存单元是如何编号的?

电脑分为 32位 和 64位 的,以 32位 举例

32位 - 有32根地址线 - 通电会产生 1/0 电信号 - 电信号转换成数字信号 - 变成32个 1/0 组成的二进制序列

共有 2 (32) = 4,294,967,296 种不同的序列

00000000000000000000000000000000
00000000000000000000000000000001
…………
01111111111111111111111111111111
11111111111111111111111111111111

我们就可以用这些二进制序列来给内存单元编号,一个字节的编号就会由32个0/1组成,这就是它们的地址。

内存地址只是一个编号,代表一个内存空间。在计算机中存储器的容量是以字节为基本单位的。也就是说一个内存地址代表一个字节(8bit)的存储空间。例如经常说 32位 的操作系统最多支持4GB的内存空间,也就是说CPU只能寻址2的32次方(4GB)空间。

类比到生活中,一栋宿舍楼相当于一块内存区域,被划分成了一个个宿舍,为了方便管理和找到某一个宿舍,我们给每一间宿舍都编了门牌号,比如307,表示3楼7号,这样很容易就找到它们的位置在哪里了。

2、获取变量的地址

#include
int main()
{
     
	//num在内存要被分配 4字节 的内存空间
	int num = 10;
	//%p - 以地址的形式打印
	printf("%p\n", &num);
	return 0;
}

运行结果:004FFD64(这个地址是变量num所在内存空间的第一个字节的地址)

【C语言学习之路】第一篇:与C语言的初次见面_第18张图片

在VS2019中,F10进入调试模式,选择调试菜单 - 窗口 - 内存,就可以找到内存窗口

【C语言学习之路】第一篇:与C语言的初次见面_第19张图片

输入 &变量名,取出变量的地址,这个地址是变量所在内存空间的第一个字节的地址

【C语言学习之路】第一篇:与C语言的初次见面_第20张图片

3、地址的存储 - 指针变量

指针变量存放相同数据类型的变量的首地址

//定义一个整型指针变量,存储整型变量num的地址
int num = 10;
int* p = &num;
//字符型指针变量指向字符型变量
char ch = 'b';
char* str = &ch;

指针变量的使用举例:通过指针变量存放的变量的地址,找到所指向的变量

#include
int main()
{
     
	int num = 10;
    //指针变量 p
	int* p = &num;
    //* 是 解引用操作符 / 间接访问操作符,访问指针 p 指向的内存空间
	*p = 20;
	
    printf("num = %d\n", num);
	return 0;
}

运行结果:num = 20

4、指针变量的大小

任何类型的指针变量在 32位平台 是4个字节大小,64位平台 是8个字节大小

指针变量占几个字节跟语言无关,取决于变量的地址的存储需要多大的空间,进一步讲,是与系统的寻址能力有关*(提示:结合上面内容 - 1、什么是内存 - 内存是如何编号的? - 进行理解)*

32位系统 - 32根地址总线 - 一个地址是 32 个比特位 - 4字节

64位系统 - 64根地址总线 - 一个地址是 64 个比特位 - 8字节

所以,不管你是整型变量还是字符型变量还是浮点型变量,在 32/64 位平台中,你的第一个字节(首地址)的编址是由 32/64 个比特位组成的二进制序列,占 4/8 个字节

【C语言学习之路】第一篇:与C语言的初次见面_第21张图片
#include
int main()
{
     
	printf("%d字节\n", sizeof(char *));
	printf("%d字节\n", sizeof(int *));
	printf("%d字节\n", sizeof(double *));
	return 0;
}

运行结果:在(32位平台)均输出 4字节、在(64位平台)均输出 8字节

  • VS2019中,这里可以切换程序运行的平台 x86(32位) / x64(64位)
【C语言学习之路】第一篇:与C语言的初次见面_第22张图片

(13)结构体

结构体是C语言中特别重要的内容,结构体使得C语言可以描述复杂类型

比如描述一个人,用之前学过的任何一种类型变量都不足以描述清楚,int? float? char?,似乎都没内味,人是一个复杂对象,有姓名,年龄,性别等等;描述一本书,有作者,定价,书类等等

而我们用结构体,就可以创造一些类型,来描述这些复杂类型了

接下来,我们来创建一个学生的结构体

struct Student
{
     
	char name[20];  //姓名
	int age;  //年龄
	double grade;  //成绩
};

结构体变量的定义与初始化

  • 定义时初始化
  • 定义之后初始化

一般在定义时初始化,比较方便;定义之后初始化,就只能对每一个成员一一赋值初始化了

//定义一个结构体变量并初始化
struct Student s1 = {
      "玛卡巴卡",20,85 };
//定义之后初始化
strcpy(s1.name, "李四");  //因为name是数组名,它是一个地址,不能用 s1.name = "李四";
						 //strcpy - 字符串拷贝函数 - 库函数 - 引用头文件string.h
s1.age = 25;
s1.grade = 90;

访问结构体变量中的成员

//方法(1):结构体变量.结构体成员
printf("name: %s age: %d grade: %lf\n", s1.name, s1.age, s1.grade);
	
//方法(2):结构体指针->结构体成员
struct Student* ps = &s1;
printf("name: %s age: %d grade: %lf\n", ps->name, ps->age, ps->grade);

//方法(3):太麻烦,建议用 ->操作符 访问更直观
printf("name: %s age: %d grade: %lf\n", (*ps).name, (*ps).age, (*ps).grade);

你可能感兴趣的:(C语言学习之路,编程语言,c语言,字符串,指针,math.h)