C 语言入门,有这一篇就够了~

目录

一、什么是 C 语言?

二、第一个 C 语言程序

三、数据类型

四、变量和常量

4.1 - 变量

4.2 - 常量

五、字符串和转义字符

5.1 - 字符串

5.2 - 转义字符

六、选择语句和循环语句

七、函数

7.1 - 函数的使用

7.2 - scanf 函数的介绍

八、数组

九、操作符

十、关键字

10.1 - 关键字 auto、register

10.2 - 关键字 static

10.3 - 关键字 typedef

十一、#define 定义的常量和宏

十二、指针

十三、结构体



一、什么是 C 语言?

C语言是一门通用的计算编程语言,广泛应用于底层开发

我们把底层编程归类于非常接近设备的编程,使用比较底层的编程语言,例如 C 语言、汇编语言。这与上层编程相反,例如用户空间的应用程序,使用上层的编程语言,例如 Python、Java。

二十世纪八十年代,为了避免各开发厂商用的 C 语言语法有差异,由美国国家标准协会为 C 语言制定了一套完整的美国国家标准语法,称为 ANSI C,作为 C 语言最初的标准。

ANSI:American National Standards Institute,即美国国家标准协会。

C 语言是一门面向过程的计算机编程语言,与 C++、Java 等面向对象的编程语言有所不同。

C语言的编译器主要有:Clang、GCC、WIN-TC、MSVC、Turbo C 等。

GCC 是类 Unix 操作系统(Linux、macOS 等)所用的编译器,MSVC 是 Windows 所用的编译器,编译的结果不能混用,也就是说使用 MSVC 编译的可执行文件是不能在 Linux 上运行的(另外 Linux 的可执行文件也不是 .exe)。

一、什么是编译?

机器语言是用二进制代码(即由 0 和 1 构成的信息串)表示的计算机能直接识别和执行的一种机器指令的集合。不同型号的计算机,其机器语言是不相通的,按着一种计算机的机器指令编制的程序,不能在另一种计算机上执行。

机器语言中的内容有一部分是固定的指令,我们通过一些助记符(比如 ADD,SUB 等)来代替这些固定的指令,这就是汇编语言。汇编语言在运行前需要通过汇编过程转换成机器指令。

汇编并不是最好的编程工具,1972 年,丹尼斯·里奇开发出了 C 语言,这是一种比汇编语言更适合用来编程的语言。C 语言跟自然语言比较接近,而相对远离了机器语言,这种人容易读懂的语言被称为高级语言,编译就是将高级语言转换为低级语言的一个过程。补充:还有另一种转换方式叫做解释,编译和解释的区别在于,编译一次性将所有的高级语言翻译为低级语言,解释则一句一句地将高级语言解释为低级语言,著名的解释型语言有 JavaScript、python 等。

二、什么是编译器?

编译器就是用来将高级语言转换成低级语言的程序。



二、第一个 C 语言程序

写一个 C 语言代码,将 "hello world!" 打印在终端上

#include <stdio.h>
​
int main()  
{
    printf("hello world!\n");  // '\n' 表示换行
    return 0;  
}
  1. printf 是 C 语言标准库函数,使用前需要声明,即将相应的头文件包含(include)进来。

  2. stdio --> standard input & output,即标准输入输出; 后缀名 .h --> header,即表示该文件是头文件。

  3. int --> integer,即整型或整数的意思;

  4. main --> 主函数名。

    一个项目或工程(project)中可以有多个 .c 源文件,但是必须有且只能有一个主函数,因为主函数是程序的入口,而入口有且只能有一个。

  5. 将 0 返回(return)给主函数,表示程序正常退出。

  6. 使用 ctrl + f5 编译源文件并执行程序。

  7. 使用 ctrl + 鼠标左键点击 "stdio.h" 可以直接跳转到相应的文档,或者使用鼠标右键点击 "stdio.h" + 转到文档

  8. 注释

    • C 语言风格的注释:/*xxxxx*/缺点是不能嵌套注释,因为 /* 会匹配第一个遇到的 */

    • C++ 风格的注释://xxxxx缺点是只能注释一行



