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/C++基础--基础操作和面向过程

由于打算捡起嵌入式,从c语言基础开始进行学习。

1. C中的类型转换

原则:占用内存少的类型自动向多的类型转换,有符号自动向无符号转换。
在这里插入图片描述

// 自动类型转换
printf("%d\n", 5 / 2); // double自动转向int
printf("%lf\n", 5.0 / 2); // 参与运算的成员全部变为double类型
int a1 = -8; unsigned int b1 = 7;
if (a1 + b1 > 0) { printf("a1 + b1 > 0 \n"); } // 这里参与运算时a1变为无符号,所以结果为15

// 强制类型转换
double a2 = 4.2; float b2 = 5.8f;
printf("%d\n", int(a2 + b2));

2. 原/反/补码

正数:原码=反码=补码。
负数:反码等于原码除符号位外,每一位进行取反;补码等于反码+1.

3. 变量

普通全局变量:作用范围项目中所有程序。
静态全局变量:作用范围为所在的.c文件。

普通局部变量:生命周期为所在的复合语句或函数开始调用到结束。
静态局部变量:生命周期为所在的复合语句或函数第一次调用开始,之后不进行释放。

4. C语言编译过程

1.预编译:将.c的头文件和宏展开,生成.i文件。
2.编译:将.i文件生成.s汇编文件。
3.汇编:将.s文件生成.o目标文件。
4.链接:将多个.o文件链接成目标文件-可执行程序。

5. 带参宏

#define S(a,b) a*b  // 和带参函数区别在于浪费了空间,换取了时间
int main() 
{
	int num;
	num = S((3 + 5), 4);
	printf("%d\n", num); // 32
	return 0;
}

6. 指针

野指针:指向非法内存空间的指针。

int* p = (int *)0x1100;
cout << *p << endl;  //这里没有输出,因为0x1100这个内存空间并没有申请,所以是非法的
system("pause");

常量指针-const在前:指针的指向可以修改,但是指向的值不可以更改。
指针常量-const在后:指针的指向不可以修改,指向的值可以改。
常量指针常量–如const int * const p:都不可以改。
普通指针操作:

int a = 100, b = 200;
int* p1, * p2 = &b;  // 这里的*修饰指针变量,这里的*p1由于指向尚未确定,属于野指针
p1 = &a;
printf("%d\n", *p1);  // 这里的*取值
printf("%d\n", sizeof(p1));  // 64位系统指针变量8字节
printf("%d\n", *p2);

//指针与数组
int a[5] = {0, 0, 0, 0, 0};
int* p;
p = a; // 首字母a代表a[]的首地址
p[2] = 100;
*(p + 1) = 100;
*(a + 3) = 100;
for (int i = 0; i < 5; i++) {
	printf("%d\n", p[i]);
}

//指针运算
int a[5] = {0, 0, 0, 0, 0};
int* p = &a[0], * q = &a[2];
printf("%d\n", q - p);  // 指针减法返回的是中间隔几个元素
q = p; // 相同类型指针才可以赋值

//指针数组--函数传参
//void fun(const char* q[], int x) 
void fun(const char** q, int x) 
{
	for (int i = 0; i < x; i++) {
		printf("q[%d]=%s\n", i, q[i]);
	}
}

int main() 
{
	const char* p[3] = { "hello", "world", "kitti" };
	fun(p, 3); // 参数p的类型是指针的指针
	return 0;
}

//一维数组指针,配合二维数组使用
int (*p)[5];  // p到p+1会跳过一个5个元素的一维数组
//对n维数组指针取*会将其变为n-1维数组指针
int a[10];
a; a + 1; // a代表数组首地址,是一个int指针,a+1指向数组下一个元素
&a; // 是一个一维数组指针,&a+1指向下一个一维数组

