我试着用C写一个基本的问答程序,它基本上会储存卡片和答案。但与此同时,我尝试使用我学到的新技术,如各种函数和动态内存分配。
当我改变常量时,我希望程序能够缩放,不应该像K&R所定义的那样有“神奇的数字”。问题是,我不能在fscanf的格式参数中使用变量。我需要手动定义字符串长度。为了克服这个限制,我尝试编写一个字符串级联函数,该函数将生成fscanf格式参数。
例如:
char * scanf_string = et_concat(5, "%", CARD_SIZE, "s | %", ANSWER_SIZE, "s");常数定义为星座。
#define CARD_SIZE 200
#define ANSWER_SIZE 1000
#define CONCAT_SIZE 20et_concat函数在etstring.h. h中。--这是分段错误发生的地方.
#include <stdarg.h>
#include <string.h>
char * et_concat (int count, char * str, ...)
{
va_list ap;
int j;
char *concatted_string = (char *) malloc (count*CONCAT_SIZE+1);
va_start(ap, str);
for (j = 0; j < count; j++) {
strcat(concatted_string, va_arg(ap, char *));
}
va_end(ap);
return concatted_string;
}我试图调用et_concat的代码在reader.c中。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "consts.h"
#include "etstring.h"
int iterate_inputs()
{
char card[CARD_SIZE];
char answer[ANSWER_SIZE];
FILE *fp = fopen("data.txt","r");
if (fp == NULL)
{
return EXIT_FAILURE;
}
char * scanf_string = et_concat(5, "%", CARD_SIZE, "s | %", ANSWER_SIZE, "s");
printf(scanf_string);
fscanf(fp, scanf_string, card, answer);
printf("%s | %s\n", card, answer);
fclose(fp);
return EXIT_SUCCESS;
}提前谢谢。
发布于 2014-10-06 10:13:30
您的级联例程有各种错误和缺点:
str中的第一个字符串,但您只打印了变量参数,其中只剩下4个。您实际上跳过了第一个字符串,而第五个字符串是垃圾。'\0'就足以初始化以零结尾的空字符串.还有一个问题是,你的论点并不都是字符串。一个简单的解决方案是使用strinfification macro。必须首先展开参数,以便字符串具有实际值,而不是宏的名称,这对scanf没有任何意义。此解决方案的问题是,在格式字符串中给定的大小不包括终止的'\0',因此您应该像这样分配缓冲区:
char card[CARD_SIZE + 1];下面是您的实现的一个修正版本,它仍然不检查缓冲区溢出情况,不过:
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#define CARD_SIZE 200
#define ANSWER_SIZE 1000
#define CONCAT_SIZE 20
char *et_concat(int count, ...)
{
va_list ap;
char *concatted_string = malloc(count * CONCAT_SIZE + 1);
int j;
*concatted_string = '\0';
va_start(ap, count);
for (j = 0; j < count; j++) {
strcat(concatted_string, va_arg(ap, char *));
}
va_end(ap);
return concatted_string;
}
#define STR1(X) #X
#define STR(X) STR1(X)
int main()
{
char *scanf_string = et_concat(5,
"%", STR(CARD_SIZE), "s | %", STR(ANSWER_SIZE), "s");
printf("'%s'\n", scanf_string);
free(scanf_string);
return 0;
}编辑我在代码中编写了STR1(CARD_SIZE),而它应该是STR。现在修好了。
发布于 2014-10-06 09:51:30
有一种构造格式字符串的更简单的方法。因为“变量”是编译时常量,所以可以使用宏对它们进行字符串化,然后让编译器将它们连接起来。编译器将自动将相邻的字符串常量连接成单个字符串,例如。"this" "that" -> "thisthat".忏悔:我完全检查了https://stackoverflow.com/questions/2653214/stringification-of-a-macro-value得到这些(简单!?)宏可以正常工作。
$cat strfy.c
#define CARD_SIZE 200
#define ANSWER_SIZE 20
#define XSTR(x) STR(x)
#define STR(x) #x
printf("%" XSTR(CARD_SIZE) "s | %" XSTR(ANSWER_SIZE) "s", card, answer);
$cpp -P strfy.c
printf("%" "200" "s | %" "20" "s", card, answer);正如M Oehm所指出的,为了将整数折叠成字符串,仍然需要这种带有变量函数的宏。否则,函数需要更加复杂才能处理不同类型的参数。
还有一种方法是使用sprintf构建格式字符串。但是这种方法有一个额外的复杂性,你需要预先计算字符串的大小。这个问题可以通过使用以snprintf作为目标字符串的NULL来解决,返回值将是所需的大小。
#define CARD_SIZE 200
#define ANSWER_SIZE 1000
#include <stdlib.h>
#include <stdio.h>
int main()
{
char *fmt;
size_t fmt_sz;
char card[CARD_SIZE + 1];
char answer[ANSWER_SIZE + 1];
fmt = malloc(fmt_sz = snprintf(NULL, 0, "%%%ds | %%%ds", CARD_SIZE, ANSWER_SIZE) + 1);
snprintf(fmt, fmt_sz, "%%%ds | %%%ds", CARD_SIZE, ANSWER_SIZE);
//printf("%s", fmt);
scanf(fmt, card, answer);
return 0;
}https://stackoverflow.com/questions/26213488
复制相似问题