c函数中返回字符串数组、char[]和char*的区别与转换(详细)

  • Post author:
  • Post category:其他




一、字符指针可以返回,而作为局部变量的字符数组不能直接返回的原因


更多详见参考博客:

c函数中返回字符串数组

直接放一段代码比较:

char * fork_user_name()
{
    char name[] = "veryitman";//!警告
    //!不应该返回一个局部变量 name 的地址(函数内部的变量在栈内存上)。
    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;
}

结果:

fork_user_name: (null)
fork_user_name2: veryitman

结论:

在函数中的局部变量只要是返回类似 int[]、char[]、long[] 地址的,都是不正确的做法。



二、如何返回局部变量的字符数组



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、返回形参指针变量

char * v_string(char *s1, char *s2)
{
    char *tmp = s1;
    // 省略...
    return tmp;
}



三、字符数组与字符指针的区别


此段转自:


https://blog.csdn.net/u012611878/article/details/78291036



相同点

首先 这两种类型都可以对应一个字符串,比如:

char * a=”string1”;
char b[]=”string2”;
printf(“a=%s, b=%s”, a, b);

其中a是一个

指向char变量的指针

,b则是一个

char数组

(字符数组),

其次 ,很多时候

二者可以混用

,像函数传参数的时候,实参可以是char*,形参可以是 char[],比如:

void fun1(char b[]){
printf("%s",b);
}
int main(){
char *a="HellowWorld";
fun1(a);
}

反过来,实参可以是char[],形参可以是 char *也是可以的。

存在即合理,char *和char[]肯定是有本质的不同。



不同点

1.

char*是变量,值可以改变, char[]是常量,值不能改变。


比如:

char * a="string1";
char b[]="string2";
a=b; //OK
a="string3"; //OK
b=a; //报错!左边操作数只读
b="string3"; //报错!左边操作数只读

解释: a是一个char型指针变量,其值(指向)可以改变; b是一个char型数组的名字,也是该数组首元素的地址,是常量,其值不可以改变 。

2.

char[]对应的内存区域总是可写,char*指向的区域有时可写,有时只读


比如:

char * a="string1";
char b[]="string2";
gets(a); //试图将读入的字符串保存到a指向的区域,运行崩溃!
gets(b) //OK

解释: a指向的是一个字符串常量,即指向的内存区域只读; b始终指向他所代表的

数组在内存中的位置,始终可写

注意,若改成这样gets(a)就合法了:

char * a="string1";
char b[]="string2";
a=b; //a,b指向同一个区域,注意这里改变了a的指向
gets(a) //OK
printf("%s",b) //会出现gets(a)时输入的结果

解释: a的值变成了是字符数组首地址,即&b[0],该地址指向的区域是char *或者说

char[8],习惯上称该类型为字符数组,其实也可以称之为“字符串变量”,区域可读可写。

注意:char *本身是一个

字符指针变量

,但是它既可以指向字符串常量,又可以指向字符串变量,

指向的类型

决定了对应的字符串能不能改变。

3.char * 和char[]的初始化操作有着根本区别:

char *a="Hello World";
char b[]="Hello World";
printf("%s, %d\n","Hello World", "Hello World");
printf("%s, %d %d\n", a, a, &a);
printf("%s, %d %d\n", b, b, &b);

在这里插入图片描述

结果可见:尽管都对应了相同的字符串,但”Hellow World”的地址 和 a对应的地址相同,与b指向的地址有较大差异;&a 、&b都是在同一内存区域,且&b

b

根据c内存区域划分知识,我们知道,局部变量都创建在栈区,而常量都创建在文字常量区,显然,a、b都是栈区的变量,但是a指向了常量(字符串常量),b则指向了变量(字符数组),指向了自己(&b

b==&b[0])。

说明以下问题:

  • char * a=”string1”;是实现了3个操作:

1声明一个char*变量(也就是声明了一个指向char的指针变量)。

2在内存中的文字常量区中开辟了一个空间存储字符串常量”string1”。 3返回这个区域的地址,作为值,赋给这个字符指针变量a

最终的结果:指针变量a指向了这一个字符串常量“string1” (注意,如果这时候我们再执行:char *

c=”string1”;则,c==a,实际上,只会执行上述步骤的1和3,因为这个常量已经在内存中创建)

  • char b[]=”string2”;则是实现了2个操作:

1声明一个char 的数组, 2为该数组“赋值”,即将”string2”的每一个字符分别赋值给数组的每一个元素,存储在栈上。

最终的结果:“数组的值”(注意不是b的值)等于”string2”,而不是b指向一个字符串常量

PS:

实际上, char * a=”string1”; 的写法是不规范的!

因为a指向了即字符常量,一旦strcpy(a,”string2”)就糟糕了,试图向只读的内存区域写入,程序会崩溃的!尽管VS下的编译器不会警告,但如果你使用了语法严谨的Linux下的C编译器GCC,或者在windows下使用MinGW编译器就会得到警告。

所以,我们还是应当按照”类型相同赋值”的原则来写代码:


const char * a=”string1”;

保证意外赋值语句不会通过编译。



小结

对于

const char * a="string1"
char b[]="string2";

1.a是const char 类型, b是char const类型 ( 或者理解为 (const char)xx 和 char (const xx) )

2.a是一个

指针变量

,a的值(指向)是

可以改变

的,但a只能指向(字符串)常量,指向的区域的内容不可改变;

3.b是一个

指针常量

,b的值(指向)

不能变

;但b指向的目标(数组

b在内存中的区域)的内容是可变

4.作为函数的声明的参数的时候,char []是被当做char *来处理的!两种

形参声明写法完全等效



四、字符数组和字符指针的转换



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[]

不能直接向上面那样转,可

使用strnpy

#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));
    //strncpy把源字符串的字符复制到目标数组。然而,它总是正好向dst写入len个字符。如果strlen(src)的值小于len,dst数组就用额外的NUL字节填充到len长度,如果strlen(src)的值大于或等于len,那么只有len个字符被复制到dst中
    printf("c_str_array: %s\n", c_str_array);
    return 0
}

PS:strncpy并没有拷贝串后的\0字符,而strcpy却拷贝了。这充分说明,strncpy是为拷贝字符而生的,而strcpy是拷贝字符串而生的。但两者都不能越界拷贝。

strnpy详解见:

深入理解strncpy这个函数



别再耍流氓了: 请别再用strcpy, 而用strncpy



版权声明:本文为weixin_43629813原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。