//多级指针
int a = 0x12345678;
int* p;
int** q;
int*** m;
p = &a;
q = &p;
m = &q;
printf("&a = %p ; p = % p\n", &a, p);
printf("&p = %p ; q = % p\n", &p, q);
printf("&q = %p ; m = % p\n", &q, m);

//指针作为函数的返回值
//如果不加static,p调用完fun()后str[]就会被释放,p指向非法内存空间,会变为野指针
char* fun() {
	static char str[] = { "hello world" }; 
	return str;
}

int main() {
	char* p;
	p = fun();
	printf("p=%s\n", p);
	return 0;
}

//函数指针
int(*p)(int, int);
int max(int x, int y);
p=max;

//函数指针也可以作为函数的参数
int add(int x, int y) {
	return x + y;
}
int process(int(*p)(int, int), int x, int y) {
	int ans;
	ans = (*p)(x, y);
	return ans;
}
int main() 
{
	int num;
	num = process(add, 10, 20);
	printf("%d\n", num);
	return 0;
}

7. 字符串

7.1 字符串的可修改性

1.数组(无const修饰)中的字符串,可以修改。
2.文字常量区的字符串,不可修改:

char* str = (char*)"12345";
printf("%s\n", str);  // 输出12345
*str = 'y';
printf("%s\n", str);  // 无输出,说明不可更改

3.堆区的字符串可以修改

#pragma warning(disable:4996)  // VS中禁用了strcpy,加上这句启用
int main() 
{
	char* str;  // 可以看到堆区无法直接初始化,只能先让指针指向堆区,再用strcpy/scanf拷贝
	str = (char*)malloc(20);
	//strcpy(str, "I LOVE C#");
	strcpy_s(str, 20, "I LOVE C#");  //微软提供了该api,中间的参数位缓冲区大小,表示最多拷贝多少字节
	printf("str=%s\n", str);
	return 0;
}

7.2 字符串处理函数

int main() 
{
    //strlen 获取字符串长度
    //strcpy_s
    //strncpy_s
    //strcmp
	char a[] = { "sssssssssssss" };
	char b[] = { "world" };
	strcpy_s(a, strlen(b) + 1, b);
	strncpy_s(a, b, 2);  // 将b的前两个字符拷贝给a
	printf("%s\n", a);
	printf("%d\n", strcmp(a, b)); //a大返回1,b大返回-1,相等返回0,逐字符按对应ASCII码比
	
	//strcat_s
	//strncat_s
	char a[20] = { "ssss\0sssssssss" };
	strcat_s(a, strlen(a)+strlen("world")+1, "world");  //会从a的第一个\0后面追加字符串"world"
	strncat_s(a, "world", 2); //追加“wo”

	//strchr 首位匹配查找字符
	//strrchr 末位匹配
	char b[] = { "world" };
	char* str = b;
	char* p = strchr(str, 'w');
	char* p = strrchr(str, 'w');
	if (p == NULL) {
		printf("Not found\n");
		return 0;
	}
	printf("p-str=%d\n", p - str); //返回是第几个字符
	//strstr-字符串内查找字符串
	char c[] = { "rl" };
	char* p = strstr(b, c);
	if (p == NULL) {
		printf("Not found\n");
		return 0;
	}
	printf("p-b=%d\n", p-b);
		
	//atoi/atol/atof--字符串转换int,long int,float
	char b[] = { "123" };
	printf("b=%d\n", atoi(b));
	//strtok_s
	char a[] = { "aabaaabaaabababaaab" };
	char* ans = NULL;  //用于存储切割下来的字符串
	char* buf = NULL;  //剩余待切割的字符串
	ans = strtok_s(a, "b", &buf);
	while (ans != NULL) {
		printf("切下:%s\n", ans);
		printf("剩余待切割:%s\n", buf);
		ans = strtok_s(NULL, "b", &buf);
	}
	
	//sprintf_s-将字符串写入缓冲区
	char a[2];
	//sizeof(a)>=待写入内容的长度
	//如果超出sizeof(a),函数会报错然后将缓冲区设置为一个空字符串
	sprintf_s(a, sizeof(a), "%d,%d,%d", 2022, 10, 12);
	printf("a=%s\n", a);
	return 0;
}