三、数据类型

C 语言的基本数据类型

  • 数值类型

    1. 整型,它又分为短整型 short、整型 int、长整型 long 以及长长整型 long long;

    2. 浮点型,它又分为单精度浮点型 float 和双精度浮点型 double。

  • 字符类型:char。

#include <stdio.h>

int main()
{
	// %d 是整型数据的占位符,打印出来的是十进制整型数据
	printf("%d\n", sizeof(char));  // 1
	printf("%d\n", sizeof(short));  // 2
	printf("%d\n", sizeof(int));  // 4
	printf("%d\n", sizeof(long));  // 4
	printf("%d\n", sizeof(long long));  // 8
	printf("%d\n", sizeof(float));  // 4
	printf("%d\n", sizeof(double));  // 8

	return 0;
}
  1. sizeof 是一个操作符,用来计算相应数据类型的变量将在内存空间中所占的大小,单位是:字节

  2. 计算机中常见的存储单位

    比特/字节/千字节/兆字节/太字节,即 bit/Byte/KB/MB/GB/TB

    1 Byte = 8bit

    1 KB = 1024 Byte,1 MB = 1024 KB,1 GB = 1024 KB,1 TB = 1024 GB

    计算机存储的最小单位是比特(bit);

    计算机存储的基本单位是字节(Byte);

    2^10 = 1024。

  3. 使用 ctrl + d 可以快速复制粘贴某一行。



四、变量和常量

4.1 - 变量

#include <stdio.h>

int global = 10;  // 全局变量,它定义在大括号{} 外面

extern int g_a;  // 使用 extern 关键字声明外部符号
// 全局变量 g_a 创建在其他源文件中

int main()
{
	// 一、定义变量
	char ch = 'w';  // 定义了一个名为 ch 的字符型变量,其初始值为 'w'
	int age = 18;  // 定义了一个名为 age 的整型变量,其初始值为 18
	float height = 175.5f;  // 在 175.5 后面加上 f,表示它是 float 类型,而非 double
	double weight = 75.6;

	// 二、变量的分类
	int local = 20;  // 局部变量,定义在大括号里面
	int global = 20;
	printf("%d\n", global);  // 20
    
    // 三、变量的使用
	// 输入两个数,并输出这两个数的和
	int num1 = 0;
	int num2 = 0;
	int sum = 0;
	scanf("%d%d", &num1, &num2);  // & 是取地址操作符
	sum = num1 + num2;
	printf("sum = %d\n", sum);
    
    // 四、变量的作用域和生命周期
	{
		int a = 10;  
		// 定义在内层大括号里的局部变量 a,
		// 它可用的范围(作用域)就是这个大括号里。
		printf("a = %d\n", a);  // a = 10
	}
	// printf("a = %d\n", a);  // error
    printf("g_a = %d\n", g_a);  // g_a = 20

	return 0;
}
  1. 变量的命名

    • 只能由字母、数字和下划线组成;

    • 不能以数字开头;

    • 长度不能超过 63 个字符;

    • 变量名区分大小写;

    • 变量名不能使用关键字。

  2. 变量的分类

    • 局部变量;

    • 全局变量。

    当局部变量和全局变量同名时,局部变量优先使用。

  3. 解决无法使用 scanf 标准库函数的问题

    首先使用软件 everything 找到名为 newc++file.cpp 文件,接着使用 Notepad++(也可以使用其他软件,例如记事本等)打开该文件,并将 #define _CRT_SECURE_NO_WARNINGS 1 复制到文件当中。注意,Notepad++ 会提示权限不够,无法保存,此时就需要使用管理员的身份打开 Notepad++,最后再将复制的内容保存下来。

    经过这样的操作以后,每次创建的新源文件,都会有 #define _CRT_SECURE_NO_WARNINGS 1 这段内容。

  4. 变量的作用域和声明周期

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

      局部变量的作用域是变量所在的局部范围;全局变量的作用域是整个工程。

    • 变量的生命周期(life time)指的是变量从创建到销毁之间的一个时间段。

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

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

                 

