1.VSCode无法正常调试 241011
解决方法:调试步骤:
1.先在终端中编译文件到debug的文件夹中(一定添加-g,否则大多数参数无法显示)
此为编译指令
gcc -g YOUR_PROGRAM.c -o e:\YOUR_PATH\build\Debug\outDebug.exe
2.确保launch.json中路径正确
3.点击开始调试,就可以正常开始调试了
PS:vscode初始配置我是看下面的视频,一遍搞定,全站质量最高,不是什么傻逼营销号,强烈推荐!
https://www.bilibili.com/video/BV1kR4y1M7R8
2.刷题网站和本地运算结果不同 1012
这是一段我求自守数的题目和代码:
/*Q431.(语言: C)自守数是指一个数的平方的尾数等于该数的自然数。求200000以内的自守数。
**输入格式要求:提示信息:"It exists following automorphic numbers smaller than 200000:\n"
**输出格式要求:" %ld"
程序运行示例如下:
It exists following automorphic numbers smaller than 200000:
0 1 5 6 25 ** 376 *** 9376 ***25 109376*/
#include <stdio.h>
#include <math.h>
int length(long long num){ //测量数字长度
int length=0;
if (num==0){ //0也有一位
return 1;
}
while (num!=0){
num/=10;
length++;
}
return length;
}
long long fetch(long long squared_num,int behind_length){ //取出平方后数字和其本身长度相同的后几位数
long long fetched=0;
fetched=(squared_num)%(long long)pow(10,behind_length);
return fetched;
}
int is_zishoushu(long long num){
if (fetch(num*num,length(num))==num){ //判断平方之后数字的后几位是否和平方之前的数字相等
return 1;
} else {
return 0;
}
}
int main(){
printf("It exists following automorphic numbers smaller than 200000:\n");
for (long long j=0;j<=200000;j++){
if (is_zishoushu(j)==1){
printf(" %ld",j);
}
}
printf("\n"); // 输出换行
return 0;
}
本地的输出结果和刷题网站上面的输出结果总是不同,这让我非常疑惑。询问了GPT,是这段代码出现了问题:
long long fetch(long long squared_num,int behind_length){ //取出平方后数字和其本身长度相同的后几位数
long long fetched=0;
fetched=(squared_num)%(long long)pow(10,behind_length);
return fetched;
}
GPT原话:
pow()
函数的使用 :pow()
函数返回的是浮点数,而在涉及大数运算时,浮点数计算精度容易出现微小误差。这种误差可能导致不同平台上的输出不同。在 C 中使用整数时,尽量避免使用浮点运算来保证精度。- 平方数尾数的提取 :你在
fetch()
函数中使用了pow()
函数来取模运算,但由于pow()
返回浮点数,可能会引发误差。
于是,我听取了他的建议,把这段代码改成了这样:
long long fetch(long long squared_num,int behind_length){ //取出平方后数字和其本身长度相同的后几位数
long long mod = 1;
while (behind_length--) {
mod *= 10; // 用乘法代替 pow()
}
return squared_num % mod;
}
避免使用精度差的pow函数,转而使用乘法来进行指数运算,使结果相同了。
3.int爆了 1012
题目和代码和上面的一样,大家有没有注意到我的long long?这是我更改了许多int的结果。
GPT原话:
long long
和 int
在 C 语言中的范围主要区别在于它们能存储的整数值的大小。具体如下:
int
- 大小 : 通常是 4 字节 (32 位)。
有符号
int
的范围 :- 最小值 : -2,147,483,648
- 最大值 : 2,147,483,647
无符号
unsigned int
的范围 :- 最小值 : 0
- 最大值 : 4,294,967,295
long long
- 大小 : 通常是 8 字节 (64 位)。
有符号
long long
的范围 :- 最小值 : -9,223,372,036,854,775,808
- 最大值 : 9,223,372,036,854,775,807
无符号
unsigned long long
的范围 :- 最小值 : 0
- 最大值 : 18,446,744,073,709,551,615
总结区别:
int
通常是 32 位 ,表示的范围较小,适合较小范围的整数计算。long long
通常是 64 位 ,可以表示更大范围的整数,适合处理更大规模的数值。
在使用时,如果你要处理非常大的数字,long long
是一个更合适的选择,而对于一般范围的整数运算,int
就已经足够了。
所以,在使用时要注意数字范围。
4.二分查找和数据类型的烦人碰撞 1013
在我学习python时,我一直认为二分查找最终总会有个结果。
结果发现是我记混了,那是二叉树的二分查找,最后当然有结果。
有时候二分查找是只能近似近似再近似,程序在达到你设定的精度后(例如10−6)才会自己停下。
而且,由于浮点数计算精度的问题,再加上昨天pow()
函数带来的不精确,原本答案是2的方程最后只能解出个2.00几。
看题目:
Q1309.(语言: C)用二分法求方程2x^3-4x^2+3x-6=0在(-10, 10)之间的根。 **输入格式要求:"%f,%f" 提示信息:"请输入x1,x2的值:" **输出格式要求:"方程的根=%6.2f\n" 程序的运行示例如下: 请输入x1,x2的值:-10,10 方程的根= xxx
答案一眼就能看出来是2。我的代码如下:
#include <stdio.h>
#include <math.h>
#include <windows.h> // 仅适用于 Windows 系统
double f(double x){
return 2*pow(x,3)-4*pow(x,2)+3*x-6;
}
int main(){
SetConsoleOutputCP(65001);
double x1,x2,mid;
double eps=1e-6;
printf("请输入x1,x2的值:");
scanf("%lf,%lf",&x1,&x2);
if (f(x1)*f(x2)>0){
printf("无根");
}
while (fabs(f(x1)-f(x2))>eps){
mid=(x1+x2)/2;
if (f(mid)==0){
break;
} else if (f(mid)*f(x1)<0) {
x2=mid;
} else {
x1=mid;
}
}
printf("方程的根=%6.2f\n",mid);
return 0;
}
但是控制台输出的就是2.000几。
而且,我在其中更改了很多次数据类型(和昨天一样!!!)为了让我对数据更加熟悉,我又从gpt那里偷来一个表格:
记住这个表格,应该是没啥问题了。
对于近似二分查找的算法,我还是有些不熟悉,日后需要再熟悉熟悉。
5.控制台乱码 10.13
这也是一个困扰了我很久(指2天)的问题。
为啥学校的刷题网站就能正常输出,不乱码,而我本地就乱码?
网上的system("chcp 65001")
命令有时候有效有时候直接错误,这又是为什么?
好在,GPT给我提供了一个新的命令,同样是<windows.h>
头文件,只需要用这个命令(已经出现在上面的代码中):SetConsoleOutputCP(65001);
这样我也是成功正确输出了。
6.直接删除字符串子串太困难了,我选择直接复制
一切源于这道很有意思的题目:
Q3696.(语言: C)判断字符串str2是否是字符串str1的子串,如果是则从str1中删除str2,并输出更新后的str1;否则输出原字符串str1。输入为两个字符串str1和str2(字符串内不含空格),中间用空格间隔,输出为删除操作后的str1.假设字符串的最大长度不超过20. 程序运行样例1: abccdffi cd abcffi 程序运行样例2:(如果str2出现多次要全部删除) abcslfkjbcd bc aslfkjd 输入输出格式:scanf("%s",...... scanf("%s",...... printf("%s\n",......
直接删除字符串需要用到内存操作,目前我还没学到这里,所以我不想用这个,这是gpt的代码:
#include <stdio.h>
#include <string.h>
void removeSubstring(char *str, const char *sub) {
char *pos;
int len_sub = strlen(sub);
// 查找子字符串的位置
while ((pos = strstr(str, sub)) != NULL) {
// 将后面的部分移动到前面
memmove(pos, pos + len_sub, strlen(pos + len_sub) + 1); // +1 是为了移动结束符'\0'
}
}
int main() {
char str[100] = "Hello, this is a sample string.";
const char *sub = "sample ";
printf("原始字符串: %s\n", str);
removeSubstring(str, sub);
printf("删除后的字符串: %s\n", str);
return 0;
}
需要使用内存删除的函数。内存操作目前对我来说还是太超前了,于是我决定:把主串里面的内容,跳过子串,复制到新的字符串里面去。
在这中间我遇到了以下两个问题(或者说是犯了两个错误):
1.'\0'
是单个字符
代码写着好好的,结果编译器天天报错,一问gpt,原来这玩意是单个字符,我之前一直用的双引号"\0"
。
2.在跳过的过程中,不能直接比较字符本身,指针位置才是最准确的
为啥会不通过呢?因为我的代码是这样的:
#include <stdio.h>
#include <string.h>
int main() {
char str1[21], str2[21], str3[21];
int p1, p3;
// 输入两个字符串
scanf("%s", str3);
scanf("%s", str2);
// 初始化 str1
str1[0] = '\0';
// 使用指针 p 查找子串 str2 在 str3 中的位置
char *p = strstr(str3, str2);
// 循环查找并删除所有出现的子串
while (p != NULL) {
// 复制 str3 中 str2 之前的部分
p1 = 0; // 重置 p1
p3 = 0; // 重置 p3
// 复制 str3 中 str2 之前的部分
while (str3[p3] != *p) {
str1[p1] = str3[p3];
p1++;
p3++;
}
// 跳过 str2 的部分
p3 += strlen(str2);
// 继续复制 str3 中 str2 之后的部分
while (str3[p3] != '\0') {
str1[p1] = str3[p3];
p1++;
p3++;
}
// 添加字符串结束符
str1[p1] = '\0';
// 将 str1 的内容复制回 str3,以便继续查找
strcpy(str3, str1);
// 查找下一个子串
p = strstr(str3, str2);
}
// 输出结果
printf("%s\n", str1);
return 0;
}
而系统给出的例子中,两个c是连续的,这样,按照我代码寻找跳过点的逻辑,在第一个c就停下来了,必定出错。
我问gpt怎么办,它没想出来,结果我自己想出来了:比较指针在数组中的位置。
但是怎么比较,我就想不出来了。这又不是python,strstr()
函数返回的可是指针的位置啊,我的p3指针和p指针地址又不一样,咋比。
结果我让gpt想一下,它一下子就想出来了:直接用p指针减去str字符串就行了啊!
因为字符串如果被当做指针使用,它会指向字符串首个字符的地址。所以这样相对一减,p的下标就出来了。
然后我就绕过了这个bug。代码如下:
#include <stdio.h>
#include <string.h>
int main(){
char str2[21],str1[21],str3[21];
int p1=0,p3=0;
scanf("%s",str3);
scanf("%s",str2);
char *p=strstr(str3,str2);
// 初始化 str1
str1[0] = '\0';
while (p!=NULL) {
p1=0,p3=0;
// 计算 str2 在 str3 中的位置
int index = p - str3; // p 指向的地址减去 str3 的地址,得到下标
while (p3<index) {
str1[p1]=str3[p3];
p1++;
p3++;
}
p3+=strlen(str2);
while (str3[p3]!='\0') {
str1[p1]=str3[p3];
p1++;
p3++;
}
str1[p1]='\0';
strcpy(str3,str1);
p=strstr(str3,str2);
}
str1[p1]='\0';
printf("%s\n",str1);
return 0;
}
问题总算是解决了一部分。最后应该只剩下多个子串挤在一起出现的问题了。这个我也没啥思路,如果有思路欢迎在评论区提出来。
真是酣畅淋漓的一次解决问题啊。。。
7.致敬Python屎山——半小时拉了坨大的 1021
题目:
Q3943.(语言: C)输入一个数字(0<num<4000),转换成罗马数字并存储在字符串中输出,
在罗马数字中有其中不同的符号:I,V,X,L,C,D,M,分别对应阿拉伯数字,
Sybol Value
I 1
V 5
X 10
L 50
C 100
D 500
M 1000,
所以1表示为”I”,而2则表示为”II”,即”I”+”I”,直到4,不再这样直接相加,而是表示为5-1,即”IV”, 超过5以后,分别用VI表示 6,VII表示 7,VIII表示 8,而9则表示为10-1,即“IX“,以此类推,40表示为”XL”, 80表示为“LXXX",90表示为”XC”,100表示为”C",300表示为“CCC”,400表示 为“CD”,600表示为“DC”,900表示为“CM”…
输入提示:"Please input number:\n"
输入格式: "%d"
输出格式: "%s"
程序运行结果实例1:
Please input number: ↙
3999
MMMCMXCIX
程序运行结果实例2:
Please input number: ↙
149
CXLIX
程序运行结果实例3:
Please input number:↙
138
CXXXVIII
我花了半小时时间,写了一坨:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
int num,first_num,strp=0;
char str[20];
printf("Please input number:\n");
scanf("%d",&num);
while (num>0){
if (num/1000>=1){
first_num=num/1000;
num=num%1000;
for (int i=0;i<first_num;i++){
str[strp]='M';
strp+=1;
}
} else if (num/100>=1){
first_num=num/100;
num=num%100;
if (first_num==4 || first_num==9){
if (first_num==4){
str[strp]='C';
strp+=1;
str[strp]='D';
strp+=1;
} else {
str[strp]='C';
strp+=1;
str[strp]='M';
strp+=1;
}
} else if (first_num<4) {
for (int i=0;i<first_num;i++){
str[strp]='C';
strp+=1;
}
} else if (first_num==5) {
str[strp]='D';
strp+=1;
} else {
str[strp]='D';
strp+=1;
for (int i=0;i<first_num-5;i++){
str[strp]='C';
strp+=1;
}
}
} else if (num/10>=1){
first_num=num/10;
num=num%10;
if (first_num==4 || first_num==9){
if (first_num==4){
str[strp]='X';
strp+=1;
str[strp]='L';
strp+=1;
} else {
str[strp]='X';
strp+=1;
str[strp]='C';
strp+=1;
}
} else if (first_num<4) {
for (int i=0;i<first_num;i++){
str[strp]='X';
strp+=1;
}
} else if (first_num==5) {
str[strp]='L';
strp+=1;
} else {
str[strp]='L';
strp+=1;
for (int i=0;i<first_num-5;i++){
str[strp]='X';
strp+=1;
}
}
} else {
first_num=num;
num=0;
if (first_num==4 || first_num==9){
if (first_num==4){
str[strp]='I';
strp+=1;
str[strp]='V';
strp+=1;
} else {
str[strp]='I';
strp+=1;
str[strp]='X';
strp+=1;
}
} else if (first_num<4) {
for (int i=0;i<first_num;i++){
str[strp]='I';
strp+=1;
}
} else if (first_num==5) {
str[strp]='V';
strp+=1;
} else {
str[strp]='V';
strp+=1;
for (int i=0;i<first_num-5;i++){
str[strp]='I';
strp+=1;
}
}
}
}
str[strp]='\0';
printf("%s",str);
}
这边也是致敬我初学py的时候了。
我想不出更简洁的实现了,如果有人想出来可以打在评论区。我稳gpt它是这样想的:
#include <stdio.h>
int main() {
int num;
char *roman[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX",
"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC",
"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM",
"M", "MM", "MMM"};
printf("Please input number:\n");
scanf("%d", &num);
if (num <= 0 || num > 3999) {
printf("Invalid input. Please enter a number between 1 and 3999.\n");
return 1;
}
// 计算千位、百位、十位和个位
printf("%s", roman[num / 1000 + 27]); // M: 1000
printf("%s", roman[(num % 1000) / 100 + 20]); // C: 100
printf("%s", roman[(num % 100) / 10 + 10]); // X: 10
printf("%s\n", roman[num % 10]); // I: 1
return 0;
}
真的挺讨厌这种题目的。。。
8.我学会了C语言中的结构体和链表 1024
在网课的反复观看和gpt的帮助,我终于学会了这个抽象的东西。。。
以下是我自己写的示例代码,基本每一行代码都有注释。
#include <stdio.h>
#include <stdlib.h>
typedef struct Link
{
int data;
struct Link* Next;
}Link;
int main(){
Link *head=NULL,*tail=NULL,*p=NULL;
//1.创建链表12345
head=(Link*)malloc(sizeof(Link));//给head分配内存空间,成为第一块链表
head->data=1;//把1赋值给第一块链表的数据部分
head->Next=NULL;//第一块链表的next指向空
tail=head;//尾节点指针指向第一块链表
for (int i=0;i<5;i++){//循环5次,创建长度为5的链表
p=(Link*)malloc(sizeof(Link));//给p分配下一块链表的空间
tail->Next=p;//上一个节点的next指向新节点
p->data=i+2;//给当前节点的数据赋值
tail=p;//上一个节点指针转移为本节点
}
tail->Next=NULL;//创建结束,最后一块节点的next指向null
//2.遍历链表
p=head;//活动指针先指向第一个节点
while (p!=NULL){//直到遍历到尾节点才结束
//Link *temp=p;//将指针temp指向p,为了free内存
printf("%d->",p->data);//输出链表内容
p=p->Next;//节点往下遍历
//free(temp);//现场删除上一块内存
}
printf("\n");
//3.插入数据在头节点(插个0)
p=(Link*)malloc(sizeof(Link));//给0节点分配内存
p->data=0;//给0节点赋值数据0
p->Next=head;//0节点的next指向原先链表的头节点
head=p;//对3无关紧要,为4插入数据准备头节点
while (p!=NULL){//直到遍历到尾节点才结束
//Link *temp=p;//将指针temp指向p,为了free内存
printf("%d->",p->data);//输出链表内容
p=p->Next;//节点往下遍历
//free(temp);//现场删除上一块内存
}
//4.插入数据在中间(在3和4中间插入3)
Link *insert;//定义要插入的数据位Link类
insert=(Link*)malloc(sizeof(Link));//给待插入的数据分配内存
insert->data=3;//把3赋值给待插入数据的data
p=head;//动态指针指向头节点
while (p->data<3 && p!=NULL){//寻找合适插入位置
p=p->Next;//动态指针往后遍历,在指到3时停下
}
insert->Next=p->Next;//待插入数据next指向3后面的4
p->Next=insert;//原链表中3指向待插入的3
printf("\n");
p=head;//动态指针指向头节点
while (p!=NULL){//直到遍历到尾节点才结束
Link *temp=p;//将指针temp指向p,为了free内存
printf("%d->",p->data);//输出链表内容
p=p->Next;//节点往下遍历
free(temp);//现场删除上一块内存
}
//5.插入数据在最后(大同小异,懒得写)
return 0;
}
就算之后忘记了看这个应该也能想起来。
9.又是一道链表题目 1031
又是一道链表题目
站在10月的尾巴上,我花了两天时间做出这道链表题目。
并不是因为这道题目有多难,而是因为:我尽力的在寻找他的逻辑错误,结果发现是我大括号的位置在我复制粘贴代码的时候被改变了。
这导致我删除A链表中元素的程序只删除了第一个元素就直接结束了。
这道题目还是比较经典的,所以发上来作纪念。
/*Q3794.(语言: C)3. 编程实现链表基本操作,并自定义函数实现以下功能
(1)建立链表A和B,输入用空格隔开,-1结束。
(2)求2个链表的差A-B。具体做法是,对于链表A中的每个元素e,在链表B中进行查找,
若存在与e相同的元素,则从A中将其删除。
(3)输出链表中的元素
(4)释放链表所占内存空间。
结构体定义为:
typedef struct LST
{
int num;
struct LST *next;
} LST;
输入提示:"请输入链表数据(整型)并以空格空开,以-1为最后一个数,回车结尾:\n"
输入格式:"%d"
输入提示:"请输入链表数据(整型)并以空格空开,以-1为最后一个数,回车结尾:\n"
输入格式:"%d"
输出提示:"输出A链表中删除后的结点信息:\n"
输出格式:"%4d"*/
#include <stdio.h>
#include <stdlib.h>
typedef struct LST
{
int num;
struct LST *next;
} LST;
int main(){
LST *headA=NULL,*headB=NULL,*pA=NULL,*pB=NULL,*temp=NULL,*prevA=NULL,*p2A=NULL;
int count=0;
//create link A
printf("请输入链表数据(整型)并以空格空开,以-1为最后一个数,回车结尾:\n");
while (1) {
if (count==0){
headA=(LST*)malloc(sizeof(LST));
scanf("%d",&headA->num);
count=1;
headA->next=NULL;
pA=headA;
continue;
}
temp=(LST*)malloc(sizeof(LST));
scanf("%d",&temp->num);
if (temp->num==-1){
pA->next=NULL;
break;
}else {
pA->next=temp;
pA=temp;
}
}
//create link B
printf("请输入链表数据(整型)并以空格空开,以-1为最后一个数,回车结尾:\n");
count=0;
while (1) {
if (count==0){
headB=(LST*)malloc(sizeof(LST));
scanf("%d",&headB->num);
count=1;
headB->next=NULL;
pB=headB;
continue;
}
temp=(LST*)malloc(sizeof(LST));
scanf("%d",&temp->num);
if (temp->num==-1){
pB->next=NULL;
break;
}else {
pB->next=temp;
pB=temp;
}
}
//delete
printf("输出A链表中删除后的结点信息:\n");
pA=headA;
prevA=headA;
pB=headB;
int found=0;
while (pA!=NULL){
found=-1;
pB=headB;
while (pB!=NULL){
if (pA->num==pB->num){//if found
found=pB->num;
break;//run away
}
pB=pB->next;
}
if (found!=-1){
p2A=headA;
while (p2A->num!=found && p2A!=NULL){//finding...
prevA=p2A;
p2A=p2A->next;
}
if (p2A==headA){//head
headA=p2A->next;
}else if (p2A->next==NULL){//tail
prevA->next=NULL;
}else {//middle
prevA->next=p2A->next;
}
}
pA=pA->next;
}
pA=headA;
while (pA!=NULL){
printf("%4d",pA->num);
pA=pA->next;
}
return 0;
}
10.Transfer points in functions 1105
To prove the Goldbach Conjecture within 1000.
Code:
#include <stdio.h>
#include <stdlib.h>
int is_prime(int num){
int flag=0;
for (int i=2;i<num;i++){
if (num%i==0){
flag=1;
return 0;
}
}
if (flag==0){
return 1;
}
}
int* create_prime(int n){
int *a=(int*)malloc(sizeof(int)*1000);
a[0]=2;
int ap=1;
for (int i=3;i<1000;i+=2){
if (is_prime(i)==1){
a[ap]=i;
ap+=1;
}
}
return a;
}
int* is_goldbach(int num){
int *a=create_prime(1000);
int *b=(int*)malloc(sizeof(int)*3);
for (int i=0;i<1000;i++){
if (a[i]==NULL){
break;
}
for (int j=0;j<1000;j++){
if (a[i]+a[j]==num){
b[0]=1;
b[1]=a[i];
b[2]=a[j];
return b;
}
}
}
b[0]=0;
return b;
}
int main(){
printf("Now let's start");
for (int i=4;i<=1000;i+=2){
int *a=is_goldbach(i);
if (a[0]==0){
printf("Not true!");
return 0;
}
printf("%d=%d+%d\n",i,a[1],a[2]);
}
printf("True!");
}
持续更新中