8. 动态内存分配

8.1 动态申请内存:

//malloc(size_t Size)--申请多少个字节的内存
//malloc申请的内存存放的内容一开始是随机的
int main() 
{
	int* p;
	int i, n;
	printf("请输入您要申请的int数组的元素个数\n");
	scanf_s("%d", &n, 4); //最多输入四位数
	p = (int*)malloc(n * 4);
	if (p == NULL) {      // 如果申请未成功,终止
		printf("malloc err\n");
		return 0;
	}
	for (i = 0; i < n; i++) {
		p[i] = i;
		printf("p[%d]=%d\n", i, p[i]);
	}
	free(p);  //释放malloc,calloc,realloc申请的内存
	//p依旧指向原先申请的内存但是内存不可用,所以p变为野指针
	return 0;
}

//calloc(size_t nmemb, size_t Size)--申请nmemb块,每一块有Size个字节
//calloc申请的内存初始的时候内容为0
	p = (int*)calloc(n, 4);

//realloc(void *s, unsigned int newsize)-重新申请内存
//1.malloc和calloc单次申请的内存连续,但是多次申请的内存不一定连续,用realloc可以在原先申请的内存(*s指向的内存)基础上申请连续的新内存区(newsize字节大小)。
//2.用malloc和calloc申请内存后想要释放申请的一部分内存,可以使用realloc。
	char *p;
	p = (char *)malloc(100);
	p = (char *)realloc(p, 150);

8.2 内存泄漏:

概念:申请的内存首地址丢失,既不能访问,也无法释放,就是内存泄漏。
案例一

//p指向别的地址了,导致动态申请的50个字节泄漏
char* p;
p = (char*)malloc(50);
p = (char *)"hello";

案例二

//p是局部变量,每一次调用完fun(),局部变量就会销毁,导致动态申请的内存泄漏
void fun() {
	char* p;
	p = (char*)malloc(50);
	// 解决方案1 free(p);
}
int main() {
	fun();
	return 0;
}

//解决方案2--想在main中使用fun中申请的内存
char* fun(){
	char* p;
	p = (char*)malloc(50);
	return p;
}
int main() {
	char* q;
	q = fun();
	free(q);
	return 0;
}

8.3 空间设定函数memset:

void* memset(void *prt, int value, size_t num)
功能:将ptr指向的内存空间的num个字节全部赋值位value参数。

char b[] = { "hello world" };
memset(b, '\0', 1);  //初始化内存内容
printf("b=%s\n", b);

9. 结构体

9.1 结构体定义,声明和成员调用:

typedef struct date {
	int month;
	int day;
}Birthday;

struct stu {
	int age;
	char name[10];
	char sex;
	Birthday birthday;  // 引入另一个结构体变量作为成员属性
};

int main() {
	Birthday Bob_birthday = { 2, 14 };
	stu Bob={12, "Bob", '男', Bob_birthday};  //c++可以省略struct
	strcpy_s(Bob.name, 10, "bob");
	cout << "名字:" << Bob.name + "\n";
	cout << "生日:" << Bob.birthday.month << "." << Bob.birthday.day;

	//结构体数组
	stu edu[3]; //该数组有三个学生元素
	return 0;
}

9.2 结构体指针:

结构体定义同上。

// 结构体的函数调用方式
void printf1(stu x) {
	x.age = 99; //值传递不会改变结构体成员对应的值
	cout << x.age << x.name << x.sex << x.birthday.month <<endl;
}
void printf2(const stu* x) {
	//(*x).age = 89;  //地址传递会改变结构体成员对应的值,可+const防止误操作
	cout << (*x).age << (*x).name << (*x).sex << (*x).birthday.month << endl;
}

