C语言基础 (四)指针与数组
程序员文章站
2024-02-17 15:34:34
...
数据保存在内存中,而每一块内存空间都有一个编号,称为内存地址。
指针变量用来存放这个地址编号的变量称为指针变量;
通过指针可以访问和处理指针所指向的对象,增加了访问数据的手段,使程序更加灵活。
指针本身也是一个变量,它存储的是另一个变量的地址。存放变量地址的变量是指针变量。因此,一个指针变量的值就是某个变量的地址。为了表示指针和他所指向的变量之间关系,在程序中用*符号表示指向。
在32位系统中,指针变量都是占4个字节,64位系统占8个字节。指针变量中保存的只是地址编号,不管是什么类型的指针保存的都是地址编号。所以同一系统中什么类型的指针变量都是占相同的字节数。
打印地址编号%d输出的是十进制的地址 %p输出的是16进制的地址。
指针变量的四个特点
1、 它的值就是一个地址
2、 指针变量的值可以改变,存储一个新的地址,即指向别的变量;
3、 指针指向一个特定的类型,即指针本身也是有类型的。
4、 多个指针变量可以指向同一个值。
int a=5;
int *p=&a; //在声明指针时,*放在类型后边,表示变量是指针变量。
*p=100; //p是a的地址,*p就是a
//int*q=100; //q指向地址编号为100 的存储单元,这是一种错误的写法
*p =*p * *p;
*p--; //* ++的优先级一样,结合性是从右到左,所以p会进行++就不再指向a
printf("%d\n",a); //通过变量a访问数据5
printf("%d\n",*p); //指针前加* 表示对指针的解引用,即访问指针指向的变量;
//在声明之外的地方,*表示对指针解引用;
声明指针变量 char *p int *p flaot * p 等等;
野指针
int a=5;
int *p;//没有初始化的指针,叫做野指针,即指针指向不明确的指针
*p=100;//指针的指向不明确,就不能解引用。必须先有地址再赋值;
空指针 int*q=NULL;//NULL表示空 被初始化成null的指针叫空指针,指向内存编号为0的内存
//空指针也不可以解引用
空指针与野指针的区别:
野指针:未被初始化的指针,里面的内容是垃圾地址,它的值是不明确的;
空指针:被初始化的指针,里面的地址是0;
注意不要解引用空指针和野指针
int a=5;
int b=10;
int *p;
p=&a; //p本身的值是a的地址,p指向a
p=&b; //修改p本身的值,存储一个新的地址,即指向新的变量,p指向b
printf("%d",*p); //指针本身也是有类型的。要指向特定类型的变量。
int a=5;
int *p=&a; //在声明指针时,*放在类型后边,表示变量是指针变量。
*p=100; //p是a的地址,*p就是a
//int*q=100; //q指向地址编号为100 的存储单元,这是一种错误的写法
*p =*p * *p;
*p--; //* ++的优先级一样,结合性是从右到左,所以p会进行++就不再指向a
printf("%d\n",a); //通过变量a访问数据5
printf("%d\n",*p); //指针前加* 表示对指针的解引用,即访问指针指向的变量;
//在声明之外的地方,*表示对指针解引用;
int a=5;
int *p; //没有初始化的指针,叫做野指针,即指针指向不明确的指针
*p=100; //指针的指向不明确,就不能解引用。
int *q=NULL;
printf("%d",*q); // 空指针不能解引用
void change(int *x,int *y){ //参数是传一个指针变量。
int temp=0; //通过解引用交换,变量的值;
if (*x < *y) {
temp=*x;
*x=*y;
*y=temp;
}
//通过数组名地址,打印数组元素
void pfarray(){
int a[5];
for (int i=0; i<5; i++) {
scanf("%d",&a[i]);
}
for (int k=0; k<5;k++) {
printf("%d",*(a+k)); a+k就是第k个元素的地址
}
}
void exchange(int *p,int *q){//相当于
int temp=*p; //temp=a;
*p=*q; //a=b;
*q=temp; //b=temp
}
//修改形参不会影响实参 在传值,传指针时,都适用
void changebyad(int *p,int *q){
int *temp=p;
p=q;
q=temp; //在函数中只交换了形参p和q的指向,并没有修改p和q指向的变量
printf("&p=%p &q=%p\n",p,q ); //并不能实现值的交换
}
int *max(int ,int);
#include <stdio.h>
int main(){
int a=5;
int b=10;
int *r=max(a,b);
printf("%d",*r); //虽然也可以返回正确的值,但是不能这么用
}
int *max(int x,int y){//x,y是max函数的形参,只存在函数中,函数执行结束,p和q就会释放掉;
return x>y? &x:&y;//不要返回局部变量的地址,当局部变量的地址释放掉之后,指针就变成了野指针
}
#include <stdio.h>
int main(){
int a=5;
int *p=&a;//p中保存的是a的地址
//指针变量也是一个变量,本身也有地址
int **q=&p; // 保存一维指针地址的指针称为二维指针
//**q是二维指针。
printf("%d\n",a);
printf("%d\n",*p);
printf("%d\n",**q);
//q-->&p 关于指针要记住两个值
//*q-->p 1、指针本身的值
//**q-->*p 2、指针加* 的值
return 0;
}
void exchage (int **x,int **y){//利用二维指针进行地址的交换
int *temp=*x; //temp=p;
*x=*y; //p=q;
*y=temp; //q=temp;
}
指针和数组
int a[5]={1,2,3,4,5};
printf("%p\n",&a[0]); //结果为0x7fff5fbff7b0
printf("%p\n",a); //结果为0x7fff5fbff7b0
//数组名a是数组中首元素的地址,即a【0】的地址
//数组名a是指向数组首元素的指针
int *p=a; // p的值就是数组首元素的地址
//a=&a[1] 这样写是错的,a的值不能变,只能指向数组中的首元素。
printf("%p\n",p);
printf("%p\n",&a[1]);
printf("%p\n",&a[2]);
return 0;
int a[5]={1,2,3,4,5};
int *p=a;
//printf("%p %p\n",&a[1],(p+1));
//首元素地址加1就是第二个元素的地址
//首元素地址加n就是第n+1个元素的地址(下标为n的元素的地址)
// for (int i=0; i<5;i++) {
// printf("%d\t",*(p+i));//p+i就是下标为i的元素的地址
// }
for (int k=0; k<5; k++) {
printf("%d\n",*(p+k));//指针加1并不是地址加1而是偏移一个元素
}
for ( p=a; p<=&a[4]; p++) {
printf("%d\t",*p);
}
printf("\n");
p--;//指针加1,指向后边一个元素,指针减1,指向前边一个元素(就算不是数组也能指回去)
printf("%d",*p);
}
int a[5]={1,2,3,4,5};
int *p=a;
fun1(p);
printf("%d\n",*p);
for (int k=0; k<5; k++) {
printf("%d",*(p+k));
}
}
void fun(int *q){
(*q)++;
}
void fun1(int *q){
q++; //只修改形参,不是影响实参
(*q)++; // 第二个元素的值会加1;
}
//通过使用二维指针修改 指针变量m中的值,改变指针变量m的指向
void fun2(int **x);
void fun(int *x);
int main(){
int a[5]={1,3,5,6,7};
int *m=a;
for (int i=0; i<5; i++) {
fun(m); //使元素的值加1;
fun2(&m); //指针变量m指向的元素一次向后加1
}
for (int k=0;k<5; k++) {
printf("%d",a[k]) ;
}
printf("\n");
}
void fun2(int **x){
(*x)++; //*x就是指针变量m中存放的值即数组a的首地址 然后进行加加操作就指
} //向了数组中的下一个元素
void fun(int *x){
(*x)++; //*x就是*p 指向数组中元素的值,首元素执行 ++ 操作
}
内存分配
局部变量,在函数中或某个程序块中声明的变量叫做局部变量。
全局变量 在所有的函数的外边声明的变量叫做全局变量
int main(){
int a=5; //该变量是局部变量,所在的内存区是在栈上
int *p=&a; //p也是局部变量,也在栈上
const int b=10; //b是常量,在常量区;
//栈上的空间有程序自动分配和释放
void *malloc(size_t size); //不是空,是一个没有类型的指针,在堆区请求一块存储空间,size请求空间大小,malloc返回这个指针指向请求的堆空间,如果请求失败,则返回null;
int main(){
int a=5;
int *p=&a;
int *q=(int *)malloc(4);//在堆区分配4个字节的空间,将该空间的地址返回值赋值给指针q;
//这块空间没有名字,只能通过这块空间的地址去访问
*q=100;
printf("%d\n",*q);//q是栈上的指针保存堆空间的地址
float *pf=(float *)malloc(4);
*pf=5.9;
printf("%f\n",*pf);
free(q);
free(pf);//释放堆空间 空间指正释放一次,不能重复释放。释放掉之后就不要再访问了。
printf("%f\n",*pf);//释放后仍能被打印出来
//每个存储单元都有两个状态,一个占用状态,一个空闲状态
//分配空间就是把这块空间由空闲状态标记为占用状态,释放空间,就是把这块空间由占用状态标记为空闲状态,并没有抹掉这块空间的内容。
//p的值一直没有改变,空间释放后依然指向这块空间,变成野指针了。通过指针还可以继续访问这块空间
return 0;
常量指针,指向常量的指针,指针本身的值可以修改,但指针加* 的值不能修改。
//指针常量 指针本省是个常量,指针本身的值不能改,但指针加*的值可以改。
//const 修饰谁,谁就不能变const int *p修饰的是*则指向的变量的值不能变
//int *const p修饰的是p则p的值不能改变即存的地址不能改变;
//int main(){
// int a=10;
// int b=20;
// const int *p=&a;
// int* const q=&b;
// p=&b;
// // *p=100;
//}
//引用 常量引用
//j的值不可以修改,i的值可以修改。
传值:形参是实参的值的拷贝,形参的改变不会影响实参,单向传递。
传指针:也是传值的一种方式,是通过形参简洁改变实参所指向的变量的值
传应用:形参就是实参,改变形参就是改变实参,双向传递。
局部变量和全局变量重名,全局变量会被隐藏,通过::作用域符号访问该全局变量
引用定义已定义的全局变量 extern
上一篇: C语言学习笔记(四) --指针