指针与字符串
size of
一个运算符,给出某个类型或变量在内存中所占据的字节数
size of(int)
size of(i)
运算符&
scanf("%d", &i) 里的&
获得变量的地址,操作数必须是变量
地址的大小是否与int相同,取决于编译器
int i;
printf("%p", &i);
常用的几种格式符:
%o输出八进制,%d输出十进制,%x输出十六进制,%f输出浮点型数据,%c输出单个字符,%s输出字符串,%l输出长整型
%p输出十六进制,用于输出变量地址
&只能对变量取地址
指针
就是保存地址的变量
指针变量
变量的值是内存地址
普通变量的值是实际的值
指针变量的值是具有具体的值的变量的地址
作为参数的指针
void(int *p)
在被调用时获得了某个变量的地址
在函数里面可以通过这个指针访问外面这个变量
#include<stdio.h>
void f(int *p);
int main(){
int i;
printf("&i=%p\n", &i);
f(&i);
return 0;
}
void f(int *p){
printf("p=%p\n", p);
}
访问地址上的变量 用*
*是一个单目运算符,用来访问指针的值所表示的地址上的变量;
可以作右值,也可以作左值
int k = *p
*p = k+1
#include<stdio.h>
void f(int *p);
void g(int k);
int main(){
int i=3;
printf("&i=%p\n", &i);
f(&i);
g(i);
return 0;
}
void f(int *p){ //获取i的地址
printf("p=%p\n", p); // p保存的是i的地址
printf("*p=%d\n", *p); // *p 用i的地址访问i
*p = 10; //对i做修改
}
void g(int k){
printf("k=%d\n", k);
}
指针与数组
传入函数的数组,
函数参数表中的数组实际上就是指针;
sizeof(a) == sizeof(int *)
但是可以用数组运算符[]进行计算
这也是为什么不能在函数里用sizeof(a)/sizeof(a[0]) 来计算数学组长度
字符类型
char
可以看作是一个整数,也可以看作是一个字符
用单引号表示字符自变量,'a', '1'
'' 也表示一个字符
printf 和 scanf 里用 %C 来输入输出字符
字符的输入输出
#include<stdio.h>
int main()
{
char c;
char b;
c = 1; // 整数1
b = '1'; // 字符1
if(c==b){
printf("相等\n");
}
else{
printf("不相等\n");
}
printf("c = %d\n", c); // 输出结果1
printf("b = %d\n", b); // 输出结果49。 计算机内部字符1对应的数值是49
return 0;
}
如何输入'1' 这个字符给 char C?
scanf("%c", &c); // 输入1
scanf("%d", &i); c=i; //输入49
#include<stdio.h>
int main()
{
char c;
scanf("%c",&c); // 以字符形式读入,输入1
printf("c = '%c'\n", c); // 输出结果字符1
printf("c = %d\n", c); // 输出结果数值49
return 0;
}
#include<stdio.h>
int main()
{
int i;
char c;
scanf("%d",&i); // 以整数形式读入,输入1
c = i;
printf("c = %d\n", c); // 输出结果整数1
printf("c = '%c'\n", c); // 输出结果' '
return 0;
}
--------
#include<stdio.h>
int main()
{
int i;
char c;
scanf("%d",&i); // 以整数形式读入,输入49
c = i;
printf("c = %d\n", c); // 输出结果整数49
printf("c = '%c'\n", c); // 输出结果字符'1'
return 0;
}
1的ASCII编码值是49,所以当c == 49时,它代表字符'1'
混合输入
scanf("%d %c", %i, &c);
scanf("%d%c", &i, &c);
有什么不同?
#include<stdio.h>
int main()
{
int i;
char c;
scanf("%d %c",&i, &c); // 中间有空格,前面的整数会把后面的字符前面的空格读完
printf("i = %d, c = %d, c = '%c'\n", i, c, c);
return 0;
}
#include<stdio.h>
int main()
{
int i;
char c;
scanf("%d%c",&i, &c); // 中间没有空格,前面的整数只读到整数结束为止,后面的输入(包括空格)留给%c读入
printf("i = %d, c = %d, c = '%c'\n", i, c, c);
return 0;
}
字符计算
#include<stdio.h>
int main()
{
char c = 'A';
c++;
printf("%c\n", c);
return 0;
}
// 得到B
#include<stdio.h>
int main()
{
char c = 'A';
c+=2;
printf("%c\n", c);
return 0;
}
// 得到C
#include<stdio.h>
int main()
{
int i = 'Z' - 'A';
printf("%d\n", i);
return 0;
}
// 得到25
一个字符如果加一个数字,得到ASCII码表中那个数之后的字符;
两个字符减,得到他们在表中的距离。
在ASCII码表中,
数字0-9从小到大排序;
字母按顺序排列,大写字母和小写字母分开排列
大小写转换:
'a' - 'A' 可以得到两段之间的距离,
于是A + 'a' - 'A' 可以把一个大写字母变成小写字母,
#include<stdio.h>
int main()
{
char t = 'A';
int i = 'a' - 'A';
t = t + i;
printf("%c\n", t);
return 0;
}
而a + 'A' - 'a' 可以把一个小写字母变成大写字母
#include<stdio.h>
int main()
{
char t = 'a';
int i = 'A' - 'a';
t = t + i;
printf("%c\n", t);
return 0;
}
逃逸字符
\b 回退一格
打印\b的作用并不是将之前的字符从屏幕上清除,而是将光标前移一个位置,而仅仅将光标前移并不能将此前的字符清除,除非输出一个空格将其覆盖
\t 到下一制表位
\n 换行
\r 回车
\" 双引号
\' 单引号
\\ 反斜杠本身
字符串
以0(整数0)结尾的一串字符;
0或'/0'是一样的,不是'0';
0标志字符串的结束,不是字符串的一部分,计算字符串长度的时候不包括0;
字符串以数组的形式存在,以数组或指针的形式访问,更多的是以指针的形式;
处理字符串的函数放在string.h
字符串变量
char *str = "Hello";
char world[] = "Hello";
char line[10] = "Hello";
字符串常量
"Hello" 会被编译器变成字符串数组放在某处,这个数组的长度是6,结尾还有表示结束的0
两个相邻的字符串常量会被自动连接起来
字符串
C语言的字符串是以字符数组的形式存在的
不能用运算符对字符串进行计算
通过数组的方式可以遍历字符串
唯一特殊的地方在字符串字面量可以用来初始化字符数组
标准库提供了一系列字符串函数
字符串常量
char* s = "Hello world";
s 是一个指针,指向字符串常量,这个字符串常量的位置位于程序的代码段,只能读不能写
实际上是const char* s,由于历史的原因,编译器接受不带const的写法
试图对s所指的字符串做写入,会导致程序崩溃
想要对字符串做修改,应该用数组定义字符串:
char s[] = "Hello world";
用指针还是数组?
char* s = "Hello world";
char s[] = "Hello world";
数组:
表示字符串在这里,知道在哪里
作为本地变量,空间被自动回收
指针:
不知道在哪里
只对字符串读,不做修改
作为函数的参数
动态分配空间
构造一个字符串,用数组;
处理一个字符串,用指针
char* 不一定是字符串:
字符串可以表示为char*,但是char* 不一定是字符串;
本意是指向字符的指针,可能指向的是字符的数组;
只有当它所指的字符数组的结尾有结束符0,才能说指的是字符串
字符串赋值
char* t = "title";
char* s;
s = t;
并没有产生新的字符串,而是让s也指向t所指的字符串,对s做的任何操作就是对t做的
字符串输入输出
char string[8];
scanf("%s", string);
printf("%s", string);
scanf 读入一个单词,以空格/tab/回车结束
这样的scanf是不安全的,不知道要读入的内容的长度
scanf("%ns",string); // n 表示最多读入的长度
常见错误:
①char* string;
scanf("%s", string);
以为char* 是字符串类型,定义了一个字符串类型的变量string就可以使用了。要初始化才能使用。
②空字符串
char buffer[100] = ''"
是一个空字符串,buffer[0] == "\0"
char* buffer[] = "";
这个数组的长度只有1,只有"\0", 放不下任何字符串
字符串函数
字符串函数存放在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 < s2
字符串大小的比较是以ASCII 码表上的顺序来决定,此顺序亦为字符的值。strcmp()首先将s1 第一个字符值减去s2 第一个字符值,若差值为0 则再继续比较下个字符,若差值不为0 则将差值返回。例如字符串"Ac"和"ba"比较则会返回字符"A"(65)和'b'(98)的差值(-33)。
返回值:若参数s1 和s2 字符串相同则返回0。s1 若大于s2 则返回大于0 的值。s1 若小于s2 则返回小于0 的值。
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必须有足够的空间
一般使用安全版本:
char* strncpy(char* restrict dst, const char* restrict src, size_t n); // 最多拷贝n个字符
char* strncat(char* restrict s1, const char* restrict s2, size_t n); // 最多连接n个字符
int strncmp(const char* s1, const char* s2, size_t n); //只比较前面n个字符
字符串中找字符:
char* strchr(const char* s, int c );
char* strrchr(const char* s, int c);