1、字符串指针
示例;输出字符串数组
1. #include <stdio.h>
2. #include <string.h>
3.
4. int main(){
5. char str[] = "<http://baidu.com>";
6. int len = strlen(str), i;
7. //直接输出字符串
8. printf("%s\\n", str);
9. //每次输出一个字符
10. for(i=0; i<len; i++){
11. printf("%c", str[i]);
12. }
13. printf("\\n");
14. return 0;
15. }
运行结果:
<http://baidu.com>
<http://baidu.com>
字符数组归根结底还是一个数组,上节讲到的关于指针和数组的规则同样也适用于字符数组。更改上面的代码,使用指针的方式来输出字符串:
1. #include <stdio.h>
2. #include <string.h>
3.
4. int main(){
5. char str[] = "<http://baidu.com>";
6. char *pstr = str;
7. int len = strlen(str), i;
8.
9. //使用*(pstr+i)
10. for(i=0; i<len; i++){
11. printf("%c", *(pstr+i));
12. }
13. printf("\\n");
14. //使用pstr[i]
15. for(i=0; i<len; i++){
16. printf("%c", pstr[i]);
17. }
18. printf("\\n");
19. //使用*(str+i)
20. for(i=0; i<len; i++){
21. printf("%c", *(str+i));
22. }
23. printf("\\n");
24.
25. return 0;
26. }
运行结果:
<http://baidu.com>
<http://baidu.com>
<http://baidu.com>
除了字符数组,C 语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如:
char *str = "<http://http>://baidu.com";
或者
char *str;
str = "<http://baidu.com>";
字符串中所有字符在内存中是连续排列的,str指向的是字符串的第0个字符;我们通常将第0个字符的地址称为字符串的首地址。字符串中每个字符的类型都是char,所以str的类型也必须是char*。
示例:如何输出这种字符串:
1. #include <stdio.h>
2. #include <string.h>
3.
4. int main(){
5. char *str = "<http://baidu.com>";
6. int len = strlen(str), i;
7.
8. //直接输出字符串
9. printf("%s\\n", str);
10. //使用*(str+i)
11. for(i=0; i<len; i++){
12. printf("%c", *(str+i));
13. }
14. printf("\\n");
15. //使用str[i]
16. for(i=0; i<len; i++){
17. printf("%c", str[i]);
18. }
19. printf("\\n");
20.
21. return 0;
22. }
运行结果:
<http://baidu.com>
<http://baidu.com>
<http://baidu.com>
这两种方式最根本的区别是在内存中的存储区不一样,字符数组存储在全局数据区或栈区,第二种形式的字符串存储在常量区,全局数据区和栈区的字符串有服务和写入的权限,而常量区的字符串只有读取权限,没有写入权限
内存权限的不同导致的一个明显结果就是,字符数组在定义后可以读取和修改每个字符,而对于第二种形式的字符串,一旦被定义就只能读取不能修改,任何对它的赋值都是错误的
我们将第二种形式的字符串你称为字符串常量,意思很明显,常量只能读取不能写入
示例:
1. #include <stdio.h>
2. int main(){
3. char *str = "Hello World!";
4. str = "I love C!"; //正确
5. str[3] = 'P'; //错误
6.
7. return 0;
8. }
这段代码能够正常编译个链接,但在运行时会出现段错误(Segment fault)或者写入位置错误。
第四行代码是正确的,可以改变指针变量本身的指向;第五行代码是错误的,不能修改字符串中的字符
1.1到底使用字符数组还是字符串常量
在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,那么只能使用字符数组,不能使用字符串常量。
获取用户输入的字符串就是一个典型的写入操作,只能使用字符数组,不能使用字符串常量,请看下面的代码:
1. #include <stdio.h>
2. int main(){
3. char str[30];
4. gets(str);
5. printf("%s\\n", str);
6.
7. return 0;
8. }
运行结果: C C++ Java Python JavaScript C C++ Java Python JavaScript
总结:
c语言中有两种表示字符串的方法,一种是字符数组,另一种是字符串常量,他们在内存中的存储位置不同,使得字符数组可以读取和修改,而字符串常量只能读取不能修改。
2、多变的访问方式
C 语言中的指针使得代码的编写非常灵活,如果指针能够和数组结合,那将会有更多的“花招”,请看下面的代码:
1. #include <stdio.h>
2.
3. int main(){
4. char str[20] = "baidu.com";
5.
6. char *s1 = str;
7. char *s2 = str+2;
8.
9. char c1 = str[4];
10. char c2 = *str;
11. char c3 = *(str+4);
12. char c4 = *str+2;
13. char c5 = (str+1)[5];
14.
15. int num1 = *str+2;
16. long num2 = (long)str;
17. long num3 = (long)(str+2);
18.
19. printf(" s1 = %s\\n", s1);
20. printf(" s2 = %s\\n", s2);
21.
22. printf(" c1 = %c\\n", c1);
23. printf(" c2 = %c\\n", c2);
24. printf(" c3 = %c\\n", c3);
25. printf(" c4 = %c\\n", c4);
26. printf(" c5 = %c\\n", c5);
27.
28. printf("num1 = %d\\n", num1);
29. printf("num2 = %ld\\n", num2);
30. printf("num3 = %ld\\n", num3);
31.
32. return 0;
33. }
运行结果:
s1 = baidu.com
s2 = idu.com
c1 = a
c2 = c
c3 = a
c4 = e
c5 = c
num1 = 101
num2 = 2686736
num3 = 2686738
1)str即是数组名称,也是一个指向字符串的指针;指针可以参加运算,加1相当于数组下标加1
printf()输出字符串时,要求给出一个起始地址,并从这个地址开始输出,直到遇见字符串结束标志\0.s1为字符串str第0个字符的地址,s2为第2个字符的地址,所以printf()的结果分别为
baidu.com
idu.com
2)指针可以参加运算,str+4 表示第 4 个字符的地址,c3 = *(str+4) 表示第 4 个字符,即 'a'。
3)其实,数组元素的访问形式可以看做 address[offset],address 为起始地址,offset 为偏移量:c1 = str[4]表示以地址 str 为起点,向后偏移 4 个字符,为 'a';c5 = (str+1)[5]表示以地址 str+1 为起点,向后偏移 5 个字符,等价于 str[6],为 'c'。
4)字符与整数运算时,先转换为整数(字符对应的 ASCII 码)。num1 与 c4 右边的表达式相同,对于 num1,*str+2 == 'c'+2 == 99+2 == 101,即 num1 的值为 101,对于 c4,101 对应的字符为 ‘e’,所以 c4 的输出值为'e'。
5)num2 和 num3 分别为字符串 str 的首地址和第 2 个元素的地址。
进阶示例;
1. #include <stdio.h>
2. #include <stdlib.h>
3.
4. int main(){
5. char str[20] = {0};
6. int i;
7.
8. for(i=0; i<10; i++){
9. *(str+i) = 97+i; // 97 为字符 a 的 ASCII 码值
10. }
11.
12. printf("%s\\n", str);
13. printf("%s\\n", str+2);
14. printf("%c\\n", str[2]);
15. printf("%c\\n", (str+2)[2]);
16.
17. return 0;
18. }
运行结果:
第 5 行代码用来将字符数组中的所有元素都初始化为\0,这样在循环结束时就无需添加字符串结束标志。
前面三个 printf() 比较容易理解,第四个 printf() 可以参照上面的说明 3),str+2 表示指向第 2 个元素,(str+2)[2] 相当于 *(str+2+2),也就是取得第 4 个元素的值。