gpt4 book ai didi

c - 是否在 C 未定义行为中将任意数量的字符串与嵌套函数调用连接起来?

转载 作者:太空宇宙 更新时间:2023-11-03 23:39:50 24 4
gpt4 key购买 nike

我有一个应用程序,它通过使用文本片段创建完整文件路径名的一系列字符串连接来构建文件路径名。

问题是,将少量但任意数量的文本字符串连接在一起的方法是否成功取决于未定义的行为

一系列嵌套函数的求值顺序是否有保证?

我发现了这个问题 Nested function calls order of evaluation然而,它似乎更多地是关于参数列表中的多个函数,而不是一系列嵌套函数。

请原谅以下代码示例中的名称。它与源代码的其余部分一致,我正在先进行一些测试。

我对连接多个字符串的需求的第一个切入点是一个如下所示的函数,它将最多三个文本字符串连接成一个字符串。

typedef wchar_t TCHAR;

TCHAR *RflCatFilePath(TCHAR *tszDest, int nDestLen, TCHAR *tszPath, TCHAR *tszPath2, TCHAR *tszFileName)
{
if (tszDest && nDestLen > 0) {
TCHAR *pDest = tszDest;
TCHAR *pLast = tszDest;

*pDest = 0; // ensure empty string if no path data provided.

if (tszPath) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszPath++); nDestLen--) pLast = pDest;
if (tszPath2) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszPath2++); nDestLen--) pLast = pDest;
if (tszFileName) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszFileName++); nDestLen--) pLast = pDest;
}

return tszDest;
}

然后我遇到了一个案例,我有四段文本要放在一起。

考虑到这一点,似乎很可能很快就会发现五个的情况,所以我想知道是否有不同的方式来处理任意数量的字符串。

我想到的是如下两个函数。

typedef wchar_t TCHAR;

typedef struct {
TCHAR *pDest;
TCHAR *pLast;
int destLen;
} RflCatStruct;

RflCatStruct RflCatFilePathX(const TCHAR *pPath, RflCatStruct x)
{
TCHAR *pDest = x.pLast;
if (pDest && pPath) for ( ; x.destLen > 0 && (*pDest++ = *pPath++); x.destLen--) x.pLast = pDest;
return x;
}

RflCatStruct RflCatFilePathY(TCHAR *buffDest, int nLen, const TCHAR *pPath)
{
RflCatStruct x = { 0 };

TCHAR *pDest = x.pDest = buffDest;
x.pLast = buffDest;
x.destLen = nLen;

if (buffDest && nLen > 0) { // ensure there is room for at least one character.
*pDest = 0; // ensure empty string if no path data provided.
if (pPath) for (pDest = x.pLast; x.destLen > 0 && (*pDest++ = *pPath++); x.destLen--) x.pLast = pDest;
}
return x;
}

这两个函数的使用示例如下。具有这两个函数的这段代码似乎可以在 Visual Studio 2013 中正常工作。

TCHAR buffDest[512] = { 0 };
TCHAR *pPath = L"C:\\flashdisk\\ncr\\database";
TCHAR *pPath2 = L"\\";
TCHAR *pFilename = L"filename.ext";

RflCatFilePathX(pFilename, RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pPath)));
printf("dest t = \"%S\"\n", buffDest);


printf("dest t = \"%S\"\n", RflCatFilePathX(pFilename, RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pFilename))).pDest);


RflCatStruct dStr = RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pPath));
// other stuff then
printf("dest t = \"%S\"\n", RflCatFilePathX(pFilename, dStr).pDest);

最佳答案

函数调用的参数在函数被调用之前被完全评估。因此,将按预期顺序评估对 RflCatFilePath* 的调用。 (这由 §6.5.2.2/10 保证:“在函数指示符和实际参数的计算之后但在实际调用之前有一个序列点。”)

如评论中所述,snprintf 函数可能是解决此问题的更好选择。 (asprintf 会更好,并且有一个可在 Windows 上运行的免费 shim。)snprintf 的唯一问题是您可能必须调用它两次.如果有足够的空间,它总是返回缓冲区中应该存储的字节数,所以如果返回值不小于缓冲区的大小,您将需要分配一个更大的缓冲区(您现在知道其大小)并再次调用 snprintf

asprintf 会为您做这些,但它是标准库的 BSD/Gnu 扩展。

在连接文件路径的情况下,操作系统/文件系统支持最大字符串长度,您应该能够找出它是什么(尽管它可能需要在非 Posix 系统上进行特定于操作系统的调用).因此,如果连接不适合 512 字节的缓冲区,则简单地返回错误指示可能是合理的。

为了好玩,我包含了一个递归可变参数连接器:

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

static char* concat_helper(size_t accum, char* chunk, va_list ap) {
if (chunk) {
size_t chunklen = strlen(chunk);
char* next_chunk = va_arg(ap, char*);
char* retval = concat_helper(accum + chunklen, next_chunk, ap);
memcpy(retval + accum, chunk, chunklen);
return retval;
} else {
char* retval = malloc(accum + 1);
retval[accum] = 0;
return retval;
}
}
char* concat_list(char* chunk, ...) {
va_list ap;
va_start(ap, chunk);
char* retval = concat_helper(0, chunk, ap);
va_end(ap);
return retval;
}

由于 concat_list 是可变参数函数,您需要在参数末尾提供 (char*)NULL。另一方面,您不需要为每个新参数重复函数名称。因此,示例调用可能是:

concat_list(pPath, pPath2, pFilename, (char*)0);

(我想你需要一个 wchar_t* 版本,但变化应该很明显。注意 malloc。)出于生产目的,递归可能应该被替换通过遍历参数列表两次的迭代版本(参见 va_copy),但我一直喜欢“来回”递归模式。

关于c - 是否在 C 未定义行为中将任意数量的字符串与嵌套函数调用连接起来?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48817240/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com