指针的基本概念
指针:就是内存中某个字节地址编号指针变量:用来存放地址变量
定义指针变量格式:指针所指向的数据类型 * 变量名
指针变量 8个字节的存储空间
没有初始化的指针里面放的也是垃圾值,这种没有指向指针我们成为野指针
指针的初始化方式
1、先定义再进行初始化
int *p;
p = &a;
2、定义同时进行初始化
int *pp = &a;
指针注意点
1、指向是什么样类型的指针将来指针存放什么类型的变量的地址2、指针变量不可直接赋值一个整形常量,只能存放变量地址
3、指针没有初始化不可以访问指针指向存储空间,里面是垃圾值,是一个没有指向指针,也就是说是一个野指针;如果你操作一个野指针就回导致系统崩溃
4、指针的指向是可以改变的
5、多个指针可以指向同一变量
定义指针的目的:
1、使用地址代替值拷贝 可以节省内存 2、共享数据
2、指针是用来访问它指向存储空间中的数据的,但是指针变量里面存放的仅仅变量的首地址,如果没有数据类型,访问它所在指向存储空间的时候不知道取多少字节数据3、指针区分类型 是为了当通过指针访问对应的存储空间的时候,取相应字节的数据
指针与数组
void test1(){//数组名不是一个指针
int nums[] = {1,2,3,4,5};
int *p = nums;
//第一个不同点
// sizeof 计算变量所在存储空间的字节数
int numsLength = sizeof(nums);
printf("%d\n",numsLength);
//指针变量占8个字节存储空间
printf("%lu\n",sizeof(p));
//sizeof(nums) != sizeof(p)
//当一个数组赋值给一个指针变量,有些信息就丢失了,这个称为信息遗失,比如数组长度
//第二个不同点
// nums = &nums 数组名的地址就是数组的地址,所以可以使用指针代替数组类型作为函数参数
printf("%p\n",nums);
printf("%p\n",&nums);
// p ! = &p 指针中存放地址与指针本身地址不同
printf("%p\n",p);
printf("%p\n",&p);
//第三个不同点
//数组名是不占用存储空间,当程序编译的时候所有出现数组名的地方都会被替换为数组的地址,所以不可以改变数组名的指向
//指针变量的指向是可以改变的
int num1s[] = {7,9};
// nums = num1s;
p = num1s;
printf("%d\n",p[1]);
}
指针运算
void test(){
//指针加法
int nums[] = {1,2,3,4,5};
int *p = nums;
// printf("%p\n",p);
// printf("%p\n",p+1);
//
// char *cp = nums;
// printf("%p\n",cp+1);
//
// double *cd = nums;
// printf("%p\n",cd+1);
指针 + 1 是加指针变量所指向的数据类型所占的字节数
printf("%p\n",p);
printf("%p\n",p-1);
char *cp = nums;
printf("%p\n",cp-1);
double *cd = nums;
printf("%p\n",cd-1);
指针 - 1 是减去指针所指向的数据类型所占字节数
/*
指针可以+整形常量
指针可以-整形常量
*/
// p + cp; //两个指针不可以相加
long len = &nums[4] - p;
printf("%ld\n",len);
//两个地址之间有多个这种类型元素
char names[] = "abc";
long length = &names[3] - names;
printf("%ld\n",length);
//指针是受限的无符号的长整形
//指针的运算一般都是针对数组的
}
void test1(){
int nums[] = {1,2,3};
//nums++ == nums = nums + 1;//数组名是不占用存储空间的,它的指向不可以改变
//(&nums[0])++;//地址常量不可以赋值
int *p = nums;
p++;//指针变量中的值是可以改变的
}
指针与字符串
void test(){
//定义字符串变量的第一种方式
char names[] = "ym";
//使用数组定义的字符串是存放栈中的,它里面的字符是可以随便改的
//第二种定义字符串变量的方式
char *name1 = "ym";
//使用指针定义的字符串是存放在常量区的,是不可以修改的
names[0] = 'z';
names[1] = 'j';
printf("%s\n",names);
*name1 = 'z';
printf("%s\n",name1);
}
// char *name = "abc";
// scanf("%s",name);//错误的,因为此时name指向的是常量区
// printf("%s\n",name);
// scanf("%4s",name1);
// %s遇到空格就结束了
// printf("%s\n",name1);
void test2(){
const char *name = "zj";
定义指针变量时候,* 前面加 const 表示指针所指向这个存储区域是只读的
// *name = 'z';
const int a = 10;
// a = 20;
const 放在变量前面说明这个变量是只读的
const int *p = &a;
int b = 12;
// *p = 12;
p = &b;
printf("%d\n",*p);
int * const pp = &b;
// pp = &a;
const 放在 * 后面说明这个指针变量是不可以修改的
}
返回值为指针函数
注意点:不要在函数返回局部变量的指针,因为函数结束时候局部变量就被释放了
char * test(){
char words[] = "today is good day";
使用指针定义的字符,它是指向常量区,常量区的数据在程序启动的就被加载到内存了,直到程序退出才释放
char *words = "today is good day";
return words;
}
int main(int argc, const char * argv[]){
// char *words = test();
// printf("%s\n",words);
int *num = test1();
int b = 10;
num = &b;
printf("%d\n",*num);
//告诉操作这个块存储空间我不用了
free(num);
//指针使用完毕要清空
num = NULL;
//printf("%d\n",*num);
1 只要有一个malloc 就必须有一个对应的free
2 当你没有释放对内存的存储空间不可以改变指针的指向
return 0;
}
函数指针
函数指针:指向函数的指针,称为函数指针
void test(){
//给函数指针进行赋值
//调用函数时候必须是有小括号的
//如果没有小括号是函数的地址
printf("%p\n",test);
//函数名就是函数的地址
// test = &test;
pointer = test;//此处不带小括号
//第一种方式
pointer();
//第二种方式
(*pointer)();
// 1、只要定义指针变量必定是有名字
// 2、既然是指针就要有*号
// 3、若为函数指针就要把它括起来
// 4、把它将要指向的函数的左边拷贝其左边
// 5、把它将要指向的函数的右边拷贝其右边
int (*sumPointer)(int num1,int num2);
sumPointer = sum;
int result = sumPointer(10,20);
printf("result = %d\n",result);
}
总结
指针:就是内存中字节的地址编号
指针变量:用来存放地址变量,我们称为指针变量,只要知道变量的地址,就可以访问对应存储空间
指针的定义格式:
所指向的数据类型 * 指针变量名
指针初始化的方式
1、先定义指针变量,然后在进行初始化
2、定义的同时进行初始化
*作用:1、当用在变量定义的时候,他是一个类型说明符,说明定义的这个变量是一个指针变量
2、在不是变量定义的时候,它是一个操作符,访问指针变量所指向的存储空间
&作用:&它一个操作符,取出变量的地址
当用作操作符的时候 * 与 &是一对反操作
指针的作用:
1、以传递地址代替数据拷贝
2、在不同函数中共享数据
指针的注意点:
1、什么指向什么样类型的指针,只能存放什么样类型的变量地址
2、指针不可以赋值一个整形常量
3、不可以访问没有初始化的指针所指向存储空间,指针没有初始化它里面是一个垃圾值,这个指针是没有指向的,我们成为这样的指针为野指针
4、指针的指向是可以改变的
5、多个指针可以指向同一个变量
指针的使用场景
1、当一个函数中需要访问其他函数中的变量的时候,就需要使用指针
2、当一个函数需要多个返回值的时候就要想到使用指针
指针与函数
1、指针作为函数参数传递它是地址传递
2、局部变量的指针不可以作为函数返回值,因为局部变量在函数结束的时候就被销毁了。
指针与数组
1、数组像一个指针,数组访问其成员的方法与指针访问数组成员的方法相同
我们可以使用访问指针所执行的存储空间的方式区访问数组中元素
int array[] = {1,2,3};
int *p = array;
array[1] = *(array + 1) = p[1] = *(p+1) = *(p++)
2、数组不是一个指针
1、sizeof(array) != sizeof(p),把一个数组赋值一个指针变量,那么有些信息就丢失是了,我们成为信息遗失,比如数组所在存储空间
sizeof(array)数组所占用存储空间的字节数
sizeof(p) 指针变量所占字节数(8字节)
2、array == &array p != &p
数组名就是数组的地址,指针的地址与指针所指向的地址不同
3、数组的指向是不可以改变的,指针指向是可以改变的
指针的运算:
p + n 就相当于加上n个p所指向类型占用的存储空间的字节数
p - n 就相当于减去n个p所指向类型占用的存储空间的字节数
p1 - p2 它指的是p1 到 p2之间可以存放多少个指针所指向数据类型的数据
指针与字符串
1、定义字符串的两种方式
1、使用字符数组,它是存放在栈中,字符串中的字符是可以任意修改的
char name[] = "zbz";
2、使用char * ,它是存储常量区,它是只读的
const char *name = "zbz";
2、const作用
1、在定义普通变量的时候,用在普通变量前,说明这个变量是只读的
2、在定义指针变量的时候,用在*前面,说明指针所指向存储区域是只读的
3、在定义指针变量的时候,用在*后面,说明指针变量的是只读的
3、字符数组
1、使用char类型二维数组,字符串是存放在栈中的
char names[][10] = {"zbz","xmf"};
2、使用char类型的指针数组,这个指针数组中存放是常量字符的地址,此时的字符串是一个常量是不可以修改的
char * names[] = {"zbz","xmf"}
3、指向数组的指针
char (*p)[10] = names;
函数指针:指向函数指针
int sum(int a,int b) { return a + b;}
1、既然是一个变量,那么就有名字
2、若为指针变量就要有一个*
3、若要指向函数就要把它去阔起来
4、把它所要执行的函数的左边拷贝它左边
5、把它所要指针向的函数的右边拷贝到它的右边
int (*sumPointer)(int a,int b)
sumPointer = sum
6、函数的名字就是函数的地址 sum = &sum
sumPointer();//通常使用这种
(*sumPointer)();
c语言中动态分配内存(堆内存)
比如要给一个整形数分配存储空间
int *p = malloc(sizeof(int));
1、使用使用完毕需要自己回收内存
free(p);
2、在这个没有调用free函数之前必须有一个指针指向这块存储区域,否则这块存储区域就不会释放了,内存溢出
3、当是用free释放p所指向存储空间的后,需要把p变量清空 p = NULL;
多级指针
int a = 10;
int *p = &a;
二级指针
int **pp = &p;
*pp == p;
**pp == a;
三级指针
int ***ppp = &pp;
*ppp == pp;
**ppp == p;
***ppp == a