4.2 - 常量

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

  • 字面常量

  • const 修饰的常变量

  • #define 定义的标识符常量

  • 枚举常量

#include <stdio.h>

#define N 10

enum RGB  
{
	// 自定了一种名为 RGB 的枚举类型,
	// RED、GREEN、BLUE 是这个类型的可能取值,
	// 它们是常量,所以叫做枚举常量,且默认值分别是 0、1、2。
	RED,
	GREEN,
	BLUE
};

int main()
{
	// 一、字面常量
	// 为了区分八进制、十进制和十六进制数,
	// 八进制数以 0 开头,且后面不能出现 8 和 9,
	// 十进制数以非 0 数字开头,
	// 十六进制以 0x 或 0X 开头。
	printf("%d\n", 010);  // 8 --> 1 * 8^1 + 0 * 8^0
	printf("%d\n", 10);  // 10
	printf("%d\n", 0x10);  // 16 --> 1 * 16^1 + 0 * 16^0
	printf("%d\n", 0X10);  // 16

	// 二、const 修饰的常变量
	const float pi = 3.14f;
	// pi = 3.1415926  // error
	// 注意:所谓的常变量是具有了常量的属性,即无法直接更改,但本质上还是变量。
	// int n = 10;
	// int arr[n] = { 0 };  // error
	int arr[10] = { 0 };  // ok

	// 三、#define 定义的标识符常量
	int arr1[N] = { 0 };  // ok

	// 四、枚举常量
	enum RGB color = RED;  // 定义了一个 RGB 类型的变量 color
	printf("%d\n", RED);  // 0
	printf("%d\n", GREEN);  // 1
	printf("%d\n", BLUE);  // 2
	int arr2[BLUE] = { 0 }; // ok

	return 0;
}


五、字符串和转义字符

5.1 - 字符串

例如 "hello world!\n" 这样由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称为字符串

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = { 'b', 'i', 't' };
	char arr2[] = { 'b', 'i', 't', '\0' };
	char arr3[] = "bit";  // 等价于:{ 'b', 'i', 't', '\0' }

	// 使用 sizeof(数组名) 可用求出整个数组所占内存空间的大小
	printf("%d\n", sizeof(arr1));  // 3
	printf("%d\n", sizeof(arr2));  // 4
	printf("%d\n", sizeof(arr3));  // 4

	// 使用 strlen 库函数求字符串的长度,
	// 此时需要包含头文件 string.h。
	printf("%d\n", strlen(arr1));  // 随机值
	printf("%d\n", strlen(arr2));  // 3
	printf("%d\n", strlen(arr3));  // 3

	// %s 是字符串的占位符
	printf("%s\n", arr1);  // bit + 随机
	printf("%s\n", arr2);  // bit
	printf("%s\n", arr3);  // bit

	return 0;
}
  1. 字符串的结束标志

    字符串的结束标志是一个 '\0' 的转义字符,所以将字符串 "bit" 存储在数组 arr3 中时,不仅仅存储了字符 'b'、'i'、't',还存储了 '\0'。

  2. 计算字符串的长度

    在计算字符串的长度时,字符串的结束标志 '\0' 是不计算在内的。

  3. 输出字符串

    在输出字符串时,直到遇到字符串的结束标志 '\0' 才停止。

5.2 - 转义字符

#include <stdio.h>

