指针就是地址,地址就是指针;
地址就是内存单元的编号;
指针变量是以地址为内容的变量;
1基本类型指针
起步
#include <stdio.h>
int main(){
int *p;
int i=0;
p=&i;
/* 对 p=&i的理解:保存了i的地址,也称p指向i;
p不是i,i也不是p,修改i的值,p不变,修改p的值,i不变;
指针变量指向了一个变量,那么(*指针变量)完全等同于(那个变量);
即在代码中 *p 完全等同于 i ;*/
return 0;
}
野指针问题
#include <stdio.h>
int main (){
int *p;
int *q;
int i=0;
p=&i; //p指向了i;
p=q;//q没有具体指向,是一个野指针,此时p被赋值,也成了野指针;
printf("%d,%q",*p,*q)//当p,q是野指针的时候,*p,*q 操作是不被允许的;
/*因为q在声明之后没有赋值,因此q的值是一个垃圾值
因此,*q此时代表的是以这个垃圾值为地址的一个变量
当前程序大概率并没有读取此变量的权限,程序报错*/
return 0;
}
互换两个变量的值问题
朴素的想法
#include <stdio.h>
void swap(int a,int b){
int t=0;
t=a;
a=b;
b=t;
printf("a=%d,b=%d\n",a,b);
}
int main(){
int a=3,b=5;
swap(a,b);
printf("a=%d,b=%d",a,b);
return 0;
}
不朴素但不行
不行因为是意图更改变量的地址
#include <stdio.h>
void swap(int * p,int * q){
int * t;
t=p;
p=q;
q=t;
}
int main(){
int a=3,b=5;
swap(&a,&b);
printf("a=%d,b=%d",a,b);
return 0;
}
行
#include <stdio.h>
void swap(int * p,int * q){//定义了两个int * 型号的指针变量p,q;
//p接收了a的地址,q接收了a的地址
int t;
t=*p; //*p 读作:p这个地址下的值
*p=*q;//读作:以p的内容为地址的变量=以q的内容为地址的变量的值;
*q=t;
}
int main(){
int a=3,b=5;
swap(&a,&b);//变量a的地址给了p,变量b的地址给了q;
printf("a=%d,b=%d",a,b);
return 0;
}
通过被调函数来更改主函数普通变量的值的一般步骤:
1.将要修改的变量的地址作为实参传递给函数;
2.函数的形参要为指针变量;
3.在函数内以(*变量= xxx)的形式修改变量;
2指针和数组
指针和一维数组
一维数组名是一个指针常量
#include <stdio.h>
int main()
{
int a[5];
int b[5];
//a=b;//error,因为a,b均为常量;
printf("%#x \n",&a[0]);
printf("%#x",a);
return 0;
}
一位数组指针与下标
如果p是一个指针变量,则p[ i ]完全等价于*( p + i);
通过函数来处理数组
#include <stdio.h>
//要通过一个函数来处理一个数组,应当传递数组首元素地址以及数组长度
void f(int * parr,int len){
for(int i= 0;i<len;i++){
printf("%d ",*(parr+i));//这里p为指针变量,此时*(p+i)=p[i]
//上面的*(parr+i)完全可以写做parr[i]
}
printf("\n");
}
int main()
{
int a[5]={1,2,3,4,5};
int b[10]={2,2,2,2,2,2,2,2};
f(a,5);
f(b,10);
return 0;
}
指针变量的运算
指针变量的加乘除没有意义
当两个指针指向同一片内存区域时,相减有意义
指针变量的大小
和系统控制内存的地址线数量有关,数据的地址和数据本身没有任何关系,比如说 int a=10,a这个变量值为10,但是其地址却是一个和计算机处理器位数密切相关的值,如果是32位系统,那么其地址就是由这32根总线组合的某一个32位二进制数字串,八位是一个字节,那么32位就是4个字节,相对应的,64位系统中就是8个字节。
动态内存分配
传统形式定义的数组长度固定,不能扩容,开辟的内存空间不能被手动释放,且不能被跨函数调用(当数组所在函数执行完被销毁后)
动态数组的创建
malloc函数的简单使用和理解
#include <stdio.h>
#include<malloc.h>
int main()
{
int i = 5; //静态分配了4个字节
int * p = (int *) malloc(4);
/*
1.4代表malloc为本程序动态分配了4个字节
2.malloc函数会返回分配的内存空间的首字节的地址
因此需要有一个(int *)来声明这片地址是为了整形而开辟
即当(int *)malloc(100)时,系统会自动以4个字节为一个整体读取这一片数据
3.这行代码一共开辟了4+sizeof(p)个字节,p本身的内存是静态分配
p指向的内存是动态分配
4.此时p指向了malloc函数开辟的前sizeof(int)个字节
*/
*p = 5;//合法,上述代码实现的*p本质上就是一个int变量
free(p);
/*free(p)表示把p指向的内存释放,p本身为静态分配,只能由系统释放*/
printf("Hello, World! \n");
return 0;
}
创建动态的一维数组
动态扩建或者缩小内存: realloc函数;
#include <stdio.h>
#include <malloc.h>
int main()
{
int * parr;
int len;
printf("输入数组的长度:");
scanf("%d",&len);
parr = (int*)malloc(4*len);//类似于int parr[len]
parr[1] = 23;
printf("%d",parr[1]);
return 0;
}
静态内存和动态内存的比较
静态内存由系统创建和释放,在系统的栈里面分配
动态内存可由程序创建和释放,在系统中的堆里分配
3指针和函数
静态变量不能跨函数调用
动态可以
4指针和结构体
如何定义结构体 (3 种方式)
#include <stdio.h>
struct student
{
int age;
double score;
char sex;
};
int main()
{
struct student st1={1,2,'d'};//声明,赋值
printf("%d %lf %c",st1.age,st1.score,st1.sex);
return 0;
}
结构体的赋值和初始化
struct Student
{
int age;
..
}
struct Student st1 = {3,...}//第一种
struct Student st2;//第二种
st2.age = 3;
结构体内部成员的取出
- 用.符号
- 指针变量名->成员名
struct Student st = {.....};
struct Student * pst = &st;
pst->age;
*pst->age 在计算机内部被转化成(pst).age后执行
*所以pst->age等价于(pst).age等价于st.age
pst->age的含义:
pst所指向的那个结构体变量中的age这个成员;
结构体变量和结构体指针作为函数参数传递的问题
#include <stdio.h>
#include <string.h>
struct student
{
int age;
double score;
char name[10];
};
void InputStudent(struct student * pstu){
pstu->age = 12;
pstu->score = 99;
strcpy(pstu->name,"embed");
}
//当函数不需要改变参数的值时,形参可以不用指针,此处s科完全看做st1的拷贝
void OutputStudent(struct student s){
printf("%d %f %c", s.age , s.score , s.name );
}
//当然,用指针更加节省空间
void OutputStudent1(struct student *s){
printf("%d %f %c", (*s).age , (*s).score , (*s).name );
}
int main()
{
struct student st1;
InputStudent( &st1 );
OutputStudent( st1 );
OutputStudent1( &st1 );
return 0;
}
结构体变量的运算
不能加减乘除,只能相互赋值
实现一下冒泡排序
#include <stdio.h>
void sort(int *a, int len)
{
int i,j,t;
for(i = 0;i < len-1;i++)
{
for (j = 0;j < len-1-i; j++)
{
if(a[j]>a[j+1])
{
t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
}
}
int main()
{
int a[6] = {6,5,4,3,2,1};
sort(a,6);
for(int i = 0;i < 6;i++){
printf("%d",a[i]);
}
return 0;
}
结构体的综合运用,学生信息管理
5多级指针
多级指针
#include <stdio.h>
#include <malloc.h>
int main()
{
int i = 10;
int * p = & i; // p是一个int型指针变量,只能指向int类型数据数据
int ** q = & p;// q是一个 int* 型指针变量,只能指向int*类型的数据
int *** r = & q;// r是一个 int** 型指针变量,只能指向int**类型的数据
//***r 等价于 变量i
// r = &p //error,类型不匹配
return 0;
}
如果p是 int* 类型,那么&p是int**类型,注意跨函数调用的形参写法;
#include <stdio.h>
void f (int ** q){
//本函数内 *q == p
}
void g(){
int i=10;
int * p = &i;
}
int main()
{
g();
return 0;
}