自定义 STRCAT 被太多的参数淹没
Custom STRCAT is overwhelmed by too many arguments
我正在尝试编写一个自定义 strcat
代码,它用 \n
分隔参数,最后一个参数除外,并用 [=15=]
.
终止字符串
最多 5 个参数工作正常,但如果我尝试传递第六个参数,我会收到一条奇怪的响应:
MacBook-Pro-de-Domingo% ./test ok ok ok ok ok
ok
ok
ok
ok
ok
MacBook-Pro-de-Domingo% ./test ok ok ok ok ok ok
ok
ok
ok
ok
ok
P/Users/domingodelmasok
这是我的自定义 strcat
代码:
char cat(char *dest, char *src, int current, int argc_nb)
{
int i = 0;
int j = 0;
while(dest[i])
i++;
while(src[j])
{
dest[i + j] = src[j];
j++;
}
if(current < argc_nb - 1)
dest[i + j] = '\n';
else
dest[i + j] = '[=11=]';
return(*dest);
}
UPDATE 完成调用功能:
char *concator(int argc, char **argv)
{
int i;
int j;
int size = 0;
char *str;
i = 1;
while(i < argc)
{
j = 0;
while(argv[i][j])
{
size++;
j++;
}
i++;
}
str = (char*)malloc(sizeof(*str) * (size + 1));
i = 1;
while(i < argc)
{
cat(str, argv[i], i, argc);
i++;
}
free(str);
return(str);
}
这是怎么回事?
谢谢!
编辑:修复错误。
代码有不少问题:
sizeof (char) == 1
根据 C 标准。
cat()
要求目标是一个字符串(以 [=18=]
结尾),但不附加它本身(current >= argc_nb - 1
除外)。这是一个错误。
free(str); return str;
是一个释放后使用错误。如果调用 free(str)
,str
中的内容将不可挽回地丢失,无法访问。 free(str)
应该简单地删除;这里不合适。
C 中的数组索引为 0。但是,concator()
函数会跳过第一个字符串指针(因为 argv[0]
包含用于执行程序的名称)。这是错误的,最终会绊倒某人。相反,让 concator()
添加数组中的所有字符串,但使用 concator(argc - 1, argv + 1);
.
调用它
可能还有更多,但在这一点上,我认为使用更合适的方法从头开始重写是合适的。
考虑以下 join()
函数:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char *join(const size_t parts, const char *part[],
const char *separator, const char *suffix)
{
const size_t separator_len = (separator) ? strlen(separator) : 0;
const size_t suffix_len = (suffix) ? strlen(suffix) : 0;
size_t total_len = 0;
size_t p;
char *dst, *end;
/* Calculate sum of part lengths */
for (p = 0; p < parts; p++)
if (part[p])
total_len += strlen(part[p]);
/* Add separator lengths */
if (parts > 1)
total_len += (parts - 1) * separator_len;
/* Add suffix length */
total_len += suffix_len;
/* Allocate enough memory, plus end-of-string '[=10=]' */
dst = malloc(total_len + 1);
if (!dst)
return NULL;
/* Keep a pointer to the current end of the result string */
end = dst;
/* Append each part */
for (p = 0; p < parts; p++) {
/* Insert separator */
if (p > 0 && separator_len > 0) {
memcpy(end, separator, separator_len);
end += separator_len;
}
/* Insert part */
if (part[p]) {
const size_t len = strlen(part[p]);
if (len > 0) {
memcpy(end, part[p], len);
end += len;
}
}
}
/* Append suffix */
if (suffix_len > 0) {
memcpy(end, suffix, suffix_len);
end += suffix_len;
}
/* Terminate string. */
*end = '[=10=]';
/* All done. */
return dst;
}
逻辑很简单。首先,我们找出每个组件的长度。请注意,separator
仅在部分之间添加(因此出现 parts-1
次),并且 suffix
在最后。
((string) ? strlen(string) : 0
习语只是表示 "if string
is non-NULL, strlen(0)
, otherwise 0
"。我们这样做是因为我们允许 NULL 分隔符和后缀,但是 strlen(NULL)
是未定义的行为。)
接下来,我们为结果分配足够的内存,包括未包含在长度中的字符串结尾 NUL 字符 [=18=]
。
为了附加每个部分,我们保持结果指针不变,而是使用一个临时的 end
指针。 (到目前为止,它是字符串的结尾。)我们使用一个循环,将下一部分复制到 end
。在第二部分及后续部分之前,我们复制该部分之前的分隔符。
接下来,我们复制后缀,最后是字符串结尾 '[=40=]'
。 (重要的是 return 指向字符串开头的指针,而不是结尾,当然;这就是为什么我们保持 dst
指向新的结果字符串,并且 end
在我们附加每个子字符串的位置。)
您可以从命令行使用它,例如使用以下 main()
:
int main(int argc, char *argv[])
{
char *result;
if (argc < 4) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s SEPARATOR SUFFIX PART [ PART ... ]\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
result = join(argc - 3, (const char **)(argv + 3), argv[1], argv[2]);
if (!result) {
fprintf(stderr, "Failed.\n");
return EXIT_FAILURE;
}
fputs(result, stdout);
return EXIT_SUCCESS;
}
如果你将上面的编译为例如example
(我用的是gcc -Wall -O2 example.c -o example
),然后是运行
./example ', ' $'!\n' Hello world
在 Bash shell 输出中
Hello, world!
(最后换行)。 运行
./example ' and ' $'.\n' a b c d e f g
产出
a and b and c and d and e and f and g
(同样在末尾换行)。 $'...'
只是一个 Bash 习惯用法,用于指定字符串中的特殊字符; $'!\n'
在 Bash 中与 "!\n"
在 C 中相同,$'.\n'
等同于 Bash 在 C 中 ".\n"
(删除部分之间的自动换行符,并允许使用一个字符串而不是一个字符作为分隔符和后缀,是出于两个原因的深思熟虑的选择。主要的一个是阻止任何人只是复制-粘贴这个作为一些练习的答案。第二个是为了表明虽然它可能 听起来 比只使用单个字符更复杂,但实际上它只是很少的额外代码;如果您考虑实际用例,允许将字符串用作分隔符会打开很多选项。)
上面的示例代码只经过了非常轻微的测试,可能包含错误。如果您发现或不同意我上面写的任何内容,请在评论中告诉我,以便我进行审查并根据需要进行修复。
我正在尝试编写一个自定义 strcat
代码,它用 \n
分隔参数,最后一个参数除外,并用 [=15=]
.
最多 5 个参数工作正常,但如果我尝试传递第六个参数,我会收到一条奇怪的响应:
MacBook-Pro-de-Domingo% ./test ok ok ok ok ok
ok
ok
ok
ok
ok
MacBook-Pro-de-Domingo% ./test ok ok ok ok ok ok
ok
ok
ok
ok
ok
P/Users/domingodelmasok
这是我的自定义 strcat
代码:
char cat(char *dest, char *src, int current, int argc_nb)
{
int i = 0;
int j = 0;
while(dest[i])
i++;
while(src[j])
{
dest[i + j] = src[j];
j++;
}
if(current < argc_nb - 1)
dest[i + j] = '\n';
else
dest[i + j] = '[=11=]';
return(*dest);
}
UPDATE 完成调用功能:
char *concator(int argc, char **argv)
{
int i;
int j;
int size = 0;
char *str;
i = 1;
while(i < argc)
{
j = 0;
while(argv[i][j])
{
size++;
j++;
}
i++;
}
str = (char*)malloc(sizeof(*str) * (size + 1));
i = 1;
while(i < argc)
{
cat(str, argv[i], i, argc);
i++;
}
free(str);
return(str);
}
这是怎么回事?
谢谢!
编辑:修复错误。
代码有不少问题:
sizeof (char) == 1
根据 C 标准。cat()
要求目标是一个字符串(以[=18=]
结尾),但不附加它本身(current >= argc_nb - 1
除外)。这是一个错误。free(str); return str;
是一个释放后使用错误。如果调用free(str)
,str
中的内容将不可挽回地丢失,无法访问。free(str)
应该简单地删除;这里不合适。C 中的数组索引为 0。但是,
concator()
函数会跳过第一个字符串指针(因为argv[0]
包含用于执行程序的名称)。这是错误的,最终会绊倒某人。相反,让concator()
添加数组中的所有字符串,但使用concator(argc - 1, argv + 1);
. 调用它
可能还有更多,但在这一点上,我认为使用更合适的方法从头开始重写是合适的。
考虑以下 join()
函数:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char *join(const size_t parts, const char *part[],
const char *separator, const char *suffix)
{
const size_t separator_len = (separator) ? strlen(separator) : 0;
const size_t suffix_len = (suffix) ? strlen(suffix) : 0;
size_t total_len = 0;
size_t p;
char *dst, *end;
/* Calculate sum of part lengths */
for (p = 0; p < parts; p++)
if (part[p])
total_len += strlen(part[p]);
/* Add separator lengths */
if (parts > 1)
total_len += (parts - 1) * separator_len;
/* Add suffix length */
total_len += suffix_len;
/* Allocate enough memory, plus end-of-string '[=10=]' */
dst = malloc(total_len + 1);
if (!dst)
return NULL;
/* Keep a pointer to the current end of the result string */
end = dst;
/* Append each part */
for (p = 0; p < parts; p++) {
/* Insert separator */
if (p > 0 && separator_len > 0) {
memcpy(end, separator, separator_len);
end += separator_len;
}
/* Insert part */
if (part[p]) {
const size_t len = strlen(part[p]);
if (len > 0) {
memcpy(end, part[p], len);
end += len;
}
}
}
/* Append suffix */
if (suffix_len > 0) {
memcpy(end, suffix, suffix_len);
end += suffix_len;
}
/* Terminate string. */
*end = '[=10=]';
/* All done. */
return dst;
}
逻辑很简单。首先,我们找出每个组件的长度。请注意,separator
仅在部分之间添加(因此出现 parts-1
次),并且 suffix
在最后。
((string) ? strlen(string) : 0
习语只是表示 "if string
is non-NULL, strlen(0)
, otherwise 0
"。我们这样做是因为我们允许 NULL 分隔符和后缀,但是 strlen(NULL)
是未定义的行为。)
接下来,我们为结果分配足够的内存,包括未包含在长度中的字符串结尾 NUL 字符 [=18=]
。
为了附加每个部分,我们保持结果指针不变,而是使用一个临时的 end
指针。 (到目前为止,它是字符串的结尾。)我们使用一个循环,将下一部分复制到 end
。在第二部分及后续部分之前,我们复制该部分之前的分隔符。
接下来,我们复制后缀,最后是字符串结尾 '[=40=]'
。 (重要的是 return 指向字符串开头的指针,而不是结尾,当然;这就是为什么我们保持 dst
指向新的结果字符串,并且 end
在我们附加每个子字符串的位置。)
您可以从命令行使用它,例如使用以下 main()
:
int main(int argc, char *argv[])
{
char *result;
if (argc < 4) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s SEPARATOR SUFFIX PART [ PART ... ]\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
result = join(argc - 3, (const char **)(argv + 3), argv[1], argv[2]);
if (!result) {
fprintf(stderr, "Failed.\n");
return EXIT_FAILURE;
}
fputs(result, stdout);
return EXIT_SUCCESS;
}
如果你将上面的编译为例如example
(我用的是gcc -Wall -O2 example.c -o example
),然后是运行
./example ', ' $'!\n' Hello world
在 Bash shell 输出中
Hello, world!
(最后换行)。 运行
./example ' and ' $'.\n' a b c d e f g
产出
a and b and c and d and e and f and g
(同样在末尾换行)。 $'...'
只是一个 Bash 习惯用法,用于指定字符串中的特殊字符; $'!\n'
在 Bash 中与 "!\n"
在 C 中相同,$'.\n'
等同于 Bash 在 C 中 ".\n"
(删除部分之间的自动换行符,并允许使用一个字符串而不是一个字符作为分隔符和后缀,是出于两个原因的深思熟虑的选择。主要的一个是阻止任何人只是复制-粘贴这个作为一些练习的答案。第二个是为了表明虽然它可能 听起来 比只使用单个字符更复杂,但实际上它只是很少的额外代码;如果您考虑实际用例,允许将字符串用作分隔符会打开很多选项。)
上面的示例代码只经过了非常轻微的测试,可能包含错误。如果您发现或不同意我上面写的任何内容,请在评论中告诉我,以便我进行审查并根据需要进行修复。