int main()
{
	// 一、常用的转义字符
	// 1. \b 
	printf("%s\n", "abc\b");  // abc
	printf("%s\n", "abc\bdef");  // abdef
	// 2. \r
	printf("%s\n", "abc\r");  // abc
	printf("%s\n", "abc\rdef");  // def
	// 3. \ddd
	// 字符 'A' 对应的 ASCII 码值为 65
	printf("%c\n", '\101');  // A
	printf("%c\n", '\x041');  // A

	// 二、练习
	printf("%d\n", strlen("c:\test\628\test.c"));  // 答案:14
	return 0;
}
  1. 转义字符

    转义字符(Escape Character)是指在 ASCII 码 和 Unicode 等字符集中无法被键盘录入的字符被当作特殊用途而需要转换回它原来意义的字符转义即表示字符已经转换了意义。

    无法被键盘录入的字符

    ASCII 码值为 7 的转义字符 \a 便是无法被键盘录入的字符,\a 被转换后的意义就是响铃符号(BEL),但是键盘上没有任何单个按键是可用表示响铃符,因此需要转义字符 \a 来表示

    被当作特殊用途而需要转换回它原来的意义的字符

    双引号 " 是字符串的开头和结尾,所以要使用 \" 来表示;反斜杠 \ 是转义字符的开头,所以要用 \\ 来表示。

  2. 常用的转义字符

    转义字符意义ASCII 码值(十进制)
    \a响铃(BEL)007
    \b退格(BS),将当前位置移到前一格/列008
    \f换页(FF),将当前位置移到下页开头012
    \n换行(LF),将当前位置移到下一行开头010
    \r回车(CR),将当前位置移到本行开头013
    \t水平制表(HT),跳到下一个 TAB 位置009
    \v垂直制表(VT)011
    \'单引号039
    \"双引号034
    \\反斜杠092
    \ddd1~3 位八进制数所对应的字符八进制转十进制
    \xdd2 位八进制数所对应的字符十六进制转十进制

    转义字符的初衷是用于 ASCII 编码,所以它的取值范围有限,超出范围的转义字符的行为是未定义的,有的编译器会将编码值直接输出,有的编译器会报错。

  3. 区分 0、'0' 和 '\0'

    0 是一个数字;'0' 是一个字符,它的 ASCII 码值是 48;'\0' 是一个转义字符,它的 ASCII 码值是 0。



六、选择语句和循环语句

写一个 C 语言程序,输入成绩,然后进行判断(优秀/良好/及格/不及格):

#include <stdio.h>

int main()
{
	int grade = 0;
	printf("请输入你的成绩:>");
	scanf("%d", &grade);
	if (grade >= 90)
		printf("优秀");
	else if (grade >= 70)
		printf("良好");
	else if (grade >= 60)
		printf("及格");
	else
		printf("不及格");
	
	return 0;
}

C 语言的循环语句有

  • while 语句

  • for 语句

  • do...while 语句

写一个 C 语言程序,输出 1 + 2 + ... + 100 的值

#include <stdio.h>

int main()
{
	int sum = 0;
	int i = 1;
	while (i <= 100)
	{
		sum += i;
		i += 1;
	}
	printf("sum = %d\n", sum);  // sum = 5050

	return 0;
}


七、函数

7.1 - 函数的使用

写一个 C 语言程序,输入两个数,然后输出两个数的和(使用函数)

#include <stdio.h>

int add(int x, int y)
{
	return x + y;
}

int main()
{
	int num1 = 0;
	int num2 = 0;
	int sum = 0;
	scanf("%d%d", &num1, &num2);
	sum = add(num1, num2);
	printf("sum = %d\n", sum);

	return 0;
}
  1. 主函数就是一种函数,main 是函数名,int 是它的返回值类型,返回 0 表示程序正常退出。同理 add 是函数名,返回值类型是 int,它的功能是返回两个数的和

  2. 函数的特点

    可以进行复用,简化代码。

7.2 - scanf 函数的介绍

#include <stdio.h>

