字符数组和字符指针
字符数组名可以作为左值吗?当然不行
比如
char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'};
str++;
不可以这么干,因为字符数组名是一个常量指针,也就是是一个const char*
#include <stdio.h> int main() { char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'}; printf("sizeof(str): %d\n",sizeof(str)); printf("str: %s\n",str); str = str + 1; //error return 0; }
运行结果如下:
当数组名为左值时,它的类型是字符数组;当数组名为右值时,它的数据类型是字符指针。
#include <stdio.h> #include <string.h> int main(void) { char buf[10]; char *p= “afdal”; buf = p; //将一个char* 赋给一个char[10],类型不一样,必然不成功 printf(“the buf is:%s\n”,buf); return 0; }
问题2:
字符数组如何进行初始化?
#include <stdio.h> int main() { char ptr[20] = "hello world"; //success char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'}; //success char ctr[20]; ctr = "hello world"; // error: incompatible types when assigning to type ‘char[20]’ from type ‘char *’ return 0; }
在给字符数组初始化的时候,会自动在字符数组的结尾加上'\0'
#include <stdio.h> int main() { char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'}; printf("sizeof(str): %d\n",sizeof(str)); printf("str: %s\n",str); int i = 0; while(*(str + i) != '\0') //判断是否加上'\0' { printf("%c\n",*( str + i++)); } return 0; }
运行结果如下:
问题3:
字符数组越界访问能编译通过吗?
字符数组越界访问编译可以通过,没有报错,这样会出现很多的问题
#include <stdio.h> int main() { char str[12] = {'h','e','l','l','o',' ','w','o','r','l','d'}; printf("%c\n",str[100]); return 0; }
打印为空
问题4:
字符数组和字符指针又有什么区别呢?
首先在内存的中位置不同,字符数组保存的字符串存放在内存的栈中,而字符指针指向的字符串保存在内存的静态存储区中。
其次字符数组保存的字符串属于字符串变量,可以被修改,而字符指针指向的字符串是属于字符串常量,不能被修改。
#include <stdio.h> int main() { char str[12] = {'h','e','l','l','o',' ','w','o','r','l','d'}; char* ptr = "hello world"; str[0] = 'f'; ptr[0] = 'f'; //将字符指针指向的字符串修改,将出现段错误,因为该内存地址只读,因为该字符串属于常量 return 0; }
运行结果:
段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等等情况。
#include <stdio.h> int main() { char* ptr = "hello world"; ptr[11] = '!'; //往常量字符串末尾添加字符,相当于连接两个字符串,出错 ptr[12] = '\0'; return 0; }
这样也会出现段错误。
问题5:
字符指针是不是和字符数组名都是指针常量呢?
不是,字符指针,可以改变它的指向,也就是可以为左值。可以将一个字符指针指向一个字符串常量后又指向另外一个字符串常量。字符指针在为初始化之前,他是一个未定义的值,将指向任何可能的地方,所以在使用字符指针时一定要注意初始化。
#include <stdio.h> int main() { char* ptr; printf("ptr: %c\n",*ptr); return 0; }
运行结果:
编译可以通过,但是ptr指向的内存地址是任意的
当然也可以将一个字符指针指向一个字符数组。
#include <stdio.h> int main() { char str[12] = {'h','e','l','l','o',' ','w','o','r','l','d'}; char* ptr = str; printf("ptr: %s\n",ptr); return 0; }
运行结果:
问题6:
如果一个字符指针指向一个堆中动态内存。那么我们如何初始化该内存中的值?
#include <stdio.h> #include <stdlib.h> int main() { char* str = (char*)malloc(sizeof(char)*20); printf("str:%p\n",str); //str在堆中内存首地址 str = "hello world"; printf("str:%p\n",str); //str在静态存储区内存首地址 char* ptr = "I love you"; printf("ptr:%p\n",ptr); //str在静态存储区内存首地址 return 0; }
运行结果如下:
很明显前后的地址不一样,前一个指向堆中的内存的地址,而后一个指向了静态存储区中的内存的地址。我本以为我通过上述方式进行初始化str的时候,我可以将堆中内存进行初始化,我发现我错了,字符指针将被重新指向。但是我想如果我们将str声明为const呢?那将会出现什么样的结果呢?
#include <stdio.h> #include <stdlib.h> int main() { const char* str = (char*)malloc(sizeof(char)*20); //我以为const将修饰str,使得str不能再作为左值而出现,我错了,const修饰的是由str指向的字符串, printf("str:%p\n",str); //该字符串不能再被修改 str = "hello world"; printf("str:%p\n",str); return 0; }
运行结果:
如果我们将上述的代码做如下修改,程序还能编译通过吗?
#include <stdio.h> #include <stdlib.h> int main() { const char* str = (char*)malloc(sizeof(char)*20); printf("str:%p\n",str); str[0] = 'h'; //这种赋值才是对str原来所指向的堆中的内存的赋值,编译不过,因为str指向的堆中的内存是只读的。 str = "hello world"; printf("str:%p\n",str); return 0; }
运行结果如下:
上述就引发了我思考为什么const修饰的是str指向的字符串,而不是str本身呢?
原来,char* const str才是真正的修饰str本身,使得str为只读的,不能再被修改,也就是重新指向其他内存地址。而const char* str 和 char const* str 的意义是一致的,都是修饰的是*str。
#include <stdio.h> #include <stdlib.h> int main() { char* const str = (char*)malloc(sizeof(char)*20); printf("str:%p\n",str); str[0] = 'h'; str = "hello world"; //变量str被重新指向,error,编译不过 printf("str:%p\n",str); return 0; }
运行结果如下:
那么我们又该如何初始化一个字符指针指向的内存呢?
其实我们可以很清楚的分析,如果str是一个字符指针,也就是一个变量,变量是可以被重新赋值的,而每一个字符串本身是有一个地址的,str = “hello world”,必然就是改变了str的值,使得str保存着"hello world"的内存首地址,而一旦你将str = "I love you",必然str将保存这"I love you"的内存首地址,这都是毋庸置疑的。
#include <stdio.h> int main() { printf("hello world:%p\n","hello world"); //将打印出"hello world"的内存地址 printf("I love you:%p\n","I love you"); //将打印出"I love you"的内存地址 return 0; }
运行如下:
下面我得回到这样一个问题?
str 和 *str的区别,很明显,str是一个指针,而*str是str指向的内存的存放的值,既然我们想改变str指向内存中的值,既*str的值,那么为何不将*str作为左值,来初始化str所指向内存的值呢?而*str 不就是 str[0]吗?所以很明显了。上述问题:那么我们又该如何初始化一个字符指针指向的内存呢?当然这个仅限于字符指针指向的是由mlloc分配的堆中的内存以及字符数组指向的栈中的内存。而字符指针如果指向字符常量,不可修改字符指针指向的内存的值,因为字符常量是只读的。
#include <stdio.h> #include <stdlib.h> int main() { char* const str = (char*)malloc(sizeof(char)*20); printf("str:%p\n",str); str[0] = 'A'; //数组下标型赋值 *(str + 1) = 'l'; //指针++型赋值 str[2] = 'e', *(str + 3) = 'x'; printf("str:%s\n",str); printf("str:%p\n",str); return 0; }
运行如下:
#include <stdio.h> #include <stdlib.h> int main() { char str[20]; printf("str:%p\n",str); str[0] = 'A'; *(str + 1) = 'l'; str[2] = 'e', *(str + 3) = 'x'; //str[4] = '\0'; //当我们没有手动给字符串结尾附上'\0' printf("str:%s\n",str); printf("str:%p\n",str); return 0; }
运行如下:
上述的运行结果说明上述程序并没有自动给字符串结尾附上'\0',对于字符数组除非这样赋值,str[20] = "hello world",不然你就得手动给字符串附上字符串结尾'\0'。
#include <stdio.h> #include <stdlib.h> int main() { char str[20]; printf("str:%p\n",str); str[0] = 'A'; *(str + 1) = 'l'; str[2] = 'e', *(str + 3) = 'x'; str[4] = '\0'; printf("str:%s\n",str); printf("str:%p\n",str); return 0; } ~
运行结果:
附上一段我关于memcpy()和memmove()的代码的实现。
#include <stdio.h> #include <stdlib.h> void mem_copy(char* const dest, const char* const src) { for(int i = 0; *(src + i) != '\0'; i++) *(dest + i) = *(src + i); } void mem_move(char* const dest, const char* const src) { int i = 0; while(*(src + i) != '\0') i++; for(; i != -1; i--) *(dest + i) = *(src + i); } int main() { char* src = "hello world"; char* const dest1 = (char*)malloc(sizeof(char) * 20); char dest2[20]; mem_copy(dest1,src); mem_move(dest2,src); printf("dest1:%s\n",dest1); printf("dest2:%s\n",dest2); return 0; }
当我们需要将内存的中的某一地址段的内容拷贝到内存中另一地址段中,以上是将字符串常量区的内容拷贝到堆中和栈中,我们可以看到我分别使用了malloc和字符数组,当然我们得考虑可能在拷贝的过程中会有地址段重叠的问题,重叠该怎么拷贝,解决方案很简单,就是从尾向前拷贝,即可。
运行结果如下:
以上是我对c语言中字符串操作的一些理解
上一篇: c语言字符数组与字符串的使用详解