内容简介:在阅读本篇文章之前,建议大家看一下下面2篇文章:这篇文章主要分享三个点:1、为什么作为局部变量的字符数组不能直接返回,而字符指针却可以?
在阅读本篇文章之前,建议大家看一下下面2篇文章:
这篇文章主要分享三个点:
1、为什么作为局部变量的字符数组不能直接返回,而字符指针却可以?
2、当字符数组是局部变量的时候,函数如何返回它?
3、字符数组(char [])和字符指针(char *)如何互转?
局部变量的字符数组
在C中如果我们直接返回字符数组,编译会直接报警告。如下示例:
char * fork_user_name() { char name[] = "veryitman"; return name; }
在Xcode中编译警告信息是这样的:
Address of stack memory associated with local variable 'name' returned
在 Linux 上面GCC编译显示警告是这样的:
warning: function returns address of local variable [-Wreturn-local-addr]
无论哪种警告信息,基本意思都是告诉我们不应该返回一个局部变量 name
的地址(函数内部的变量在栈内存上)。
如果我们修改一下代码,将 char
改为指针变量 char *
,示例如下:
char * fork_user_name2() { char *name = "veryitman"; return name; }
无论是Linux的GCC还是Xcode的Clang编译器都不会报出警告。
首先我们要知道,常量是放在数据段里面的。
这里比较特殊,局部变量 name
保存在栈中,但是字符串 veryitman
的值是一个常量,保存在常量区。即便函数返回了,数据段里面的常量数据也还不会消亡,它会直到程序结束才会消失,其内存空间直到程序运行结束才会被释放。 所以,返回的地址是一个实际存在的有效地址。
char * fork_user_name() { char name[] = "veryitman"; return name; } char * fork_user_name2() { char *name = "veryitman"; return name; } int main() { printf("fork_user_name: %s\n", fork_user_name()); printf("fork_user_name2: %s\n", fork_user_name2()); return 0; }
用GCC编译、运行后的打印结果,如下:
fork_user_name: (null) fork_user_name2: veryitman
总之,在函数中的局部变量只要是返回类似 int[]
、 char[]
、 long[]
地址的,都是不正确的做法。
一切皆有可能
下面例子是不正确的,如下:
char * v_string() { char rest[10] = {'\0'}; return rest; }
1、使用 static
在 C语言 中,用 static
限定外部变量与函数,该外部变量或者函数除了对该所在的文件可见外,其他文件都无法访问。 而用 static
声明内部变量,则该变量是某个特定函数的局部变量,只能在该函数中使用。但它与自动变量不同的是,不管其所在函数是否被调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。换句话说, static
类型的内部变量是一种只能在某个特定函数中使用但一直占据存储空间的变量。
所以使用static修饰一下,就没有问题了。示例如下:
char * v_string() { static char rest[10] = {'\0'}; return rest; }
2、使用 malloc
这种方式可以解决这个问题,是因为使用 malloc
分配的内存是在堆上而不是在栈内存上面。但是要记得将其在调用方使用 free
释放申请的内存空间,否则容易造成内存泄漏问题。
具体可以看看 双宿双飞的 malloc 和 free ) 这篇文章。
char * v_string() { char *p = (char *)malloc(10 * sizeof(char)); p = "\0"; return p; }
3、全局变量
这个很好理解。全局变量在程序真个生命周期中都是有效的,所以使用全局变量也可以解决类似问题。
但是这种方案就会让这个封装的方法不够内聚,因为它依赖了全局变量。
char g_rest[100]; char * v_string() { strcpy(g_rest, "verytiamn"); return g_rest; }
4、返回形参指针变量
在Linux Kernel(内核源码版本5.0.7)中,函数 strcpy
的实现如下:
#ifndef__HAVE_ARCH_STRCPY /** * strcpy - Copy a %NUL terminated string * @dest: Where to copy the string to * @src: Where to copy the string from */ #undefstrcpy char *strcpy(char *dest, const char *src) { char *tmp = dest; while ((*dest++ = *src++) != '\0') /* nothing */; return tmp; } EXPORT_SYMBOL(strcpy); #endif
参考内核实现,我们可以修改一下自己的代码,示例如下:
char * v_string(char *s1, char *s2) { char *tmp = s1; // 省略... return tmp; }
这里补充另外一个知识点,函数 strcpy
在glibc和Linux Kernel中实现不一样。
在glibc的新版中(2.29版本),本质是调用了函数 memcpy
, 实现如下:
#include<stddef.h> #include<string.h> #undefstrcpy #ifndefSTRCPY #defineSTRCPY strcpy #endif /* Copy SRC to DEST. */ char * STRCPY(char *dest, const char *src) { return memcpy (dest, src, strlen (src) + 1); } libc_hidden_builtin_def (strcpy)
包括 strncpy
在glibc和Linux Kernel中实现也不一样,有兴趣的可以去看看源码。
字符数组和字符指针的互转
字符数组转字符指针即 char [] 转 char *
这种情况下,可以直接进行赋值,示例如下:
int main() { char c_str_array[] = "veryitman.com"; char *p_str; p_str = c_str_array; printf("p_str: %s\n", p_str); return 0; }
字符指针转字符数组即 char * 转 char []
是不是也可以直接进行赋值呢?撸段代码看看,如下:
int main() { char c_str_array[] = "veryitman.com"; char *p_str = "veryitman.com"; c_str_array = p_str; printf("c_str_array: %s\n", c_str_array); return 0; }
很遗憾,编译报错,GCC编译错误截图如下:
Clang编译错误如下:
可以考虑使用 strncpy
来实现,示例代码如下:
#include<stdlib.h> #include<stdio.h> #include<string.h> int main() { char c_str_array[] = "veryitman.com"; char *p_str = "veryitman.com"; strncpy(c_str_array, p_str, strlen(p_str)); printf("c_str_array: %s\n", c_str_array); return 0 }
时间可以改变一切,但你得做点什么!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 函数式编程之数组的函数式编程
- 巧用 PHP 数组函数
- javascript数组常用函数与实战总结
- c# – 数组的GetUpperBound()和GetLowerBound()函数
- python – 如何从numpy数组中确定什么是概率分布函数?
- PHP代码篇(二)-- array_column函数将二维数组格式化成固定格式的一维数组,及优化查询方法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。