C语言 - char *和char []的区别
1、声明不同
1.1 如何声明一个char*字符串
你可以这样:
char* str = "test"; //str是一个指针,存放在栈区,"test"是一个常量,存放在常量区,VS2017要求这句声明前面必须加上const,因为它所指向的常量字符串是不可更改的
还可以这样:
char* str = (char*)malloc(10 * sizeof(char));
strcpy(str, "qwewqe"); //对其进行赋值
printf("%s\n", str);
free(str);
还可以这样:
char* str =new char[20] { 'a' }; //直接将字符串内所有的元素都设为字符 'a'
delete str;
或者这样:
int main()
{
int a;
a = 10;
char * arr = new char[a];
cin.getline(arr, a);//最多读a个字符,如果提前遇到换行符,会立即停止
cout << arr << endl;
return 0;
}
1.2 如何声明一个char []字符串
你可以这样:
char cat[4] = {'T', 'O', 'M', '\0'}; //如果最后一个字符不是 '\0' ,那么cat就只是一个字符数组,而不是字符串了
delete [] cat;
还可以这样:
char cat[4] = "cat"; //注意字符数是3,而cat的大小是4
或者这样:
char cat[] = "cat"; //让编译器自动判断大小
计算字符串长度:
int main()
{
//使用sizeof统计字符串长度
char cat[] = "cathgdhgfd65y7u6";
int len = sizeof(cat) / sizeof(cat[0]);
printf("cat = %s len = %d\n", cat ,len);
//使用strlen统计字符串长度
char* str = "cathgdhgfd65y7u6";
int len2 = strlen(str)+1;
printf("str = %s len2 = %d\n", str, len2);
return 0;
}
[]内如果要限定大小,只能用const size_type,包括字面值。
2、概念不同
c语言中没有特定的字符串类型,常用以下两种方式定义字符串:1)字符数组;2)指向字符串的指针。
char *str声明的是一个指针,这个指针可以指向任何字符串常量。
char str[]声明的是一个字符数组,数组的内容可以是任何内容,严格意义上说,末尾加上'\0'之后才能算是字符串。
3、变量不同
char *str的str是指针变量,str的值未初始化(局部变量的话,全局则自动初始化为NULL)
char str[]的str是地址常量,str的值是str[]的地址
4、内存的分配方式不同
内存分配可分为三种:静态存储区、栈区、堆区
- 1、静态存储区:该内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,它主要存放静态数据、全局数据和常量。
- 2、栈区:它的用途是完成函数的调用。在执行函数时,函数内局部变量及函数参数的存储单元在栈上创建,函数调用结束时这些存储单元自动被释放。
- 3、堆区:程序在运行时使用库函数为变量申请内存,在变量使用结束后再调用库函数释放内存。动态内存的生存期是由我们决定的,如果我们不释放内存,就会导致内存泄露。
char []定义的是字符串数组,该字符数组保存在全局数据区或栈区,因此数组的内容是可以改变的:
char str[6] = {"hello"}; //虽然只初始化了5个元素,但由于编译器会自动在末尾加'\0',所以size为6
str[0] = 'H'; //合法
char *定义的是字符串指针变量,该指针变量指向一个字符串,该指针的值是该字符串在内存中的地址,所以可以修改指针的值,但不能修改指针指向的值:
char *str = {"hello"};
str[0] = 'H'; //非法
把字符串 h 改成 H,出现段错误,本质原因:*str="hello"存放在常量区,是无法修改的。而数组是存放在栈中,是可以修改的。
5、char *作为函数返回值时
指针作为返回值时会出现:在函数返回后,指针指向的内存单元被释放了,这样就会导致指针成了野指针。
//这样会报错,因为str数组元素为局部变量,存储在栈内,函数结束后,内容失效
char* func(char* name)
{
char str[5];
strcpy(str,name);
return str;
}
错误分析:
str为地址,返回值为char*,是进行了值传递,没有问题,但是,数组中的元素为局部变量,存储在栈中,函数外无效。
解决方法1:将数组定义为static
char* func(char *name)
{
static char str[5];
strcpy(str,name);
return str;
}
解决方法2:字符串为静态常量(存储在常量区)
char* func()
{
char *str= "hello";
//char str[]= "hello";
return str;
}
解决方法3:设置为动态数组
char* func(char* name)
{
char *str= (char*)malloc(5*sizeof(char));
strcpy(str,name);
return str;
}
解决方法4:设置为全局变量
char str[10];
char* func(char* name)
{
strcpy(str,name);
return str;
}
小结:通过以上方法,返回字符串,只要返回字符串首地址即可。
1)调用函数时,若直接输出的话,用一个字符指针接受即可:char *result = func();
2)调用函数时,若要获取返回的字符串:char result[5]; strcpy(result, func());
补充知识:
今天看到了gets()与puts()函数,发现了一个奇怪的点:字符串可以直接赋值给字符指针变量。例如以下:
char * p="EDS";
puts(p); //输出结果为EDS
学过指针让我清楚明白了指针变量是不能直接赋值的,而这里的字符指针却直接被赋值字符串。这让我深感疑惑了,经过查阅,才知道:
char * p="EDS"; 双引号在这里做了这三件事情:
- 1. 申请了空间(在常量区),存放了字符串
- 2. 在字符串尾加上了'/0'
- 3. 返回该字符串的首地址
先来看puts()这个函数
函数原型
int puts(const char *string);
参数
输入:字符串指针
输入可以是字符串数组,也可以是字符串常量,例如:
char a[15]="1234"; //字符串数组
char * p="EDS"; //字符串常量
p=a; //将a的首地址赋值给p,p指向了a字符串数组
puts(p); //输出结果为1234
puts()函数的输入是一个字符串指针。当使用一个字符串数组名作为输入时,数组名此时表示一个指向数组的指针值,这符合puts()的输入要求,并能够正确输出。但是,当输入为一个字符串常量时,函数仍然能够正常的输出字符串。
理由如下:
如上所述,双引号的加入,返回了字符串常量的地址值,即指针,这样才能够满足puts()函数对输入参数的要求,而这个指针应该指向了字符串常量实际所在的地址。
程序运行时,双引号分配了常量区空间,字符串常量将占用内存空间,这样才能保证puts()函数能通过指针找到要输出的数据。 既然字符串常量占用内存,那么应该就能够通过得到它的地址并输出。
另外看一下下面的这组区别,char a[10] = “hello”; 这是数组的初始化,和a[0] = ‘h’ a[1] = ‘e’…是一个道理,但是换成char a [10],然后a = “hello”就不行了 “hello”赋值的值是一个地址,而a虽然也有地址,但是这与指针是不一样的,指针的值是地址,而数组的值虽然也是地址,但是却是一个地址常量,所以不能给常量赋值。所以,可以把字符串赋值给指向字符指针变量p,而不能把字符串赋值给一个字符数组