int main()
{
	int num1 = 0;
	int num2 = 0;
	int sum = 0;
	int n = scanf("%d %d", &num1, &num2);
	sum = num1 + num2;
	printf("sum = %d, n = %d\n", sum, n);
	// 1. 输入 1   2(1 和 2 之间有多个空格),输出 sum = 3,n = 2
	// 2. 输入 1\n2(\n,即换行),输出 sum = 3,n = 2
	// 3. 输入 1\t2(\t,即水平制表符),输出 sum = 3,n = 2

	 // 练习 - BC41 你是天才吗?
	// 输入描述:
	//【多组输入】,每行输入包括一个整数表示的智商。
	// 输出描述:
	// 针对每行输入,输出“Genius”(智商大于等于 140)。
	int IQ = 0;
	while (scanf("%d", &IQ) != EOF)
	{
		if (IQ >= 140)
			printf("Genius\n");
	}
	return 0;
}
  1. scanf 函数的读取

    格式化字符串(format string)规定了 scanf 函数如何从输入缓冲区(比如键盘)中读取数据。scanf 会读取并忽略输入缓冲区中下一个非空白字符之前的所有空白字符(whitespace,包括空格、换行和 tab),然后读取格式化字符串中规定格式的数据。

    若格式化字符串中包括空白字符,则空白字符会与输入缓冲区中任意数量的连续空白字符相匹配,并将其从缓冲区中清除。例如 scanf("%d %d", &num1, &num2);,其中的格式化字符串会要求 scanf 首先从缓冲区中读取一个整型(若之前存在空白字符则忽略跳过),在跳过输入缓冲区中连续的空白字符(与格式化字符串中的空白字符匹配),最后再读取一个整型。

  2. scanf 函数的返回值

    scanf 通常返回的是成功赋值的数据项数,如果出错或是遇到 end of file,则返回 EOF

    如果想从键盘上输入 EOF,在 windows 的 DOS(Disk Operating System) 窗口使用 ctrl + z 或 f6;在 Unix 系统上使用 ctrl + d。

    EOF 的本质是 -1。



八、数组

数组是一组相同数据类型元素的集合

#include <stdio.h>

int main()
{
	// 一、数组的定义
	// arr 是数组名,
	// int 是数组元素的数据类型,
	// 5 表示数组中最多放 5 个元素。
	int arr[5] = { 1, 2, 3, 4, 5 };
	// 当数组不完全初始化时,剩余的元素默认初始化为 0
	int arr1[5] = { 0 };  // 第一个元素初始化为 0,其余元素默认为 0
	int arr2[5] = { 1, 2, 3 };  // 第 4 和 5 个元素默认为 0

	// 二、数组的下标
	int i = 0;
	while (i < 5)
	{
		printf("%d ", arr2[i]);
		i += 1;
	}
	// 1 2 3 0 0
    
    // 三、变长数组
    // int n;
    // scanf("%d", &n);
    // int arr3[n];
	
	return 0;
}
  1. 数组的下标

    C 语言规定:数组的每个元素都有一个下标,下标是从 0 开始的

    数组可以通过下标来访问

  2. 变长数组(variable-length array,简称 VLA)

    C 语言中,直到 C99 标准出现之前,声明或定义数组时在方括号内只能使用整数常量表达式,而 C99 做了很多改进,允许数组的 [] 中的值是整型变量或是整型表达式

    变长数组是指用整型变量或表达式声明或定义的数组,而不是说数组的长度会随时变化,变长数组在其生命周期内的长度是固定的。

    变长数组是不能进行初始化的。

    VS2019 不支持 C99 标准中的变长数组。



九、操作符

#include <stdio.h>

