这是C的一个好的子系统吗?

另请参见C Tokenizer


这是我写的C的快速substr()(是的,变量初始化需要移动到函数的开始等,但你明白了)

我已经看到很多substr()的“智能”实现,简单的一行调用strncpy()!

它们都是错的(strncpy不保证空终止,因此调用可能不会产生正确的子字符串!)

这可能更好吗?

带出虫子!

char* substr(const char* text, int nStartingPos, int nRun) { char* emptyString = strdup(""); /* C'mon! This cannot fail */ if(text == NULL) return emptyString; int textLen = strlen(text); --nStartingPos; if((nStartingPos < 0) || (nRun <= 0) || (textLen == 0) || (textLen < nStartingPos)) return emptyString; char* returnString = (char *)calloc((1 + nRun), sizeof(char)); if(returnString == NULL) return emptyString; strncat(returnString, (nStartingPos + text), nRun); /* We do not need emptyString anymore from this point onwards */ free(emptyString); emptyString = NULL; return returnString; } int main() { const char *text = "-2--4--6-7-8-9-10-11-"; char *p = substr(text, -1, 2); printf("[*]'%s' (\")\n", ((p == NULL) ? "" : p)); free(p); p = substr(text, 1, 2); printf("[*]'%s' (-2)\n", ((p == NULL) ? "" : p)); free(p); p = substr(text, 3, 2); printf("[*]'%s' (--)\n", ((p == NULL) ? "" : p)); free(p); p = substr(text, 16, 2); printf("[*]'%s' (10)\n", ((p == NULL) ? "" : p)); free(p); p = substr(text, 16, 20); printf("[*]'%s' (10-11-)\n", ((p == NULL) ? "" : p)); free(p); p = substr(text, 100, 2); printf("[*]'%s' (\")\n", ((p == NULL) ? "" : p)); free(p); p = substr(text, 1, 0); printf("[*]'%s' (\")\n", ((p == NULL) ? "" : p)); free(p); return 0; } 

