[C语言]初学者的一些理解-指针
指针
什么是指针?
简单的来讲:指针就是一个变量,它跟其他类型的变量是一样的概念,只不过它储存的是内存地址(内存单元编号)。
比如:我们的身份证就是一个指针变量,它所存储的是我们的家庭住址。
我们的住址,还有房间的编号,门牌号,它们都是指针常量。
假如有个同学借你的身份证去上网回来,你不在宿舍,他就把身份证夹在了放在书架的书里。然后写了张纸条放在桌子上,就去玩了,你回来看到纸条你就知道身份证放在哪里了。
(纸条就是一个指针变量,它所存储是你身份证的地址。)
这时又来了个同学向你借书,你叫他自己找,在书架上的“第三层第一格里”(这就是一个指针常量)
当理解指针的概念时,就有这样的思想了,指针就是地址,地址就是指针。
但是要搞清楚的是,指针是指针,指针变量是指针变量,刚开始就是这里,
搞糊涂我了,我以为指针就是指针变量,那么指针变量就是地址了。
指针的声明
数据类型 指针操作符(*) 变量
int *p;//声明一个储存int型变量地址的指针
char *cp;//声明一个存储char型变量地址的指针
书上说的指向int型的指针,其实就是存储int型变量地址的指针。
这些都是我片面的理解,错了请喷,但是喷了请告诉我为什么??谢谢!
指针的使用
指针变量的赋值和初始化
为指针变量赋值就是设定指针所指向地址的过程
①
int i = 10;//声明一个int型变量,并初始化为10
int *p;//声明一个指向int型的指针变量
p = &i;//使p指向i,&i是取i的地址。
②
int i = 8;//声明一个int型变量,并初始化为8
int *p = &i;//声明一个指向int型的指针变量,并初始化指向i
注意指针的初始化,当我们不为指针变量初始化时,我们应该为其赋值为NULL == 0
int *p = NULL;//使p指向空。就可以避免造成一些不必要的错误。
指针的一些简单应用
①测试指针类型的字节长度。
#include <stdio.h>
int main(void)
{
int *ip = NULL;
char *cp = NULL;
float *fp = NULL;
double *dp = NULL;
long *lp = NULL;
long long *llp = NULL;
printf("sizeof(int*) = %d\n",sizeof(ip));
printf("sizeof(char*) = %d\n",sizeof(cp));
printf("sizeof(float*) = %d\n",sizeof(fp));
printf("sizeof(double*) = %d\n",sizeof(dp));
printf("sizeof(long *) = %d\n",sizeof(lp));
printf("sizeof(long long *) = %d\n",sizeof(llp));
return 0;
}
可以得出所有类型指针的字节长度都为4个字节。
②指针如何访问和修改所指的变量。
#include <stdio.h>
int main(void)
{
int i = 10;
int *p = &i;
printf("通过*p访问i\n");
printf("i = %d\n",i);//输出i的值
printf("*p = %d\n",*p);//输出p所指地址的内容(就是值)
/* *p是取指针变量所指地址的内容 */
printf("\n通过*p 修改 i的值\n");
*p = 3;
printf("i = %d\n",i);
printf("*p = %d\n",*p);
return 0;
}
可以得出在此代码中 *p = = i i = = *p
③多次赋值指针变量的变化。
#include <stdio.h>
int main(void)
{
int x = 8;
int y = 6;
int *p = NULL;
printf("&x = %p\n",&x);//取x的地址
printf("&y = %p\n",&y);//取y的地址
printf("&p = %p\n",&p);//取p本身的地址
printf("&*p = %p\n",&*p);//取p所指的地址
p = &x;
printf("p = &x\n");
printf("&x = %p\n",&x);
printf("&p = %p\n",&p);
printf("&*p = %p\n",&*p);
p = &y;
printf("p = &y\n");
printf("&y = %p\n",&y);
printf("&p = %p\n",&p);
printf("&*p = %p\n",&*p);
return 0;
}
就像其他类型的变量,每一个赋值就是改变他的值。
const指针
① 指向const的指针变量
声明方式:
int const *p;
const int *p;
就是指向const型变量的指针变量
#include <stdio.h>
int main(void)
{
const int a = 8;
const int b = 10;
const int *p;//其声明方式
p = &a;
printf("*p = %d\n",*p);
p = &b;
printf("*p = %d\n",*p);
//*p = 10;错误不可通过*p修改b的值
return 0;
}
可以重新为p赋值,但是不可以通过指针修改变量的值
② const 型指针变量,声明方式如下:
int *const p;
声明一个const型指针变量,赋值后就不可以,再为其赋值了。如同其他类型的变量一样。
如:
const int i = 10;
i = 8;//这是错误的
#include<stdio.h>
int main(void)
{
int i = 8;
nt j = 10;
int *const p = &i;
printf("i = %d\n",i);
printf("*p = %d\n",*p);
//p = &j;//错误
return 0;
}
③ 指向const的const指针变量,声明方式如下:
const int *const p;
声明一个指向const型变量的const型指针变量。
不可通过指针修改变量的值。
不可在为其赋值。
#include <stdio.h>
int main(void)
{
int i = 6;
const int *const p = &i;
printf("i = %d\n",i);
printf("*p = %d\n",*p);
return 0;
}
int i = 8;
const int *const p;
p = &i;//这是错误的。
*p = 10;//不可通过*p改变其值
指针与函数
指针形参
指针作为形参实现交换两个数。
#include <stdio.h>
void exch(int *x,int *y)
{
int t = 0;
t = *x;
*x = *y;
*y = t;
}
int main(void)
{
int i = 10;
int j = 11;
exch(&i,&j);
printf("i = %d\n",i);
printf("j = %d\n",j);
return 0;
}
/*****************************************
输出结果
i = 11
j = 10
请按任意键继续. . .
******************************************/
成功交换两个数,如果不用指针作为形参可以实现吗?
#include <stdio.h>
void exch(int x,int y)
{
int t = 0;
t = x;
x = y;
y = t;
}
int main(void)
{
int i = 10;
int j = 11;
exch(i,j);
printf("i = %d\n",i);
printf("j = %d\n",j);
return 0;
}
交换失败,这是为什么?(函数传递 是把实参赋值给形参。)
因为这个函数只是把实参的值传递给形参,相等于:
x = i,y = j;
形参和实参不是同一个地址,是互不相干的,都有自己的家(内存地址)
但是指针作为形参时就不一样了,因为实参传递给形参是地址,那函数内部就可以直接访问内存了。
指针型函数
函数是形参可以为指针型,那函数的返回值可以为指针型吗?
答案是可以的,指针型函数可以实现。
其声明方式如下:
数据类型 *函数名(形参列表)
int *a(void);
编写一个函数返回最大数的地址。
#include <stdio.h>
int *p_max(int *p,int *p1)
{
if(*p > *p1)
return p;
else
return p1;
}
int main(void)
{
int x,y;
int *pt;
printf("请输入两个整数:");
scanf("%d %d",&x,&y);
pt = p_max(&x,&y);
printf("*pt = %d\n",*pt);
return 0;
}
函数型指针
函数定义的时候实际上就是定义了一个函数变量,那么是否可以将这个变量的地址赋值给指针呢?当然了,可以使用函数型指针实现。
函数型指针就是指向函数的指针变量。
其声明方式如下:
int f(int x,int y);
int (*p)(int,int);
函数型指针的赋值和调用
#include <stdio.h>
int add(int x,int y)
{
return x + y;
}
int main(void)
{
int t = 0;
int (*p)(int,int);
p = add;
t = (*p)(8,8);
printf("t = %d\n",t);
printf("%d\n",p(8,8));
return 0;
}
赋值
如:
int (*p)(int,int);
p = 函数名;
int (*p)(int,int) = 函数名;
调用有两种形式:
① (*p)(形参列表);
② p(形参列表);
注意:要保证左值和右值一样,如
int add(int x,int y);
int amx(int x);
int (*p)(int,int) = add;
p = amx;//这是错误,因为他们不匹配
void型指针
书上关于void型指针一页都没有,为什么介绍的这么少。
void型指针有什么用?用在哪里?
当你不知道这个指针变量要指向什么类型的地址时,就可以使用void指针了。
#include <stdio.h>
int main(void)
{
void *vp;
int i = 10;
char c = '8';
float f = 9.3;
int *p;
printf("vp = &i\n");
vp = &i;
//printf("*vp = %d\n",*vp);
p = vp;
printf("*p = %d\n",*p);
printf("vp = &c\n");
vp = &c;
p = vp;
printf("*p = %c\n",*p);
printf("vp = &f\n");
vp = &f;
p = vp;
printf("*p = %f\n",*p);
return 0;
}
指针与数组
指向数组元素的指针
指向数组元素的指针变量,其指向的地址属于一个数组中的某一个元素。
如:
#include <stdio.h>
int main(void)
{
int array[10] = {1};
int *p = &array[0];
int *p1 = array[0];
int *p2 = &array;
int *p3 = array;
printf("&array[0] = %p\n",&array[0]);
printf("&*p = %p\n",&*p);
printf("&*p1 = %p\n",&*p1);
printf("&*p2 = %p\n",&*p2);
printf("&*p3 = %p\n",&*p3);
return 0;
}
我想的是这些输出都是相同的,但是为什么&*p1(array[0])不同???
因为 p1 指向的是 array[0]
而array[0] == 1; 所以 p1 = 1; p1所指向的是内存中 1单元。
注意:虽然指针变量可以赋值为整数,但是不用这样做,很容易出现错误。
假如我们随便为指针变量赋值了个整数,而这个地址有程序中变量占用了。
那不是很危险,严重会导致那个程序崩溃。万一那个地址是系统占用的呢?
指针访问数组
数组不仅可以通过下标访问,还可以使用指针变量来操作数组元素。
指针访问数组的两种方式。
#include <stdio.h>
int main(void)
{
int array[10] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
int *p = array;
or(i = 0;i < 10;++i)
printf("%d\t",*(p+i));
printf("\n");
for(i = 0;i < 10;++i)
printf("%d\t",*p++);//把*p++ 看成*(p = p + 1);先使地址自增。
printf("\n");
return 0;
}
数组与指针的关系。
数组和指针在什么情况相等?在数组作为函数形参时.
数组作为形参的三种形式
int fun(int array[10]);
int fun_1(int array[]);
int fun_2(int *array);
举例证明
①
#include <stdio.h>
void fun(int array[100])
{
printf("fun:\n");
printf("sizeof(array) = %d\n",sizeof(array));
}
void fun_1(int array[])
{
printf("fun_1:\n");
printf("sizeof(array) = %d\n",sizeof(array));
}
void fun_2(int *array)
{
printf("fun_2:\n");
printf("sizeof(array) = %d\n",sizeof(array));
}
int main(void)
{
int array[10] = {0};
printf("array = %d\n",sizeof(array));
fun(array);
fun_1(array);
fun_2(array);
return 0;
}
输出结果
array = 40
fun:
sizeof(array) = 4
fun_1:
sizeof(array) = 4
fun_2:
sizeof(array) = 4
在main函数内array的字节长度为40
为什么在这几个函数内部是4呢?
这就证明了 在这种情况下 指针 等效于 数组参数
还有不管你给数组形参多少容量,到编译时都转换为指针
指向二维数组的指针变量
竟然指针可以指向一维数组,那么就一定可以指向二维数组了。
二维数组的地址和指针的关系
#define N 3
int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};
int (*p)[N] = array;
int (*p1)[N][N] = array;
array 和 &array 表示整个二维数组的地址
array[n] 和 &array[n] 表示第n个一维数组的首地址
array[n][n] 表示第n个一维数组的第n个元素
&array[n][n] 表示第n个一维数组的第n个元素的地址
p+n 等效于 array 和 &array
*(p+n)+n 等效于 &array[n][n]
*(*(p+n)+n) 等效于 array[n][n]
*(*p1+n) 等效于 array 和 &array
*(*p1+n)+n 等效于 &array[n][n]
*( *(*p+n)+n ) 等效于 array[n][n]
指针法访问数组
①使用指向数组元素的指针
#include <stdio.h>
#define N 3
int main(void)
{
int i = 0;
int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};
int *p = &array;//p指向array的首地址
for(i = 0;i < N*N;++i)
{
printf("%d\t",*(p+i));
if(i % N == 2)
printf("\n");
}
return 0;
}
②使用一维数组型指针
#include <stdio.h>
#define N 3
int main(void)
{
int i = 0;
int j = 0;
int array[N][N] = {{1,2,3},{3,4,5},{6,7,8}};
int (*p)[N] = array;//指向一维数组的指针,该一维数组的容量N
for(i = 0;i < N;++i)
{
for(j = 0;j < N;++j)
printf("[%p] = %d\t",&array[i][j],array[i][j]);
printf("\n");
}
for(i = 0;i < N;++i)
{
for(j = 0;j < N;++j)
printf("%d\t",*(*(p+i)+j));//输出array[i][j]的值
printf("\n");
}
printf("\n2:\n");
for(p = array;p < array+N;++p)
{
for(j = 0;j < N;++j)
printf("%d\t",(*p)[j]);//输出array[i][j]的值
printf("\n");
}
return 0;
}
③使用二维数组型指针
#include <stdio.h>
#define N 3
int main(void)
{
int i = 0;
int j = 0;
int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};
int (*p)[N][N] = array;
for(i = 0;i < N;++i)
{
for(j = 0;j < N;++j)
printf("[%d] = %d\t",&array[i][j],*( *( *p+i )+j ));
printf("\n");
}
return 0;
}
二维数组作为形参
二维数组的地址也可以用做函数的参数。
使用数组作为形参的关键是能够成功的从实参得到有效数组地址。
在传递二维数组的地址时,可以使用三种不同的方式。
①使用指向数组元素的指针,声明方式如下:
int fun(int *p);
代码:
#include <stdio.h>
#define N 3
void print(int *p)
{
int i = 0;
int j = 0;
for(i = 0;i < N;++i)
{
for(j = 0;j < N;++j)
printf("%d\t",*p++);
printf("\n");
}
}
void reverse(int *p)
{
int i = 0;
int j = 0;
for(i = 0;i < N;++i)
{
for(j = 0;j < N;++j)
printf("%d\t",*(p+j*N+i));
printf("\n");
}
}
int main(void)
{
int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};
printf("输出数组:\n");
print(array);
printf("翻转数组:\n");
reverse(array);
return 0;
}
②使用指向一维数组的指针,声明方式如下:
int fun(int (*p)[]);
#include <stdio.h>
#define N 3
void print(int (*p)[N])
{
int i,j;
for(i = 0;i < N;++i)
{
for(j = 0;j < N;++j)
printf("%d\t",*(*(p+i)+j));
printf("\n");
}
}
void print_1(int (*p)[N])
{
int i,j;
for(i = 0;i < N;++i,++p)
{
for(j = 0; j < N;++j)
printf("%d\t",*(*p+j));//p++等效于 p+i
printf("\n");
}
}
int main(void)
{
int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};
printf("print:\n");
print(array);
printf("print_1:\n");
print(array);
return 0;
}
③使用指向二维数组的指针,声明方式如下:
int fun(int (*p)[n][n]);
#include <stdio.h>
#define N 3
void print(int (*p)[N][N])
{
int i,j;
for(i = 0;i < N;++i)
{
for(j = 0;j < N;++j)
printf("%d\t",*( *(*p+i)+j ));
printf("\n");
}
}
void print_1(int (*p)[N][N])
{
int i,j;
for (i = 0; i < N; ++i)
{
for (j = 0; j < N; ++j)
printf("%d\t", *(**p + i * N + j));//不太理解
printf("\n");
}
}
int main(void)
{
int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};
printf("print:\n");
print(array);
printf("print_1:\n");
print_1(&array);
return 0;
}
指针与字符
字符指针:指向字符的指针,其跟指向int型变量的指针一样的声明。
char c = ‘n’;
int *p = &c;
字符指针访问
这些都跟int型指针一样。
int c = ‘c’;
int *p = &c;
printf(“%c\n”,*p);
指针与字符串
指向字符串的指针。声明方式如下:
①
char str[] = “Hello,world”;
char *p = str;
②
char *pstr = “Hello,world”;
指针访问字符串
例1
#include<stdio.h>
int main(void)
{
char str[] = “Hello,world”;
char *p = str;
printf(“%s\n”,p”);
return 0;
}
例2
#include <stdio.h>
int main(void)
{
char *p = "Hello,world";
printf("%s\n",p);
return 0;
}
访问字符串中的某的元素。
①
#include <stdio.h>
int main(void)
{
char *p = "Hello,world";
int i = 0;
for(i = 0; *p != '\0';++i)
printf("%c",*p++);
return 0;
}
②
#include <stdio.h>
int main(void)
{
char *p = "Hello,world";
int i = 0;
for(i = 0;p[i] != '\0';++i)
printf("%c",p[i]);
printf("\n");
return 0;
}
演示指针字符和字符串的一些特性
/* 字符指针和字符串 */
#include <stdio.h>
int main(void)
{
char * p1 = "Hello, world!";
char * p2 = "Hello, world!";
char * p3 = "Hello, world";
printf("%p\n", p1);//4个字节
printf("%d\n", sizeof(p1));//①
printf("%p\n", p2);//4个字节
printf("%d\n", sizeof(p2));//①
printf("%p\n", "Hello, world!");//①
printf("%d\n", sizeof("Hello, world!"));//这个是14字节
printf("%p\n", p3);//另开辟了一个地址
printf("%d\n", sizeof(p3));//4个字节
return 0;
}
//①处是同一个地址
指针数组:存储地址的数组
指针数组的声明:char *p_array[n];
指针数组的赋值:
char *p_array[3] = {NULL};//都指向空
char *p_array[3] = {“printf”,”scanf”,”hello”};
char *p_array[3] = {NULL};
p_array[0] = “printf”;
p_array[1] = “scanf”;
p_array[2] = “hello”;
试例:
#include <stdio.h>
int main(void)
{
char *p_array[3] = {NULL};
p_array[0] = "printf";
p_array[1] = "scanf";
p_array[2] = "hello";
int i = 0;
for(i = 0;i < 3;++i)
puts(p_array[i]);
return 0;
}
额额,指针难啊!!打算先放一边。