学习目标
1.【掌握】指针与指针之间的运算
2.【掌握】指针与字符串
3.【掌握】指针与中括号
4.【掌握】字符串数组
5.【掌握】字符串函数补充
6.【掌握】const关键字
7.【掌握】内存管理
一、指针与指针之间的运算
减法运算:
如果有两个指针都分别指向同一个数组中的某个元素,我们可以通过指针减法得到这两个元素之间相差多少个元素。注意指针与指针的减法运算,只有运用在数组中才有意义,并且指针之间只有减法没有加法。
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
int main() {
int arr[5] = {10,20,30,40,50};
int *p1 = arr;
int *p2 = &arr[4];
long num = p2 - p1;//返回long类型
// long num1 = p2 + p1; //这样是错误的。。。
printf("num = %ld\n",num);//结果为4,代表这两个指针指向的元素之间相差4个元素
return 0;
}
关系运算:
可以判断两个指针谁在高字节,谁在低字节,或者是否指向同一个变量等等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
int main() {
int arr[5] = {10,20,30,40,50};
int *p1 = arr;
int *p2 = &arr[4];
int result = p1 > p2;
int result1 = p1 < p2;
int result2 = p1 == p2;
int result3 = p1 != p2;
//.......
//打印结果,1为真,0为假
printf("result = %d\nresult1 = %d\nresult2 = %d\nresult3 = %d\n",result,result1,result2,result3);
return 0;
}
二、指针与字符串
内存分区
为了便于系统管理,内存之中主要分为5个区域,存储在不同区域的数据我们系统的管理方式是不一样的。
1.栈空间:所有的局部变量存储在栈空间之中。
2.堆空间:可以由程序员手动申请字节空间来使用。
3.BSS段:存储未被初始化的全局变量、静态变量。全局变量虽然有默认值,但是在初始化之前,这个变量是存储在BSS段的。
4.数据段(常量区):存储已被初始化的全局变量、静态变量、常量数据、字符串常量数据。
5.代码段(代码区):执行一个程序,会将这个程序加载到内存,一个程序中其实就是代码,这个程序的代码存储在代码段区域。
1
2
3
4
5
6
7
8
9
10
11
//存储方式
char chs[] = "jack";//字符串数据是以字符数组的形式存储在字符数组之中,将字符串的每一个字符存储在字符数组之中,并以'\0'作为结束符。
char *str = "jack";//字符串数据以字符指针的形式存储,直接给一个字符指针初始化一个字符串数据。
//当他们都是局部变量的时候
char chs[] = "jack";//会在栈空间之中创建一个长度为5的字符数组,将字符串的每一个数据存储到该元素中,并自动追加'\0'。
char *str = "jack";//str指针是一个局部变量,所以这个指针声明在栈空间中,"jack"这个字符串是以字符数组的形式存储在数据段之中。str指针存储的是数据段之中字符数组的地址。
//当他们都是全局变量的时候
char chs[] = "jack";//这个时候chs字符数组是创建在数据段之中的
char *str = "java";//str指针存储在数据段,"jack"字符串也是存储在数据段
注意:
以字符数组存储的字符串,可以通过下标或者指针去修改存储在字符数组之中的每一个元素的值。
以字符指针的形式存储的字符串,是存储在数据段的,存储在数据段的数据具有恒定性,一旦创建就无法修改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
int main(){
char ch[] = "jack";
char *pp = &ch;//声明一个指针指向字符数组
*pp = 'a';//这样是允许的
// ch = "zhou";//这样是不允许的,ch是数组地址,是常量无法修改的
char *p = "jack";
// *p = 'a';//这样是不允许的
printf("p = %p\n",p);//第一个地址、第二个地址相同
p = "jack";//这里他不会重新创建,因为常量区已经有了,直接返回他的地址给p。地址不会改变
printf("p = %p\n",p);//第二个地址、第一个地址相同
p = "zhou";//这里是在数据段重新创建一个新的"zhou"字符串,让指针p指向它。这里只是修改指针指向,不是修改字符串数据
printf("p = %p\n",p);//第三个地址和第一、第二个地址不同
return 0;
}
三、指针与中括号
所有指针名后面都可以写一个中括号,其中写一个整数。
指针名[n] == *(指针名 + n)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <string.h>
int main(){
char *str = "fafafdfyurfadfaafafgdg";
int count = 0;
unsigned long length = strlen(str);
//第一种写法,*(str + i)
for (int i = 0; i < length; i++) {
if (*(str + i) == 'a') {
count++;
}
}
printf("a一个出现了%d次\n",count);
count = 0;//重置次数
//第二种写法,str[i]
for (int i = 0; i < length; i++) {
if (str[i] == 'a') {
count++;
}
}
printf("a一个出现了%d次\n",count);
return 0;
}
四、字符串数组
使用二维字符数组来存储多个字符串数据,每个字符串的长度必须限制并且不能超过限制长度,灵活性很差。
1
2
3
4
5
6
#include <stdio.h>
int main(){
char name[3][20] = {"zhou","jian","feng"};//灵活性差
return 0;
}
使用字符指针数组来保存多个字符串数据,每一个字符串数据是以字符数组的形式存储在数据段的,字符指针数组的元素存储的是字符串在数据段的字符数组的地址。所以不限制字符串数据的长度,灵活性很好。
1
2
3
4
5
6
#include <stdio.h>
int main(){
char *name[] = {"zhou","jian","feng","fjalfjlajflajfladjflafj"};//灵活性好
return 0;
}
案例:对字字符串进行各种排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdio.h>
#include <string.h>
int main(){
//随便整一堆字符串
char *names[] = {
"hexiaoping",
"zhoujinafeng",
"jack",
"rose",
"canglaoshi",
"laowang",
"tongzhuo"
};
//计算字符指针数组长度
int length = sizeof(names) / sizeof(char *);
//用冒泡排序来按照字符串长度来排序
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - 1 - i; j++) {
//分别计算字符串的长度
int len = sizeof(names[i]);
unsigned long len1 = strlen(names[j]);
unsigned long len2 = strlen(names[j+1]);
if (len1 > len2) {
char *temp = names[j+1];
names[j+1] = names[j];
names[j] = temp;
}
printf("%d ",len);
}
}
//用选择排序来按照字符顺序来排序
for (int i = 0; i < length - 1; i++) {
for (int j = i+1; j < length; j++) {
//比较字符串的大小
int res = strcmp(names[i], names[j]);
if (res > 0) {
char *temp = names[i];
names[i] = names[j];
names[j] = temp;
}
}
}
//遍历字符串,输出结果
for (int i = 0; i < length; i++) {
printf("%s\n",names[i]);
}
return 0;
}
五、字符串函数补充
fputs()函数
作用:可以将字符串输出到指定的流之中。可以输出到标准输出流(控制台),也可以输出到文件中。
语法:fputs(字符串,输出流);
关于输出流:stdout 代表标准输出流,也就是我们的控制台。
fopen()函数可以返回指定的文件的指针。
"w" write 写入数据到这个文件,如果文件不存在则创建再写入,如果已经存在则替换文件。
"r" read 从这个文件之中读取数据
"a" apped 追加,在文件末尾追加数据
fgets()函数
作用:从指定的流之中读取字符串数据。这个流可以是标准输入流(控制台),也可以是文件流。
语法:fgets(用于存储读取出来的字符串的字符数组名,最多读取多少长度的字符串,指定流);
当指定流是标准输入流stdin的时候,第二个参数要和数组的长度保持一致。假如第二个参数为n,如果用户输入的字符串长度大于等于了n,只会存储n-1个字符。如果用户输入的字符串长度等于了n- 1,刚好完美。如果用户输入的字符串长度小于了n-1,那就会多接收个'\n'。
案例:从控制台输入字符串,并存储到指定文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <string.h>
int main(){
//打开文件
FILE *fp = fopen("fputs.txt", "w+");
char str[100];
if (fp != NULL) {
printf("请输入字符串存储到文件中\n");
//从控制台读取99个以内的字符数据并存储到字符数组str中
fgets(str, 100, stdin);
unsigned long len = strlen(str);
if (str[len-1] == '\n') {
str[len-1] = '\0';
}
//从字符数组str从输出字符存储到文件中
fputs(str, fp);
}
fclose(fp);//关闭文件
return 0;
}
|
|