IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言遗漏笔记 -> 正文阅读

[C++知识库]C语言遗漏笔记

前言

在课程学习过程中,深刻感到自己C语言基础不足,因此写下这篇博客来作为复习回顾。
==声明:==该笔记面向有基础的同学,只记录本人之前学习中遗漏或者模糊的部分。

1. C语言概述

1.1 起源和特点

贝尔实验室D.M.Ritchie在B语言基础上研发出C语言。C语言可以直接对硬件操作,具有高级语言和低级语言的特性,且效率只比汇编低10%-20%.

1.2 语言开发环境

使用Visual Studio 2022,其中需要知道,一个解决方案可以包含多个项目,每一个项目必须生成一个可执行文件。

2. 数据类型、运算符及表达式

2.1 整型和浮点型

计算机在保存不同的数据类型时所占用的内存大小是不同的。

									32位系统					64位系统
char                                  1                              1
int                                   4                              4
double                                8                              8
long                                  4                              8
long long                             8                              8

int类型范围是KaTeX parse error: Can't use function '\~' in math mode at position 11: -2^{32}-1 \?~? 2^32 -1,即-2147483648 ~ 2147483647

以0开头的数字在计算机中默认为八进制,以0x开头的数字默认为十六进制:

int t1, t2;
t1 = 012;
t2 = 0x121;
printf("t1的值:%d\nt2的值:%d", t1, t2); // 输出12 和 289

可以用sizeof运算符来获得某变量或数据类型所占用字节数:

#include <iostream>

struct student {
	int age;
	int sex;
};
int main()
{
	int t2;
	t2 = 012;
	printf("student结构体占用的字节数是:%d\nt2占用的字节数是:%d", sizeof(student),sizeof(t2));
	// 输出结果为8 和 4,可以看出,结构体占用大小为成员类型总和
	return 0;
}

在常数后面加字母U或u,代表unsigned int
在常数后面加字母L或l,代表long
在常数后面加字母F或f,代表float

unsigned int t1 = 23U;
long int t2 = 189L;
float t3 = 2.13F;

int a = 10;
a = 2.13F;		// a的类型还是int
printf("a的值是:%d", a); // 输出2

float提供6位有效数字(考虑四舍五入)
double提供15~16位有效数字
有效数字:假如原数为1234.5678,但是有效数字为1,则最后存下的数字是1000.0000;若原数字是0.12345,但是有效数字是1,则存下0.1XXXX,X表示不确定。

2.2 字符型

本质上字符型常量存到一个字符型变量是将对应的ASCII码存到该变量中:

char c1, c2;
c1 = 97;
c2 = 98;
printf("c1 = %c, c2 = %c\n", c1, c2);	// a b
printf("c1 = %d, c2 = %d", c1, c2);		// 97 98

字符常量’a’和字符串常量"a"的区别:

  • 内存占用不同,前者占1字节,后者占2字节。

  • “a"本质上由’a’ 和 ‘\0’ 两个字符构成,其中’\0’ 是结束标志:

    printf("dfafdadf\0dfasdfa");	// 只输出dfafdadf
    
  • char a[1000] = "asdfaf\0dfef";	// 使用内存视图会发现\0对应的ASCII码是00(16进制)
    printf(a);
    

2.3 强制类型转换

语法格式:(类型名)(表达式名)

强制类型转换并不会改变原变量的数据类型,而是产生一个中间变量:

float x;
int y;
x = 1.23;
y = (int)x;
printf("x = %f, y = %d", x, y); 	// 1.230000 1

2.4 逗号表达式和逗号运算符

逗号运算符是优先级最低的运算符,基本语法是:逗号表达式1, 逗号表达式2, 基本规则是先求解表达式1,再求解表达式2:

int x, a;
a = (4, 5);	// a = 5
a = (3 * 5, 6 + 8); // a = 14
a = 3 * 5, a * 4;	// a = 60

3. 程序的基本结构与语句

3.1 while和do … while

int cnt1 = 5, cnt2 = 5;
while (cnt1 >= 0)
{
printf("cnt1的值是:%d\n", cnt1);
cnt1--;
}

puts("");
do
{
printf("cnt2的值是:%d\n", cnt2);
cnt2--;
} while (cnt2 >= 10);			// 即使条件不符合也至少会执行一次

3.2 #include <> 和 “” 的区别

#include <文件名>:Visual Studio会直接去系统目录下找该文件。
#include "文件名":Visual Studio会先从源文件所在目录开始查找,若找不到再去系统目录寻找,因此常用""包含一些自己写的头文件。

3.3 printf函数

  • printf使用格式控制符%s:在输出时,printf不会输出字符串数组结尾的\0:

    printf("中国的英文拼写是:%s", "CHINA");		// 输出 CHINA
    

