8.1 指针
8.1.1 取地址运算:&取得变量的地址
- sizeof是一个运算符,给出某个类型或变量在内存中所占据的字节数;
- scanf(“%d”, &i)里的&是运算符,作用是获得变量的地址,它的操作数必须是变量;
- 地址的大小是否相同取决于编译器,在64位系统和32位系统时不同;
- &不能对没有地址的东西取地址。
8.1.2 指针:指针变量就是记录变量地址的变量
指针
- 就是保存地址的变量;
- int i;
- int* p;
- int* p,q;
- int *p,q;
指针变量
- 变量的值是内存的地址;
- 普通变量的值是实际的值;
- 指针变量的值是具有实际值的变量的地址。
作为参数的指针
- void f(int *p);
- 在被调用时得到了某个变量的地址;
- int i=0;f(&i);
- 在函数里面可以通过这个指针访问外面的这个i;
访问那个地址上的变量*
- *是一个单目运算符,用来访问指针的值所表示的地址上的变量;
- 可以做右值也可以做左值;
- int k=*p;
- *p=k+1;
传入地址
- 为什么int i;scanf(“%d”, i);编译没有报错?
- 因为整数和地址一样大,scanf将整数当成了地址。
8.1.3 指针与数组:为什么数组传进函数后的sizeof不对了
传入函数的数组成了什么?
- 函数参数表中的数组实际上是指针;
- sizeof(a)==sizeof(int*);
- 但是可以用数组的运算符[]来计算;
数组参数
- 以下四种函数原型等价的;
- int sum(int &ar,int n);
- int sum(int *,int);
- int sum(int ar[],int n);
- int sum(int [],int n);
数组变量是特殊的指针
- 数组变量本身表达地址,因此;
- int a[10];int *p=a//无需用&取地址;
- 但是数组的但愿表达的是变量,需要用&取地址;
- a==&[0];
- []运算符可以对数组做也可以对指针做;
- p[0]<==>a[0];
- *运算符可以对指针做也可以对数组做;
- *a=25;
- 数组变量是const的指针,所以不能被赋值;
- int a[]<==>int * const a;
8.2 字符类型
8.2.1 字符类型
字符类型
- char是一种整数同时也是一种特殊的类型:字符;
- 用单引号表示字符的字面量,如’a’、‘1’;
- "也是一个字符;
- printf和scanf里用%c来输入输出字符;
代码如下
#include
int main()
{
char c;
char d;
c=1;
d='1';
if(c==d){
printf("相等\n");
}else{
printf("不相等\n");
}
printf("c=%d\n", c);
printf("d=%d\n", d);
return 0;
}
输出
不相等
c=1
d=49
字符的输入输出
- 如何输入’1’这个字符给char c?
- scanf(“%c”, &c);——>1
- scanf(“%d”, &i);c=i;——>49
代码如下
#include
int main()
{
char c;
scanf("%c", &c);
printf("c=%d\n", c);
printf("c='%c'\n", c);
return 0;
}
输出
1
c=49
c='1'
当代码改成这样
#include
int main()
{
int i;
char c;
scanf("%d", &i);
c=i;
printf("c=%d\n", c);
printf("c='%c'\n", c);
return 0;
}
输出
1
c=1
c=''
49
c=49
c='1'
- ‘1’的ASCLL编码是49,所以当c==49时,它代表’1’。
- printf(“%d %c”,c,c);
- 一个49各自表述。
混合输入
- scanf(“%d %c”,&i,&c);和scanf(“%d%c”,&i,&c);有何不同?
代码如下
#include
int main()
{
int i;
char c;
scanf("%d %c", &i, &c);
printf("i=%d, c=%d, c='%c'\n", i,c,c);
return 0;
}
输出
12 1
i=12, c=49, c='1'
12a
i=12, c=97, c='a'
12 a
i=12, c=97, c='a'
当代码是下面时
#include
int main()
{
int i;
char c;
scanf("%d%c", &i, &c);
printf("i=%d, c=%d, c='%c'\n", i,c,c);
return 0;
}
输出
12 1
i=12, c=32, c=' '
12a
i=12, c=97, c='a'
12 a
i=12, c=32, c=' '
字符计算
- 一个字符加一个数字得到ASCLL码表中那个数之后的字符;
- 两个字符的减,得到它们表中的距离;
大小写转换
- 字母在ASCLL表中是顺序排列的;
- 大写字母和小写字母是分开排列的,并不在一起;
- ‘a’-'A’可以得到两段的距离,于是
- a+‘a’-'A’可以把一个大写字母变成小写字母,而a+‘A’-'a’可以把一个小写字母变成大写字母。
8.2.2 逃逸字符
- 用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠“\”开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符;

