c中无限字符串的strcat
strcat for unlimited string in c
我想在 c 中实现 strcat 但对于无限字符串,该函数将分配最终字符串所需的必要内存,我想到了这个:
char* strcat_ex(const char* str, ...) {
if(!str)return 0;
const char* current = str;
char* res = 0;
va_list args;
va_start(args, str);
// calculate the length of the final string
size_t len = 0;
for(; current; current = va_arg(args, char *))
len += strlen(current);
// allocate the string
res = malloc(sizeof(char) * len + 1);
if(!res)return 0;
// copy the strings to the final destination
size_t cur = 0;
for(; current; current = va_arg(args, char *)) {
size_t cur_len = strlen(current);
memcpy_s(res + cur, cur_len, current, cur_len);
}
va_end(args);
return res;
}
- 是否已经实现了任何标准功能。
如果不是我该如何改进这段代码,例如:
1-我计算了两次字符串的长度,怎么只计算一次。
2-是否存在任何安全问题。
3-其他改进代码的建议。
在此循环之前
size_t cur = 0;
for(; current; current = va_arg(args, char *)) {
size_t cur_len = strlen(current);
memcpy_s(res + cur, cur_len, current, cur_len);
}
您必须使用宏 va_start
重新初始化 args
。
变量cur
在循环中没有改变,总是等于0。所以所有的字符串都被复制到相同的地址res + 0
。
每次退出程序之前,您都必须调用宏 va_end
。
函数可以看下面的演示程序所示。
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
char * strcat_ex( const char *s, ... )
{
char *result = NULL;
if ( s != NULL )
{
va_list ap;
size_t n = 0;
va_start( ap, s );
for ( const char *p = s; p != NULL; p = va_arg( ap, const char * ) )
{
n += strlen( p );
}
va_end( ap );
++n;
result = ( char * )malloc( n );
if ( result != NULL )
{
n = 0;
va_start( ap, s );
for ( const char *p = s; p != NULL; p = va_arg( ap, const char * ) )
{
strcpy( result + n, p );
n += strlen( p );
}
va_end( ap );
}
}
return result;
}
int main(void)
{
char *s;
s = strcat_ex( "1", "2", "3", "4", "5", NULL );
puts( s );
free( s );
s = strcat_ex( "Hello", " ", "World!", NULL );
puts( s );
free( s );
return 0;
}
程序输出为
12345
Hello World!
您可以添加一个检查,确保所有字符串的总长度不大于 size_t
类型对象中可存储的最大值。那就是有np溢出。
首先,一些一般性评论。
if(!str)return 0;
虽然常量 0
保证等同于空指针,但我不相信检查指针是否为假。这可能不是常见环境的问题,但如果您来自 Windows 背景,那么戳 C 兼容性龙 尤其是 是个坏习惯很容易沉迷于 Microsoft 扩展,尤其是 如果您对 C 标准没有广博的了解。不要戳边缘情况。如果您的意思是 null,请使用 NULL
.
说到 Microsoft 扩展,memcpy_s
是一个非标准的 Microsoft 扩展。使用 memcpy
或提供兼容性包装器。
va_arg
必须在每次使用时重新初始化,它不会自行重启。将 va_start
和 va_end
想象成 va_args
周围的大括号。基本模式是这样的:
va_start(args, first);
for(
current = first;
current != NULL;
current = va_arg(args, char *)
) {
...
}
va_end(args);
你不知道有多少可变参数,va_arg
会继续读取堆栈上的垃圾。您要么必须传入容易出错的参数数量,要么使用标记值,例如将最后一个参数设为 NULL。
我会考虑将其命名为 strcat*
以外的名称,因为它具有不同的界面。像 strcat
这样使用它会导致难以调试的错误。 strcat
的形式是 strcat( dest, src )
,而您的函数是 dest = strcat_ex( src, src, ... )
。有人写strcat_ex( dest, src, src, ... )
.
会很有诱惑力
想到的名字是strjoin
。 join
其他语言的函数包括提供定界符的功能。这表明您可以向该函数添加一个有用的功能,您通常希望使用 ", "
或 " "
等分隔符连接字符串,这将是第一个参数。
为避免必须读取所有字符串两次,您可以利用参数数量非常少的优势。参数的数量受堆栈大小和程序员的理智限制。因此,多次遍历少量参数比多次调用字符串 strlen
更便宜。
您需要对参数进行三个循环。
- 找出有多少参数。
- 分配一个数组来保存它们的长度。
- 存储字符串长度及其总长度。
- 分配目的地。
- 复制字符串。
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
char* strjoin(const char* first, ...) {
if( first == NULL ) {
return NULL;
}
va_list args;
int i;
const char *current;
// 1. Figure out how many arguments there are.
va_start(args, first);
size_t num_args = 0;
for(
current = first;
current != NULL;
current = va_arg(args, char *)
) {
num_args++;
}
va_end(args);
// * Allocate an array to hold their lengths.
size_t str_sizes[num_args];
// 2. Store the string lengths and their total length.
size_t dest_size = 0;
va_start(args, first);
for(
i = 0, current = first;
current != NULL;
i++, current = va_arg(args, char *)
) {
str_sizes[i] = strlen(current);
dest_size += str_sizes[i];
}
va_end(args);
// * Allocate the destination.
char *dest = malloc(dest_size + 1);
if( dest == NULL ) {
return NULL;
}
// 3. Copy the strings.
char *dest_pos = dest;
va_start(args, first);
for(
i = 0, current = first;
current != NULL;
i++, current = va_arg(args, char *)
) {
memcpy( dest_pos, current, str_sizes[i] );
dest_pos += str_sizes[i];
}
va_end(args);
// Add the final null byte
dest_pos[0] = '[=12=]';
return dest;
}
int main() {
char *all = strjoin( "foo", "bar", "baz", "biff", "wibble", NULL );
puts(all);
}
我想在 c 中实现 strcat 但对于无限字符串,该函数将分配最终字符串所需的必要内存,我想到了这个:
char* strcat_ex(const char* str, ...) {
if(!str)return 0;
const char* current = str;
char* res = 0;
va_list args;
va_start(args, str);
// calculate the length of the final string
size_t len = 0;
for(; current; current = va_arg(args, char *))
len += strlen(current);
// allocate the string
res = malloc(sizeof(char) * len + 1);
if(!res)return 0;
// copy the strings to the final destination
size_t cur = 0;
for(; current; current = va_arg(args, char *)) {
size_t cur_len = strlen(current);
memcpy_s(res + cur, cur_len, current, cur_len);
}
va_end(args);
return res;
}
- 是否已经实现了任何标准功能。
如果不是我该如何改进这段代码,例如:
1-我计算了两次字符串的长度,怎么只计算一次。
2-是否存在任何安全问题。
3-其他改进代码的建议。
在此循环之前
size_t cur = 0;
for(; current; current = va_arg(args, char *)) {
size_t cur_len = strlen(current);
memcpy_s(res + cur, cur_len, current, cur_len);
}
您必须使用宏 va_start
重新初始化 args
。
变量cur
在循环中没有改变,总是等于0。所以所有的字符串都被复制到相同的地址res + 0
。
每次退出程序之前,您都必须调用宏 va_end
。
函数可以看下面的演示程序所示。
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
char * strcat_ex( const char *s, ... )
{
char *result = NULL;
if ( s != NULL )
{
va_list ap;
size_t n = 0;
va_start( ap, s );
for ( const char *p = s; p != NULL; p = va_arg( ap, const char * ) )
{
n += strlen( p );
}
va_end( ap );
++n;
result = ( char * )malloc( n );
if ( result != NULL )
{
n = 0;
va_start( ap, s );
for ( const char *p = s; p != NULL; p = va_arg( ap, const char * ) )
{
strcpy( result + n, p );
n += strlen( p );
}
va_end( ap );
}
}
return result;
}
int main(void)
{
char *s;
s = strcat_ex( "1", "2", "3", "4", "5", NULL );
puts( s );
free( s );
s = strcat_ex( "Hello", " ", "World!", NULL );
puts( s );
free( s );
return 0;
}
程序输出为
12345
Hello World!
您可以添加一个检查,确保所有字符串的总长度不大于 size_t
类型对象中可存储的最大值。那就是有np溢出。
首先,一些一般性评论。
if(!str)return 0;
虽然常量 0
保证等同于空指针,但我不相信检查指针是否为假。这可能不是常见环境的问题,但如果您来自 Windows 背景,那么戳 C 兼容性龙 尤其是 是个坏习惯很容易沉迷于 Microsoft 扩展,尤其是 如果您对 C 标准没有广博的了解。不要戳边缘情况。如果您的意思是 null,请使用 NULL
.
说到 Microsoft 扩展,memcpy_s
是一个非标准的 Microsoft 扩展。使用 memcpy
或提供兼容性包装器。
va_arg
必须在每次使用时重新初始化,它不会自行重启。将 va_start
和 va_end
想象成 va_args
周围的大括号。基本模式是这样的:
va_start(args, first);
for(
current = first;
current != NULL;
current = va_arg(args, char *)
) {
...
}
va_end(args);
你不知道有多少可变参数,va_arg
会继续读取堆栈上的垃圾。您要么必须传入容易出错的参数数量,要么使用标记值,例如将最后一个参数设为 NULL。
我会考虑将其命名为 strcat*
以外的名称,因为它具有不同的界面。像 strcat
这样使用它会导致难以调试的错误。 strcat
的形式是 strcat( dest, src )
,而您的函数是 dest = strcat_ex( src, src, ... )
。有人写strcat_ex( dest, src, src, ... )
.
想到的名字是strjoin
。 join
其他语言的函数包括提供定界符的功能。这表明您可以向该函数添加一个有用的功能,您通常希望使用 ", "
或 " "
等分隔符连接字符串,这将是第一个参数。
为避免必须读取所有字符串两次,您可以利用参数数量非常少的优势。参数的数量受堆栈大小和程序员的理智限制。因此,多次遍历少量参数比多次调用字符串 strlen
更便宜。
您需要对参数进行三个循环。
- 找出有多少参数。
- 分配一个数组来保存它们的长度。
- 存储字符串长度及其总长度。
- 分配目的地。
- 复制字符串。
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
char* strjoin(const char* first, ...) {
if( first == NULL ) {
return NULL;
}
va_list args;
int i;
const char *current;
// 1. Figure out how many arguments there are.
va_start(args, first);
size_t num_args = 0;
for(
current = first;
current != NULL;
current = va_arg(args, char *)
) {
num_args++;
}
va_end(args);
// * Allocate an array to hold their lengths.
size_t str_sizes[num_args];
// 2. Store the string lengths and their total length.
size_t dest_size = 0;
va_start(args, first);
for(
i = 0, current = first;
current != NULL;
i++, current = va_arg(args, char *)
) {
str_sizes[i] = strlen(current);
dest_size += str_sizes[i];
}
va_end(args);
// * Allocate the destination.
char *dest = malloc(dest_size + 1);
if( dest == NULL ) {
return NULL;
}
// 3. Copy the strings.
char *dest_pos = dest;
va_start(args, first);
for(
i = 0, current = first;
current != NULL;
i++, current = va_arg(args, char *)
) {
memcpy( dest_pos, current, str_sizes[i] );
dest_pos += str_sizes[i];
}
va_end(args);
// Add the final null byte
dest_pos[0] = '[=12=]';
return dest;
}
int main() {
char *all = strjoin( "foo", "bar", "baz", "biff", "wibble", NULL );
puts(all);
}