【问题标题】:long long int don't take long int welllong long int 不需要 long int 好
【发布时间】:2020-02-24 15:53:53
【问题描述】:

当我的一个朋友让我调试控制台游戏项目的问题时,我发现了一个奇怪的 C 语言中的 fprintf 转换,我无法弄清楚。该项目需要跟踪所有信息,包括分数和使用 C 更新到 .txt 文件的时间。

-初始化

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
.
.
.
// in main
    time_t timer;
    time (&timer);
    int num=0,c;
    scanf(" %s %ld", inputname,c);
    fscanf(fp, "%d", &num);
    num++;
    char **name;
    long *times;
    ull *score;

    name = new char *[num];
    for(int i=0; i<num; i++){
        name[i] = new char[MAX];
    }
    times = new long [num];
    score = new ull[num];

    //new data
    name[num-1] = inputname;
    score[num-1] = c;
    times[num-1] = timer;

-I/O 部分

.
.
.
// input from file after dynamic variables have fix size
    for(int i=0; i<num-1; i++){
        fscanf(fp, " %s%d", name[i], &score[i]);
        fscanf(fp, "%ld", &times[i]);
    }

    fseek(fp , 0 , SEEK_SET);
    fprintf(fp, "%d\n", num);

//print back to fp
    for(int i=0; i<num; i++){

        fprintf(fp, "%s %ld ",name[i], score[i]);
        fprintf(fp, "%ld\n",times[i]);  
    }

问题。如果我改变了

    fprintf(fp, "%s %ld ",name[i], score[i]);
    fprintf(fp, "%ld\n",times[i]);  

单行

fprintf(fp, "%s %ld %ld\n",name[i], score[i],times[i]);

输出文件将有

NAME1 SCORE1 TIMEINLONG1
NAME2 SCORE2 0
NAME3 SCORE3 0
.
.
.

而不是

NAME1 SCORE1 TIMEINLONG1
NAME2 SCORE2 TIMEINLONG2
.
.
.

经过一番尝试,我发现通过颠倒分数和时间的顺序

fprintf(fp, "%s %ld %ld\n",name[i], times[i],score[i]);

我有正确的输出

NAME1 TIMEINLONG1 SCORE1
.
.
.

再次。

那么,C 究竟如何处理输出流。我以为编译器会收集"..." 中的参数来组装一个字符串,然后将其刷新到输出流。显然它忽略了第一个(时间[0])之后的时间[i]。

是我初始化时间[]的方式的问题吗? 还是 fprintf() 的问题?

为这么长的页面道歉,但我现在对fprint 感到很困惑。


已解决

将标题从“fprintf with dynamic memory”编辑为“long long int don't take long int well, 反之亦然。”

问题不在于动态内存的东西(对不起,我不知道),而是我用来获取数据并将数据放入 printf、fprintf、scanf、fscanf 的类型。根据@Adrian Mole 的说法,mismatch 将 long int 分配给 long long int 类型变量是未定义的行为,以下 I/O 操作将无法正确完成。好像long long int比long long有更多空间,不兼容。

没有

long long int c;
scanf("%d",&c);
printf("%d",c);
//or
scanf("%ld",&c);
printf("%ld",c);

是的

long long int c;
scanf("%lld",&c);
printf("%lld",c);

【问题讨论】:

  • 你在使用指针时不一致。 scanf 接受一个指针地址。
  • %ld 对于 unsigned long long 不正确。可能 fprintf 正在获得分数的前 32 位并将其视为时间。我假设 ull 是 unsigned long long。
  • 您在阅读c 时已经有未定义的行为,因为%ld 不适合阅读`int。
  • 您应该将此问题标记为 C++,因为它显然不是 C。
  • @thebusybee 抱歉,我以为我在 C 上,正在编辑它

标签: c++ printf


【解决方案1】:

这里有一个“未定义行为”的经典示例! score[i] 的变量是(假设 ull 类型的合理定义)是一个 unsigned long long int(可能/可能是 64 位),但 scanf 和“冒犯” printf 调用使用 %ld 格式说明符(可能/可能指的是 32 位变量)。

当调用 printf 时,格式说明符和给定参数之间存在“不匹配”,您就处于未定义行为领域!

通过为对应的score[i] 参数指定%llu 格式来解决此问题!例如,代替:

fprintf(fp, "%s %ld %ld\n",name[i], score[i],times[i]);

使用:

fprintf(fp, "%s %llu %ld\n", name[i], score[i], times[i]);
// score[i] is ull ^ | ^ OK - times[i] is (signed) long!

请随时要求进一步澄清和/或解释。

【讨论】:

  • @RobertSsupportsMonicaCellio 嗯 - 不确定我是否已准备好“几乎完美”,但请查看最新编辑。
  • 是的,它运作良好。我认为这是time_t和long int之间转换的问题。原来它是我用于 score 的 I/O 的类型
  • 但这是否意味着 C 确实有合适的转换来将 long int 分配给 long long int?
【解决方案2】:

问题从以下几行开始:

int num=0,c;
scanf(" %s %ld", inputname, c);

首先,您应该写&amp;c 而不是c 作为最后一个参数。既然scanf写入它的参数,它应该接收参数的地址(字符串除外,我懒得解释为什么)。

第二,由于cint,你需要%d,而不是%ld。请注意尺寸。

至于您的 fprintf - 再次注意尺寸。由于scoreull*,并且我假设ull 表示unsigned long long,因此您需要使用%llu 格式打印它,而不是%ld。 (注意 - Andrian Mole 也指出了这篇文章,并在我发布我的答案之前发布了他的答案。)

【讨论】:

    【解决方案3】:

    另一个错误是:

    name[num-1] = inputname;
    

    这会将指针重新分配给堆分配的内存并导致内存泄漏。 inputname数组应该复制到name[n]数组中,例如,

    snprintf(name[num-1], MAX, "%s", inputname);
    

    scanf %s 可能会溢出其目标数组参数,除非您对目标大小进行硬编码,例如:

    #define MAX 256
    int num=0,c;
    char inputname[MAX];
    scanf("%256s %d", inputname, c);
    

    【讨论】:

    • 谢谢回答。你的意思是如果我们通过复制一定长度而不是直接赋值来获取字符串会更好?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多