指针入门 ******
int i = 123;
//一般计算机中用16进制数来表示一个内存地址
printf("%#x\n",&i);
//int* int类型的指针变量 pointer指针 指针变量只能用来保存内存地址
//用取地址符&i 把变量i的地址取出来 用指针变量pointer 保存了起来
//此时我们可以说 指针pointer指向了 i的地址
int* pointer = &i;
printf("pointer的地址 = %#x\n",pointer);
printf("*pointer的值%d\n",*pointer);
*pointer = 456;//修改pointer的值
printf("i的值是%d\n",i);
system("pause");
由于pointer存的是i的内存地址,多以对pointer的操作就是对变量i的操作*pointer在等号左边就是赋值操作,在等号右边是取值操作
java zhong 两个值的交换操作
![IJUK
6LYQGDJ_2]~D10%X0.png](https://img.haomeiwen.com/i2648920/33ec6258097a0db3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 指针常见错误
- 声明了指针变量后 未初始化直接通过*p 进行赋值操作 运行时会报错
- 未赋值的指针称为野指针
- 指针类型错误 如int* p 指向了double类型的地址, 通过指针进行读取操作时,读取值会出错
- 声明了指针变量后 未初始化直接通过*p 进行赋值操作 运行时会报错
#include<stdio.h>
#include<stdlib.h>
/**
*/
main(){
//野指针 指针使用之前要初始化 赋给它一个自己程序中声明的变量的地址
//指针使用的时候要注意 int类型的指针要指向int类型的内存地址, double类型的指针要指向double类型的地址 ....
//如果乱指会出bug
int i;
double d = 3.1415;
int* pointer = &d;
printf("pointer的值=%#x\n",pointer);
printf("*pointer = %d\n",*pointer);
system("pause");
}
指针的练习
Paste_Image.png- 值传递和引用传递(交换两个数的值)
-
引用传递本质是把地址传递过去
-
所有传递其实本质都是值传递,引用传递其实也是传递一个值,但是这个值是一个内存地址
void swap(int* p, int* p2){ int temp = *p; *p = *p2; *p2 = temp; } main(){ int i = 123; int j = 456; //将i, j的地址传递过去 swap(&i,&j); printf("i = %d, j = %d", i, j); }
-
- 返回多个值
- 把地址作为参数传入函数中,当函数执行完毕时,参数的值就已经被修改了
#include<stdio.h>
#include<stdlib.h>
/**
"*" 的几个含义 int* 声明一个int类型的指针变量
x * y 乘法运算
int* pointer;
*pointer; 取出指针变量pointer 中保存的内存地址对应的内存中的值
*/
function(int* pointer, int* pointer2){
*pointer *= 2;
*pointer2 *=2;
}
main(){
int i =1;
int j = 2;
//char c;
function(&i,&j);
printf("i = %d,j = %d\n",i,j);
system("pause");
}
多级指针
-
int* p; int 类型的一级指针 int** p2; int 类型的二级指针
-
二级指针变量只能保存一级指针变量的地址
-
有几个* 就是几级指针 int*** 三级指针
-
通过int类型三级指针 操作int类型变量的值 ***p
int i = 123; //int类型一级指针 int* p = &i; //int 类型 二级指针 二级指针只能保存一级指针的地址 int** p2 = &p; //int 类型 三级指针 三级指针只能保存二级指针的地址 int*** p3 = &p2; //通过p3 取出 i的值 printf("***p3 = %d\n", ***p3);
-
多级指针案例 取出子函数中临时变量的地址
#include<stdio.h>
#include<stdlib.h>
/**
多级指针 数星星 有几个星就是几级指针 取值通过三级指针拿到最初的变量的值 ***point3
*/
main(){
int i = 123;
//int类型的一级指针
int* pointer = &i;
//int类型的二级指针 二级指针只能保存一级指针的地址
int** pointer2 = &pointer;
//int类型的三级指针
int*** pointer3 = &pointer2;
printf("***pointer3 = %d\n",***pointer3);
system("pause");
}
数组和指针的关系
- 数组占用的内存空间是连续的
- 数组变量保存的是第0个元素地址,也就是首地址
- *(p + 1):指针位移一个单位,一个单位是多少个字节,取决于指针的类型
#include<stdio.h>
#include<stdlib.h>
/**
数组实际上就是一块连续的内存空间
*/
main(){
// char array[] = {'a','b','c','d','\0'};
int array[] = {1,2,3,4};
printf("array[0]的地址%#x\n",&array[0]);
printf("array[1]的地址%#x\n",&array[1]);
printf("array[2]的地址%#x\n",&array[2]);
printf("array[3]的地址%#x\n",&array[3]);
printf("array的地址%#x\n",&array);
//数组变量名的地址实际上是第一个元素的地址
char* pointer = &array;
//int* pointer = &array;
char array2[] = "hello from c"
//c语言当中的char类型的数组就是c语言当中的字符串,现在*pointer存的是数组的首地址,printf("%s\n",pointer2);是字符串的输出,所以从char数组的首地址开始输出字符串
char* pointer2="hello from c";
//printf("%s\n",pointer2);
/*
printf("*(pointer+0)=%c\n",*(pointer+0));
printf("*(pointer+0)=%c\n",*(pointer+1));
printf("*(pointer+0)=%c\n",*(pointer+2));
printf("*(pointer+0)=%c\n",*(pointer+3));
*/
printf("*(pointer+0)=%d\n",*(pointer+0));
printf("*(pointer+1)=%d\n",*(pointer+1));
printf("*(pointer+2)=%d\n",*(pointer+2));
printf("*(pointer+3)=%d\n",*(pointer+3));
system("pause");
}
指针的长度
- 不管变量的类型是什么,它的内存地址的长度一定是相同的
- 类型不同只决定变量占用的内存空间不同
- 32位环境下,内存地址长度都是4个字节,所以指针变量长度只需4个字节即可
- 区分指针类型是为了指针位移运算方便
#include<stdio.h>
#include<stdlib.h>
/**
32位操作系统地址总线是32位 4个字节的变量来保存32位操作系统的内存地址 1byte 8位 4*8=32
32位操作系统 指针变量占4个字节
64位操作系统 指针变量占8个字节
*/
main(){
int* pointer;
double* pointerD;
printf("int类型的指针变量占%d个字节\n",sizeof(pointer));
printf("double类型的指针变量占%d个字节\n",sizeof(pointerD));
system("pause");
}
#include<stdio.h>
#include<stdlib.h>
/**
main函数获取子函数中临时变量的地址
*/
function(int** pointer){
int i = 123;
*pointer = &i;
printf("i的地址%#x\n",&i);
}
main(){
int* pointer1;
function(&pointer1);
printf("pointer1的值%#x\n",pointer1);
system("pause");
}
Paste_Image.png
堆栈概念 静态内存分配 动态内存分配
- 栈内存
- 系统自动分配
- 系统自动销毁
- 连续的内存区域
- 向低地址扩展
- 大小固定
- 栈上分配的内存称为静态内存
- 静态内存分配
- 子函数执行完,子函数中的所有局部变量都会被销毁,内存释放,但内存地址不可能被销毁,只是地址上的值没了
#include<stdio.h>
#include<stdlib.h>
/**
栈内存 系统统一分配统一回收
静态内存分配 栈内存大小固定的 内存地址是连续的
*/
int* getData(){
int array[] ={1,2,3,4,5};
printf("%#x\n",&array);
return &array;
}
int* getData2(){
int array[] ={5,4,3,2,1};
printf("%#x\n",&array);
return &array;
}
main(){
int* pointer = getData();
getData2();
printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
printf("%#x\n",pointer);
system("pause");
}
- 堆内存
- 程序员手动分配
- java:new
- c:malloc
- 空间不连续
- 大小取决于系统的虚拟内存
- C:程序员手动回收free
- java:自动回收
- 堆上分配的内存称为动态内存
- 程序员手动分配
#include<stdio.h>
#include<stdlib.h>
/**
java new对象就会申请一块堆内存
c malloc memory allocation 内存分配
c的堆内存 程序员手动申请手动释放 malloc
free
申请一块堆内存 动态内存分配
堆内存 不连续的
堆内存大小不固定 取决机器的状态
*/
main(){
//malloc 接收的参数 申请内存大小 返回一个内存地址值 申请到的也是一块连续的内存空间
int* pointer = malloc(sizeof(int)*5);
*(pointer+0) = 1;
*(pointer+1) = 2;
*(pointer+2) = 3;
*(pointer+3) = 4;
*(pointer+4) = 5;
//C for 循环 循环的临时变量i 要先声明再使用
int i;
for(i = 0;i<5;i++){
printf("第%d个元素的值= %d\n",i,*(pointer+i));
}
free(pointer);
printf("第一个元素的值%d\n",*(pointer+0));
system("pause");
}
#学生管理系统#
#include<stdio.h>
#include<stdlib.h>
/**
保存班级人数
申请一块堆内存保存学生的学号
来了几个插班生
扩展一下堆内存
保存插班生的学号
realloc re-
*/
main(){
printf("请输入班级的人数:");
int count;
scanf("%d",&count);
//申请一块堆内存
int* pointer = malloc(sizeof(int)*count);
int i;
for(i = 0;i<count;i++){
printf("请输入第%d个学生的学号:",i+1);
scanf("%d", pointer+i);
}
for(i = 0;i<count;i++){
printf("第%d个学生的学号是:%d\n",i+1,*(pointer+i));
}
printf("请输入插班生的人数:");
//声明一个变量increment用来保存 插班生的人数
int increment;
//接受用户的输入
scanf("%d",&increment);
//重新申请一块足够大的内存
//如果 malloc申请到的内存后面还有足够的空间 realloc会在malloc申请的内存空间后继续申请足够大的内存空间
//如果 malloc申请到的内存后面没有足够的空间 realloc会找到一块足够大的堆内存 并且把 malloc申请到的内存中的值复制过来
pointer = realloc(pointer,sizeof(int)*(count+increment));
for(i = count;i<count+increment;i++){
printf("请输入第%d个学生的学号:",i+1);
scanf("%d", pointer+i);
}
for(i = count;i<count+increment;i++){
printf("第%d个学生的学号是:%d\n",i+1,*(pointer+i));
}
system("pause");
}
结构体
- 结构体中的属性长度会被自动补齐,这是为了方便指针位移运算
- 结构体中不能定义函数,可以定义函数指针
- 程序运行时,函数也是保存在内存中的,也有一个地址
- 结构体中只能定义变量
- 函数指针其实也是变量,它是指针变量
- 函数指针的定义 返回值类型(*变量名)(接收的参数);
- 函数指针的赋值: 函数指针只能指向跟它返回值和接收的参数相同的函数
#include<stdio.h>
#include<stdlib.h>
/**
c结构体 类似java的class struct来声明c的结构体
结构体的大小大于等于结构体中每一变量的占字节数的和
结构体的大小是最大的那个变量所占字节数的整数倍
C结构体中不能定义函数
函数指针的定义 返回值(*函数指针变量名字)(返回值);
-> 间接引用运算符
*/
void study(){
printf("good good study!\n");
}
typedef struct Student{
int age; //8
int score; // 4
char sex; //1
void(*studypointer)();
} stud;
main(){
stud stu = {18,100,'f'};
stu.studypointer = &study;
stu.studypointer();
struct Student* stuPointer = &stu;//结构体指针
printf("*stuPointer.age = %d\n",(*stuPointer).age);
(*stuPointer).sex ='m';
printf("stu.sex = %c\n",stu.sex);
printf("stuPointer->age = %d",stuPointer->age);
//printf("stu.age = %hd\n",stu.age);
//printf("stu.score = %d\n",stu.score);
//printf("stu.sex = %c\n",stu.sex);
// printf("结构体student占%d个字节\n",sizeof(stu));
system("pause");
}
联合体
- 长度等于联合体中定义的变量当中最长的那个
- 联合体只能保存一个变量的值
- 联合体共用同一块内存
#include<stdio.h>
#include<stdlib.h>
/**
联合体占字节数取决于 其中成员 占内存空间最大的那一个
*/
union u{
int num; //4
double d; //8
}
main(){
union u u1;
u1.num = 123;
u1.d = 123.456;
printf("union占%d个字节\n",sizeof(u1));
printf("u1.num = %d\n",u1.num);
system("pause");
}
网友评论