我的示例代码取自Split string with delimiters in C
我发现如果我注释掉#include <string.h>
程序,那么当我尝试运行它时会崩溃。为什么会崩溃?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char** str_split(char* a_str, const char a_delim)
{
char** result = 0;
size_t count = 0;
char* tmp = a_str;
char* last_comma = 0;
char delim[2];
delim[0] = a_delim;
delim[1] = 0;
/* Count how many elements will be extracted. */
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_comma = tmp;
}
tmp++;
}
/* Add space for trailing token. */
count += last_comma < (a_str + strlen(a_str) - 1);
/* Add space for terminating null string so caller
knows where the list of returned strings ends. */
count++;
result = malloc(sizeof(char*) * count);
if (result)
{
size_t idx = 0;
char* token = strtok(a_str, delim);
while (token)
{
assert(idx < count);
*(result + idx++) = strdup(token);
token = strtok(0, delim);
}
assert(idx == count - 1);
*(result + idx) = 0;
}
return result;
}
int main()
{
char months[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char** tokens;
printf("months=[%s]\n\n", months);
tokens = str_split(months, ',');
if (tokens)
{
int i;
for (i = 0; *(tokens + i); i++)
{
printf("month=[%s]\n", *(tokens + i));
free(*(tokens + i));
}
printf("\n");
free(tokens);
}
return 0;
}
答案 0 :(得分:2)
如果代码不包含<string.h>
,则<string.h>
中原型化的程序使用的所有函数的返回类型将被假定为int
,即32位。
如果在64位系统上,这使得函数返回指针(64位)很可能会失败。这通常是strdup()
的情况,它只返回引用内存的64位地址的32位来保存重复的“字符串”。
我明确暗示上述情况会是这样的警告
warning: implicit declaration of function ‘strdup’
C编译器发出的警告意味着它是一个警告。这样对待它。
答案 1 :(得分:0)
<string.h>
标头包含C标准指定的标准字符串函数的声明(可能,根据您的设置,还有一些其他非标准函数,如strdup()
,由POSIX定义但不是由C)。
从1990 ISO C标准开始,允许调用没有可见声明的函数,但编译器会对如何定义该函数做出某些假设;特别是,它会假设它返回int
个结果。如果它实际上没有这样定义,则调用具有未定义的行为。在某些系统上,调用一个返回char*
的函数,就像它返回一个int
一样,将显示工作;在其他方面,它可能以各种可怕的方式失败(这实际上更好,因为它使查找和纠正错误更容易)。
一种可能的情况是类型int
和char*
具有不同的大小(例如,它们在64位系统上通常分别为32位和64位)。或者它们可能具有相同的大小,但可以从不同寄存器中的函数返回。
从1999年标准开始,调用没有可见声明的函数是非法的(约束违反); “隐式int
”规则被删除。
这意味着任何符合C99或更高版本标准的编译器必须发出诊断消息。在某些情况下,该诊断可能是一个警告,然后编译器可以继续根据C90规则编译您的程序 - 或者根据编译器开发人员碰巧选择的任何规则。
如果你没有(直接或间接地)包含strlen()
,你的编译器至少不会警告你对<stdio.h>
的调用,你应该使用编译器支持的任何选项来增加它警告级别。
在某些情况下,您可以忽略警告,但最安全的做法是将编译器警告视为致命错误。