为什么 char 数组在 C 中为空
why char array is empty in C
我的代码如下:
char* int2str(int val);
void main(){
char *s = int2str(1001);
printf("----s=%s\n",s);
}
char* int2str(int val){
char turnStr[10];
sprintf(turnStr, "%d", val);
//printf("turnStr=%s\n",turnStr);
return turnStr;
}
上面的代码打印出空字符串,但是当我取消注释行时:printf("turnStr=%s\n",turnStr)
它能够打印出正确的字符串。
我知道函数结束时堆栈space不能return,但我很困惑当我添加printf("turnStr=%s\n",turnStr)
时,它可以打印出字符串
您正在返回对局部变量 char turnStr[10]
的引用。当函数退出时,该引用使用的内存将被清除。所以在 main()
你留下了一个悬空指针:char *s
指向不再有效的内存。
char
数组存储在int2str
函数的栈帧中。这意味着当函数仍然是 运行 时,堆栈上的内存是稳定可用的。这就是为什么您可以打印出字符串的原因。但是,一旦您从该函数 return 中退出,就无法保证内存会得到维护并且可以像您看到的那样被清除或重新使用。
返回对本地对象的引用(指针)是未定义的行为。许多现代编译器发出警告并为该指针分配 NULL - 例如 gcc。这段代码的另一个问题是另一个 UB。您的 char 数组不够长,无法容纳字符串
如何整理(举例):
char* int2str(int val);
void main(){
char *s = int2str(1001);
printf("----s=%s\n",s);
}
char* int2str(int val){
static char turnStr[20];
sprintf(turnStr, "%d", val);
//printf("turnStr=%s\n",turnStr);
return turnStr;
}
对于根据 C 标准的初学者,不带参数的函数 main
应声明为
int main( void )
即它的 return 类型应该是 int
.
您的程序有未定义的行为,因为函数 int2str
的 returned 指针指向一个具有自动存储持续时间的本地数组,该数组在退出该函数后将不存在。因此指针将具有无效值。本地数组占用的内存可以被任何其他函数的调用覆盖(例如通过调用 main 中的 printf
)。
所以你必须为目标字符串动态分配内存。在函数中使用具有静态存储持续时间的本地数组不是一个好主意,因为多次调用该函数将导致先前的结果字符串将被覆盖。
注意,例如 INT_MIN
的值(函数的用户可以传递任何有效的整数值)可以等于 -2147483648
,这需要 [=19 的字符数组=] 元素来存储表示这样一个数字的字符串。
要计算所需的字符串大小,您可以调用 C 函数 snprintf
,第二个参数等于 0
.
这是一个演示程序。
#include <stdio.h>
#include <stdlib.h>
char * int2str( int x )
{
int n = snprintf( NULL, 0, "%d", x );
char *s = malloc( n + 1 );
if ( s )
{
snprintf( s, n + 1, "%d", x );
}
return s;
}
int main(void)
{
char *s = int2str( 1001 );
if ( s ) puts( s );
free( s );
return 0;
}
它的输出是
1001
精彩!
基本问题是您 return 编辑了堆栈上某物的地址,它被其他东西改变了。我尝试了最近的 gcc,它甚至没有 return 堆栈指针,所以我尝试了 gcc 4.4.5 并重现了你的行为。
我尝试将 main 更改为:
void main(){
char *s = int2str(1001);
printf("----s=%s\n",s);
s = int2str(1002);
printf("----s=%s\n",s);
}
第二个 printf() 输出 1002。
我认为发生的事情是 printf 有一些局部变量放置在与数组相同的位置,如果您之前调用过 printf(),则不会使用这些局部变量。
请注意,它不是打印为空,而是打印为垃圾。该垃圾可能以 NUL 开头,也可能不是。
无论如何,其他人都说你不应该这样做是对的。有多种解决方案,包括:
- 动态内存分配(这意味着您需要释放它)
- 传入缓冲区(添加参数...你应该传入长度)
- 使用静态缓冲区(线程化或多次使用有问题)
- return按包含文本的值构建结构(可以复制超过应有的数量,这可能会导致性能问题,并且您必须在调用者中保存结构)
- 完全取消此功能(这可能不是一个好的解决方案,具体取决于您在做什么)
我的代码如下:
char* int2str(int val);
void main(){
char *s = int2str(1001);
printf("----s=%s\n",s);
}
char* int2str(int val){
char turnStr[10];
sprintf(turnStr, "%d", val);
//printf("turnStr=%s\n",turnStr);
return turnStr;
}
上面的代码打印出空字符串,但是当我取消注释行时:printf("turnStr=%s\n",turnStr)
它能够打印出正确的字符串。
我知道函数结束时堆栈space不能return,但我很困惑当我添加printf("turnStr=%s\n",turnStr)
时,它可以打印出字符串
您正在返回对局部变量 char turnStr[10]
的引用。当函数退出时,该引用使用的内存将被清除。所以在 main()
你留下了一个悬空指针:char *s
指向不再有效的内存。
char
数组存储在int2str
函数的栈帧中。这意味着当函数仍然是 运行 时,堆栈上的内存是稳定可用的。这就是为什么您可以打印出字符串的原因。但是,一旦您从该函数 return 中退出,就无法保证内存会得到维护并且可以像您看到的那样被清除或重新使用。
返回对本地对象的引用(指针)是未定义的行为。许多现代编译器发出警告并为该指针分配 NULL - 例如 gcc。这段代码的另一个问题是另一个 UB。您的 char 数组不够长,无法容纳字符串
如何整理(举例):
char* int2str(int val);
void main(){
char *s = int2str(1001);
printf("----s=%s\n",s);
}
char* int2str(int val){
static char turnStr[20];
sprintf(turnStr, "%d", val);
//printf("turnStr=%s\n",turnStr);
return turnStr;
}
对于根据 C 标准的初学者,不带参数的函数 main
应声明为
int main( void )
即它的 return 类型应该是 int
.
您的程序有未定义的行为,因为函数 int2str
的 returned 指针指向一个具有自动存储持续时间的本地数组,该数组在退出该函数后将不存在。因此指针将具有无效值。本地数组占用的内存可以被任何其他函数的调用覆盖(例如通过调用 main 中的 printf
)。
所以你必须为目标字符串动态分配内存。在函数中使用具有静态存储持续时间的本地数组不是一个好主意,因为多次调用该函数将导致先前的结果字符串将被覆盖。
注意,例如 INT_MIN
的值(函数的用户可以传递任何有效的整数值)可以等于 -2147483648
,这需要 [=19 的字符数组=] 元素来存储表示这样一个数字的字符串。
要计算所需的字符串大小,您可以调用 C 函数 snprintf
,第二个参数等于 0
.
这是一个演示程序。
#include <stdio.h>
#include <stdlib.h>
char * int2str( int x )
{
int n = snprintf( NULL, 0, "%d", x );
char *s = malloc( n + 1 );
if ( s )
{
snprintf( s, n + 1, "%d", x );
}
return s;
}
int main(void)
{
char *s = int2str( 1001 );
if ( s ) puts( s );
free( s );
return 0;
}
它的输出是
1001
精彩!
基本问题是您 return 编辑了堆栈上某物的地址,它被其他东西改变了。我尝试了最近的 gcc,它甚至没有 return 堆栈指针,所以我尝试了 gcc 4.4.5 并重现了你的行为。
我尝试将 main 更改为:
void main(){
char *s = int2str(1001);
printf("----s=%s\n",s);
s = int2str(1002);
printf("----s=%s\n",s);
}
第二个 printf() 输出 1002。
我认为发生的事情是 printf 有一些局部变量放置在与数组相同的位置,如果您之前调用过 printf(),则不会使用这些局部变量。
请注意,它不是打印为空,而是打印为垃圾。该垃圾可能以 NUL 开头,也可能不是。
无论如何,其他人都说你不应该这样做是对的。有多种解决方案,包括:
- 动态内存分配(这意味着您需要释放它)
- 传入缓冲区(添加参数...你应该传入长度)
- 使用静态缓冲区(线程化或多次使用有问题)
- return按包含文本的值构建结构(可以复制超过应有的数量,这可能会导致性能问题,并且您必须在调用者中保存结构)
- 完全取消此功能(这可能不是一个好的解决方案,具体取决于您在做什么)