Closed. This question is off-topic. It is not currently accepting answers.
Learn more。
想改进这个问题吗
Update the question所以堆栈溢出的值小于aa>。
四年前关闭。
我想用C语言中的mmap读写结构。
我有一个名为insert_med的函数,它允许在mmap中插入一个新的struct med,并且每个struct(带有一个唯一的键)必须写在数组的不同位置(添加新结构时,必须将其添加到数组的最后一个空位置)。
两个structs med不能有下面代码中所示的相同密钥钥匙是独一无二的。
我的代码不工作-变量“struct map”的错误消息:何时声明,何时准备好映射文件-但我不知道为什么。我想我可能做错事了。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define FILEPATH "/tmp/mmapped.bin"
#define NUMINTS (1000)
#define FILESIZE (NUMINTS * sizeof(int))
struct med{
int key;
char name[25];
int quant_min;
int quant;
};
insert_med(int argc, char *argv[]){
int i;
int fd;
int result;
struct map*; /* mmapped array of structs */
/* Open a file for writing.
* - Creating the file if it doesn't exist.
* - Truncating it to 0 size if it already exists. (not really needed)
*
* Note: "O_WRONLY" mode is not sufficient when mmaping.
*/
fd = open(FILEPATH, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
if (fd == -1) {
perror("Error opening file for writing");
exit(EXIT_FAILURE);
}
/* Stretch the file size to the size of the (mmapped) array of structs
*/
result = lseek(fd, FILESIZE-1, SEEK_SET);
if (result == -1) {
close(fd);
perror("Error calling lseek() to 'stretch' the file");
exit(EXIT_FAILURE);
}
/* Something needs to be written at the end of the file to
* have the file actually have the new size.
* Just writing an empty string at the current file position will do.
*
* Note:
* - The current position in the file is at the end of the stretched
* file due to the call to lseek().
* - An empty string is actually a single '\0' character, so a zero-byte
* will be written at the last byte of the file.
*/
result = write(fd,"",1);
if (result != 1) {
close(fd);
perror("Error writing last byte of the file");
exit(EXIT_FAILURE);
}
/* Now the file is ready to be mmapped.
*/
map = (struct*)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
struct med m;
struct med s;
int a=0;
printf("Key of med: ");
scanf("%d",&m.key);
//strcmp return "0" when the two strings are equal
for (i = 0; i <=NUMINTS; ++i) {
int a=0;
map[i]=&s;
read(fd,&s,1);
if ((strcmp(&m.key,&s.key))==0){
a++;
printf("Med %d already exits. \n",s.key);
break;
}
}
if (a==0){
printf("Name of med: ");
scanf("%s",&m.name);
printf("Quant. min. of med: ");
scanf("%d",&m.quant_min);
printf("Quant. of med: ");
scanf("%d",&m.quant);
map[i]=&m;
write(fd,&m,1);
printf("Med %d saved. \n",m.key);
}
/* Don't forget to free the mmapped memory
*/
if (munmap(map, FILESIZE) == -1) {
perror("Error un-mmapping the file");
/* Decide here whether to close(fd) and exit() or not. Depends... */
}
/* Un-mmaping doesn't close the file, so we still need to do that.
*/
close(fd);
}
在你的代码中,你有:
int result;
struct map*; /* mmapped array of structs */
您以前定义过
struct med
(而不是
struct map
),您试图定义一个类型为
struct map *
的变量,但没有给它命名。编译器抱怨是对的你可能需要这样的东西:
int result;
struct med *map = 0; /* mmapped array of struct med */
Julia
commented:
我现在有以下错误:
invalid use of undefined type "struct med"
deferencing pointer to incomplete type
在
map[i]=&s
和
map[i]=&m
中。
好吧,我没有看得比第一个错误更远,但是哪里有一个错误,通常有很多。
例如:
map = (struct*)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
需要将cast更改为
(struct med *)
。
map[i]=&s;
是错误的从语义上讲,
map[i] = s;
是正确的(将数据从
s
复制到映射数组中),但是,经过再三考虑,赋值是完全错误的别这样,删掉那一行。
您的
FILESIZE
应该是
sizeof(struct map)
的倍数,而不是
sizeof(int)
的倍数。
read()
上的
fd
充其量是可疑的-删除它;
mmap()
的整个要点是避免直接的文件I/O。
键值是整数;您不需要使用
strcmp()
来比较它们,只需使用:
if (m.key == map[i].key)
赋值
map[i]=&m;
又错了,应该删除,
write()
也错了。您有一个数组,
map
,您可以像访问任何其他数组一样访问它,系统在后台处理I/O请注意,您应该始终检查
read()
和
write()
操作是否有效,但在删除这些操作时,这将变得毫无意义。
您需要检查如何将值分配给
map
数组,并确保您不会试图从
map
数组中的未初始化值中读取值。
可能还有其他问题;我没有编译代码,更不用说运行了。但这些评论应该会对你有所帮助。
请注意,来自编译器的错误消息也应该有帮助;您需要学习如何解释它们确保您有足够的警告编译,并且您正在修复所有警告。如果您使用GCC,那么
gcc -Wall -Wextra -Werror -std=c11
是一个相当好的开始(我通常也会添加一些关于原型和函数定义的警告,其中一些(但不是全部)包含在上面)记住,C编译器对C的了解比你现在的职业生涯要多得多;仔细听它的警告,因为你应该假设它是正确的,而且你犯了一个错误这就是我所做的,我已经用C编写代码30多年了。
工作代码
mm7.c
创建的程序是
mm7
(内存映射7;除了它是素数、小且使名称唯一之外,对7没有任何意义)。
相当数量的代码留在问题中,而不是从头重写请注意,代码很小心地验证输入—例如,它检测并释放EOF,而不是轻松地继续,不做任何有用的事情。它包括一个输出循环来打印存储的数据它使用函数来管理部分输入。最好把所有的输入都用函数来处理。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define FILEPATH "/tmp/mmapped.bin"
#define NUMINTS (1000)
#define FILESIZE (NUMINTS * sizeof(struct med))
struct med
{
int key;
char name[25];
int quant_min;
int quant;
};
static void print_med(const char *tag, const struct med *med)
{
printf("%s: %4d: Q(%2d, min %2d): %s\n",
tag, med->key, med->quant, med->quant_min, med->name);
}
static int med_in_map(const struct med *map, int num_meds, int key)
{
for (int i = 0; i < num_meds; ++i)
{
if (key == map[i].key)
{
printf("Med %d already exists.\n", key);
return 1;
}
}
return 0;
}
static int get_new_key(const struct med *map, int num_meds, int *key)
{
while (printf("Key of med: ") > 0 && scanf("%d", key) == 1)
{
if (med_in_map(map, num_meds, *key) == 0)
return 0;
}
return EOF;
}
int main(void)
{
int fd;
int result;
struct med *map; /* mmapped array of structs */
/* Open a file for writing.
* - Creating the file if it doesn't exist.
* - Truncating it to 0 size if it already exists. (not really needed)
*
* Note: "O_WRONLY" mode is not sufficient when mmapping.
*/
fd = open(FILEPATH, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
if (fd == -1)
{
perror("Error opening file for writing");
exit(EXIT_FAILURE);
}
/* NB: ftruncate(fd, FILESIZE); is simpler */
/* Stretch the file size to the size of the (mmapped) array of structs */
result = lseek(fd, FILESIZE - 1, SEEK_SET);
if (result == -1)
{
close(fd);
perror("Error calling lseek() to 'stretch' the file");
exit(EXIT_FAILURE);
}
/* Something needs to be written at the end of the file to
* have the file actually have the new size.
* Just writing an empty string at the current file position will do.
*
* Note:
* - The current position in the file is at the end of the stretched
* file due to the call to lseek().
* - An empty string is actually a single '\0' character, so a zero-byte
* will be written at the last byte of the file.
*/
result = write(fd, "", 1);
if (result != 1)
{
close(fd);
perror("Error writing last byte of the file");
exit(EXIT_FAILURE);
}
/* Now the file is ready to be mmapped. */
map = (struct med *)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED)
{
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
/* Input loop */
int num_meds;
for (num_meds = 0; num_meds < NUMINTS; num_meds++)
{
struct med m;
memset(&m, '\0', sizeof(m));
if (get_new_key(map, num_meds, &m.key) == EOF)
break;
printf("Name of med: ");
if (scanf("%s", m.name) != 1)
break;
printf("Quant. min. of med: ");
if (scanf("%d", &m.quant_min) != 1)
break;
printf("Quant. of med: ");
if (scanf("%d", &m.quant) != 1)
break;
map[num_meds] = m;
printf("Med %d saved.\n", m.key);
}
/* Output loop */
printf("\nRecorded meds:\n");
for (int i = 0; i < num_meds; i++)
{
char buffer[32];
snprintf(buffer, sizeof(buffer), "M%.4d", i);
print_med(buffer, &map[i]);
}
/* Don't forget to free the mmapped memory */
if (munmap(map, FILESIZE) == -1)
{
perror("Error un-mmapping the file");
/* Decide here whether to close(fd) and exit() or not. Depends... */
}
/* Un-mmapping doesn't close the file, so we still need to do that. */
close(fd);
/* Remove file? */
/* unlink(FILEPATH); */
}
示例数据输入文件
mm7.data
这将模拟尝试重复数字的终端输入。
1 Hydrocontin 3 5
1
2 Paxodontin 1 1
2
37 Ibuprofen 2 12
129 Butoxydione 12 29
4 Placebo 2 22
37
129
4
2
1
9231 Aspirin 99 99
使用数据文件运行的示例:
$ ./mm7 < mm7.data
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 1 saved.
Key of med: Med 1 already exists.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 2 saved.
Key of med: Med 2 already exists.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 37 saved.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 129 saved.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 4 saved.
Key of med: Med 37 already exists.
Key of med: Med 129 already exists.
Key of med: Med 4 already exists.
Key of med: Med 2 already exists.
Key of med: Med 1 already exists.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 9231 saved.
Key of med:
Recorded meds:
M0000: 1: Q( 5, min 3): Hydrocontin
M0001: 2: Q( 1, min 1): Paxodontin
M0002: 37: Q(12, min 2): Ibuprofen
M0003: 129: Q(29, min 12): Butoxydione
M0004: 4: Q(22, min 2): Placebo
M0005: 9231: Q(99, min 99): Aspirin
$
每个实际药物的提示都在一行,因为输入是从文件读取的,而不是从键盘读取的。
数据文件的十六进制转储
/tmp/mmapped.bin
$ odx /tmp/mmapped.bin
0x0000: 01 00 00 00 48 79 64 72 6F 63 6F 6E 74 69 6E 00 ....Hydrocontin.
0x0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0020: 03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F ............Paxo
0x0030: 64 6F 6E 74 69 6E 00 00 00 00 00 00 00 00 00 00 dontin..........
0x0040: 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 ................
0x0050: 25 00 00 00 49 62 75 70 72 6F 66 65 6E 00 00 00 %...Ibuprofen...
0x0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0070: 02 00 00 00 0C 00 00 00 81 00 00 00 42 75 74 6F ............Buto
0x0080: 78 79 64 69 6F 6E 65 00 00 00 00 00 00 00 00 00 xydione.........
0x0090: 00 00 00 00 00 00 00 00 0C 00 00 00 1D 00 00 00 ................
0x00A0: 04 00 00 00 50 6C 61 63 65 62 6F 00 00 00 00 00 ....Placebo.....
0x00B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00C0: 02 00 00 00 16 00 00 00 0F 24 00 00 41 73 70 69 .........$..Aspi
0x00D0: 72 69 6E 00 00 00 00 00 00 00 00 00 00 00 00 00 rin.............
0x00E0: 00 00 00 00 00 00 00 00 63 00 00 00 63 00 00 00 ........c...c...
0x00F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* (2484)
0x9C40:
$
添加
memset()
是因为十六进制转储在数据结构中显示了垃圾-无害垃圾,但垃圾仍然:
0x0000: 01 00 00 00 48 79 64 72 6F 63 6F 6E 74 69 6E 00 ....Hydrocontin.
0x0010: 2F 62 69 6E 3A 2F 75 73 72 2F 67 6E 75 2F 62 69 /bin:/usr/gnu/bi
0x0020: 03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F ............Paxo
0x0030: 64 6F 6E 74 69 6E 00 00 2F 62 69 6E 3A 2F 75 73 dontin../bin:/us
0x0040: 72 2F 67 6E 75 2F 62 69 01 00 00 00 01 00 00 00 r/gnu/bi........
…
初始化结构时使用:
struct med m = { .key = 0, .name = "", .quant = 0, .quant_min = 0 };
没有完全消除垃圾,因为名称后面的结构中有填充字节(这让我吃惊!):
0x0000: 01 00 00 00 48 79 64 72 6F 63 6F 6E 74 69 6E 00 ....Hydrocontin.
0x0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 2F 62 69 ............./bi
0x0020: 03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F ............Paxo
0x0030: 64 6F 6E 74 69 6E 00 00 00 00 00 00 00 00 00 00 dontin..........
0x0040: 00 00 00 00 00 2F 62 69 01 00 00 00 01 00 00 00 ...../bi........
…
使用非空文件
创建文件时,代码不再包含
O_TRUNC
,但这使得硬编码文件名不太合适(但我还没有修复)。
find_num_entries()
函数只是对数据进行线性搜索,以查找上次列表的位置。更复杂的代码将对数据进行二进制搜索,以找到空条目的起始位置在查找键的过程中也有线性搜索最好保持文件的排序顺序,这样也可以使用二进制搜索。事实上,在生产版本中有一个头记录可能是明智的,这个头记录有一个幻数来标识文件类型(如果文件没有以正确的幻数开头,那么它显然不是这个程序编写的文件,因此它的内容不应该被解释),并记录其他细节,比如条目的数量,或者最小值和最大键值。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define FILEPATH "/tmp/mmapped.bin"
#define NUM_MEDS (1000)
#define FILESIZE (NUM_MEDS * sizeof(struct med))
struct med
{
int key;
char name[25];
int quant_min;
int quant;
};
static void print_med(const char *tag, const struct med *med)
{
printf("%s: %4d: Q(%2d, min %2d): %s\n",
tag, med->key, med->quant, med->quant_min, med->name);
}
static int med_in_map(const struct med *map, int num_meds, int key)
{
for (int i = 0; i < num_meds; ++i)
{
if (key == map[i].key)
{
printf("Med %d already exists.\n", key);
return 1;
}
}
return 0;
}
static int get_new_key(const struct med *map, int num_meds, int *key)
{
while (printf("Key of med: ") > 0 && scanf("%d", key) == 1)
{
if (med_in_map(map, num_meds, *key) == 0)
return 0;
}
return EOF;
}
static int find_num_entries(const struct med *map, int max_meds)
{
int i;
for (i = 0; i < max_meds; i++)
{
if (map[i].key == 0)
break;
}
return i;
}
int main(void)
{
int fd;
int result;
struct med *map;
fd = open(FILEPATH, O_RDWR | O_CREAT, (mode_t)0600);
if (fd == -1)
{
perror("Error opening file for writing");
exit(EXIT_FAILURE);
}
result = ftruncate(fd, FILESIZE);
if (result == -1)
{
close(fd);
perror("Error calling ftruncate() to 'set' the file size");
exit(EXIT_FAILURE);
}
map = (struct med *)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED)
{
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
/* Input loop */
int num_meds;
for (num_meds = find_num_entries(map, NUM_MEDS); num_meds < NUM_MEDS; num_meds++)
{
struct med m;
memset(&m, '\0', sizeof(m));
if (get_new_key(map, num_meds, &m.key) == EOF)
break;
printf("Name of med: ");
if (scanf("%s", m.name) != 1)
break;
printf("Quant. min. of med: ");
if (scanf("%d", &m.quant_min) != 1)
break;
printf("Quant. of med: ");
if (scanf("%d", &m.quant) != 1)
break;
map[num_meds] = m;
printf("Med %d saved.\n", m.key);
}
/* Output loop */
printf("\nRecorded meds:\n");
for (int i = 0; i < num_meds; i++)
{
char buffer[32];
snprintf(buffer, sizeof(buffer), "M%.4d", i);
print_med(buffer, &map[i]);
}
/* Don't forget to free the mmapped memory */
if (munmap(map, FILESIZE) == -1)
{
perror("Error un-mmapping the file");
/* Decide here whether to close(fd) and exit() or not. Depends... */
}
/* Un-mmapping doesn't close the file, so we still need to do that. */
close(fd);
return 0;
}
您还可以使用类似于
err_syserr()
的函数简化错误报告,您可以在我的答案中找到这些函数,例如
Creating a file with given size。
我是一名优秀的程序员,十分优秀!