不同的shell会对控制字符做出不同的反应
8.3 字符串
8.3.1字符数组
字符数组
- char word[]={‘H’,‘e’,‘l’,‘l’,‘o’,‘!’};
- 这不是C的字符串,因为不能用字符串的方式做计算;
字符串
- char word[]={‘H’,‘e’,‘l’,‘l’,‘o’,‘!’,‘\0’};
- 以0(整数0)结尾的一段字符;
- 0或’\0’是一样的,但是和’0’不同;
- 0标志字符串的结束,但它不是字符串的一部分;
- 计算字符串长度时候不包含这个0;
- 字符串以数组的形式存在,以数组或指针的形式访问,更多是以指针的形式;
- string.h里有很多处理字符串的函数;
字符串变量
- char *str=‘Hello’;
- char word[]=‘Hello’;
- char line[10]=‘Hello’;
字符串常量
- “Hello”;
- "Hello"会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0;
- 两个相邻的字符串常量会被自动连接起来;
字符串
- C语言的字符串是以字符数组的形态存在的;
- 不能用运算符对字符数组做运算;
- 通过数组的方式可以遍历字符串;
- 唯一特殊的地方是字符串字面量可以用来初始化字符数组;
- 以及标准库提供了一系列字符串函数;
8.3.2 字符串变量
#include
int main()
{
int i=0;
char *s="Hello World";
char *s2="Hello World";
printf("i=%p\n", &i);
printf("s=%p\n",s);
printf("s2=%p\n",s2);
return 0;
}
输出
i=000000000062FE0C
s=0000000000404000
s2=0000000000404000
字符串常量
- char* s=“Hello World”;
- s是一个指针,初始化为指向一个字符串常量;
- 由于这个常量所在的地方,实际上s是const char* s,但是由于历史的原因,编译器接受不带const的写法;
- 但是试图对s所指的字符串做写入会导致严重的后果;
- 如果需要修改字符串,应该用数组:char s[]=“Hello World”;
#include
int main()
{
int i=0;
char *s="Hello World";
char *s2="Hello World";
char s3[]="Hello World";
printf("i=%p\n", &i);
printf("s=%p\n",s);
printf("s2=%p\n",s2);
s3[0]='B';
printf("Here!s3[0]=%c\n", s3[0]);
return 0;
}
输出
i=000000000062FE0C
s=0000000000404000
s2=0000000000404000
Here!s3[0]=B
指针还是数组?
- char *str=“Hello”;
- char word[]=“Hello”;
- 数组:这个字符串在这里,作为本地变量空间自动被回收;
- 指针:这个字符串不知道在哪里;处理参数;动态分配空间;
- 如果要构造一个字符串——>数组;
- 如果要处理一个字符串——>指针;
char是字符串?*
- 字符串可以表达为char*的形式;
- char*不一定是字符串;
- 本意是指向字符的指针,可能指向的是字符的数组(就像int*一样);
- 只有它所指的字符数组有结尾的0,才能说它所指的是字符串;
8.4 字符串计算
8.4.1 字符串输入输出
字符串赋值?
- char* t=“title”;
- char* s;
- s=t;
- 并没有产生新的字符串,只是让指针s指向了t所指的字符串,对s的任何操作就是对t做的;
字符串输入输出
- char string[8];
- scanf(“%s”,string);
- printf(“%s”,string);
- scanf读入一个单词(到空格、tab或回车为止);
- scanf是不安全的,因为不知道要读入的内容的长度;
安全的输入
- char string[8];
- scanf(“%7s”,string);
- 在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小一;
常见错误
- char *string;
- scanf(“%s”,string);
- 以为char*是字符串类型,定义了一个字符串类型的变量就可以使用了;
- 由于没有对string初始化为0,所以不一定每一次运行都出错;
空字符串
- char buffer[100]=“”;
- 这是一个空的字符串,buffer[0]==‘0’;
- char buffer[]=“”;
- 这个数组的长度只有1!
8.4.2 字符串函数
在string.h中有许多处理字符串的函数
strlen
- size_t strlen(const char *s);
- 返回s的字符串长度(不包括结尾的0);
strcmp
- int strcmp(const char *s1,const char *s2);
- 比较两个字符串,返回
- 0:s1==s2;
- 1:s1>s2;
- -1:s1
strcpy
- char *strcpy(char *restrict dst,const char *restrict src);
- 把src的字符串拷贝到dst;
- restrict表明src和dst不重叠(C99);
- 返回dst;
- 为了能链起代码来;
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);
- int strncmp(const char *s1,const char *s2,size_t n);
字符串中找字符
- char * strchr(const char *s,int c);
- char * strrchr(const char *s,int c);
- 返回NULL表示没有找到;