C中的字符串:陷阱和技巧

时间:2009-08-17 22:36:47

标签: c string

我将在下个月指导一个ACM小组(去图),现在是时候谈论C中的字符串了。除了讨论标准的lib,strcpystrcmp等。,我想给他们一些提示(类似于str[0] is equivalent to *str,以及类似的东西)。

你知道任何名单(如备忘单)或你自己在这方面的经验吗?

我已经知道ACM比赛的书籍(这很好,特别是this),但我是在追逐交易的伎俩。

谢谢。

编辑:非常感谢大家。我会接受投票最多的答案,并且适当地支持我认为相关的其他人。我希望在这里做一个总结(就像我做here,asap)。我现在有足够的材料,我确信这极大地改善了字符串的会话。再一次,谢谢。

16 个答案:

答案 0 :(得分:26)

很明显,但我认为知道字符串 nothing 比字节数组更重要,由零字节分隔。 你可能知道C字符串并不是所有用户友好的。

  • 在字符串中的某处写一个零字节会截断它。
  • 走出界限通常会结束。
  • 永远不要使用strcpy,strcmp,strcat等,而是使用他们的安全变体:strncmp,strncat,strndup,...
  • 避免strncpy。 strncpy并不总是零划分你的字符串!如果源字符串不适合目标缓冲区,则会截断字符串,但不会在缓冲区末尾写入nul字节。此外,即使源缓冲区比目标缓冲区小,strncpy仍然会用零覆盖整个缓冲区。我个人使用strlcpy。
  • 不要使用printf(string),而是使用printf(“%s”,string)。如果用户在字符串中添加%d,请尝试考虑后果。
  • 您无法将字符串与
    if( s1 == s2 )
                doStuff(s1);
    进行比较 您必须比较字符串中的每个字符。使用strcmp或更好的strncmp。
    if( strncmp( s1, s2, BUFFER_SIZE ) == 0 )
             doStuff(s1);

答案 1 :(得分:5)

滥用strlen()会大大恶化性能。

for( int i = 0; i < strlen( string ); i++ ) {
    processChar( string[i] );
}

至少具有O(n 2 )时间复杂度,而

int length = strlen( string );
for( int i = 0; i < length; i++ ) {
    processChar( string[i] );
}

至少具有O(n)时间复杂度。对于那些没有时间去思考它的人来说,这并不是那么明显。

答案 2 :(得分:3)

以下函数可用于实现非变异strtok

strcspn(string, delimiters)
strspn(string, delimiters)

第一个找到您传入的分隔符集中的第一个字符。第二个字符在您传入的分隔符集中找到第一个字符

我更喜欢这些strpbrk,因为如果它们无法匹配,它们会返回字符串的长度。

答案 3 :(得分:3)

str[0]相当于0[str],或者更为一般str[i]i[str]i[str]*(str + i)

NB

这不是特定于字符串,但它也适用于C数组

答案 4 :(得分:3)

stdlib 中的str n *变体不一定为null终止目标字符串

例如:来自MSDN关于strncpy的文档:

  

strncpy函数复制   strSource的初始计数字符   到strDest并返回strDest。 如果   count小于或等于   strSource的长度,一个空字符   不会自动附加到   复制字符串。如果count更大   比strSource的长度,   目标字符串用null填充   字符长度计数。

答案 5 :(得分:2)

strtok不是线程安全,因为它使用可变私有缓冲区在调用之间存储数据;你也不能交错或劝说strtok电话。

更有用的替代方法是strtok_r只要你能就使用它。

答案 6 :(得分:2)

在使用字符串时将strlen()sizeof()混淆:

char *p = "hello!!";
strlen(p) != sizeof(p)

sizeof(p)在编译时产生指针的大小(4或8个字节),而strlen(p)在运行时计算空终止的char数组的长度(本例中为7)

答案 7 :(得分:2)

kmm已经很好了。以下是我开始编写C代码时遇到的问题。

  1. 字符串文字有自己的内存部分,并且始终可以访问。因此,它们可以是函数的返回值。

  2. 字符串的内存管理,特别是高级库(不是libc)。如果字符串由函数返回或传递给函数,谁负责释放字符串?

  3. 什么时候应该使用“const char *”和何时使用“char *”。如果函数返回“const char *”,它会告诉我什么。

  4. 所有这些问题都不难学,但很难弄清楚你是否接受过教育。

答案 8 :(得分:1)

我发现char buff[0]技术非常有用。 考虑:

struct foo {
   int x;
   char * payload;
};

VS

struct foo {
   int x;
   char payload[0];
};

请参阅https://stackoverflow.com/questions/295027

请参阅链接了解含义和变化

答案 9 :(得分:1)

我会讨论何时何时不使用strcpystrncpy以及可能出现的问题:

char *strncpy(char* destination, const char* source, size_t n);

char *strcpy(char* destination, const char* source );

我还会提到ansi C stdlib字符串函数的返回值。例如,询问“if if if statement pass or failed?”

if (stricmp("StrInG 1", "string 1")==0)
{
    .
    .
    .
}

答案 10 :(得分:1)

也许您可以通过以下示例

来说明sentinel'\ 0'的值

char * a =“hello \ 0 world”; char b [100]; 的strcpy(B,A); 的printf(B);

在我的热情中,我曾经用手指烧过我使用strcpy()来复制二进制数据。它大部分时间都有效,但有时会神秘失败。当我意识到二进制输入有时包含零字节并且strcpy()将在那里终止时,揭开了神秘感。

答案 11 :(得分:0)

您可以提及索引寻址。

元素地址是基地址+索引* sizeof元素

答案 12 :(得分:0)

常见错误是:

char *p;
snprintf(p, 3, "%d", 42);

直到你使用最多sizeof(p)字节才有效..然后有趣的事情发生了(欢迎来到丛林)。

<强>释

使用char * p,您将在堆栈上分配用于保存指针(sizeof(void*)字节)的空间。这里正确的是分配一个缓冲区或只是在编译时指定指针的大小:

char buf[12];
char *p = buf;
snprintf(p, sizeof(buf), "%d", 42); 

答案 13 :(得分:0)

我指出了过度依赖内置字符串函数的性能缺陷。

char* triple(char* source)
{
   int n=strlen(source);
   char* dest=malloc(n*3+1);
   strcpy(dest,src);
   strcat(dest,src);
   strcat(dest,src);
   return dest;
 }

答案 14 :(得分:0)

指针和数组虽然具有相似的语法,但并不完全相同。给出:

char p [100]; char * p = a;

对于数组a,没有指针存储在任何地方。 sizeof(a)!= sizeof(p),对于数组,它是内存块的大小,对于指针,它是指针的大小。如果您使用类似:sizeof(a)/ sizeof(a [0]),这一点就变得很重要。此外,你不能++ a,你可以使指针成为'const'字符的'const'指针,但是数组只能是'const'字符,在这种情况下你首先要初始化它。等等等

答案 15 :(得分:0)

如果可能,请使用strlcpy(而不是strncpy)和strlcat。

更好的是,为了让生活更安全,你可以使用如下的宏:

#define strlcpy_sz(dst, src) (strlcpy(dst, src, sizeof(dst)))