int main()
{
	// 一、算术操作符
	int a = 10 / 4;
	float b = 10 / 4;
	float c = 10.0 / 4;
	printf("a = %d, b = %f, c = %f\n", a, b, c);
	// a = 2, b = 2.000000, c = 2.500000

	// 二、单目操作符:++、--
	int m = 10;
	// 1. 前置 ++
	int n = m++;
	printf("m = %d, n = %d\n", m, n);  // m = 11,n = 10
	// 2. 后置 ++
	int p = ++m;
	printf("m = %d, p = %d\n", m, p);  // m = 12,p = 12

	// 三、关系操作符和逻辑操作符
	// 写一个程序,根据年龄,判断是否为青年(18~30)
	int age = 16;
	if (18 <= age <= 30)
		printf("1-青年\n");
	if (age >= 18 && age <= 30)
		printf("2-青年\n");
	// 只输出了:1 - 青年,
	// 因为表达式 18 <= age 的结果为假,即 0,
	// 那么表达式 0 <= 30 的结果就为真了,因此执行后面的语句。

	return 0;
}
  1. 算术操作符

    +、-、*、/、%

    / 的两个操作数若都是整数,执行的则是整除;

    % 的两个操作数必须都是整数。

  2. 移位操作符

    >>、<<

  3. 位操作符

    &、^、|

  4. 赋值操作符

    =、+=、-=、*=、/=、&=、^=、|=、>>=、<<=

  5. 单目操作符

    !、-、+、&、sizeof、~、--、++、*、(类型)

    它们分别是:逻辑反操作、负值、正值、取地址、计算操作数的类型长度(以字节为单位)、对一个数的二进制按位取反、前置和后置 --、前置和后置 ++、间接访问操作符(解引用操作符)、强制类型转换。

    前置 ++/--:先使用,再 ++/--。

    后置 ++/--:先 ++/--,再使用。

  6. 关系操作符

    >、>=、<、<=、!=、==

    要注意区分 = 和 ==,前者是赋值操作符,后者是关系操作符,用来判断是否相等。

    != 则是用判断是否不相等。

  7. 逻辑操作符

    &&(逻辑与)、||(逻辑或)

  8. 条件操作符(三目操作符)

    exp1 ? exp2 : exp3

    若表达式 1(exp1)的结果为真,则计算表达式 2(exp2)的值,它的结果作为整个表达式的结果,且表达式 3(exp3)不作任何处理。

    若表达式 1(exp1)的结果为假,则计算表达式 3(exp3)的值,它的结果作为整个表达式的结果,且表达式 2(exp2)不作任何处理。

    等价于:if( exp1 ){ exp2 }else{ exp3 }。

  9. 逗号表达式

    exp1, exp2, exp3, ..., expN

  10. 下标引用、函数调用和结构体成员

    []、()、.、->



十、关键字

C 语言提供了丰富的关键字,这些关键字都是语言本身预先设定好的,具有特殊含义,专门用于特殊用途的 C 语言标识符,也称为保留字

auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

sizeof 既是操作符,又是关键字。

define不是关键字,它是编译器实现的,用来定义宏的预处理指令,不是C语言中的内容。

10.1 - 关键字 auto、register

#include <stdio.h>

int main()
{
	// 一、关键字 auto
	int num1 = 10;  
	auto int num2 = 20;
	// num1 是一个自动变量,即局部变量,实际上等同于 auto int num1 = 10;
	// num1 和 num2 具有相同的存储类

	// 二、关键字 register
	register int a = 10;
	// printf("%p\n", &a); // error

	return 0;
}
  1. 关键字 auto

    在代码块内部声明的变量都是自动变量(Automatic Variable),也就是局部变量,但也可用自动变量的关键字 auto 来明确标识存储类。auto 关键字在我们写的代码中几乎看不到,但它又无处不在。

    存储类是 C 语言和 C++ 语言的标准中,变量与函数的可访问性(即作用域 scope)与生命周期(life time)。存储类可分为 auto、register、static、extern 等。

  2. 关键字 register

    register 关键字建议编译器把局部变量或函数的形参尽可能放入 CPU 的寄存器(reigister)中,以便快速访问。因此变量的字节长度不应该超过寄存器的长度,并且不要用取地址符(&)取获得此变量的内存地址。

    寄存器是 CPU 内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。

