在动态分配的内存中将字符串后的int存储错误

时间:2018-02-08 06:15:22

标签: c++ malloc

我正在尝试使用malloc创建24字节内存空间的程序。我们的想法是在12 byte频谱中的前10个字节中存储名称(字符串),在后2个字节中存储年龄(int)。我希望对两组数据执行此操作,因此我分配 2 x 12 = 24字节。我想将空间用作异构2D数组

下图说明了我想如何组织内存空间。

enter image description here

这是我的代码:

void* ptr = malloc(24);
string* st;
string t_string;
int i=0, c=1, t_int;
int* it;

while(i < 24){
    cout << "Enter name and age (" << (c++) << "): ";
    cin >> t_string >> t_int;
    st = (string*)(ptr) + i;
    *st = t_string;
    it = (int*)(ptr) + i + 10;
    *it = t_int;

    i+=12;
}

cout << endl;

i=0;
while(i < 24){
    st =  (string*)(ptr) + i;
    it = (int*)(ptr) + i + 10;
    cout << *st << " is " << *it << " years old.\n";

    i+= 12;
}

问题是当我输入两个名字和年龄时,第一个年龄总是显示错误。这是一个输出:

Enter name and age (1): Progyammer 19
Enter name and age (2): Coderguy00 19

Progyammer is 1629513069 years old.
Coderguy00 is 19 years old.

每一次,它都只能与第一个人一起发生。


为什么会发生这种情况,我该如何解决?

2 个答案:

答案 0 :(得分:2)

你这是错误的方式。你最好创建一个包含九个字符(加上空字符)char指针的结构数组,并使用一个short(不是int,即4个字节)来表示该年龄。

struct Person {
    const char name[10];
    short age;
};

然后您可以像这样创建两个Person数组:

struct Person arr[2] /* the 2 is optional */ = { 
    {"Haskell", 12}, {"Ada", 21} 
};

答案 1 :(得分:1)

虽然您应该在C中使用struct或在C ++中使用class,但如果您在某些微控制器上限制了堆栈空间,那么额外的4字节填充12字节块有所不同,没有理由你不能完全按照自己的意愿去做 - 但我建议使用C而不是C ++来实现这种类型的hackery。此外,如果您只是为学习项目执行此操作,处理通用数据块,了解严格别名规则 C11 Standard - §6.5 Expressions (p6),并了解块内的寻址 - 那也没关系。

首先,您需要创建24字节的分配内存块,类型为char(请参阅上面的§6.5表达式)。为内存块分配/验证存储后,您是否需要创建逻辑来存储和访问块中您希望的信息。 (你没有一个12字节块的数组,你只有一个24字节的块,所以数组索引对你没有好处,它是指针算法或什么都没有。

所需的指针运算确实没有什么困难。您知道您需要将每个12字节块视为存储区域。因此,循环遍历内存块,您可以使用(pointer + i * BLKSZ12)来定位每个12字节块的开头。从那里,您知道您的short将从头开始存储另一个STRSZ(即10)个字节。因此,当您循环播放时,您可以使用short存储或访问每个对应的pointer + i * BLKSZ12 + STRSZ。然后,只需从存储偏移量中存储或检索age即可。

完全放弃,您可以执行以下操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STRSZ 10
#define BLKSZ 12
#define BUFSZ 512

int main (void) {

    char *block,            /* pointer to generic block of memory */
        buf[BUFSZ] = "";    /* buffer for fgets use */
    size_t i = 0,           /* general loop var / counter */
        nblks = 2;          /* number of BLKSZ blocks to allocate */

    /* allocate block of nblks * BLKSZ bytes / validate */
    if (!(block = malloc (nblks * BLKSZ))) {
        perror ("malloc-block");
        exit (EXIT_FAILURE);
    }

    while (i < nblks) {     /* for each of the blocks */
        size_t len;         /* var for holding string length */
        printf ("\nenter name (9 char max): "); /* prompt/validate */
        if (!fgets (buf, BUFSZ, stdin)) {   /* read/validate input */
            fprintf (stderr, "warning: user canceled input.\n");
            exit (EXIT_FAILURE);
        }
        len = strlen (buf);                 /* get length */
        if (len && buf[len - 1] == '\n')    /* check trailing '\n' */
            buf[--len] = 0;                 /* overwrite w/nul-character */
        else if (len == BUFSZ - 1) {        /* check all input fit in buf */
            fprintf (stderr, "error input too long or buf.\n");
            continue;           /* go get next name */
        }
        if (len > STRSZ - 1) {  /* validate name fits */
            fprintf (stderr, "error: name exceeds %d chars.\n", STRSZ - 1);
            continue;
        }
        strcpy (block + i * BLKSZ, buf);
        printf ("enter age              : "); /* prompt/validate */
        if (!fgets (buf, BUFSZ, stdin)) {   /* read/validate input */
            fprintf (stderr, "warning: user canceled input.\n");
            exit (EXIT_FAILURE);
        }   /* convert age to short from buf */
        if (sscanf (buf, "%hd", (short*)(block + i * BLKSZ + STRSZ)) != 1) {
            fprintf (stderr, "error: conversion failed, block %zu.\n", i);
            continue;
        }
        i++;    /* we got a valid block, increment block count */
    }

    printf ("\nNames       Age\n");
    for (i = 0; i < nblks; i++) /* output info stored in each block */
        printf ("%-10s  %2hd yrs.\n", block + i * BLKSZ, 
                *(short*)(block + i * BLKSZ + STRSZ));

    free (block);   /* free block */

    return 0;
}

示例使用/输出

$ ./bin/alloc_blk24

enter name (9 char max): Mary
enter age              : 25

enter name (9 char max): Joeseph
enter age              : 90

Names       Age
Mary        25 yrs.
Joeseph     90 yrs.

内存使用/错误检查

每次在C中动态分配内存时,由您来验证是否已正确使用该内存,没有内存错误以及已分配的所有内存已被释放。在Linux上,valgrind工具是首选工具。所有操作系统都有类似的工具。它们易于使用,您只需通过它们运行程序即可。

例如,使用valgrind,您可以确认您只分配了一个24字节的内存块,没有内存错误,并且在程序执行之前已分配的所有内存都已正确释放,例如

valgrind ./bin/alloc_blk24
==26366== Memcheck, a memory error detector
==26366== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26366== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==26366== Command: ./bin/alloc_blk24
==26366==

enter name (9 char max): Mary
enter age              : 25

enter name (9 char max): Joeseph
enter age              : 89

Names       Age
Mary        25 yrs.
Joeseph     89 yrs.
==26366==
==26366== HEAP SUMMARY:
==26366==     in use at exit: 0 bytes in 0 blocks
==26366==   total heap usage: 1 allocs, 1 frees, 24 bytes allocated
==26366==
==26366== All heap blocks were freed -- no leaks are possible
==26366==
==26366== For counts of detected and suppressed errors, rerun with: -v
==26366== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

正如所有人都建议的那样,包括在内,这不是你应该使用一块内存来保存两种不相关的数据类型(缺少在内存关键硬件上,或者用于学习练习)的方法。优点是C提供了执行此操作所需的所有工具,仅仅是出于讨论的原因。 C可以让你做任何可以在计算机上以闪电般速度完成的事情 - 无论你是否应该 - 只是问题的另一个方面。

如果您还有其他问题,请与我们联系。