3.4 scanf函数

默认识别回车和空格作为输出结束。

4. 逻辑运算和判断选择

4.1 优先级

  • ! > 算术运算符 > 关系运算符 > &&和 || > 赋值运算符

4.2 逻辑运算符的短路原则

当目前能够判断出整个式子真假时,依据短路原则,不会继续执行,例如a && b && c,只要a若为假,则后续不再执行。

5. 循环控制

5.1 goto

	int i = 1, sum = 0;
loop:
	if (i <= 100)
	{
		sum += i;
		i++;
		goto loop;
	}
	printf("1+2+...+100的总和是:%d", sum);

for循环

for (int i = 1; i <= 9; i++)
{
    for (int j = 1; j <= i; j++)
    {
        int k = i * j;
        printf("%d * %d = %d  ", i, j, k);
    }
    puts("");
}

6. 数组

6.1 字符数组

转义字符\0即数字0,因此,当我们数组初始化时,如果给值个数大于数组长度,但是实际上输出时,遇见默认的0也会自动停止。

字符串常量在定义时,系统会在其末尾加上\0:

char s[100] = "I am happy";		// 实际上占11个位置

出现烫烫烫的原因:

char s[] = { 'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y' };
printf("%s\n", s);	// 输出 I am happy烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫攼鍓?

printf需要遇见\0停止,但是显然s没有终止符,因此会输出很多垃圾信息,直到巧合遇见了\0。

字符数组在使用scanf进行输入时,数组名前面不用加&,因为数组名本身就是地址。

6.2 strcat函数

基本语法:strcat (字符数组1, 字符数组2),将字符串2拼接到字符串1后面,并且自动去除\0。

注意:

  • 该函数在<string.h>头文件中。

  • 字符数组1的大小需要足够大。

char str1[10] = "one";
char str2[10] = "two";
printf("%s", strcat(str1, str2));	// 输出 onetwo

6.3 strcpy函数

基本语法:strcpy(字符数组1, 字符串2)

char s1[10] = "one1234";
char s2[10] = "cat";
printf("%s", strcpy(s1, s2));	// 输出 cat

该函数的本质是将字符串2(连同结束标志)挨个赋值到字符数组1的前几位,这样当输出时,因为结束标志的存在,只会输出字符串2。

6.4 strcmp函数

基本语法:strcmp(字符串1, 字符串2),使用ASCII码进行比较。

  • 当字符串1 = 字符串2,则返回0。
  • 当字符串1 > 字符串2,则返回正整数。
  • 当字符串1 < 字符串2,则返回负整数。
char s1[10] = "hello";
char s2[15] = "hello";
if (!strcmp(s1, s2))
    printf("两个字符串相等");

6.5 strlen函数

该函数的作用是统计字符串的长度, 其中不包括结束标志。

注意该函数和sizeof的区别:

char str[120] = "ope1";
printf("strlen(str)结果是:%d\n", strlen(str));	// 4
printf("sizeof(str)结果是:%d\n", sizeof(str));	// 120

7. 函数

7.1 函数概述

函数调用时传递给函数的参数称为实参,函数接收数据的参数称为形参,在函数调用时,实参的值会自动赋给形参。

为了通用性,在工程中,一般将所有自定义函数的声明写在一个.h头文件中,然后使用#include将这个头文件包含进来。

7.2 函数的嵌套调用

void qtfunc1();
void qtfunc2();
void qtfunc3();

int main()
{
	qtfunc1();

	return 0;
}

void qtfunc1()
{
	printf("qtfunc1开始执行------\n");
	qtfunc2();
	printf("qtfunc1结束执行------\n");
}

void qtfunc2()
{
	printf("qtfunc2开始执行------\n");
	qtfunc3();
	printf("qtfunc2结束执行------\n");
}

void qtfunc3()
{
	printf("qtfunc3开始执行------\n");
	printf("qtfunc3结束执行------\n");
}

7.3 函数的递归调用

int dg_jiecheng(int n)
{
	if (n == 1)
		return 1;
	return dg_jiecheng(n - 1) * n;
}

7.4 局部静态变量

局部变量在程序运行结束后自动释放内存,而静态局部变量则可以保存上一次函数调用结束的值。

void functest()
{
	static int c = 4;	// 静态局部变量
	printf("c的值是:%d\n", c);	// 输出4 5 6
	c++;
	return;
}

int main()
{
	functest();
	functest();
	functest();

	return 0;
}

8. 编译预处理

8.1 整体流程