10.2 - 关键字 static

#include <stdio.h>

void test()
{
	static int a = 0;
	a++;
	printf("%d ", a);
}

int main()
{
	// 一、static 修饰局部变量
	int i = 0;
	while (i < 10)
	{
		test();
		// test 函数被调用了 10 次,
		// 打印了 1 2 3 4 5 6 7 8 9 10
		i++;
	}
	printf("\n");
	
	return 0;
}
  1. static 修饰变量的作用

    使用 static 关键字修饰变量,使之变成了静态变量(静态局部变量和静态全局变量)static 修饰变量的作用

    • 改变变量的生命周期

      局部变量被 static 修饰后,变成了静态局部变量,它的作用域没有发生改变,生命周期发生了改变。静态局部变量从被定义起就存在,不随函数块结束而销毁,下此进入该函数时,静态局部变量保持上一次的值,只初始化一次,如果没有对其初始化,则系统自动为其赋值为 0。

    • 改变变量的作用域

      局部变量变成静态局部变量,作用域没有发生改变,生命周期发生了改变,而全局变量被 static 修饰后,变成了静态全局变量,它的生命周期没有发生改变,作用域发生了改变。静态全局变量的作用域是本源文件内部,而全局变量的作用域为整个工程。

    • 改变变量的存储区域

      静态变量的存储位置是全局数据区(即静态区)。在被 static 修饰前,全局变量就存储在全局数据区,而局部变量则存储在栈区。

  2. 内存的五大区域

    内存区域存放内存释放时间
    栈(stack)局部变量和函数参数等临时变量由编译器自动分配释放
    堆(heap)动态内存分配一般由程序员分配释放,若程序员不释放,程序结束时可能由 OS 回收
    全局数据区(静态区)全局变量和静态变量程序结束时释放
    常量区常量程序结束时释放
    程序代码区二进制表示的程序代码/
  3. static 修饰函数

    static 修饰函数,使之变成了静态函数。static 修饰的函数与普通函数的区别在于:用 static 修饰的函数限定在本源文件内部,不能在其他源文件中使用。

10.3 - 关键字 typedef

typedef 顾名思义是类型定义,这里应该理解为类型重命名

#include <stdio.h>

typedef unsigned int u_int;

int main()
{
	unsigned int num1 = 10;
	u_int num2 = 20;
	size_t num3 = 30;
	// num1、num2 和 num3 的类型都是一样的,都是无符号整型。

	return 0;
}


十一、#define 定义的常量和宏

#include <stdio.h>

#define MAX 100

#define ADD(x, y) ((x) + (y))

int main()
{
	// MAX 是 #define 定义的标识符常量
	int count = MAX;
	printf("次数:%d\n", count);  // 次数:100

	// ADD 是 #define 定义的宏
	int sum = ADD(10, 20);
	printf("sum = %d\n", sum);  // sum = 30

	return 0;
}

宏(英语:Macro)是一种批量处理的称谓。计算机科学里的宏是一种抽象(Abstract),它根据一系列预定义的规则替换一定的文本模式。解释器或编译器在遇到宏时会自动进行这一模式替换。对于编译语言,宏展开在编译时发生,进行宏展开的工具常被称为宏展开器。



十二、指针

#include <stdio.h>