int main() 
{
	Birthday Bob_birthday = { 2, 14 };
	stu Bob={12, "Bob", "f", Bob_birthday};
	stu* p = &Bob;
	printf1(Bob);
	printf2(&Bob); 
	cout << (*p).name << p->name;
	return 0;
}

9.3 共用体:

结构体的每个成员都有自己的内存空间,共用体则是所有成员公用一段内存空间,具有同样的地址,其空间大小由占据内存长度最大的成员大小决定。
共用体起作用的成员是最后一次存放的成员,会覆盖上一个成员的值。

// 共用体定义方式-struct换成union即可
typedef union date {
	int month;
	int day;
}Birthday;

union date a = {12}; // 初始化共用体只能给第一个成员赋值

10. 枚举

enum week {= 1,,,,,,//枚举元素-默认0开始编号的符号常量
	// 这里指定了从1开始编号
};

int main() 
{
	week workday;
	workday =; //枚举变量只能用枚举元素给其赋值
	cout << workday << endl;
	return 0;
}

11. 文件

11.1 文件指针及顺序读写

在程序中代表一个文件,对文件的操作转化为对文件指针的操作。
顺序读写指的是从头开始读写文件,顺序读写各个数据。

int main() {
	FILE* fp;
	fp = fopen("a.txt", "r");
	if (fp == NULL) { cout << "打开失败" << endl; }
	int fl = fclose(fp);
	if (fl == 0) { cout << "关闭成功" << endl; }

	FILE* fp, *fp_read, *fp_write;
	char str[100];
	fp_read = fopen("a.txt", "r");
	if (fp_read == NULL) { cout << "打开失败" << endl; }
	fp_write = fopen("b.txt", "w");
	if (fp_write == NULL) { cout << "打开失败" << endl; }
	fgets(str, 6, fp_read);  //从fpread代表的文件中读取6-1个字符到str,中间碰到\n也会结束读取
	printf("str=%s\n", str);
	fputs(str, fp_write);    //将str中的内容写入fp_write代表的文件

	return 0;
}

//fread--fwrite
//(void *ptr,size_t size,size_t nmemb,FILE *stream)
//fwrite:将ptr指向的内存里的数据向stream代表的文件中写入,有nmemb块,每块size个字节
struct stu {
	char name[10]; //cout不能输出string
	int num;
	int age;
}boya[2], boyb[2];

int main() 
{
	FILE* fp;
	fp = fopen("a.txt", "wb+");  //wb+,可读可写,二进制
	if (fp == NULL) { cout << "打开失败" << endl; }
	for (int i = 0; i < 2; i++) {
		cout << "请输入第" << i << "结构体的参数" << endl;
		scanf("%s %d %d", boya[i].name, &boya[i].num, &boya[i].age);
	}
	fwrite(boya, sizeof(stu), 2, fp); //写完后光标在文件末尾
	rewind(fp);  //接下来需要从头开始读取,所以重置光标位置
	fread(boyb, sizeof(stu), 2, fp); //将fp代表的文件中的内容读到boyb中
	for (int i = 0; i < 2; i++) {
		cout << boyb[i].name
			<< boyb[i].num
			<< boyb[i].age << endl;
	}
	fclose(fp);
	return 0;
}

11.2 随机读写

只想读取文件的某一部分,所以要去移动光标到位置再进行读写,这种读写称为随机读写。

	int len = ftell(fp); //测文件当前读写文职距文件开始有多少个字节
	fseek(fp, 10, SEEK_CUR); //从当前指针位置向文件末尾方向移动10个字节
	//中间参数为正数向末尾移动,负数向文件头移动
	//SEEK_SET/CUR/END,预定义的宏,代表文件开头/当前/末尾位置
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 20:56:54  更:2022-10-22 21:00:41 
 
开发: 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/19 4:38:29-

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