文章目录
Lecture 4 指针与字符串
1. 指针
指针就是保存地址的变量;变量的值是内存的地址
- 普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
单目运算符 *:取值,用来访问指针的值所表示的地址上的变量;可以做右值也可以做左值
int k = *p;
*p = k + 1;
1.1 sizeof
是一个运算符,给出某个类型或变量在内存中所占据的字节数,例如
sizeof(int);
sizeof(i);
1.2 运算符&
获得变量的地址,它的操作数必须是变量;&不能对没有地址的东西取地址
int i;
printf("%p", &i);
地址的字节数是否与int相同取决于编译器(32位架构还是64位架构)
int a[10];
printf("%p %p %p", a, &a[0], &a);
//将输出三个相同的地址
1.3 数组与指针
1.3.1 函数参数表里的数组
函数参数表里的数组实际上是指针,但是可以用数组的运算符[]进行运算。
在子函数中,sizeof(a) == sizeof(int *)
;这就是为什么函数参数表里要用另外一个变量len记录数组长度,而不能直接用sizeof(a) / sizeof(a[0])计算。
下面四种函数原型是等价的:
int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
1.3.2 数组变量是特殊的指针
- 数组变量本身表达地址,所以
int a[10];
int *p = a; //无需用&取地址
;
但是数组的单元表达的是变量,需要用&取地址; - [ ]运算符可以对数组做,也可以对指针做 p[0] a[0];
- *运算符可以对指针做,也可以对数组做
*a = 25;
- 数组变量是类型为const的指针,因此不能被赋值
int a[];
int *const a = …;
2. 字符串
2.1 大小写转换
字母在ASCII表中是顺序排列的,大写字母和小写字母是分开排列的,并不在一起, 'a'-'A'
可以得到两端之间的距离,于是
a + 'a' - 'A'
可以把一个大写字母变成小写字母a + 'A' - 'a'
可以把一个小写字母变成大写字母
2.2 逃逸字符
用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠\开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符,例如:printf("请分别输入身高的英尺和英寸,如输入\"5 7\"表示5英尺7英寸:");
以上语句中,如果不加\,则语句中的双引号的前一个"会与开头的"配对,导致程序出错。
2.3 字符数组与字符串
2.3.1 字符数组
字符数组char word[]={'H', 'e', 'l', 'l', 'o', '!'};
这不是C语言的字符串,因为不能用字符串的方式做计算。
2.3.2 字符串
字符串 char word[]={'H', 'e', 'l', 'l', 'o', '!', '\0'};
char word[] = "Hello!";
- 字符串是指以0(整数0)结尾的一串字符,0或’\0’是一样的,但是和’0’不同;
- 0标志字符串的结束,但它不是字符串的一部分,计算字符串长度时不包含这个0;
- 字符串以数组的形式存在,以数组或指针的形式访问,更多的时候是以指针的形式;
- string.h里有很多处理字符串的函数。
2.4 定义字符串
char *str = "Hello";
char word[] = "Hello";
char line[10] = "Hello";
2.4.1 字符串常量
- "Hello"叫做字符串常量,会被编译器编程一个字符数组在某处,这个数组的长度是6,结尾还有表示结束的0;
- 两个相邻的字符串常量会被自动连接起来。
- C语言的字符串是以字符数组的形态存在的:不能用运算符对字符串做运算;通过数组的方式可以遍历字符串。
- 唯一特殊的地方是字符串常量(即" ")可以用来初始化字符数组;以及标准库提供了一系列字符串函数。
char *s = "Hello, world!";
- s是一个指针,初始化为指向一个字符串常量;
- 由于这个常量所在的地方,实际上s是 const char *s,但是由于历史原因,编译器接受不带const的写法;
- 但是试图对s所指的字符串做写入会导致严重的后果;
- 如果需要修改字符串,应该用数组来定义
char s[] = "Hello, world!";
2.4.2 使用指针还是数组定义字符串?
char *s = "Hello, world!";
char s[] = "Hello, world!";
- 数组:这个字符串在这里,作为本地变量空间自动被回收;
- 指针:这个字符串不知道在哪里;
- 处理参数(函数参数)
- 动态分配空间
- 如果要构造一个字符串
数组 (可对字符串进行修改)
如果要处理一个字符串 指针(无法对字符串进行修改)
2.4.3 char*是字符串?
- 字符串可以表达为char*的形式;
- char*不一定是字符串;
- 本意是指向字符的指针,可能指向的是字符的数组(就像int*一样);
- 只有它所指的字符数组有结尾的0,才能说它所指的是字符串。
- 常见错误:
char *string;
scanf("%s", string);
错以为char *是字符串类型,定义了一个字符串类型的变量string就可以直接使用了;由于没有对string初始化为0,所以不一定每次运行都出错(但不知道在什么时候就会出错)
2.4.4 空字符串
char buffer[100]= "";
这是一个空的字符串,buffer[0] == '\0'
char buffer[] = "";
这个数组长度只有1
3.字符串输入输出
char string[8];
scanf("%s", string);
printf("%s", string);
scanf读入一个单词(到空格、tab或回车为止);scanf是不安全的,因为不知道要读入的内容的长度
安全的输入:scanf("%7s", string);
在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该是数组的大小减1。
4.字符串函数简介
使用字符串函数前首先需要#include <string.h>
-
strlen
size_t strlen(const char *s);
返回s的字符串长度(不包括结尾的0) -
strcmp
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
(比较前n个)
比较两个字符串,返回值0:s1== s2 ; 1(正数):s1>s2 ; -1(负数):s1<s2
-
strcpy
char *strcpy(char *restrict dst, const char *restrict src);
- 把src的字符串拷贝到dst(restrict表明src和dst不重叠,C99)
- 返回dst(为了能链起代码来)
- 把src的字符串拷贝到dst(restrict表明src和dst不重叠,C99)
-
strcat
char *strcat(char *restrict s1, const char *restrict s2);
- 把s2拷贝到s1的后面,接成一个长的字符串
- 返回s1
- s1必须具有足够的空间
strcpy和strcat都可能出现安全问题(目的地没有足够的空间)
安全版本
char *strncpy(char *restrict dst, const char *restrict src, size_t n);
char *strncat(char *restrict s1, const char *restrict s2, size_t n);
-
strchr
char *strchr(const char *s, int c);
从左往右找字符
char *strrchr(const char *s, int c);
从右往左找字符
找到则返回一个指针,该指针指向那个位置;返回NULL(0)表示没有找到 -
strstr
字符串中找字符串
strchr函数使用示例:
#include <stdio.h>
#include <string.h>
int main()
{
char s[]={"ABCDEFG"};
char *p1,*p2;
p1=strchr(s,'B'); //strchr()用来查找某字符在字符串中首次出现的位置
p2=strchr(s,'D');
printf("%p\n",p1); // 'B'首次出现的地址
printf("%p\n",p2); // 'D'首次出现的地址
printf("%s\n",p1); // 打印字符串数组某字符出现之后的字符串,包括该字符
printf("%s\n",p2);
return 0;
}
运行结果:
000000000062FE01
000000000062FE03
BCDEFG
DEFG
strchr更多应用可参考:字符串搜索函数strchr用法简介