以下函数用法正确的个数是:
void test1(){ unsigned char array[MAX_CHAR+1],i; for(i = 0;i <= MAX_CHAR;i++){ array[i] = i; } } char*test2(){ char p[] = "hello world"; return p; } char *p = test2(); void test3(){ char str[10]; str++; *str = '0'; }
楼上说的都有道理,但是没解释出本质区别。 区别就在于两个定义中str的类型不同。 第一个,在 char * str; 中,明显str的类型是char*。这没什么特殊的,只是一个简单的char指针。 第二个,char str[];是不能这样定义的,所以假定用 char str[6] = "abcde"; 那么这里str的类型是char[6](6个,不是5个,因为最后有个0) ,而这种定义语句也是定义一个char[6]类型的唯一方法。char[6]是什么意思呢?他不是char*,也不是char* const (以下讨论这个),而是一个特殊的类。这个类的大小是6字节(比如sizeof(str)返回6),它可以用[ ]来返回一个char&(比如str[3]的类型是char&),但是自身不能直接被修改,比如str ="abcde";就不可以。 我刚才说char[6]很像char* const。这是因为,假如有 char* const str2 = "abcde"; 那么str和str基本有同样的行为,比如str2[3]也是一个char&,而且str2 = "abcde"也不可以用,但是char* const和char[6]有本质的不同。前者只是一个指针。什么意思呢,就是str2的值是"abcde"的地址(而"abcde"被存在其他的地方),而str的内存值直接就是6个字节,"abcde"。所以两者还是截然不同的类型。比如,sizeof(str2)是4(32位操作系统)或8(64位),而sizeof(str)是6. char[6]可以被直接转换为char*。转换的时候,得到的结果是一个指向第一个元素的指针。反过来,char*也可以转换为char[6],转换的结果是一个变量指向char*所指向的那个内存区域。 楼上说的str++可以在char* str时用而不能在char str[6]时用,这是因为数组类型 char[6]不能用作一个左值。 楼上还有说char * str可以指向任何字符串常量。这个是无必要的。char *可以指向任何内存区域,不一定必须是字符串常量。当然,如果指向一个无效内存,用的时候会出问题。 另外楼上说一个"Hello World!"放在常量储存区,一个"Hello World!"放在线程堆栈区,这也是不对的。事实上,无论如何,"Hello World!"本身都在常量储存区。而: char *str = "Hello World!"; 会在堆栈上储存一个4个字节(假定32位系统)的指针指向储存"Hello World!"的区域。 而 char str[] = "Hello World!"; 会首先在堆栈上先分配一个13字节的char数组,然后把常量储存区的那个"Hello World!"复制过来。
1.unsigned char
取值范围为0-255
,如果MAX_CHAR
大于255的话,i
大于255,会再次返回0,导致死循环
2.p是一个局部变量,是一个字符数组,局部变量在栈上,返回p的地址之后,p原来指向的字符数组,在栈上的内存已被释放,如果函数返回后再访问的话,会报访问非法地址的错误。
注意:这里区分
char* p = "hello world"; char p[] = "hello world";
二者的区别。
(1)
首先,"hello world"
是一个无名的字符串常量,也就是const char*
类型,常量在程序运行过程中,保存在数据段里面,其生命周期随着程序的结束而结束。而p是一个局部变量,保存在栈上,随着函数的返回而销毁。
(2)
char* test2() { char* p = "hello world"; return p; }
这里定义一个char*
类型的局部变量p,然后再将const char*
类型的字符串常量赋值给p,然后将p的值通过函数返回值返回。
再在main
函数中访问的时候,虽然局部变量p已经销毁了,但是p的值通过函数返回值传递了过来,并且其值是一个指针,所指向的区域是"hello world"
这个字符串常量的存储区域,不会随着局部函数的返回而销毁,因此可以访问。
(3)
char* test2() { char p[] = "hello world"; return p; }
此时char p[] = "hello world"
这句话的本质是一个字符数组的初始化操作。这里的局部变量p会开辟一个新的地址内存区域,来保存字符数组,然后再将该数组的首地址返回。这里虽然可以返回p的地址,但是局部变量p所指向的内存区域会随着函数的返回而销毁。因此在main
函数中即使得到了之前局部变量p的地址,其对应的字符数组的内存区域已经销毁,对其进行访问的话会报Segmentation fault (core dumped)
段错误,访问非法地址。
可以运行下面这个程序,在函数的局部作用域内,将地址打印出来就一目了然了
#include <stdio.h> char* test2() { char* p1 = "hello world"; char p2[] = "hello world"; printf("%x\n", "hello world");//845bc008 printf("%x\n", p1);//845bc008 printf("%x\n", p2);//6a2423bc return p1; // return p2;//返回p2会造成段错误 } int main(int argc, char *argv[]) { char* p = test2(); printf("%s\n", p); }
3.数组名str
是一个指针常量,不能作为左值,以及进行自增自减操作。