目录
一.指针
1.指针变量
定义:数据在内存中的地址称为指针;存放地址的变量是指针变量,用来指向另一个变量。
格式为:数据类型 *指针变量名;
也可以在定义指针变量时对其初始化:数据类型 *指针变量名=值; 如
int *p=&a
注:该值前面必须要加取地址符&。
2.引用指针变量
- 赋值,如p=&a;
- 引用值,如printf("%o" , p); 作用是以八进制数形式输出p的值;
- 引用指向的变量,如p=&a; printf("%d" , *p); 输出的是a的值。
3.指针变量的运算
指针变量保存的是地址,而地址本质上是一个整数,所以指针变量可以进行部分运算如++、--、+、- 、比较运算。
#include <stdio.h>
int main()
{
int a = 10, *pa = &a, *paa = &a;
double b = 99.9, *pb = &b;
char c = '@', *pc = &c;
printf("&a=%#X, &b=%#X, &c=%#X\n", &a, &b, &c);
printf("pa=%#X, pb=%#X, pc=%#X\n", pa, pb, pc);//最初的值
pa++; pb++; pc++;
printf("pa=%#X, pb=%#X, pc=%#X\n", pa, pb, pc);//加法运算
pa -= 2; pb -= 2; pc -= 2;
printf("pa=%#X, pb=%#X, pc=%#X\n", pa, pb, pc);//减法运算
if(pa == paa)
{
printf("%d\n", *paa);
}
else{
printf("%d\n", *pa);
} //比较运算
return 0;
}
结果为
可以看出,pa、pb、pc 每次加 1,它们的地址分别增加 4、8、1,正好是 int、double、char 类型的长度;减 2 时,地址分别减少 8、16、2,正好是 int、double、char 类型长度的 2 倍。
总结:
- 指针的每一次递增,它其实会指向下一个元素的存储单元。
- 指针的每一次递减,它都会指向前一个元素的存储单元。
- 指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。
4.指针数组和数组指针
a.指针数组
数组中的所有元素都是连续排列的,整个数组占用的是一块内存,定义数组时所给出的数组名可以认为是一个指针,它指向数组的第0个元素。我们把第0个元素的地址成为数组的首地址。
例,以指针的方式遍历数组元素:
#include <stdio.h>
int main(){
int arr[] = { 99, 15, 100, 888, 250 };
int len = sizeof(arr) / sizeof(int); //求数组长度
int i;
for(i=0; i<len; i++){
printf("%d", *(arr+i) ); //*(arr+i)等价于arr[i]
//arr表示数组首地址,arr+i指向数组的第i个元素,*(arr+i)表示取第i个元素的数据
}
printf("\n");
return 0;
}
结果为
99 15 100 888 250
b.数组指针
数组指针,即指向数组的指针。
例:
int arr[] = { 99, 15, 100, 888, 252 };
int *p = arr;
注:arr本身被转换成了一个指针,可以直接赋值给指针变量p,int *p = arr; 也可以写作 int *p=&arr[0];
区分:
*p++:等价于*(p++),表示先取得第n个元素的值,再将p指向下一个元素;
*++p:等价于*(++p),会先进行++p运算,使得p的值增加,指向下一个元素,整体上相当于 *(p+1),会获得第n+1个数组元素的值;
(*p) ++:先取得第n个元素的值,再对该元素的值加1。
c.指针与数组辨析
数组名称和指针变量的唯一区别是,不能改变数组名称指向的地址,即数组名称可视为一个指向数组首元素地址的指针常量。
也就是说数组名指针是定死在数组首元素地址的,其指向不能被改变。比如数组名不允许自加A++
,因为这会它是一个不可改变的指针常量,而一般指针允许自加p++
;还有常量不能被赋值,即若有数组名 A
,指针p
,则A = p
是非法的。
5.字符串指针
除了字符数组,C语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如:
char *str = "c.program";
或者:
char *str;
str = "c.program";
输出字符串:
#include <stdio.h>
#include <string.h>
int main(){
char *str = "c.program";
int len = strlen(str), i;
printf("%s\n", str); //直接输出字符串
for(i=0; i<len; i++){
printf("%c", *(str+i));
}
printf("\n"); //使用*(str+i),该形式的字符串称为字符串常量
for(i=0; i<len; i++){
printf("%c", str[i]);
}
printf("\n"); //使用str[i]
return 0;
}
结果均为 c.program
注: 字符串常量只能读取,不能写入。
6.指向指针的指针
指向指针的指针变量必须声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:int **var; 第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
二.结构体
定义:用来存放不同类型数据的数据类型。
struct 结构体名
{
结构体所包含的变量或数组
};
其中,所包含的变量或数组都称为结构体的成员。其定义方式与变量和数组的方式相同,但不能初始化。
1.结构体变量
即,用结构体来定义变量。
例:
struct stu stu1, stu2; //定义了两个变量,都是stu类型,关键字struct不能少
也可以在定义结构体的同时定义结构体变量,将变量放在结构体定义的最后即可。
例:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //小组
float score; //成绩
} stu1, stu2;
2.成员的获取和赋值
获取结构体成员的一般格式为: 结构体变量名. 成员名;
也可以在后面加=赋值,如stu1.name="Tom"; (对成员进行逐一赋值)
赋值:除逐一赋值外,还可以在定义结构体时整体赋值,如:
struct{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1, stu2 = { "Tom", 01, 19, 'A', 135.6 };
3.结构体数组
即数组中的每个元素都是一个结构体。
定义结构体数组,例:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
}class[5];
在定义的同时也可以初始化:
struct {
char *name;
int no;
char *gender;
int sage;
}class[] = {
{"Li Ping",5,"男",18},
{"Zhang Ping",4,"男",19},
{"He Fang",1,"女",18},
{"Chen Ling",2,"女",19},
{"Li He",3,"男",20}
}; //对数组中全部元素赋值时,可以不给出数组长度
4.结构体指针
当一个指针变量指向结构体时,称为结构体指针。
定义形式:
struct 结构体名 *变量名
例:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1 = { "Tom", 12, 18, 'A', 136.5 };//结构体
struct stu *pstu = &stu1;//结构体指针
也可以定义结构体的同时定义结构体指针:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1 = { "Tom", 12, 18, 'A', 136.5 }, *pstu = &stu1;
获取结构体成员
通过结构体指针可以获取结构体成员,形式为:
(*pointer).memberName
或
pointer->memberName
例:
//读取结构体成员的值
printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", (*pstu).name, (*pstu).num, (*pstu).age, (*pstu).group, (*pstu).score);
printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", pstu->name, pstu->num, pstu->age, pstu->group, pstu->score);