在C语言中使用可变函数进行字符串连接
我正在尝试用C语言编写一个基本的测验程序。它基本上会存储卡片和答案。 但与此同时,我正在尝试使用我学到的新技术,如可变函数和动态内存分配 我希望程序能够随着我改变常数而扩展,不应该有K&R定义的“幻数”。问题是我不能在fscanf的format参数中使用变量。我需要手动定义字符串长度。为了克服这个限制,我尝试编写一个字符串concatation函数,该函数将生成fscanf format参数 e、 g 常数在常量h中定义在C语言中使用可变函数进行字符串连接,c,malloc,dynamic-memory-allocation,scanf,variadic-functions,C,Malloc,Dynamic Memory Allocation,Scanf,Variadic Functions,我正在尝试用C语言编写一个基本的测验程序。它基本上会存储卡片和答案。 但与此同时,我正在尝试使用我学到的新技术,如可变函数和动态内存分配 我希望程序能够随着我改变常数而扩展,不应该有K&R定义的“幻数”。问题是我不能在fscanf的format参数中使用变量。我需要手动定义字符串长度。为了克服这个限制,我尝试编写一个字符串concatation函数,该函数将生成fscanf format参数 e、 g 常数在常量h中定义 #define CARD_SIZE 200 #define ANSWER_
#define CARD_SIZE 200
#define ANSWER_SIZE 1000
#define CONCAT_SIZE 20
et_concat函数位于etstring.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(整数计数,字符*str,…)
{
va_列表ap;
int j;
char*concatted_string=(char*)malloc(count*concated_SIZE+1);
va_启动(ap、str);
对于(j=0;j
我试图从reader.c中调用et_concat的代码
#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;
}
#包括
#包括
#包括
#包括“常数h”
#包括“etstring.h”
int迭代_输入()
{
字符卡[卡片大小];
字符应答[应答大小];
文件*fp=fopen(“data.txt”,“r”);
如果(fp==NULL)
{
返回退出失败;
}
char*scanf_string=et_concat(5,“%”,卡片大小,“s,”,答案大小,“s”);
printf(扫描字符串);
fscanf(fp、扫描字符串、卡片、应答);
printf(“%s |%s\n”,卡片,答案);
fclose(fp);
返回退出成功;
}
非常感谢。有一种更简单的方法来构造格式字符串。由于“变量”是编译时常量,您可以使用宏对它们进行字符串化,然后让编译器为您连接它们。编译器将自动将相邻的字符串常量连接成单个字符串,例如,
“this”“that”
->“this”“that”
。坦白:为了让这些(简单!?)宏正常工作,我完全检查了
正如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;
}
#定义卡尺寸200
#定义答案大小1000
#包括
#包括
int main()
{
char*fmt;
尺寸(单位)(平方米);;
字符卡[card_SIZE+1];
字符应答[应答大小+1];
fmt=malloc(fmt_sz=snprintf(NULL,0,%%%ds |%%ds),卡片大小,答案大小)+1);
snprintf(fmt、fmt_sz、%%ds |%%ds)、卡片大小、答案大小);
//printf(“%s”,fmt);
扫描(fmt、卡片、应答);
返回0;
}
您的连接例程有各种错误和缺点:
- 您已经读取了非可变参数
中的第一个字符串,但只打印可变参数,其中只剩下四个。基本上跳过第一个字符串,第五个字符串是垃圾str
- 分配字符串后,您不必初始化它,这样它可能包含垃圾。将第一个字符设置为
足以初始化以零结尾的空字符串'\0'
- 您分配了一个固定的缓冲区,并没有真正检查溢出。这有点违反直觉:要么分配缓冲区(稍后必须释放)并允许任意字符串长度,要么传入长度受限的缓冲区,而不关心例程中的分配
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;
}
#包括
#包括
#包括
#包括
#定义卡的大小为200
#定义答案大小1000
#定义CONCAT_尺寸20
字符*et_concat(整数计数,…)
{
va_列表ap;
char*concatted_string=malloc(count*concated_SIZE+1);
int j;
*concatted_字符串='\0';
va_启动(ap,计数);
对于(j=0;j
Edit我在代码中写了
STR1(卡片大小)
,而它应该是STR
。现已修复。您的函数需要5个C字符串,但CARD\u SIZE
和ANSWER\u SIZE
是整数常量。你可以编写一个函数或宏来字符串化它们。printf(str)
-请不要这样做。如果字符串恰好包含%
,会发生什么情况?请使用printf(“%s”,str)
或put(str)
。回答得好。我喜欢snprintf
的建议。毕竟,如果您的格式都是十进制数,那么很容易估计最大字符串长度而不会造成浪费。非常感谢。对我来说,严格化要简单得多
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;
}