C语言学习笔记之指针
2015-10-02 13:25
836 查看
//-------------------------------指针
内存单元的指针和内存单元的内容是两个不同的概念。
内存单元的编号也叫做地址。既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。
(上面的说法貌似不容易理解那么举个例子:地址类似于你的家的住址有个xxx号,小区里面有很多个地址里面不同的住户就是内存单元)
总结:对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。
使用指针好处
a.为函数提供修改调用变量的灵活手段;
b.让函数有多个返回值
c.可以改善某些子程序的效率
>>在数据传递时,如果数据块较大(比如说数据缓冲区或比较大的结构),这时就可以使用指针 传递地址而不是实际数据,即提高传输速度,又节省大量内存。
d.为动态数据结构(如二叉树、链表)提供支持(这里涉及到数据结构)
3、变量的存取方式 存取方式分为两种:直接存取和间接存取
int a = 3; 直接存取:变量的赋值和取值(通过变量名进行存取值)
间接存取:通过指针(地址)间接操作完成。
//------------------------------指针变量
1、指针变量 在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。
因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。
注意: 严格意义上说,指针是一个地址,是一个常量指针变量是存放一个地址,是一个变量。
定义一个指针变量
对指针变量的定义包括三个内容:
1)指针类型说明,即定义变量为一个指针变量;
2)指针变量名;
3)变量值(指针)
其一般形式为: 类型说明符 *变量名;
int *p;
double b = 10;
double *p1 = &b;
*p1 ---> b --->10
注意:
1)在定义指针时,“*”号表示定义的变量是指针变量,变量的值只能存放地址。
2)一个类型的指针只能指向同类型的变量,不能指向其他类型的变量。
3)指针也可以被声明为全局、静态局部和局部的。
#warning 务必记清楚
1) 在定义变量的时候 *是一个类型说明符 说明定义的这个变量是一个指针变量
int * p = &a;
*p
2) 在使用的时候 *是一个操作符, 代表访问指针所指向的存储空间所对应的内容
//-------------------------------初始化和引用
指针变量初始化的方法有两种:
定义的同时进行初始化和先定义后初始化
1)定义的同时进行初始化
int a = 5; int *p = &a;
2)先定义后初始化
int a; int *p; p=&a;
3)把指针初始化为NULL
int *p=NULL;
int *q=0; 不合法的初始化:
1)指针变量不能被赋值一个整数值
(因为我们不知道这个整形常量是内存哪块地址)
2)被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的
注意点
1、多个指针变量可以指向同一个地址 int a = 5;
2、指针的指向是可以改变的
3、指针没有初始化里面是一个垃圾值,这时候我们这是一个野指针, 如果操作一个野指针
1)可能会导致程序崩溃
2)访问你不该访问数据 所以指针必须初始化才可以访问其所指向存储区域
两个有关的运算符:
& :取地址运算符;
* :指针运算符(或称“间接访问” 运算符)。
&*p ---> p
&变量名;
*指针变量名 //获取指针变量指向的存储空间的内容
*指针变量名 //获取指针变量指向的存储空间的内容 如&a表示变量a的地址,&b表示变量b的地址。变量本身必须预先说明。
关于*的使用注意:
1)在定义变量的时候 * 是一个类型说明符,说明定义的这个变量是一个指针变量
int a = 5;
int *p = &a;
int *p1 = p;
2)在不是定义变量的时候 *是一个操作符,访问指针所指向存储空间
int x = *p;
//--------------------函数实现两变量交换
分析:
常规做法不能实现变量值的交换。
指针地址交换也不能实现
指针指向的值交换可以实现。
void swapNum2(int *a,int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
如果采用普通的函数不能进行交换操作,
//----------------------指针常见的应用场景
1)在函数中间接访问调用者中的
2)函数返回多个值
//------------------------二级指针
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针(地址)的指针变量。也称为“二级指针”
int a = 5;
int * p = &a;
int ** p1 = &p;
p---指向a的地址
*p---a的地址对应的值----也就是5
p1--指针变量p的地址
*p1 ---> p的地址所对应的内容 --- a的地址
* *p1 ---->
//-------------------------指针为什么要区分类型
一个指针变量所占用的内存空间是固定的。
虽然所有的指针都只占8个字节,但不同类型的变量却占不同的字节数。
结论:定义什么类型的指针就应该指向什么类型的变量。
//----------------安全的字符串读取和输出的函数
读入字符串的方法:
1) scanf 缺点:不能接收空格
2) gets 优点:可以接受含有空格的字符串 缺点:超出数组长度导致不安全
3) fgets(); 优点:可以帮我们根据数组的长度自动截取适当的长度保存
使用格式:fgets(ch,sizeof(ch),stdin);//stdin 标准输入
输出字符串的方法:
1) printf() %s 缺点:不能自动换行
2) puts() 优点:可以自动换行
3) fputs(); 不可以自动换行
使用格式:fputs(ch,stdout); //stdout 标准输出
//-------------------const修饰符的使用
作用:const作用可以把变量变成只读的(常量)
1)修饰变量
const int a=23;
a = 20; (错误的)
2)修饰指针
const int *p; //指向可变 ,值不能变
int const *p; //同上
int * const p; //指向不可变,值可变
const int* const p;// 指向和值都不能变
技巧:
const 在 * 的左侧 指向可变,值不能变
const 在 * 的右侧 指向不可变 值可变
const 在 * 的两侧 指向和值都不可以变
//解释
//常量 指针 常量1 常量2
const int *p; // 常量指针
指向常量的指针 指向可变,值不可变
int const *p;
//指针 常量 指针变量 p = &a p
= &b
int * const p;//
指针常量 这个指针是个常量 指向不可变,值可变
//-----------------C语言中的内存管理
内存分为5大区域
栈区 -- 存放局部变量,临时变量
堆区 -- 程序运行过程中,动态分配的内存
BSS区 -- 未初始化的全局变量和静态变量
数据段 -- 已经初始化的全局变量和静态变量
代码段 -- 程序编译产生的二进制的数据
//-----------------动态内存管理
#include <stdlib.h>
1) malloc
(void *)malloc(unsigned size) //动态的向内存申请连续的n个字节 返回的是新申请的内存空间的首地址
int *p = (int *)malloc(100); //p是在栈区 malloc(100) 在堆区
malloc不会把新申请的空间初始化
注意:判断一个空间是否申请成功
if(p!=NULL){
//执行语句
}else{
//分配不成功
} //只有申请成功了,才能操作
memset(p,'0',100); //全部初始化为0
2) calloc
int *p = (int *)calloc(5,4); //申请5个长度为4的空间
calloc可以把新申请的空间初始化0
注意:判断一个空间是否申请成功
if(p!=NULL){ //只有申请成功了,才能操作
}else{
//分配不成功
}
3) realloc
重新改变一块内存空间的大小 p = (int *)realloc(p,size);
4) free
释放我们指定的内存空间
free(p); //p是我们新申请的内存单元 free后,p是一个野指针
如果不释放,造成内存的泄露
推理过程 只需要记住 我们使用了malloc或者calloc记的要free释放
/*
1)p(p释放了,空间没释放) ----> 这块动态内存空间的首地址
p是局部的指针变量 p被释放了 这块动态内存空间就放在这里浪费了 没法被使用
2)p(空间先释放,p后释放) ----> 这块动态内存空间的首地址
这块动态内存空间的被free函数释放了,p还没有被释放,这个p就是野指针了
p = NULL
*/
5、指针和函数之间的关系
1)返回值是指针的函数 指针函数
char * getday(){
return "星期一";
}
值传递 会开辟内存空间
地址传递不会开辟内存空间
2)函数指针
指向函数的指针
格式:返回值 (*指针变量名)(函数的参数);
int sum(int a,int b){
return a+b;
}
格式一:int (*p1)(int a,int b); // 函数指针p1 只能指向返回值是int类型,并且有两个int 类型参数的函数。
格式二:int (*p2)(int,int);
技巧:
声明的格式:int sum(int a,int b);
int (*p3)(int a,int b) = sum;
p3 = sum;
注意:
函数指针变量不能进行算术运算
函数调用中(*指针变量名)两边的括号不可省
下面这个程序是可以帮助理解;
#include <stdio.h>
int sum(int x,int y){
return x+y;
}
int jian(int x,int y){
return x-y;
}
int cheng(int x,int y){
return x*y;
}
int chu(int x,int y){
return x/y;
}
int main(int argc, const char *
argv[]) {
//函数指针的使用
//1、定义函数指针
int (*p)(int ,int );
//2、给函数指针初始化
p = sum; //p也指向了sum函数
switch (3) {
case 1:
p = sum;
break;
case 2:
p = jian;
break;
case 3:
p = cheng;
break;
case 4:
p = chu;
break;
default:
break;
}
//3、用函数指针间接的调用函数
// (*p)(23,45);
int s = p(23,45);
printf("%d\n",s);
return 0;
}
内存单元的指针和内存单元的内容是两个不同的概念。
内存单元的编号也叫做地址。既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。
(上面的说法貌似不容易理解那么举个例子:地址类似于你的家的住址有个xxx号,小区里面有很多个地址里面不同的住户就是内存单元)
总结:对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。
使用指针好处
a.为函数提供修改调用变量的灵活手段;
b.让函数有多个返回值
c.可以改善某些子程序的效率
>>在数据传递时,如果数据块较大(比如说数据缓冲区或比较大的结构),这时就可以使用指针 传递地址而不是实际数据,即提高传输速度,又节省大量内存。
d.为动态数据结构(如二叉树、链表)提供支持(这里涉及到数据结构)
3、变量的存取方式 存取方式分为两种:直接存取和间接存取
int a = 3; 直接存取:变量的赋值和取值(通过变量名进行存取值)
间接存取:通过指针(地址)间接操作完成。
//------------------------------指针变量
1、指针变量 在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。
因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。
注意: 严格意义上说,指针是一个地址,是一个常量指针变量是存放一个地址,是一个变量。
定义一个指针变量
对指针变量的定义包括三个内容:
1)指针类型说明,即定义变量为一个指针变量;
2)指针变量名;
3)变量值(指针)
其一般形式为: 类型说明符 *变量名;
int *p;
double b = 10;
double *p1 = &b;
*p1 ---> b --->10
注意:
1)在定义指针时,“*”号表示定义的变量是指针变量,变量的值只能存放地址。
2)一个类型的指针只能指向同类型的变量,不能指向其他类型的变量。
3)指针也可以被声明为全局、静态局部和局部的。
#warning 务必记清楚
1) 在定义变量的时候 *是一个类型说明符 说明定义的这个变量是一个指针变量
int * p = &a;
*p
2) 在使用的时候 *是一个操作符, 代表访问指针所指向的存储空间所对应的内容
//-------------------------------初始化和引用
指针变量初始化的方法有两种:
定义的同时进行初始化和先定义后初始化
1)定义的同时进行初始化
int a = 5; int *p = &a;
2)先定义后初始化
int a; int *p; p=&a;
3)把指针初始化为NULL
int *p=NULL;
int *q=0; 不合法的初始化:
1)指针变量不能被赋值一个整数值
(因为我们不知道这个整形常量是内存哪块地址)
2)被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的
注意点
1、多个指针变量可以指向同一个地址 int a = 5;
2、指针的指向是可以改变的
3、指针没有初始化里面是一个垃圾值,这时候我们这是一个野指针, 如果操作一个野指针
1)可能会导致程序崩溃
2)访问你不该访问数据 所以指针必须初始化才可以访问其所指向存储区域
两个有关的运算符:
& :取地址运算符;
* :指针运算符(或称“间接访问” 运算符)。
&*p ---> p
&变量名;
*指针变量名 //获取指针变量指向的存储空间的内容
*指针变量名 //获取指针变量指向的存储空间的内容 如&a表示变量a的地址,&b表示变量b的地址。变量本身必须预先说明。
关于*的使用注意:
1)在定义变量的时候 * 是一个类型说明符,说明定义的这个变量是一个指针变量
int a = 5;
int *p = &a;
int *p1 = p;
2)在不是定义变量的时候 *是一个操作符,访问指针所指向存储空间
int x = *p;
//--------------------函数实现两变量交换
分析:
常规做法不能实现变量值的交换。
指针地址交换也不能实现
指针指向的值交换可以实现。
void swapNum2(int *a,int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
如果采用普通的函数不能进行交换操作,
//----------------------指针常见的应用场景
1)在函数中间接访问调用者中的
2)函数返回多个值
//------------------------二级指针
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针(地址)的指针变量。也称为“二级指针”
int a = 5;
int * p = &a;
int ** p1 = &p;
p---指向a的地址
*p---a的地址对应的值----也就是5
p1--指针变量p的地址
*p1 ---> p的地址所对应的内容 --- a的地址
* *p1 ---->
//-------------------------指针为什么要区分类型
一个指针变量所占用的内存空间是固定的。
虽然所有的指针都只占8个字节,但不同类型的变量却占不同的字节数。
结论:定义什么类型的指针就应该指向什么类型的变量。
//----------------安全的字符串读取和输出的函数
读入字符串的方法:
1) scanf 缺点:不能接收空格
2) gets 优点:可以接受含有空格的字符串 缺点:超出数组长度导致不安全
3) fgets(); 优点:可以帮我们根据数组的长度自动截取适当的长度保存
使用格式:fgets(ch,sizeof(ch),stdin);//stdin 标准输入
输出字符串的方法:
1) printf() %s 缺点:不能自动换行
2) puts() 优点:可以自动换行
3) fputs(); 不可以自动换行
使用格式:fputs(ch,stdout); //stdout 标准输出
//-------------------const修饰符的使用
作用:const作用可以把变量变成只读的(常量)
1)修饰变量
const int a=23;
a = 20; (错误的)
2)修饰指针
const int *p; //指向可变 ,值不能变
int const *p; //同上
int * const p; //指向不可变,值可变
const int* const p;// 指向和值都不能变
技巧:
const 在 * 的左侧 指向可变,值不能变
const 在 * 的右侧 指向不可变 值可变
const 在 * 的两侧 指向和值都不可以变
//解释
//常量 指针 常量1 常量2
const int *p; // 常量指针
指向常量的指针 指向可变,值不可变
int const *p;
//指针 常量 指针变量 p = &a p
= &b
int * const p;//
指针常量 这个指针是个常量 指向不可变,值可变
//-----------------C语言中的内存管理
内存分为5大区域
栈区 -- 存放局部变量,临时变量
堆区 -- 程序运行过程中,动态分配的内存
BSS区 -- 未初始化的全局变量和静态变量
数据段 -- 已经初始化的全局变量和静态变量
代码段 -- 程序编译产生的二进制的数据
//-----------------动态内存管理
#include <stdlib.h>
1) malloc
(void *)malloc(unsigned size) //动态的向内存申请连续的n个字节 返回的是新申请的内存空间的首地址
int *p = (int *)malloc(100); //p是在栈区 malloc(100) 在堆区
malloc不会把新申请的空间初始化
注意:判断一个空间是否申请成功
if(p!=NULL){
//执行语句
}else{
//分配不成功
} //只有申请成功了,才能操作
memset(p,'0',100); //全部初始化为0
2) calloc
int *p = (int *)calloc(5,4); //申请5个长度为4的空间
calloc可以把新申请的空间初始化0
注意:判断一个空间是否申请成功
if(p!=NULL){ //只有申请成功了,才能操作
}else{
//分配不成功
}
3) realloc
重新改变一块内存空间的大小 p = (int *)realloc(p,size);
4) free
释放我们指定的内存空间
free(p); //p是我们新申请的内存单元 free后,p是一个野指针
如果不释放,造成内存的泄露
推理过程 只需要记住 我们使用了malloc或者calloc记的要free释放
/*
1)p(p释放了,空间没释放) ----> 这块动态内存空间的首地址
p是局部的指针变量 p被释放了 这块动态内存空间就放在这里浪费了 没法被使用
2)p(空间先释放,p后释放) ----> 这块动态内存空间的首地址
这块动态内存空间的被free函数释放了,p还没有被释放,这个p就是野指针了
p = NULL
*/
5、指针和函数之间的关系
1)返回值是指针的函数 指针函数
char * getday(){
return "星期一";
}
值传递 会开辟内存空间
地址传递不会开辟内存空间
2)函数指针
指向函数的指针
格式:返回值 (*指针变量名)(函数的参数);
int sum(int a,int b){
return a+b;
}
格式一:int (*p1)(int a,int b); // 函数指针p1 只能指向返回值是int类型,并且有两个int 类型参数的函数。
格式二:int (*p2)(int,int);
技巧:
声明的格式:int sum(int a,int b);
int (*p3)(int a,int b) = sum;
p3 = sum;
注意:
函数指针变量不能进行算术运算
函数调用中(*指针变量名)两边的括号不可省
下面这个程序是可以帮助理解;
#include <stdio.h>
int sum(int x,int y){
return x+y;
}
int jian(int x,int y){
return x-y;
}
int cheng(int x,int y){
return x*y;
}
int chu(int x,int y){
return x/y;
}
int main(int argc, const char *
argv[]) {
//函数指针的使用
//1、定义函数指针
int (*p)(int ,int );
//2、给函数指针初始化
p = sum; //p也指向了sum函数
switch (3) {
case 1:
p = sum;
break;
case 2:
p = jian;
break;
case 3:
p = cheng;
break;
case 4:
p = chu;
break;
default:
break;
}
//3、用函数指针间接的调用函数
// (*p)(23,45);
int s = p(23,45);
printf("%d\n",s);
return 0;
}
相关文章推荐
- C语言结构体里的成员数组和指针
- 哈夫曼编码的简单实例
- C语言学习笔记 之字符串
- C++容器(五):set类型
- C++容器(五):set类型
- 文章标题
- 一起talk C栗子吧(第五十回:C语言实例--最小生成树二)
- 【一套C语言控制台的输出框代码】
- C++源代码免杀之函数的动态调用
- 复习C++
- C/C++ | sizeof
- c++ primer 学习之路 二 —— 实验楼作业笔记(1-3)
- 项目26.1 成绩等级
- C/C++ 数组负数下标 - 反向寻址
- #LeetCode# #C++# Binary Tree Postorder Traversal
- C++关于引用的说明
- 自己用C语言写PIC16单片机的serial bootloader
- c++中的 堆和栈
- C++ 对象模型具体评论(特别easy理解力)
- 编程语言学习:C++学习日记0 热身