目录

C遇到的问题

C++遇到的问题

关于while(cin»n»m)的读取问题

原题是利用顺序表和链表实现两个多项式的相加

在读取时:

https://i-blog.csdnimg.cn/direct/84cdf79373654ddba76f2cd717399d62.png

我写的代码是

int n,m;
while (cin >> n>>m)  //n是系数,m是指数
{
	a[m] = n;
}
while (cin >> n>>m)
{
	b[m] = n;
}

这样是错误的,会一直让你输入字符,即使点了空格键也没用

原因是:

cin在输入时会自动跳过空格和换行符。 第一个多项式的输入是3 4 2 3 1 2,第二个多项式是-2 3 4 2 5 1 6 0。当用户使用第一个循环读取时,第一个循环会读取3 4,然后2 3,然后1 2,然后继续读下去,也就是说,原代码中的第一个循环会一直读取所有输入的两个整数,直到输入结束。后面的输入可能还在输入流中。

解决方法:用string输入+用sstream库

int main() {
    string line1, line2;
    getline(cin, line1); // 读取第一个多项式
    getline(cin, line2); // 读取第二个多项式

    // 处理第一个多项式
    stringstream ss1(line1);
    int coeff, exp;
    while (ss1 >> coeff >> exp) {
        a[exp] = coeff; // 直接赋值,输入中每个指数唯一
    }

    // 处理第二个多项式
    stringstream ss2(line2);
    while (ss2 >> coeff >> exp) {
        b[exp] = coeff;
    }

stringstream 是 C++ 标准库 <sstream> 头文件中提供的一个类模板, 它主要用于字符串和其他数据类型之间的相互转换,以及对字符串进行格式化操作,类似于标准输入输出流 cincout ,只不过它操作的对象是字符串而不是控制台或文件。

stringstream 有三种类型:

  • istringstream (字符串读取数据)
  • ostringstream (向字符串写入数据)
  • stringstream (读+写)

当你有一个包含多个数据项的字符串,并且这些数据项之间有特定的分隔符(如空格、逗号等),可以使用 stringstream 来将字符串分割成多个独立的数据项,并将其转换为合适的数据类型。

stringstream使用示例:

字符串读写(用到istringstream):

类比cin吧,但还是有点抽象了

int main() {
    std::stringstream ss;
    // 写入数据
//这个写入数据的操作和std::stringstream ss input("10 20")没啥区别
    ss << 10 << " " << 20;
    // 读取数据
    int num1, num2;
//cin>>num1是从键盘中读取数据并存储在num1中
//ss是从内部存储的字符串中读取数据并存储在num1中,很相似吧
//提取到的字符串类型会被转换成num1的类型
    ss >> num1 >> num2;
    std::cout << "num1: " << num1 << ", num2: " << num2 << std::endl;
    return 0;
}

具体原理是指针:

  • 执行 iss >> num1 时,从字符串的起始位置开始提取连续的数字字符 "123" ,将其转换为整数 123 并存储到 num1 中,此时内部指针移动到空格字符处。
  • 执行 iss >> num2 时,从空格字符后的位置开始提取连续的数字字符 "456" ,将其转换为整数 456 并存储到 num2 中。

字符串分割与解析:

这里使用stringstream,因为要分割字符串,既有字符串的输入,也有字符串的输出

#include <iostream>
#include <sstream>
#include <string>

int main() {
    std::string input = "10 20 30";
//创建一个 stringstream 对象 ss,并将包含三个整数的字符串 input 作为其初始化内容。
    std::stringstream ss(input);
    int num1, num2, num3;
//使用 >> 操作符从 ss 中依次提取数据,并将其存储到 num1、num2 和 num3 中
//就像使用 cin 从标准输入读取数据一样。
    ss >> num1 >> num2 >> num3;
    std::cout << "num1: " << num1 << ", num2: " << num2 << ", num3: " << num3 << std::endl;
    return 0;
}

将其他数据类型(如整数、浮点数等)转换为字符串(以整数为例):

这里使用ostringstream,因为要将整数转换成字符串,输出字符串,所以用ostringstream

#include <iostream>
#include <sstream>
#include <string>

int main() {
//创建一个 ostringstream 对象 oss,它专门用于向字符串写入数据
    int number = 123;
    std::ostringstream oss;
//使用 << 操作符将整数 number 写入 oss 中。
    oss << number;
//调用 oss.str() 方法获取 oss 中存储的字符串内容
    std::string str = oss.str();
    std::cout << "Converted string: " << str << std::endl;
    return 0;
}

将字符串转换为整数

这里用istringstream,因为要从字符串中读取数据,将其转换成整数

#include <iostream>
#include <sstream>
#include <string>

int main() {
    std::string str = "456";
    std::istringstream iss(str);
    int number;
    iss >> number;
    std::cout << "Converted number: " << number << std::endl;
    return 0;
}

一直头疼字符串和数字相互转化的问题,还有怎么解决未知长度输入的问题,今日一见茅厕顿开

老师上课讲的输入输出切换的方法

从文件1.txt中读取一行整数,将这些整数累加求和,最后输出结果

这里也是遇到读取未知字符长度的问题

//在使用一些较新版本的 Visual Studio 编译器时,对于部分传统的 C 标准库函数(如 scanf、strcpy、fopen 等),
//编译器会发出安全警告。这是因为这些函数在使用时可能存在缓冲区溢出等安全风险
//编译器会给出相应的警告信息
//这里define作用是告诉编译器忽略这些安全警告
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int main()
{
	//使用freopen在文件和控制台之间切换输入
	FILE* instream;
	//从文件D:\\1.txt中输入数据,读取数据用r
	//如果是要写入数据就用w
	instream = freopen("1.txt","r", stdin);
	int a, sum = 0;
	while (1)
	{
		if (scanf("%d", &a) != 1) return 0; //如果读取失败,则结束
		sum += a;
//原先的代码是if (char c = getchar() == '\n') break;
//==的优先级高于=,实际执行顺序是先判断输入字符是否是\n,然后返回0或者1
//所以正确的应该是下面这个
        if ((char c = getchar()) == '\n') break;
	}
	printf("%d", sum);//也可以输出到文件2.txt,如果没有2.txt则会现场建一个

	fclose(instream);
	return 0;
}

对于FILE instream*

在 C 语言中, FILE 是一个预定义的结构体类型,它用于表示文件流。 文件流是程序与文件之间进行数据传输的通道,通过文件流,程序可以对文件进行读取、写入等操作

这里声明了一个名为 instream 的指针变量,其类型为 FILE* 。这个指针变量用于指向一个 FILE 类型的对象,也就是指向一个文件流。通过这个指针对相应的文件进行各种操作。

文件流小示例:

基本流程就是打开文件然后输出文件中的字符

#include <stdio.h>

int main() {
    FILE* instream;  // 声明一个 FILE 类型的指针
    instream = fopen("example.txt", "r");  // 打开一个文件,将文件流指针赋值给 instream
    if (instream != NULL) {
        char ch;
        while ((ch = fgetc(instream)) != EOF) {
            putchar(ch);  // 从文件中读取字符并输出
        }
        fclose(instream);  // 关闭文件流
    } else {
        printf("无法打开文件\n");
    }
    return 0;
}

fgetc(instream):从文件流中读取字符,直到文件结束(EOF)

获取数组实际长度

问题:cin.getline(a, 10000); cin.getline(b, 10000);以后怎么得知a和b的实际长度

此时a[]和b[]为字符串

  • strlen()
  • a.size()
  • a.length()

若int a[]

  • length=sizeof(a)/4
  • 使用vector代替数组,可以直接调用size函数获取其实际长度
  • vector<int> c = {1, 2, 3, 4, 5};
    int length = c.size();

关于指针赋值和&的问题

写链表的时候遇到两行这样的代码:

node是struct类型

同样都是指针=struct,为什么第一个需要用&取地址,而第二个不用呢

node index(0, 0);
node* tail = &index;
node* newnode = new node(a, b);

第一个

node index(0, 0) :这行代码在 栈上 创建了一个 node 类型的对象 index, 栈上的对象由系统自动管理内存,当对象所在的作用域结束时,系统会自动释放该对象的内存。

第二个

new node(a, b)new 是 C++ 中用于动态内存分配的运算符,它在 堆上node 类型的对象分配内存,并调用构造函数 node(a, b) 对该对象进行初始化。堆上的内存需要程序员手动管理,使用 delete 运算符来释放。

node* newnode = ...new 运算符返回的就是新分配对象的内存地址 ,所以可以直接将 new 的返回值赋给指针 newnode ,不需要再使用取地址运算符 &

报错大合集

‘nullptr’在此作用域中尚未声明

/tmp/compiler_65pf_i0l/src:11:24: 警告:identifier ‘nullptr’ is a keyword in C++11 [-Wc++11-compat] 11 | next = nullptr; | ^~~~~~~ /tmp/compiler_65pf_i0l/src: In constructor ‘node::node(int)’: /tmp/compiler_65pf_i0l/src:11:24: 错误:‘nullptr’在此作用域中尚未声明 /tmp/compiler_65pf_i0l/src: In function ‘int main()’: /tmp/compiler_65pf_i0l/src:58:21: 错误:‘nullptr’在此作用域中尚未声明 58 | node* cur = nullptr; | ^~~~~~~

解决办法:编译器太老了不认识nullptr,把nullptr改为NULL

一运行就弹出这个窗口

原因:是错误设置了断点的原因,重新新建一个项目把代码复制过去就好了

STL list 的迭代器it报错

在 中蓝桥杯一题中遇到的

https://i-blog.csdnimg.cn/direct/bdb7dc7b98224714b6b9e12f6423fa22.png

decrement是减小的意思

这里就是it–造成的错误,当it是第一个iterator的时候不能–

上课例子:关于浅拷贝和深拷贝导致的指针链表错误

#include<iostream>
using namespace std;
struct node{...};
class linklist 
{
private:
	node* head;
public:
	//注意这种新新的写法,xxx():变量值(要赋的值) {函数体,没有就是光赋值不干别的}
	linklist() :head(nullptr) {};
	~linklist() {
		node* current = head, * nextnode;
		while (current != nullptr)
		{
			nextnode = current->next;
			delete current;
			current = nextnode;
		}
	}
	void insertAtHead(int val) { }//在链表头部插入结点
};
int main()
{
	linklist list1;
	list1.insertAtHead(2);
	list1.insertAtHead(1);
	linklist list2 = list1;
	return 0;
}

这段代码会报错,原因是指针指向的同一片区域被释放了两次

首先 linklist没有定义拷贝构造函数和赋值运算符,所以当执行

linklist list2 = list1;

的时候,编译器生成的默认拷贝构造函数会直接复制head指针的值,这就导致list1和list2的head指向的其实是同一片内存地址,也就是他俩指向的同一个链表,是浅拷贝导致的错误

所以当结束的时候会自动调用析构函数,同一片内存被delete两次,导致错误

但是,什么是深拷贝?什么是浅拷贝?

浅拷贝就是浅层拷贝 ,比如说B要从A那里浅拷贝,其实A和B就是同一个东西,当A改变的时候B也会改变,当B改变的时候A也会改变

深拷贝就是A和B是完全独立的两个东西,A的值改变不会影响B同样B的值改变不会影响A

https://i-blog.csdnimg.cn/direct/f7c8634bb6f344ef8dde8b185a4dd41f.png

字符串和数字之间相互转换的问题

每次遇到每次都不会

char字符 –> int数字

(适合输入的数字为0~9之间,比如高精度算法)

方法一:减去 ‘0’

char c = '5'; 
int num = c - '0'; // 结果是整数5

于是num就水灵灵地转换成了5

对于s[i]-=‘0’:

假设s[i]的值为字符5,‘5’的ascii码减去'0’的ascii码也就是ascii码对应为5的那个字符,不过在内存中存储s[i]的时候还是存储的二进制形式的5,只不过单拿出来s[i]是ascii码对应的3,将s[i]运算的时候还是ascii码3。

方法二:atoi()(引入头文件)

这是把整个char数组看成一个数,转换成int

char str[]="123";
int num=atoi(str);
//num就变成了123

string字符串–>int数字

(适合输入11 2 3 + * #这一行数据,里面有数字11不能拆开成单个char字符,也有除了数字字符别的其他运算符,比如后缀表达式运算)

利用stoi函数(引入库)

string b="123"
int num=stoi(b);//此时num的值是123

stoi是把string类型的字符串转换成int类型并保存在num数组中

它还有它的好兄弟 stol(表示转换成long),stoll(表示转换成long long)

int数字–>string字符串

方法一:to_string()

int num=123;
string str=to_string(num)
//此时str就是"123"

方法二:stringstream()

int num=123;
stringstream ss;
ss<<num;
string str=ss.str();
//此时字符串str的值就是"123"

int数字–>char字符

利用的就是asscii码的问题

int num=1;
char s=1+'0';
//此时s就变成了字符'1'

int多位数字–>char数组

循环/10吧

掌握这些应该就差不多了