每一个.cpp文件都会编译成为.o或者.obj(视系统而定),然后链接这些.o文件成为一个.exe文件。 其中编译过程分为预处理、编译、汇编。最常见的文件包含是将所有的头文件名包含在一个头文件中,然后再包含这个头文件。

8.2 带参数的宏定义

#define S(a, b) a*b

int main()
{
	printf("%d", S(3, 2));	// 输出6

	return 0;
}

8.3 条件编译

跨操作系统平台中,有一些代码只能在Windows下编译,有一些只能在Linux下编译,因此就需要用上条件编译。

9. 指针

9.1 地址、映射表和指针

以语句printf("i + j = %d", i + j)为例,计算机从映射表中找到 i 所对应的地址(假设为1000),然后由于为int类型,故取出1000~1004所存的值,然后依照同样的逻辑取出 j 所对应的值, 最后执行加法。

指针本质上为一种特殊的变量,存储着另一变量的地址。间接访问就是如果要存取 i 的值,可以先找到存放 i 地址的这个内存位置,也就是指向 i 的指针变量,取出其值后访问地址。

其中需要注意:指针是地址,指针变量是存放其他变量地址的变量。

9.2 指针的定义和使用

int i = 7, j = 9;
int* mypoint1, * mypoint2;	// 这里的 * 号表示正在定义一个指针变量

mypoint1 = &i;
mypoint2 = &j;
int a, b;
a = 200;
b = 300;
int *p1 = &a;
int* p2 = &b;
printf("%d %d\n", *p1, *p2);	// *是指解引用
&*p1;		// 若p1指向a,则其等价于&a
p2 = &*p1;	// 若p1指向a,则其等价于p2 = &a, 即p2指向p1的变量
*&a;		// 其等价于a
(*p1) ++;	// 若p1指向a,其等价于a ++
*p1 ++ = 5;
/* 假设p1一开始指向a,该句的完整含义:先执行*p1 = 5, 即a = 5,然后执行p1 ++,即p1指向a变量后一个单位的地址
int a = 5, b = 8;
int* pmax = &a, * pmin = &b;
int* p;
if (a < b)
{
    p = pmax;
    pmax = pmin;
    pmin = p;
}
printf("a = %d b = %d\n", a, b);
printf("max = %d min = %d\n", *pmax, *pmin);

9.3 指针变量作为函数参数

void swap(int* p1, int* p2)
{
	int tmp;
	tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

/* 以下交换函数不能实现变量 a 和变量 b互换,最终结果只是两个指针的指向变了*/
void swap(int* p1,  int* p2)
{
    int *ptmp;
    ptmp = p1;
    p1 = p2;
    p2 = ptmp;
}

9.4 指针数组和数组指针

// 指针数组
int a[3][4];
for (int i = 0; i < 3; i++)
    for (int j = 0; j < 4; j++)
        a[i][j] = 86;

int* p[4];
p[0] = &a[0][0];
p[1] = &a[0][1];
p[2] = &a[0][2];
p[3] = &a[0][3];
for (int i = 0; i < 4; i++)
    printf("value = %d\n", *p[i]);	// 输出86
// 数组指针
int (*p)[10];	// p指向一个含10个元素的一维数组
int a[10];
for (int i = 0; i < 10; i++)
    a[i] = i;
p = &a;
int* q;
q = (int*)p;	// 强制类型转换
for (int i = 0; i < 10; i++)
{
    printf("value = %d\n", *q);
    q++;
}

9.5 字符串指针

char a[] = "I love China";
char b[100];
char* p1, * p2;
p1 = a;
p2 = b;
for (; *p1 != '\0'; p1++, p2++)
    *p2 = *p1;
*p2 = '\0';
/* 这种是错误的 */
char a[100];
a = "I love China";

/* 这种是正确的 */
const char *a;
a = "I love China";

9.6 函数指针

函数代码也存储在内存中,因此可以用一个指针指向函数的起始地址,即指针。

int c;
//c = max(5, 19);
int (*p)(int a, int b);		// 定义一个函数指针变量
// 一定不能写成 int *p(int a, int b),这样表示返回值为int *的函数
p = max;
c = (*p)(5, 19);

printf("c = %d\n", c);

这里需要注意,max和p这两个变量保存的地址不同,是因为Visual Studio会建立函数-地址对应表,也就是max保存的是相对地址,而p保存的是绝对地址。

9.7 指针变量补充

指针变量也可以指向NULL,NULL是一个宏,即地址为0的单元,系统保证该单元不存放任何有效的数据。

char *p;
p = NULL;
if (p == NULL) {...}

10. 结构体

基本语法:struct 结构体名 {成员列表} 变量名列表;,使用结构体成员运算符来引用成员,如s.age

10.1 结构体指针访问成员

