学习目标
1.【掌握】字符串常用函数
2.【掌握】指针变量的声明
3.【掌握】指针变量的初始化
4.【掌握】函数与指针
5.【掌握】指针的数据类型
6.【掌握】多级指针
7.【掌握】指针与数组
一、字符串常用函数
puts和gets函数的声明在stdio.h头文件中,strcmp、strlen、strcpy、strcat函数的声明在string.h头文件中。
puts()函数
语法:puts(存储字符串数据的数组名);
输出一个字符串数据,并自动换行。不过只能输出字符串,并且不能使用格式控制符。
1 2 3 | #include <stdio.h> char name [ ] = "jack" ; puts ( name ) ; //输出name字符数组里的字符串数据 |
gets()函数
语法:gets(存储字符串数据的数组名);
从控制台接收一个字符串数据,并存储在制定的字符数组之中。并且能读取到空格,scanf不能读取。不过gets只能接收字符串。无论是gets函数还是scanf函数在接收字符串的时候都是不安全的。如果输入字符串长度不够的时候,就会出问题。
1 2 3 | #include <stdio.h> char name [ 20 ] ; gets ( name ) ; //接收20个字符内的字符串数据存储到name里 |
strcmp()函数
语法:strcmp(字符串1,字符串2);//返回int类型
根据每个字符对应的ASCII码依次比较两个字符串的每个字符的大小,如果比较到两个字符不同,就返回结果。如果返回正数,说明第一个字符串大,返回负数,说明第二个大。返回0,说明每个字符的ASCII相同。
1 2 3 4 5 | #include <string.h> char name [ ] = "afdaf" ; char name1 [ ] = "adfdg" ; int cmp = strcmp ( name , name1 ) ; //比较两个字符串的大小 printf ( "cmp = %d\n" , cmp ) ; //打印len = 5 |
strlen()函数
语法:strlen(字符串);
计算字符串的长度,一个一个字符计数,直到遇到'\0'为止。返回值是unsigned long。
1 2 3 4 | #include <string.h> char name [ ] = "afdaf" ; unsigned long len = strlen ( name ) ; printf ( "len = %d\n" , len ) ; //打印len = 5 |
strcpy()函数
语法:strcpy(字符串1,字符串2);
将字符串2拷贝到字符串1里,并且覆盖掉字符串1里的数据。如果字符串1放不下字符串2,就会保错。
1 2 3 4 5 | #include <string.h> char name [ ] = "afdaf" ; char name1 [ ] = "adfdg" ; strcpy ( name , name1 ) ; //会覆盖掉name puts ( name ) ; //输出 adfdg |
strcat()函数
语法:strcat(字符串1,字符串2);
将字符串2中的数据追加到字符串1后面,不会覆盖,只会拼接。如果字符串1放不下字符串2,就会保错。
1 2 3 4 5 | #include <string.h> char name [ 20 ] = "afdaf" ; char name1 [ ] = "adfdg" ; strcat ( name , name1 ) ; //不会覆盖掉name,只是name1连接到name屁股后面 puts ( name ) ; //输出 afdafadfdg |
二、指针变量的声明
我们声明一个变量,实际是在内存中申请一个指定字节数的字节空间来存储数据,分配变量空间的时候,是从高地址到底地址来分配的。
概念:
1.变量的值:是存储在变量空间里的数据。
2.变量的地址:声明变量时申请到的字节空间中低字节的地址。
3.指针:变量的地址就叫做指针,地址就是指针,指针就是地址,只是换了个说法而已。
4.指针变量:其实也就是一个变量,只不过这个指针变量的值是个地址。
访问变量的两种方式:
1.直接访问:直接使用变量名来访问。
2.间接访问:让一个指针指向这个变量,通过这个指针可以间接的访问这个变量。
指针变量的声明
语法:
声明普通变量:数据类型 变量名;
声明指针变量:数据类型 *指针变量名;
1 2 3 | int * p ; //表示我们声明了一个指针变量,名字叫p,*只是说明这是一个指针变量,而不是普通变量。 int * p1 ; //*的位置可以挨着数据类型,也可以单独空一格 int * p2 ; |
注意:
1 2 3 4 | int * p3 ; //*在这里只是说明这是一个指针变量,而不是普通变量。 int * p4 ; //p4只能存储int类型的变量的地址 float * p5 ; //p5只能存储float类型的变量的地址 double * p6 ; //p6只能存储double类型的变量的地址 |
指针变量名也是一个标识符,也需要符合变量的命名规则和规范。一般情况下,我们变量名都以p开头,这样让别人一眼都能看出是指针变量。
1 2 | int * p1 , p2 , p3 ; //是错误的,这样声明的只有p1才是指针变量,p2,p3是int类型普通变量 int * p1 , * p2 , * p3 ; //这才是正确的。 |
三、指针变量的初始化
指针变量中只能存储和指针变量类型相同的变量的地址,所以不能直接给指针变量赋值一个其他非地址的数据。使用&取地址运算符可以获取变量的地址。
语法:数据类型 *指针变量名 = 相同数据类型的变量的地址;
1 2 3 | int num = 10 ; int * p = & num ; //获取num的地址并赋值给指针变量p int * p1 = p ; //将p中存储的值赋值给新声明的指针变量p1。等价于 int *p1 = # |
通过指针变量访问指针变量指向的普通变量,使用*取值符来取出指针变量指向的普通变量。
1 2 3 4 | int num = 10 ; int * p = & num ; //将num的地址赋值给指针变量p * p = 20 ; //把20赋值给了p指向的num,这里的*p表示取出指针(p)的值(num的地址)的变量(num变量)。 printf ( "num = %d\n" , num ) ; //打印 num = 20 |
注意:
野指针:声明一个指针变量而没有初始化,这个指针变量的值是一个随机内存地址。通过这个指针去为指针指向的空间赋值的时候,就有可能改掉别人的数据,造成崩溃。
空指针:声明一个指针变量的时候不确定指向哪个变量,可以给他赋值NULL或者0(这里0就代表NULL,不是整型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 | #include <stdio.h> //计算最大值、最小值、平均值、高级平均值 void getMaxAndMin ( int arr [ ] , int length , int * max , int * min , int * sum , float * avg , float * avg2 ) ; //改变实参数据 void ziZeng ( int * num ) ; int main ( ) { int arr [ ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 } ; int length = sizeof ( arr ) / sizeof ( int ) ; int max = arr [ 0 ] , min = arr [ 0 ] , sum = 0 ; float avg = 0.0f , avg2 = 0.0f ; //调用函数 getMaxAndMin ( arr , length , & max , & min , & sum , & avg , & avg2 ) ; printf ( "max = %d min = %d sum = %d avg = %.2f avg2 = %.2f\n" , max , min , sum , avg , avg2 ) ; int num = 1 ; ziZeng ( & num ) ; printf ( "num = %d\n" , num ) ; } //计算最大值、最小值、平均值、高级平均值 void getMaxAndMin ( int arr [ ] , int length , int * max , int * min , int * sum , float * avg , float * avg2 ) { for ( int i = 0 ; i < length ; i ++ ) { //最大值 if ( arr [ i ] > * max ) { * max = arr [ i ] ; } //最小值 if ( arr [ i ] < * min ) { * min = arr [ i ] ; } //累加和 * sum += arr [ i ] ; } //平均值 * avg = ( float ) * sum / length ; //去掉一个最大值和一个最小值后的平均值 * avg2 = ( * sum - * max - * min ) / ( length - 2 ) ; } //改变实参数据 void ziZeng ( int * num ) { for ( int i = 0 ; i < 5 ; i ++ ) { ( * num ) ++ ; } } |
五、指针的数据类型
通过指针操作指针指向的变量的时候,只能找到被指向那个变量的低字节的地址,到底要操作几个字节的空间是根据指针的类型来决定的。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> int main ( int argc , const char * argv [ ] ) { char ch1 = 'a' ; char ch2 = 'b' ; char ch3 = 'c' ; char ch4 = 'd' ; int * p1 = & ch4 ; * p1 = 'a' ; printf ( "ch1 = %c\n" , ch1 ) ; //ch1 = 0 printf ( "ch2 = %c\n" , ch2 ) ; //ch2 = 0 printf ( "ch3 = %c\n" , ch3 ) ; //ch3 = 0 printf ( "ch4 = %c\n" , ch4 ) ; //ch4 = a return 0 ; } |
原理:这里指针操作指针指向的变量的时候,指针认为他指向的是int类型的变量,int类型占4个字节的空间,所以操作了4个字节的空间。
六、多级指针
一级指针:如果一个指针变量中存储的是一个普通变量的地址,那么这样的指针就叫做一级指针。
1 2 | int num = 10 ; int * p = & num ; |
二级指针:如果一个指针变量中存储的是一个一级指针变量的地址,那么这样的指针就叫做二级指针。
1 2 3 | int num = 10 ; int * p1 = & num ; int * * p2 = & p1 ; |
三级和多级同理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> int main ( ) { int num = 10 ; int * p1 = & num ; //p1存储num的地址 int * * p2 = & p1 ; //p2存储p1的地址 int * * * p3 = & p2 ; //p3存储p2的地址 * p1 = 100 ; //*p1取出num变量 * * p2 = 200 ; //**p2取出num变量 * * * p3 = 300 ; //***p3也是取出num变量 printf ( "num = %d\n" , num ) ; printf ( "num = %d\n" , * * * p3 ) ; return 0 ; } |
七、指针与数组
指针可以和整数进行加减运算
1 2 3 4 5 6 7 | char ch = 'a' ; char * p = & ch ; p ++ ; //p的值增加了一个char类型字节数 int num = 10 ; int * p1 = & num ; p1 ++ ; //p1的值增加了一个int类型字节数 |
遍历数组大法:
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 55 56 57 58 59 60 61 62 63 64 | #include <stdio.h> int main ( ) { int num [ ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 } ; int length = sizeof ( num ) / sizeof ( int ) ; int * p = num ; //第一种,传统遍历大法 for ( int i = 0 ; i < length ; i ++ ) { printf ( "%d " , num [ i ] ) ; } printf ( "\n" ) ; //第二种,移动地址指向 并取值来遍历 for ( int i = 0 ; i < length ; i ++ ) { printf ( "%d " , * ( p + i ) ) ; } printf ( "\n" ) ; //第三种,因为p == num,移动地址指向 并取值来遍历 for ( int i = 0 ; i < length ; i ++ ) { printf ( "%d " , * ( num + i ) ) ; } printf ( "\n" ) ; //第四种,根据每个元素的地址来获取值 for ( int i = 0 ; i < length ; i ++ ) { printf ( "%d " , * ( & num [ i ] ) ) ; } printf ( "\n" ) ; //第五种,根据每个元素的地址来获取值 for ( int i = 0 ; i < length ; i ++ ) { printf ( "%d " , * ( ( & num [ 0 ] + i ) ) ) ; } printf ( "\n" ) ; //第六种,判断地址大小 for ( ; p <= & num [ length - 1 ] ; p ++ ) { printf ( "%d " , * p ) ; } //重置p的值 p = num ; printf ( "\n" ) ; //第七种,p自增 for ( int i = 0 ; i < length ; i ++ , p ++ ) { printf ( "%d " , * p ) ; } //重置p的值 p = num ; printf ( "\n" ) ; //第八种,p自增 for ( int i = 0 ; i < length ; i ++ ) { printf ( "%d " , * ( p ++ ) ) ; } //重置p的值 p = num ; printf ( "\n" ) ; return 0 ; } |
指针数组:
所谓的指针数组,指的是一个专门用来保存多个相同数据类型指针的数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> int main ( int argc , const char * argv [ ] ) { int num1 = 10 ; int num2 = 20 ; int num3 = 30 ; int * p1 = & num1 ; int * p2 = & num2 ; int * p3 = & num3 ; int * arr [ ] = { p1 , p2 , p3 } ; int * * p4 = arr ; for ( int i = 0 ; i < 3 ; i ++ ) { printf ( "%d\n" , * * ( p4 + i ) ) ; } return 0 ; } |