输出:

 [*]'' (") [*]'-2' (-2) [*]'--' (--) [*]'10' (10) [*]'10-11-' (10-11-) [*]'' (") [*]'' (") 

如果输入无效而不是malloc() ed空字符串,我会说返回NULL 。 这样你可以用if(p)而不是if(*p == 0)来测试函数是否失败。

另外,我认为你的函数泄漏了内存,因为emptyString只在一个条件中是free() d。 你应该确保你无条件地free()return之前free()它。

至于你对strncpy()注释不是NUL-终止字符串(这是真的),如果你使用calloc()来分配字符串而不是malloc() ,如果你分配一个字节超过一个字节就不会有问题你复制,因为calloc()自动将所有值(在这种情况下,包括结尾)设置为0。

我会给你更多的笔记,但我讨厌阅读camelCase代码。 并不是说它有什么问题。

编辑:关于您的更新:

请注意,无论您的系统如何,C标准都将sizeof(char)定义为1。 如果你使用的是一个字节中使用9位的计算机(上帝保佑), sizeof(char)仍然是1.不是说sizeof(char)有什么问题 – 它清楚地显示了你的意图并提供了对称性调用calloc()malloc()用于其他类型。 但是sizeof(int)实际上是有用的( int可以是16-和32-以及这些新奇的64位计算机上的不同大小)。 你知道的越多。

我还想重申,与大多数其他C代码的一致性是在错误而不是""上返回NULL 。 我知道很多函数(比如strcmp() )如果把它们传递给NULL就可能会做坏事 – 这是可以预料的。 但是C标准库(以及许多其他C API)采用的方式是“调用者负责检查NULL ,而不是函数负责给他/她做好准备,如果他没有。” 如果你想以另一种方式做到这一点,那很酷,但这与C界面设计中更强烈的趋势相反。

另外,我会使用strncpy() (或memcpy() )而不是strncat() 。 使用strncat() (和strcat() )会模糊你的意图 – 它会让你看到代码的人认为你想要添加到字符串的末尾(你这样做,因为在calloc() ,结束就是开头),当你想要做的是设置字符串。 strncat()使得它看起来像是在添加一个字符串,而strcpy() (或另一个复制例程)会使它看起来更像你的意图。 在这种情况下,以下三行都做同样的事情 – 选择你认为最好的那一行:

 strncat(returnString, text + nStartingPos, nRun); strncpy(returnString, text + nStartingPos, nRun); memcpy(returnString, text + nStartingPos, nRun); 

另外, strncpy()memcpy()可能比strncat()更快/更有效。

text + nStartingPosnStartingPos + text相同 – 我会把char *放在第一位,因为我觉得它更清楚,但无论你想把它们放在哪个顺序取决于你。 另外,它们周围的括号是不必要的(但很好),因为+优先级高于,

编辑2:三行代码不做同样的事情,但在这种情况下,它们都会产生相同的结果。 谢谢你抓住我。

对于应该是简单的操作,你的function似乎非常复杂。 有些问题(并非所有这些都是错误):

  • strdup()和其他内存分配函数可能会失败,您应该考虑所有可能的问题。
  • 只在需要时分配资源(在这种情况下是内存)。
  • 你应该能够区分错误和有效的叮咬。 目前,您不知道substr ("xxx",1,1)或工作子substr ("xxx",1,0) malloc()故障是否产生空字符串。
  • 你不需要calloc()内存,无论如何你都要覆盖它。
  • 所有无效参数都应该导致错误或被强制转换为有效参数(并且您的API应该记录哪些参数)。
  • 在释放它之后你不需要将本地emptyString设置为NULL – 它将在函数返回时丢失。
  • 你不需要使用strncat() – 在进行任何复制之前你应该知道你可用的大小和内存,这样你就可以使用(最有可能)更快的memcpy()
  • 你使用base-1而不是base-0来克服C的粒度。

下面的部分就是我要做的事情(我更喜欢从字符串末尾算起的负值的Python习语,但我保持长度而不是结束位置)。

 char *substr (const char *inpStr, int startPos, int strLen) { /* Cannot do anything with NULL. */ if (inpStr == NULL) return NULL; /* All negative positions to go from end, and cannot start before start of string, force to start. */ if (startPos < 0) startPos = strlen (inpStr) + startPos; if (startPos < 0) startPos = 0; /* Force negative lengths to zero and cannot start after end of string, force to end. */ if (strLen < 0) strLen = 0; if (startPos >strlen (inpStr)) startPos = strlen (inpStr); /* Adjust length if source string too short. */ if (strLen > strlen (&inpStr[startPos])) strLen = strlen (&inpStr[startPos]); /* Get long enough string from heap, return NULL if no go. */ if ((buff = malloc (strLen + 1)) == NULL) return NULL; /* Transfer string section and return it. */ memcpy (buff, &(inpStr[startPos]), strLen); buff[strLen] = '\0'; return buff; } 
 char* emptyString = strdup(""); /* C'mon! This cannot fail? */ 

你需要检查null。 请记住,它仍然必须为空字符分配1个字节。

strdup可能会失败(虽然它不太可能,不值得检查,恕我直言)。 但确实有另一个问题 – 它不是标准Cfunction。 使用malloc会更好。

您还可以使用memmove函数从开始到长度返回子字符串。 改进/添加paxdiablo解决方案的另一个解决方案:

  #include  #include  #include  char *splitstr(char *idata, int start, int slen) { char ret[150]; if(slen == NULL) { slen=strlen(idata)-start; } memmove (ret,idata+start,slen); return ret; } /* Usage: char ostr[]="Hello World!"; char *ores=splitstr(ostr, 0, 5); Outputs: Hello */ 

希望能帮助到你。 使用TCC C Compilier在Windows 7 Home Premium上测试。