尝试打印字符串时出现段错误

时间:2016-08-21 08:44:32

标签: c string

当我尝试打印可变长度字符串时,我遇到了分段错误。当我使用printf或日志文件打印字符串时,字符串被打印得很好。另一个问题是,我只在一些机器上看到崩溃,因为相同的测试在另一台机器上工作正常。我真的很困惑为什么。

编辑:一个完整​​的工作代码导致我在下面粘贴的崩溃。 崩溃是Centos 6.3和Centos 6.5。在Centos 5中没有出现崩溃

#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <sys/time.h>
#include <time.h>

typedef struct {
    uint64_t  total_bytes_sent;
    uint64_t  total_bytes_received;
    uint64_t  total_blocks_sent;
    uint64_t  total_blocks_received;
    uint64_t  total_commands_sent;
    uint64_t  total_commands_received;
    uint64_t  time_to_process_data;
    char      mark_sent_time[64];
    char      mark_received_time[64];
} csperf_stats_t;

void
csperf_stats_printf(const char *format, ...)
{
    /* Write to file */
    va_list args;

    /* Write to stdout */
    va_start(args, format);
    vfprintf(stdout, format, args);
    va_end(args);
}

void
ansperf_stats_display(csperf_stats_t *stats)
{
    if (!stats) {
        return;
    }

    stats->total_blocks_sent = 1000;
    stats->total_blocks_received = 2000;
    stats->time_to_process_data = 22;

    csperf_stats_printf("%3d   %15s    %10s    %10zu    %10zu    %10zu       %10s    "
            "%10s\n\n",
                0, "hi", "testing.",
            stats->total_blocks_sent, stats->total_blocks_received,
            stats->time_to_process_data,
            "crash", "test");
}

/* Get time in millisecond */
uint64_t
csperf_network_get_time(char *buf)
{
    char            fmt[64];
    struct tm       *tm;
    struct timeval tv;
    uint64_t s;

    gettimeofday(&tv, NULL);

    if (buf) {
        if((tm = localtime(&tv.tv_sec)) != NULL) {
            strftime(fmt, sizeof(fmt), "%Y-%m-%d %H:%M:%S.%03u", tm);
            snprintf(buf, sizeof(fmt), fmt, tv.tv_usec);
        }
    }
    s = tv.tv_sec * 1000LL;
    return(s + tv.tv_usec / 1000LL);
}

int main()
{
    csperf_stats_t stats = { 0 };
    csperf_network_get_time(stats.mark_sent_time);
    csperf_network_get_time(stats.mark_received_time);
    printf("%s%s\n", stats.mark_sent_time, stats.mark_received_time);
    ansperf_stats_display(&stats);
}

以下是崩溃的代码段。

当它试图打印stats-&gt; mark_sent_time,stats-&gt; mark_received_time时,它在vfprintf()中崩溃。当我在gdb中打印字符串时,它并没有抱怨。

void
csperf_stats_printf(FILE *fd, const char *format, ...)
{
    /* Write to file */
    va_list args;

    va_start(args, format);
    if (fd) {
        vfprintf(fd, format, args);
    }
    va_end(args);

    /* Write to stdout */
    va_start(args, format);
    vfprintf(stdout, format, args);
    va_end(args);
}

void
ansperf_stats_display(csperf_stats_t *stats, FILE *fd)
{
    static int header_displayed = 0;
    static int cycle = 0;
    char total_bytes_sent_str[50];
    char total_bytes_recv_str[50];

    if (!stats) {
        return;
    }

    if (!header_displayed) {
        csperf_stats_printf(fd, "%s%s", header, seperator_line);
        header_displayed = 1;
    }

    csperf_common_calculate_size(total_bytes_sent_str,
            stats->total_bytes_sent);
    csperf_common_calculate_size(total_bytes_recv_str,
            stats->total_bytes_received);

    csperf_stats_printf(fd, "%3d   %15s    %10s    %10zu    %10zu    %10zu       %10s    "
            "%10s\n\n", ++cycle,
            total_bytes_sent_str, total_bytes_recv_str,
            stats->total_blocks_sent, stats->total_blocks_received,
            stats->time_to_process_data,
            stats->mark_sent_time, stats->mark_received_time);
}

这就是gdb显示的内容

(gdb) p stats->mark_sent_time
No symbol "stats" in current context.
(gdb) f 2
#2  0x08051f56 in ansperf_stats_display (stats=0x892ded4, fd=0x892e888) at /home/nikhil/csperf/src/csperf_stats.c:55
55          csperf_stats_printf(fd, "%3d   %15s    %10s    %10zu    %10zu    %10zu       %100s    "
(gdb) p stats->mark_sent_time
$1 = "20160821 21325800007", '\000' <repeats 79 times>
(gdb) p stats->mark_recei9ved_time
There is no member named mark_recei9ved_time.
(gdb) p stats->mark_received_time
$2 = "20160821 21325800007", '\000' <repeats 79 times>

字符串是一个100字节的数组,如下所示

char      mark_sent_time[100];
char      mark_received_time[100];

设置如下:

command->echo_timestamp = csperf_network_get_time(
        client->stats.mark_sent_time);

此函数将时间戳复制到字符串&#34; mark_sent_time&#34;

uint64_t
csperf_network_get_time(char *buf)
{
    char            fmt[64];
    struct tm       *tm;
    struct timeval tv;
    uint64_t s;

    gettimeofday(&tv, NULL);

    if (buf) {
        if((tm = localtime(&tv.tv_sec)) != NULL) {
            strftime(fmt, sizeof(fmt), "%Y-%m-%d %H:%M:%S.%03u", tm);
            snprintf(buf, sizeof(fmt), fmt, tv.tv_usec);
        }
    }
    s = tv.tv_sec * 1000LL;
    return(s + tv.tv_usec / 1000LL);
}

回溯:

(gdb) bt
#0  0x002b535e in _IO_vfprintf_internal (s=Cannot access memory at address 0xffffffff
) at vfprintf.c:1603
#1  0x08051de7 in csperf_stats_printf (fd=0x892e888, format=0x8079a6c "%3d   %15s    %10s    %10zu    %10zu    %10zu       %100s    %100s \n\n") at /home/nikhil/csperf/src/csperf_stats.c:23
#2  0x08051f56 in ansperf_stats_display (stats=0x892ded4, fd=0x892e888) at /home/nikhil/csperf/src/csperf_stats.c:55

#3  0x08050ad3 in csperf_client_shutdown (client=0x892deb0) at /home/nikhil/csperf/src/csperf_client.c:67

我错过了什么?

1 个答案:

答案 0 :(得分:2)

问题是您使用%zu在格式字符串中显示uint64_t变量。

在64位目标上,%zu适用于size_t,并且(至少在我的64位系统上)这是64位。然而,在32位系统上(至少对我而言)size_t是32位,但uint64_t变量仍将64位置于va_list结构中。 %zu仅消耗32位,这会在va_list中留下32位值,而不是下一个参数。

在打印uint64_t变量时,将"%zu"的使用替换为"%" PRIu64(在您的示例中至少有3个位置)。

您可能需要添加<inttypes.h>标头才能访问PRIu64

....正如我在评论中提到的,我不认为这个:

strftime(fmt, sizeof(fmt), "%Y-%m-%d %H:%M:%S.%03u", tm);
snprintf(buf, sizeof(fmt), fmt, tv.tv_usec);

正在做你期望的事。我认为您可能希望%03u行中的strftime延伸到snprintf行以使用tv.tv_usec,但是%u用于strftime我是星期几作为数字,您可能应该在%%03u来电中使用strftime,然后fmt snprintf只包含%03u