一、指针
1. 指针是什么
1.1指针的概念
在计算机科学中, 指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
1.2 对指针的理解
#include <stdio.h>
int main()
{
int a = 1;//开辟一块地址空间
int *p = &a;//&是取地址符,将a的地址存放在p变量中,p就是指针变量。
return 0;
}
指针就是一个特殊的变量,用来存放内存单元的地址,存放在指针中的值,都会被当做地址处理。
指针的大小在32位平台是4个字节,在64位平台是8个字节。
2. 指针和指针类型
2.1指针类型种类和定义方式
通过之前的学习,我们知道变量有不同类型,如整数型、浮点型等,相对应的,指针也有不同的类型。
当我们需要将某个变量的地址保存在p中时,我们就需要定义指针变量的类型。
char *pchar = NULL;
int *pint = NULL;
short *pshort = NULL;
long *plong = NULL;
float *pfloat = NULL;
double *pdouble = NULL;
指针的定义方式就是 :type + * 。其实哪种类型的指针就是为了存放哪种类型变量的地址。
2.2指针类型的意义
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如:
char*
的
指针解引用就只能访问一个字节,而
int*
的指针的解引用就能访问四个字节。
3. 野指针
3.1野指针的概念
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量 在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。
3.2野指针的成因与规避
成因:(1)指针未初始化
#include <stdio.h>
int main()
{
int *p;//指针未初始化
*p = 20;
return 0;
}
(2)指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++) {
//当指针指向的范围超出数组的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
(3)指针指向的空间释放
规避:(1)注意指针的初始化
如果有确切的值就给它初始化为相应的值,如果没有,则初始化为NULL。
(2)小心指针越界
编译器不报错,需注意。
(3)指针指向空间释放则将其置为NULL
(4)避免返回局部变量的地址
(5)指针使用之前检查其有效性
4. 指针运算
4.1指针+-整数
*vp++意思是先*vp;再vp++也就是说是对指针地址的++
而(*vp)++意思是对解引用的内容进行++
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];){
*vp++ = 0;
}
4.2指针-指针
int my_strlen(char *s) {
char *p = s;
while(*p != '\0' )
p++;
return p-s;
}
指针减去指针得到的绝对值是指针和指针之间元素的个数,不是所有指针都能相减,指向同一块空间的指针才能相减。
5. 指针和数组
数组名表示的是数组首元素的地址。
我们可以直接通过指针来访问数组
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int *p = arr; //指针存放数组首元素的地址
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i<sz; i++){
printf("%d ", *(p + i));
}
return 0;
}
6. 二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
这就是
二级指针
。
int main(){
int a = 10;
int *pa = &a;
int **ppa = &pa;
return 0;
}
对于二级指针的运算有:
*ppa
通过对
ppa
中的地址进行解引用,这样找到的是
pa
,
*ppa
其实访问的就是
pa
**ppa
先通过
*ppa
找到
pa
,
然后对
pa
进行解引用操作:
*pa
,那找到的就是
a
.
7. 指针数组
指针数组指的是存放指针的数组
int* arr3[5];
arr3就是一个指针数组,有五个元素,每个元素都是一个整形指针
二、结构体
1.结构体类型的声明
1.1结构的基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
1.2结构的声明
struct tag
{
member-list; //中间可以有多个、多种类型
}variable-list;
1.3结构成员的类型
结构的成员可以是标量、数组、指针,甚至是其他结构体。
2.结构体变量的定义和初始化
2.1结构体变量的定义
struct Point {
int x;
int y;
}p1 //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
2.2初始化
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu //类型声明
{char name[15];//名字
int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
3.结构体成员访问
结构体变量访问成员结构变量的成员是通过点操作符(.)访问的 。
struct Stu
{
char name[20];
int age;
};
struct Stu s;
strcpy(s.name, "zhangsan");//使用.访问name成员
s.age = 20;//使用.访问age成员
而结构体变量指针用点操作符(->) 访问。
struct Stu
{
char name[20];
int age;
};
void print(struct Stu* ps) {
printf("name = %s age = %d\n", (*ps).name, (*ps).age);
//使用结构体指针访问指向对象的成员
printf("name = %s age = %d\n", ps->name, ps->age);
}
4.结构体传参
struct S {
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s) {
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps) {
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}