struct student stu;		// 注意这里是结构体变量
struct student* ps;
ps = &stu;
(*ps).num = 1000;
ps->age = 1;

struct student stu[10];
struct student *ps;
ps = stu;				// 注意这里为结构体数组,没有&

10.2 共用体

共用体和结构体的区别更多是在内存上,而在实际用法上和结构体相差不大,具体不进行深究,遇见实际问题再说。

union myuni
{
    int carnum;
    char cartype;
    char name;
}a, b, c;

printf("%d\n", a.carnum);

10.3 枚举类型

enum color
{
	Red,
	Green,
	Blue,
	Yellow
}mycolor1, mycolor2;

int main()
{
	mycolor1 = Yellow;
	printf("%d\n", mycolor1);	// 3
	
	return 0;
}

10.4 typede定义类型

typedef struct LNode
{
    int node;
    struct LNode *next;		// 定义该类型指针
}LNode, *LinkList;			// 定义类型名和指针名
/* typedef 还可以用来重新定义数组 */
int n[100];					// 这是常规定义

int NUM[100];
typedef int NUM[100];		// 定义自己想要的类型名
NUM n;						// 这一步即生效 int n[100];

11. 位运算

11.1 简介和例子

常见位运算中&|就不再赘述,主要记录一下^~:

如果想某二进制位翻转,则只需要将其与1进行异或即可;若想保持二进制不变,则将其与0异或即可。

/* 异或 */
unsigned int temp = 38 ^ 23;
printf("%u\n", temp);

/* 左移乘2 */
unsigned int temp = 15 << 1;
printf("%u\n", temp);

/* 右移除2 */
unsigned int tmp = 14 >> 1;
printf("%u\n", tmp);
/* 位运算用于游戏任务清算例子 */
#define BIT(x) (1 << (x))	// 宏定义一个函数
enum EnumTask
{
    ETask1 = BIT(0);		// 左移一位变成1
    ETask2 = BIT(1);		// 左移一位变成2
    ETask3 = BIT(3);		// 左移一位变成4
    ETask4 = BIT(4);		// 左移一位变成8
    ETask5 = BIT(5);		// 左移一位变成16
};
unsigned int tesk = 0;
if (tesk & ETask3)
    printf("任务3已经做过\n");
else
    printf("任务3未做过\n");

12. 文件读写

12.1 fopen函数和fclose函数

fopen函数是将文件以某种方式打开。

FILE *fp;						  // FILE是一个结构体,fp是该结构体类型的指针
fp = fopen(文件名, 文件使用方式);	// 两个参数本质上都是字符串

使用fclose (文件指针)将文件关闭。

12.2 fwrite函数和fread函数

fwrite函数基本语法:fwrite(buffer, size, count, fp);

  • 文件或者指针,需要写入文件的数据就在这个地址保存着

  • size指写入字节数

  • count指写入多少字节的数据项

  • fp指文件指针,作为返回值,失败则为0,成功则返回count

struct stu
{
	char name[20];
	int age;
	double score;
};

int main()
{
	struct stu student[2];
	strcpy(student[0].name, "张三");
	student[0].age = 21;
	student[0].score = 92.1f;

	strcpy(student[1].name, "李四");
	student[1].age = 31;
	student[1].score = 32.4f;

	FILE* fp;
	fp = fopen("test.bin", "wb");
	if (fp == NULL)
		printf("打开文件失败\n");
	else
	{
		int len = sizeof(struct stu);
		int res = fwrite(&student, sizeof(struct stu), 2, fp);
		fclose(fp);
	}

	return 0;
}

fread函数参数与fwrite是一直的,并且用法类似,故省略。
ad函数

fwrite函数基本语法:fwrite(buffer, size, count, fp);

  • 文件或者指针,需要写入文件的数据就在这个地址保存着

  • size指写入字节数

  • count指写入多少字节的数据项

  • fp指文件指针,作为返回值,失败则为0,成功则返回count

struct stu
{
	char name[20];
	int age;
	double score;
};

int main()
{
	struct stu student[2];
	strcpy(student[0].name, "张三");
	student[0].age = 21;
	student[0].score = 92.1f;

	strcpy(student[1].name, "李四");
	student[1].age = 31;
	student[1].score = 32.4f;

	FILE* fp;
	fp = fopen("test.bin", "wb");
	if (fp == NULL)
		printf("打开文件失败\n");
	else
	{
		int len = sizeof(struct stu);
		int res = fwrite(&student, sizeof(struct stu), 2, fp);
		fclose(fp);
	}

	return 0;
}

fread函数参数与fwrite是一直的,并且用法类似,故省略。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:02:42  更:2022-07-17 16:06:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/14 1:12:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码