int main()
{
	// 一、定义一个指针变量
	int num = 10;
	int* p = &num;  
	// &num 是通过取地址操作 & 符取出变量 num 在内存中的地址。
	// * 表明变量 p 是指针变量,
	// int 则表明变量 p 指向的是整型变量。
	printf("%p\n", &num);
	printf("%p\n", p);
	// 输出的结果相同,表明指针变量 p 中确实存储着地址

	// 二、指针变量的解引用
	*p = 20;
	printf("num = %d\n", num);  // num = 20
	// 注意,此处的 * 是解引用操作符,而不是用来表明指针变量的符号。
	// *p 表示通过存储的地址找到对应的变量,即 *p == num。

	// 三、指针变量的大小
	printf("%d\n", sizeof(p));  // 4(32 位平台)
	printf("%d\n", sizeof(int*));  // 4(32 位平台)
	// int* 是指针变量 p 的类型

	return 0;
}
  1. 内存

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

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

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

  2. 建立内存地址

    对于 32 位系统的机器总共有 32 根地址线,64 位系统的机器总共有 64 根地址线。而在计算机的物理结构中,一根地址线可以有 0 和 1 两个电线号(即所谓的低电平和高电平),通过这两个电线号,32 根地址线一共组合起来就可以形成 2^32 个电信号组合,通过它们,我们就可以建立 2^32 个地址,用于处理器识别和访问。同理,64 根地址线一共组合起来可以形成 2^64 个电线号组合,即可以建立 2^64 个地址

    当我们有 2^32 个地址时,寻址空间就是 2^32 个字节,即 4 GB。2^32 ÷ 1024 ÷ 1024 ÷ 1024 = 2^32 ÷ 1024^3 = 2^32 ÷ 2^30 = 2^2 = 4 GB(注意:1024 = 2^10)。

    同理,当我们有 2^64 个地址时,寻址空间就是 2^64 个字节,即 2^34 GB。

  3. 指针、指针变量以及指针变量的大小

    指针也就是我们所说的地址,指针变量则是存储地址的变量。

    指针本质上是地址,但在口语中,指针一般被认为是指针变量。

    既然在指针变量中存储的是地址,而在 32/64 位系统的机器上,地址是 32/64 位(bit),即 4/8 字节(byte),因此指针变量的大小在 32 位平台是 4 个字节,64 位平台是 8 个字节

  4. 取出变量的地址

    通过取地址操作符 & 可以取出变量的地址,例如 &num。那么对于在内存空间中占 4 个字节的整型变量 num,取出它的地址时,是取出其中的哪一个呢?答案是取出第一个内存单元的地址

    测试

    具体操作步骤:按 f10 进行调试,然后点击菜单栏里的 "调试" --> "窗口" --> "内存"。

    注意:存储的内容是以十六进制的形式显示的,一个十六进制对应四个二进制数。



十三、结构体

#include <stdio.h>

// 一、定义一种结构体类型 struct Stu
struct Stu
{
	char name[20];
	char gender[10];
	int age;
};

void Print(struct Stu* ps)
{
	// ps 是一个结构体指针变量,它用来存储结构体变量的地址
	// 法一:
	// printf("%s %s %d\n", (*ps).name, (*ps).gender, (*ps).age);
	// 法二:(结构体指针变量->成员名)
	printf("%s %s %d\n", ps->name, ps->gender, ps->age);
}


int main()
{
	// 二、创建结构体变量
	struct Stu s1 = { "张三", "男", 18};
	struct Stu s2 = { "翠花", "女", 17};

	// 三、打印结构体变量的信息(结构体变量.成员名)
	printf("%s %s %d\n", s1.name, s1.gender, s1.age);
	printf("%s %s %d\n", s2.name, s2.gender, s2.age);
	// 张三 男 18
	// 翠花 女 17
	
	// 定义一个函数,作用是打印结构体变量的信息
	Print(&s1);
	Print(&s2);
	// 张三 男 18
	// 翠花 女 17

	return 0;
}

结构体是 C 语言中一种重要的数据类型,该数据类型由一组称为成员的不同数据组成,其中每个成员可以具有不同的类型。结构体类型不是由系统定义好的,而是需要程序设计者自己定义的。C 语言提供了关键字 struct 来标识所定义的结构体类型。

关键字 struct + 结构体名组合成一种类型标识符,其地位如同通常的 int、char 等类型标识符,其用途就像 int 类型标识符标识整型变量一样可以用来定义结构体变量


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值