C函数中返回字符数组
原文 C函数中返回字符数组,CSDN同步发布。
转载请注明出处,谢谢!
强力推荐-不要错过,万一能帮助到自己呢?
朋友做了一个关于 人工智能的教程,教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!点 这里 可以跳转到教程,对人工智能感兴趣的同学可以了解一下。
内容简介
在阅读本篇文章之前,建议大家看一下下面2篇文章:
- 不见得你会计算C字符串长度
- 双宿双飞的 malloc 和 free
这篇文章主要分享三个点:
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*/
#undef strcpy
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>#undef strcpy#ifndef STRCPY
# define STRCPY 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编译错误如下:
Array type 'char [14]' is not assignable
可以考虑使用 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
}
时间可以改变一切,但你总得做点什么!
C函数中返回字符数组相关推荐
- C 如何在函数中返回一个数组
int test(int *b) {int source[5]={[1]=2,[3]=4,[4]=-2}; memcpy(b,source,5*sizeof(int));return 0; }int ...
- 在qt中用c语言数组,在QT函数中返回一个数组/把一个数组传参给函数
1.把数组传参给函数 可以定义一个QVector的一个数组 QVector num(10); for(int i =0;i<10;i++) num [i] = i*i; fun(num); / ...
- 函数中参数有数组时注意的小问题(不一定要传递数组长度,不用返回数组,可以在函数中改变数组元素值)
函数中参数有数组时注意的小问题: 1.不一定要传递数组长度 2.不用返回数组,可以在函数中改变数组元素值 通过下面这个小例子来验证: #include<iostream> #include ...
- c 语言 函数返回数组_如何在C ++函数中返回数组
c 语言 函数返回数组 介绍 (Introduction) In this tutorial, we are going to understand how we can return an arra ...
- C语言试题三十五之找出一维整型数组元素中最大的值和它所在的下标,最大的值和它所在的下标通过形参传回。主函数中x是数组名,n 是x中的数据个数,max存放最大值,index存放最大值所在元素的下标。
1. 题目 请编写一个函数function,它的功能是:找出一维整型数组元素中最大的值和它所在的下标,最大的值和它所在的下标通过形参传回.主函数中x是数组名,n 是x中的数据个数,max存放最大值,i ...
- bash shell函数中返回任意值的四种方法
From: http://www.jbxue.com/article/11322.html 本文介绍下,在bash shell编程中,从函数中返回任意值的几种方法,有需要的朋友参考下. 在bash中, ...
- C语言中关于字符数组输入,scanf没执行
问题:有时C语言中关于字符数组输入,有的scanf语句没执行. 未执行scanf的代码: #include<stdio.h> int main() {int n;int t=0;scanf ...
- 《C++面向对象高效编程(第2版)》——3.16 从函数中返回引用
本节书摘来自异步社区出版社<C++面向对象高效编程(第2版)>一书中的第3章,第3.16节,作者: [美]Kayshav Dattatri,更多章节内容可以访问云栖社区"异步社区 ...
- C语言学习笔记---结构体中的字符数组和字符指针
在结构体中可以使用字符数组来存储字符串,也可以使用字符指针来存储字符串.比如: struct str{char s1[5];char s2[5];};struct str str1= {" ...
最新文章
- PS5 发布,揭秘真假难辨的虚拟和现实
- java 不知道类名_Java 中获取类名的三种方法,你知道几种?
- spring基于注解程序开发
- 华为机试题1:计算字符串最后一个单词的长度,单词以空格隔开。
- 20135310陈巧然 20135305姚歌 实验一:开发环境的熟悉
- SqlDateTime 溢出。必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59
- 7大最重要的管理方法
- 简单的01背包和完全背包
- python假设有一段英文、其中有单独的字母_峰终定律是诺贝尔奖获得者丹尼尔卡尼曼提出的。他发现,我们对一件事的整体评价,是全过程中所有元素体验的平均分。...
- python getattr用法_python3,定制类,getattr相关用法
- 下载远程url文件(或者文件流)到本地
- docker具名和匿名挂载
- 怎么是phpadmin连接mysql_phpmyadmin连接远程mysql
- javascript高级程序设计pdf(JavaScript高级程序设计PDF版)
- 【PCIe】【翻译】AER 内核驱动 《pcieaer-howto》PCI Express高级错误报告驱动程序指南
- 用C++可编程管线渲染obj模型
- python喜马拉雅_Python爬虫实战案例:取喜马拉雅音频数据详解!
- uniapp 实现微信小程序全局分享及自定义分享按钮样式
- facebook php-webdriver 安装与使用
- #35-【刷题】乐乐的方块
热门文章
- 不是java的原始数据类型6_以下哪个不是 Java 的原始数据类型_________。_中药鉴别技术答案_学小易找答案...
- 怎么设置启用远程桌面?如何让外网电脑远程本地内网?
- 王者荣耀微信一区的服务器没了,王者荣耀微信区不出现保存回放 | 手游网游页游攻略大全...
- UVA11400 简单DP
- telegram如何扫码加人
- 淘宝商品销量接口/淘宝商品销量监控接口/商品累计销量接口代码对接分享
- 转载 PCIe学习(三):PCIe DMA关键模块分析之二
- unity shader学习---光照模型(二)
- [siggraph17]decima engine的光照和aa技术
- 组策略 gpedit.msc 及修复“无法为文件 appv.admx (*.admx)找到适当的资源文件(错误=2)”报错